@use-aistack/cli 0.14.0 → 0.15.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.
- package/README.md +2 -2
- package/dist/index.js +1219 -303
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../../pricing/src/table.ts","../../pricing/src/index.ts","../src/version.ts","../src/api.ts","../src/commands/collect.ts","../src/classifier.ts","../src/stableKey.ts","../src/config.ts","../src/git.ts","../src/github-repo.ts","../src/hooks.ts","../src/mcp.ts","../src/plugins.ts","../src/scanner.ts","../src/theme.ts","../src/commands/connect.ts","../src/harness/shared/aggregate.ts","../src/harness/shared/bundled-allowlist.ts","../src/harness/shared/allowlist.ts","../src/harness/shared/recency.ts","../../workflow-rules/src/daily.ts","../../workflow-rules/src/reading.ts","../../workflow-rules/src/componentRules.ts","../../workflow-rules/src/metricRules.ts","../../workflow-rules/src/types.ts","../../workflow-rules/src/phaseRules.ts","../../workflow-rules/src/usage.ts","../../workflow-rules/src/workflowRows.ts","../src/harness/shared/window.ts","../src/harness/shared/payload.ts","../src/workflow/reducer.ts","../src/harness/claude/analyzer.ts","../src/harness/claude/scan.ts","../src/harness/claude/adapter.ts","../src/harness/codex/analyzer.ts","../src/harness/codex/scan.ts","../src/harness/codex/adapter.ts","../src/harness/grok/analyzer.ts","../src/harness/grok/scan.ts","../src/harness/grok/adapter.ts","../src/harness/opencode/analyzer.ts","../src/harness/opencode/scan.ts","../src/harness/opencode/adapter.ts","../src/harness/pi/analyzer.ts","../src/harness/pi/scan.ts","../src/harness/pi/adapter.ts","../src/harness/index.ts","../src/commands/create.ts","../src/commands/login.ts","../src/commands/sync.ts","../src/autosync/codexHook.ts","../src/autosync/optin.ts","../src/autosync/grokHook.ts","../src/autosync/hook.ts","../src/autosync/run.ts","../src/sync/stage.ts","../src/usage/days.ts","../src/usage/diff.ts","../src/workflow/git.ts","../src/workflow/extract.ts","../src/sync/grokDateCache.ts","../src/sync/summary.ts","../src/node-version.ts","../src/sync/server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BASE_URL } from \"./api.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { connectCommand } from \"./commands/connect.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { supportsNodeVersion, unsupportedNodeMessage } from \"./node-version.js\";\nimport { runStdioSyncServer } from \"./sync/server.js\";\nimport { CLI_VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n\t.name(\"aistack\")\n\t.description(\"Measure and share your AI stack from your terminal\")\n\t.version(CLI_VERSION);\n\nprogram\n\t.command(\"login\")\n\t.description(\"Authenticate with AI Stack\")\n\t.option(\"--label <label>\", \"Set the machine label\")\n\t.action((options) => loginCommand(options));\n\nprogram\n\t.command(\"collect\")\n\t.description(\"Scan and upload AI config files from your project\")\n\t.option(\"--no-global\", \"Exclude global config files (~/.claude, etc.)\")\n\t.action((options) => collectCommand({ global: options.global ?? true }));\n\nprogram\n\t.command(\"create\")\n\t.description(\"Download and write your stack's AI config files\")\n\t.action(createCommand);\n\nprogram\n\t.command(\"mcp\")\n\t.description(\n\t\t\"Run the aistack MCP server on stdio (sync preview + gated publish)\",\n\t)\n\t.action(() => {\n\t\t// stdout belongs to the protocol. Diagnostics go to stderr only.\n\t\trunStdioSyncServer({\n\t\t\tbaseUrl: BASE_URL,\n\t\t\tlog: (line) => process.stderr.write(`[aistack-mcp] ${line}\\n`),\n\t\t});\n\t});\n\n// The documented default sync surface (#56): terminal-first, TTY gate.\nprogram\n\t.command(\"sync\")\n\t.description(\"Scan, preview, and publish measured usage (rolling 30 days)\")\n\t.option(\n\t\t\"--auto [state]\",\n\t\t\"silent background sync; 'on' asks your stack for the permission and installs the SessionStart hooks, 'off' revokes both\",\n\t)\n\t.option(\n\t\t\"--every <hours>\",\n\t\t\"with --auto on: hours between auto-syncs (default 6)\",\n\t)\n\t.action((options) => syncCommand(options));\n\nprogram\n\t.command(\"connect\")\n\t.description(\"Install the in-session sync surface (MCP server + Skill)\")\n\t.argument(\"<harness>\", 'the harness to connect (\"claude\")')\n\t.action(connectCommand);\n\nif (!supportsNodeVersion(process.versions.node)) {\n\tprocess.stderr.write(`${unsupportedNodeMessage(process.versions.node)}\\n`);\n\tprocess.exitCode = 1;\n} else {\n\tprogram.parse();\n}\n","// The price table as data: dated periods keyed by model id (ADR-0012, #336).\n//\n// One shape serves three places. The Convex `modelPrices` table holds one row\n// per period. The `/api/prices` endpoint serves the same rows to the CLI. And\n// the constants in `index.ts` render themselves into the same rows, so the\n// bundled fallback and the served table go through one lookup.\n//\n// A period runs from its `from` until the next period's `from` for the same\n// (model, provider). There is no `to`. Cache tiers are ABSOLUTE rates per\n// period, never a vendor-level multiplier: models.dev reports them that way,\n// and a multiplier is wrong for at least one Google model.\n//\n// Every period names its `source`. That string is what a surface prints beside\n// a dollar figure, so the citation travels with the rate that produced it.\n\n/** The separator between a provider id and a model id in a pricing key. */\nexport const PROVIDER_SEPARATOR = \":\";\n\n/**\n * The citation for a model that runs on the user's own machine. It is not a\n * vendor list. It is the statement that no per-token charge exists.\n */\nexport const LOCAL_PRICING_TABLE_VERSION = \"local-no-charge\";\n\n/** One dated period, as stored, served and bundled. USD per million tokens. */\nexport type PriceRow = {\n\t/** The vendor's bare API id (the catalog slug). May carry `#fast`. */\n\tmodelSlug: string;\n\t/** Unset is the vendor's own rate. Set names a gateway with its own rate. */\n\tprovider?: string;\n\t/** Inclusive start, epoch ms. `0` means since the model existed. */\n\tfrom: number;\n\tinput: number;\n\toutput: number;\n\tcacheRead?: number;\n\tcacheWrite5m?: number;\n\tcacheWrite1h?: number;\n\t/** The table or dataset that priced this period. Printed beside every dollar. */\n\tsource: string;\n\t/**\n\t * Who sets the vendor rate. Wire-only: a served row carries the catalog's\n\t * provider so a `google:` key can reach a Google model's bare rate. Not a\n\t * column of `modelPrices`.\n\t */\n\tvendor?: Vendor;\n};\n\n/** A whole table, with the id the CLI prints when it says which table it used. */\nexport type PriceTable = {\n\tid: string;\n\trows: PriceRow[];\n};\n\n/**\n * Who sets the rate. A provider only reaches a vendor's rows when it IS that\n * vendor; a gateway re-serving the same model is a different price.\n */\nexport type Vendor = \"anthropic\" | \"openai\" | \"google\" | \"xai\" | \"local\";\n\n/** USD per million tokens, valid over `[from, to)`, with its citation. */\nexport type PricePeriod = {\n\t/** Inclusive lower bound, epoch ms. `null` = since the model existed. */\n\tfrom: number | null;\n\t/** Exclusive upper bound, epoch ms. `null` = still in effect. */\n\tto: number | null;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\tsource: string;\n};\n\n/**\n * Providers that ARE the vendor, so their rows price at that vendor's list.\n *\n * Deliberately absent, and each absence is a decision from ticket #122's\n * measurements: `opencode` (the opencode-zen gateway), `github-copilot`\n * (re-serves other vendors' slugs at its own terms), `openrouter`, `azure`,\n * `bedrock` and `vercel-ai-gateway` (resellers). A provider joins this map\n * when a harness is measured emitting it, never speculatively.\n */\nexport const PROVIDER_VENDOR: Record<string, Vendor> = {\n\tanthropic: \"anthropic\",\n\topenai: \"openai\",\n\tgoogle: \"google\",\n\txai: \"xai\",\n};\n\n/**\n * Explicit pricing equivalents that do not collapse catalog identity.\n *\n * Grok Build records `grok-4.6-build` for usage while identifying the selected\n * model as `grok-4.6`. The Build id remains the measured and catalog id. It may\n * borrow the base model's cited rate only when no exact rate exists.\n */\nexport const PRICING_ALIASES: Readonly<Record<string, string>> = {\n\t\"grok-4.6-build\": \"grok-4.6\",\n};\n\n/**\n * Providers that run the model on this machine. No API call, no per-token\n * charge. Ids as opencode and pi-mono spell them.\n */\nexport const LOCAL_PROVIDERS = new Set([\n\t\"ollama\",\n\t\"lmstudio\",\n\t\"llama.cpp\",\n\t\"llamacpp\",\n\t\"local\",\n]);\n\nconst FREE_PERIOD: PricePeriod = {\n\tfrom: null,\n\tto: null,\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite5m: 0,\n\tcacheWrite1h: 0,\n\tsource: LOCAL_PRICING_TABLE_VERSION,\n};\n\n/**\n * Split a pricing key into its provider and its model part.\n *\n * A key with no separator has no provider, which is what the single-vendor\n * adapters produce. The split is on the FIRST separator only, so an id that\n * carries its own colon (`ollama:llama3.2:3b`) keeps it.\n */\nexport function splitModelKey(modelKey: string): {\n\tprovider: string | null;\n\tmodel: string;\n} {\n\tconst at = modelKey.indexOf(PROVIDER_SEPARATOR);\n\tif (at === -1) return { provider: null, model: modelKey };\n\treturn {\n\t\tprovider: modelKey.slice(0, at),\n\t\tmodel: modelKey.slice(at + PROVIDER_SEPARATOR.length),\n\t};\n}\n\n/**\n * The alias rules of ADR-0012 decision 6, applied before any lookup: strip the\n * `provider:` prefix, the `#fast` suffix and a trailing `-YYYYMMDD` date. The\n * three parts come back separately so a caller can keep the ones it needs.\n */\nexport function parseMeasuredId(id: string): {\n\tprovider: string | null;\n\tslug: string;\n\tfast: boolean;\n} {\n\tconst { provider, model } = splitModelKey(id);\n\tconst [base, suffix] = model.split(\"#\");\n\treturn {\n\t\tprovider,\n\t\tslug: base.replace(/-\\d{8}$/, \"\"),\n\t\tfast: suffix === \"fast\",\n\t};\n}\n\nconst key = (slug: string, provider: string | null | undefined) =>\n\t`${provider ?? \"\"}\u0000${slug}`;\n\n/**\n * A table indexed for lookup: every (model, provider) with its periods in\n * order, each closed by the next one's start.\n */\nexport class PriceIndex {\n\tprivate readonly periods = new Map<string, PricePeriod[]>();\n\tprivate readonly vendors = new Map<string, Vendor>();\n\treadonly id: string;\n\n\tconstructor(table: PriceTable) {\n\t\tthis.id = table.id;\n\t\tconst groups = new Map<string, PriceRow[]>();\n\t\tfor (const row of table.rows) {\n\t\t\tconst k = key(row.modelSlug, row.provider);\n\t\t\tconst g = groups.get(k) ?? [];\n\t\t\tg.push(row);\n\t\t\tgroups.set(k, g);\n\t\t\tif (row.vendor && row.provider === undefined) {\n\t\t\t\tthis.vendors.set(row.modelSlug, row.vendor);\n\t\t\t}\n\t\t}\n\t\tfor (const [k, rows] of groups) {\n\t\t\trows.sort((a, b) => a.from - b.from);\n\t\t\tthis.periods.set(\n\t\t\t\tk,\n\t\t\t\trows.map((r, i) => ({\n\t\t\t\t\tfrom: r.from === 0 ? null : r.from,\n\t\t\t\t\tto: i + 1 < rows.length ? rows[i + 1].from : null,\n\t\t\t\t\tinput: r.input,\n\t\t\t\t\toutput: r.output,\n\t\t\t\t\tcacheRead: r.cacheRead ?? 0,\n\t\t\t\t\tcacheWrite5m: r.cacheWrite5m ?? 0,\n\t\t\t\t\tcacheWrite1h: r.cacheWrite1h ?? 0,\n\t\t\t\t\tsource: r.source,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\t}\n\n\t/** The vendor a bare row belongs to, when the table says. */\n\tvendorOf(slug: string): Vendor | null {\n\t\treturn this.vendors.get(slug) ?? null;\n\t}\n\n\t/** True when the table holds any row at all for this (model, provider). */\n\thas(slug: string, provider: string | null): boolean {\n\t\treturn this.periods.has(key(slug, provider));\n\t}\n\n\t/** The periods for exactly this (model, provider). */\n\trowsFor(slug: string, provider: string | null): PricePeriod[] {\n\t\treturn this.periods.get(key(slug, provider)) ?? [];\n\t}\n\n\tget size(): number {\n\t\treturn this.periods.size;\n\t}\n}\n\n/**\n * Several tables layered: the first one that holds a key answers for it. The\n * CLI layers the served table over the bundled one; the backend layers\n * `modelPrices` over the same bundled one. The provider rule is stated here,\n * once, for both.\n */\nexport class Pricer {\n\tconstructor(\n\t\tprivate readonly layers: readonly PriceIndex[],\n\t\tprivate readonly vendorHint: (slug: string) => Vendor | null = () => null,\n\t) {}\n\n\t/** The ids of the layers, in lookup order. */\n\tget tableIds(): string[] {\n\t\treturn this.layers.map((l) => l.id);\n\t}\n\n\tprivate vendorOf(slug: string): Vendor | null {\n\t\tfor (const layer of this.layers) {\n\t\t\tconst v = layer.vendorOf(slug);\n\t\t\tif (v) return v;\n\t\t}\n\t\treturn this.vendorHint(slug);\n\t}\n\n\tprivate firstLayerWith(slug: string, provider: string | null) {\n\t\treturn this.layers.find((l) => l.has(slug, provider)) ?? null;\n\t}\n\n\tprivate lookupSlug(slug: string, provider: string | null): string | null {\n\t\tif (this.firstLayerWith(slug, provider)) return slug;\n\t\tconst alias = PRICING_ALIASES[slug];\n\t\treturn alias && this.firstLayerWith(alias, provider) ? alias : null;\n\t}\n\n\t/**\n\t * Every period that applies to a pricing key, or an empty list when none\n\t * can be cited.\n\t *\n\t * A bare key is the vendor's own rate. A local provider is free. A provider\n\t * with rows of its own uses them. A provider that IS the vendor reaches the\n\t * vendor's bare rows. Anything else (a gateway, an unknown provider) holds\n\t * no rate.\n\t */\n\tperiodsFor(modelKey: string): PricePeriod[] {\n\t\tconst { provider, model } = splitModelKey(modelKey);\n\t\tif (provider === null) {\n\t\t\tconst slug = this.lookupSlug(model, null);\n\t\t\treturn slug\n\t\t\t\t? (this.firstLayerWith(slug, null)?.rowsFor(slug, null) ?? [])\n\t\t\t\t: [];\n\t\t}\n\t\tif (LOCAL_PROVIDERS.has(provider)) return [FREE_PERIOD];\n\t\tconst ownSlug = this.lookupSlug(model, provider);\n\t\tif (ownSlug) {\n\t\t\treturn (\n\t\t\t\tthis.firstLayerWith(ownSlug, provider)?.rowsFor(ownSlug, provider) ?? []\n\t\t\t);\n\t\t}\n\t\tconst vendor = PROVIDER_VENDOR[provider];\n\t\tconst vendorSlug = this.lookupSlug(model, null);\n\t\tif (!vendor || !vendorSlug || this.vendorOf(vendorSlug) !== vendor)\n\t\t\treturn [];\n\t\treturn (\n\t\t\tthis.firstLayerWith(vendorSlug, null)?.rowsFor(vendorSlug, null) ?? []\n\t\t);\n\t}\n\n\tisLocal(modelKey: string): boolean {\n\t\tconst { provider } = splitModelKey(modelKey);\n\t\treturn provider !== null && LOCAL_PROVIDERS.has(provider);\n\t}\n\n\tisPriced(modelKey: string): boolean {\n\t\treturn this.periodsFor(modelKey).length > 0;\n\t}\n\n\t/**\n\t * The rate in effect at `atMs`, or `null` when the model is unknown or the\n\t * timestamp predates every period. A `null` timestamp also yields `null`:\n\t * inventing a price for an undated record would attribute the wrong rate.\n\t */\n\tpriceAt(modelKey: string, atMs: number | null): PricePeriod | null {\n\t\tif (atMs === null) return null;\n\t\tfor (const p of this.periodsFor(modelKey)) {\n\t\t\tif (\n\t\t\t\t(p.from === null || atMs >= p.from) &&\n\t\t\t\t(p.to === null || atMs < p.to)\n\t\t\t) {\n\t\t\t\treturn p;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Every rate that applies anywhere inside `[fromMs, toMs]`. */\n\tperiodsInWindow(\n\t\tmodelKey: string,\n\t\tfromMs: number,\n\t\ttoMs: number,\n\t): PricePeriod[] {\n\t\treturn this.periodsFor(modelKey).filter(\n\t\t\t(p) =>\n\t\t\t\t(p.from === null || p.from <= toMs) && (p.to === null || p.to > fromMs),\n\t\t);\n\t}\n\n\t/**\n\t * The citation for this key: the source of the period in effect at `atMs`,\n\t * or of the latest period when no time is given. `null` when unpriced.\n\t */\n\ttableFor(modelKey: string, atMs?: number): string | null {\n\t\tconst periods = this.periodsFor(modelKey);\n\t\tif (periods.length === 0) return null;\n\t\tif (atMs !== undefined) return this.priceAt(modelKey, atMs)?.source ?? null;\n\t\treturn periods[periods.length - 1].source;\n\t}\n}\n\n/** A stable id for a served table: the row count and a hash of the rows. */\nexport function priceTableId(rows: readonly PriceRow[]): string {\n\tconst text = rows\n\t\t.map(\n\t\t\t(r) =>\n\t\t\t\t`${r.modelSlug}|${r.provider ?? \"\"}|${r.from}|${r.input}|${r.output}|${r.cacheRead ?? \"\"}|${r.cacheWrite5m ?? \"\"}|${r.cacheWrite1h ?? \"\"}|${r.source}`,\n\t\t)\n\t\t.sort()\n\t\t.join(\"\\n\");\n\t// FNV-1a, 32-bit. Enough to tell two tables apart in a log line.\n\tlet h = 0x811c9dc5;\n\tfor (let i = 0; i < text.length; i++) {\n\t\th ^= text.charCodeAt(i);\n\t\th = Math.imul(h, 0x01000193) >>> 0;\n\t}\n\treturn `modelPrices/${rows.length}-${h.toString(16).padStart(8, \"0\")}`;\n}\n\n/**\n * Narrow untrusted JSON into a table. A row that fails the shape is dropped\n * rather than failing the whole fetch; a table with no rows is `null` so the\n * caller falls back to the bundled one.\n */\nexport function parsePriceTable(body: unknown): PriceTable | null {\n\tif (typeof body !== \"object\" || body === null) return null;\n\tconst b = body as { id?: unknown; rows?: unknown };\n\tif (typeof b.id !== \"string\" || !Array.isArray(b.rows)) return null;\n\tconst num = (v: unknown): v is number =>\n\t\ttypeof v === \"number\" && Number.isFinite(v) && v >= 0;\n\tconst opt = (v: unknown): number | undefined => (num(v) ? v : undefined);\n\tconst rows: PriceRow[] = [];\n\tfor (const raw of b.rows) {\n\t\tconst r = raw as Record<string, unknown>;\n\t\tif (\n\t\t\ttypeof r?.modelSlug !== \"string\" ||\n\t\t\tr.modelSlug.length === 0 ||\n\t\t\t!num(r.from) ||\n\t\t\t!num(r.input) ||\n\t\t\t!num(r.output) ||\n\t\t\ttypeof r.source !== \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst vendor =\n\t\t\tr.vendor === \"anthropic\" ||\n\t\t\tr.vendor === \"openai\" ||\n\t\t\tr.vendor === \"google\" ||\n\t\t\tr.vendor === \"xai\" ||\n\t\t\tr.vendor === \"local\"\n\t\t\t\t? r.vendor\n\t\t\t\t: undefined;\n\t\trows.push({\n\t\t\tmodelSlug: r.modelSlug,\n\t\t\t...(typeof r.provider === \"string\" && r.provider.length > 0\n\t\t\t\t? { provider: r.provider }\n\t\t\t\t: {}),\n\t\t\tfrom: r.from,\n\t\t\tinput: r.input,\n\t\t\toutput: r.output,\n\t\t\t...(opt(r.cacheRead) !== undefined\n\t\t\t\t? { cacheRead: r.cacheRead as number }\n\t\t\t\t: {}),\n\t\t\t...(opt(r.cacheWrite5m) !== undefined\n\t\t\t\t? { cacheWrite5m: r.cacheWrite5m as number }\n\t\t\t\t: {}),\n\t\t\t...(opt(r.cacheWrite1h) !== undefined\n\t\t\t\t? { cacheWrite1h: r.cacheWrite1h as number }\n\t\t\t\t: {}),\n\t\t\tsource: r.source,\n\t\t\t...(vendor ? { vendor } : {}),\n\t\t});\n\t}\n\tif (rows.length === 0) return null;\n\treturn { id: b.id, rows };\n}\n","// Time-aware pinned price table for API-equivalent cost.\n//\n// Wayfinder ticket #37 (map #29), decision 8 of the wire-format grilling #33.\n// Moved out of the CLI by ticket #93 (map #76).\n//\n// WHY THIS IS A PACKAGE AND NOT A CLI FILE\n// Two programs price the same tokens. The CLI prices each response at ingest,\n// where the per-response timestamp still exists. The backend re-prices a\n// published snapshot at READ time, to fill the gaps a stale CLI table left\n// behind - one day of table drift published a stack at $14,764 when the same\n// tokens are worth at least $167,331 (#93). Two copies of a price table drift\n// against each other by construction, so there is one copy and both import it.\n//\n// WHY THIS IS A LIST OF PERIODS AND NOT A FLAT MAP\n// A published \"API-equivalent cost\" covers a rolling 30-day window, and a\n// window can straddle a repricing. On 2026-09-05 the window covers Aug 6 →\n// Sep 5, but `claude-sonnet-5`'s introductory rate ends Aug 31 - so 25 days\n// price at $2/$10 and 5 days at $3/$15. A flat table misprices one side or the\n// other for a month after every repricing, which breaks the honesty tenet the\n// measured layer is built on.\n//\n// So each model's price is a list of effective-from ranges, and every response\n// is priced at the rate in effect at ITS OWN timestamp. Cost therefore has to\n// accumulate at ingest (see analyzer.ts) - summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change.\n//\n// Sources: Anthropic public list prices as of 2026-07-25 (cache multipliers\n// from https://platform.claude.com/docs/en/build-with-claude/prompt-caching:\n// 5m cache write = 1.25x input, 1h cache write = 2x input, read = 0.1x input)\n// and OpenAI public list prices as of 2026-08-02\n// (https://developers.openai.com/api/docs/pricing - cached input is 10% of\n// input, the same multiplier `cacheRead` already uses; Codex reports no cache\n// writes, so the write multipliers never fire for OpenAI rows).\n// Google public list prices as of 2026-08-09\n// (https://ai.google.dev/gemini-api/docs/pricing) were added by ticket #123.\n//\n// A read-time estimate cites the table of the model it priced, which is why the\n// table id sits on the rate rather than beside it.\n//\n// WHY A KEY CAN CARRY A PROVIDER\n// Claude Code and Codex each speak to one vendor, so a bare model id names a\n// rate without ambiguity. opencode and pi-mono route many providers, and two of\n// them RE-SERVE another vendor's models under that vendor's own slug: the\n// opencode-zen gateway and github-copilot both emit `gemini-3-pro-preview`, at\n// prices no list page states (ticket #122). Pricing a re-served model at the\n// vendor's list rate would invent a figure and could overstate, which the\n// lower-bound tenet forbids.\n//\n// So a multi-provider adapter keys its rows `provider:model`, and only a\n// provider this table maps to a vendor reaches that vendor's rates. Everything\n// else - gateways, unknown providers - holds no rate and lands in\n// `unpricedTokens`, which is the honest outcome and needs no new machinery.\n//\n// The separator is `:` and not `/` on purpose: `sanitizeModelId` in the CLI\n// payload rewrites `/` to `-`, so a slash-keyed id would reach the backend as a\n// different string than the one this table holds, and the read-time re-pricer\n// would miss it.\n//\n// A LOCAL MODEL IS FREE, WHICH IS NOT THE SAME FACT AS UNPRICED\n// `ollama:qwen3-coder` costs nothing per token. `openrouter:qwen3-coder` costs\n// something this table cannot name. Both used to look identical - no row, no\n// rate, tokens excluded from coverage - so a local run read as a hole in the\n// table. Local providers now hold a real zero rate, cited as\n// `LOCAL_PRICING_TABLE_VERSION`. Their tokens count as covered and add $0.\n//\n// WHERE THE RATES LIVE NOW (#336, ADR-0012)\n// The constants below are the BUNDLED FALLBACK. The live table is the Convex\n// `modelPrices` table, served to the CLI at `/api/prices` and layered over\n// these constants by `layeredPricer`. `table.ts` holds the row shape and the\n// lookup both sides share; this file holds the constants and the module-level\n// functions the adapters call, which read whichever pricer is active.\n\nexport const PRICING_TABLE_VERSION = \"anthropic-list-2026-07-25\";\nexport const OPENAI_PRICING_TABLE_VERSION = \"openai-list-2026-08-02\";\nexport const GOOGLE_PRICING_TABLE_VERSION = \"google-list-2026-08-09\";\nexport const XAI_PRICING_TABLE_VERSION = \"models.dev@2026-08-29\";\n/**\n * The id the CLI prints when it priced against the bundled constants rather\n * than a table the server served (#336). Bump it when a constant changes.\n */\nexport const BUNDLED_PRICE_TABLE_ID = \"bundled-2026-09-10\";\n\nexport {\n\tLOCAL_PRICING_TABLE_VERSION,\n\tPRICING_ALIASES,\n\tPROVIDER_SEPARATOR,\n\tPriceIndex,\n\ttype PricePeriod,\n\ttype PriceRow,\n\tPricer,\n\ttype PriceTable,\n\tparseMeasuredId,\n\tparsePriceTable,\n\tpriceTableId,\n\tsplitModelKey,\n\ttype Vendor,\n} from \"./table.js\";\n\nimport {\n\tPROVIDER_SEPARATOR,\n\tPriceIndex,\n\ttype PricePeriod,\n\ttype PriceRow,\n\tPricer,\n\ttype PriceTable,\n\tsplitModelKey,\n\ttype Vendor,\n} from \"./table.js\";\n\nexport const CACHE_WRITE_5M_MULTIPLIER = 1.25;\nexport const CACHE_WRITE_1H_MULTIPLIER = 2.0;\nexport const CACHE_READ_MULTIPLIER = 0.1;\n\n/**\n * End of the `claude-sonnet-5` introductory rate. Anthropic documents it as \"in\n * effect through 2026-08-31\", so the post-intro period opens at the following\n * UTC midnight.\n *\n * The boundary is approximated in UTC because the announcement names a date,\n * not a timezone. A response written within a few hours of the boundary can\n * therefore be priced on the wrong side of it - worth a handful of cents on a\n * single day, and the alternative (guessing US/Pacific) is no more defensible.\n */\nexport const SONNET_5_INTRO_ENDS_MS = Date.UTC(2026, 8, 1); // 2026-09-01T00:00:00Z\n\n/**\n * How a vendor charges for cache traffic, as multipliers on its input rate.\n *\n * Only the bundled constants are written this way. A served period carries\n * absolute cache rates (ADR-0012 decision 4), and `cacheMultipliersFor` derives\n * the multipliers back from them for callers that still want the ratio.\n */\nexport type CacheMultipliers = {\n\twrite5m: number;\n\twrite1h: number;\n\tread: number;\n};\n\nconst DEFAULT_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: CACHE_WRITE_5M_MULTIPLIER,\n\twrite1h: CACHE_WRITE_1H_MULTIPLIER,\n\tread: CACHE_READ_MULTIPLIER,\n};\n\n/**\n * Cached input is 10% of input; a write is charged as plain input. Google\n * bills cache storage by the hour instead, which this table cannot see, so a\n * Google figure stays below the true one rather than above it.\n */\nconst GOOGLE_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: 1.0,\n\twrite1h: 1.0,\n\tread: 0.1,\n};\n\nconst XAI_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: 0,\n\twrite1h: 0,\n\tread: 0.25,\n};\n\n/** A bundled rate, before it is rendered into dated rows. */\ntype BundledPeriod = {\n\tfrom: number | null;\n\tinput: number;\n\toutput: number;\n};\n\ntype PriceEntry = {\n\tvendor: Vendor;\n\t/** The citation printed next to any dollar figure these rates produce. */\n\ttable: string;\n\tperiods: BundledPeriod[];\n\tcache: CacheMultipliers;\n};\n\nconst anthropic = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"anthropic\",\n\ttable: PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: DEFAULT_CACHE_MULTIPLIERS,\n});\nconst openai = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"openai\",\n\ttable: OPENAI_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: DEFAULT_CACHE_MULTIPLIERS,\n});\nconst google = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"google\",\n\ttable: GOOGLE_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: GOOGLE_CACHE_MULTIPLIERS,\n});\nconst xai = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"xai\",\n\ttable: XAI_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: XAI_CACHE_MULTIPLIERS,\n});\nconst flat = (input: number, output: number): BundledPeriod[] => [\n\t{ from: null, input, output },\n];\n\n/**\n * The priced lanes (ADR-0012 decision 7): measured ids that carry a rate but\n * name no model a person can choose. They live here and never in the catalog\n * or in `modelPrices`, so the seed migration and the served table skip them.\n */\nexport const PRICED_LANES: ReadonlySet<string> = new Set([\"codex-auto-review\"]);\n\n/**\n * Only rates we can actually cite are encoded. Inventing historical periods to\n * make the table look complete would fabricate cost for old records, so every\n * model with one known rate gets one open-ended period.\n *\n * Where models.dev and a list page disagreed on 2026-08-29, models.dev won\n * (ADR-0012 decision 12): `gpt-5.6-sol` and `gemini-3.6-flash` below.\n */\nconst PRICES: Record<string, PriceEntry> = {\n\t\"claude-fable-5\": anthropic(flat(10, 50)),\n\t\"claude-mythos-5\": anthropic(flat(10, 50)),\n\t\"claude-opus-5\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-8\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-7\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-6\": anthropic(flat(5, 25)),\n\t\"claude-sonnet-5\": anthropic([\n\t\t{ from: null, input: 2, output: 10 },\n\t\t{ from: SONNET_5_INTRO_ENDS_MS, input: 3, output: 15 },\n\t]),\n\t\"claude-sonnet-4-6\": anthropic(flat(3, 15)),\n\t\"claude-haiku-4-5\": anthropic(flat(1, 5)),\n\t// Fast mode (research preview) - Claude API only, Opus 5 / Opus 4.8 only.\n\t// Opus 4.7 fast mode was removed, so there is deliberately no 4-7 entry.\n\t\"claude-opus-5#fast\": anthropic(flat(10, 50)),\n\t\"claude-opus-4-8#fast\": anthropic(flat(10, 50)),\n\t// OpenAI (Codex) - standard-context tier (<272K; observed context window is\n\t// 258,400).\n\t\"gpt-5.5\": openai(flat(5, 30)),\n\t\"gpt-5.4\": openai(flat(2.5, 15)),\n\t\"gpt-5.4-mini\": openai(flat(0.75, 4.5)),\n\t\"gpt-5.3-codex\": openai(flat(1.75, 14)),\n\t// The gpt-5.6 family launched 2026-07-29; Terra and Luna were repriced on\n\t// 2026-07-30 (-20% / -80%). The one-day launch rates are not on the list\n\t// page and are NOT encoded - a July-29 Terra/Luna record underprices for\n\t// one day rather than carrying a rate we cannot cite (#72).\n\t// Sol: models.dev reports $4 / $20 on 2026-08-29; the earlier $5 / $30 is\n\t// not dated, so the lower rate prices the whole period (lower bound).\n\t\"gpt-5.6-sol\": openai(flat(4, 20)),\n\t\"gpt-5.6-terra\": openai(flat(2, 12)),\n\t\"gpt-5.6-luna\": openai(flat(0.2, 1.2)),\n\t// NOT on OpenAI's list page - an internal Codex routing label with no\n\t// official price (openai/codex#20981). Rate is the aggregator consensus\n\t// ($2.50 / $15.00), scoped in explicitly by ticket #72 because it carries\n\t// real token volume in Codex rollouts. A priced lane, see PRICED_LANES.\n\t\"codex-auto-review\": openai(flat(2.5, 15)),\n\t// Google (opencode, pi-mono) - Standard tier. Where a model is\n\t// context-tiered, the <=200K rate is encoded, exactly as the OpenAI rows\n\t// encode the standard-context tier: the payload carries no per-response\n\t// context length, so the cheaper side keeps the figure a lower bound.\n\t\"gemini-3.1-pro-preview\": google(flat(2, 12)),\n\t// models.dev reports $0.75 / $3.75 on 2026-08-29 (was $1.5 / $7.5).\n\t\"gemini-3.6-flash\": google(flat(0.75, 3.75)),\n\t\"gemini-3.5-flash\": google(flat(1.5, 9)),\n\t\"gemini-3-flash-preview\": google(flat(0.5, 3)),\n\t\"gemini-2.5-pro\": google(flat(1.25, 10)),\n\t\"gemini-2.5-flash\": google(flat(0.3, 2.5)),\n\t// RETIRED from Google's list page by 2026-08-09, and still the largest\n\t// single block of Google tokens measured in #122. Encoded at its launch\n\t// rate: real volume, a rate we can name. Announcement rate, <=200K tier.\n\t\"gemini-3-pro-preview\": google(flat(2, 12)),\n\t// A real Anthropic model with no row until #123. Measured in #122 as\n\t// `claude-opus-4-5-20251101`, which the dated-suffix rule strips to this key.\n\t\"claude-opus-4-5\": anthropic(flat(5, 25)),\n\t// models.dev's xAI row mirrored into the live table on 2026-08-29. Grok\n\t// Build's explicit `grok-4.6-build` pricing alias reaches this base rate.\n\t\"grok-4.6\": xai(flat(2, 6)),\n};\n\n/**\n * The constants above as dated rows. This is what the seed migration writes\n * into `modelPrices` and what the CLI prices against when the server's table\n * is out of reach. Cache tiers are rendered absolute here, once.\n */\nexport function bundledPriceTable(): PriceTable {\n\tconst rows: PriceRow[] = [];\n\tfor (const [modelSlug, entry] of Object.entries(PRICES)) {\n\t\tfor (const p of entry.periods) {\n\t\t\trows.push({\n\t\t\t\tmodelSlug,\n\t\t\t\tfrom: p.from ?? 0,\n\t\t\t\tinput: p.input,\n\t\t\t\toutput: p.output,\n\t\t\t\tcacheRead: p.input * entry.cache.read,\n\t\t\t\tcacheWrite5m: p.input * entry.cache.write5m,\n\t\t\t\tcacheWrite1h: p.input * entry.cache.write1h,\n\t\t\t\tsource: entry.table,\n\t\t\t\tvendor: entry.vendor,\n\t\t\t});\n\t\t}\n\t}\n\treturn { id: BUNDLED_PRICE_TABLE_ID, rows };\n}\n\nconst BUNDLED_INDEX = new PriceIndex(bundledPriceTable());\nconst BUNDLED_PRICER = new Pricer([BUNDLED_INDEX]);\n\n/** The pricer over the bundled constants alone. */\nexport function bundledPricer(): Pricer {\n\treturn BUNDLED_PRICER;\n}\n\n/**\n * A pricer that answers from `table` first and from the bundled constants for\n * every key the table lacks. The CLI installs the served table this way; the\n * backend builds the same shape over `modelPrices`.\n */\nexport function layeredPricer(\n\ttable: PriceTable,\n\tvendorHint?: (slug: string) => Vendor | null,\n): Pricer {\n\treturn new Pricer([new PriceIndex(table), BUNDLED_INDEX], vendorHint);\n}\n\n/**\n * The pricer the module-level functions below consult. The CLI's sync sets it\n * to the served table before scanning (#336) and every adapter prices through\n * it without knowing. Defaults to the bundled constants.\n */\nlet active: Pricer = BUNDLED_PRICER;\n\nexport function setActivePricer(pricer: Pricer | null): void {\n\tactive = pricer ?? BUNDLED_PRICER;\n}\n\n/** The ids of the tables the active pricer consults, served first. */\nexport function activePriceTableIds(): string[] {\n\treturn active.tableIds;\n}\n\n/**\n * Build the pricing key a multi-provider harness reports under. Pass the\n * harness's own provider id verbatim; this table decides what it means.\n */\nexport function modelKeyFor(provider: string, model: string): string {\n\treturn `${provider}${PROVIDER_SEPARATOR}${model}`;\n}\n\nexport type TokenCounts = {\n\tinput: number;\n\toutput: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\t/** `cache_creation_input_tokens` not covered by the TTL breakdown; priced at the 5m rate. */\n\tcacheWriteUnsplit: number;\n\tcacheRead: number;\n};\n\n/**\n * Normalize an observed `message.model` into a pricing key. Handles the\n * dated-suffix variants (`claude-haiku-4-5-20251001`). The `#fast` suffix is\n * appended by the caller from `usage.speed`. A `provider:` prefix passes\n * through untouched, so a caller can normalize a composed key.\n */\nexport function normalizeModel(model: string): string {\n\tconst { provider, model: bare } = splitModelKey(model);\n\tconst [base, suffix] = bare.split(\"#\");\n\tconst stripped = base.replace(/-\\d{8}$/, \"\");\n\tconst normalized = suffix ? `${stripped}#${suffix}` : stripped;\n\treturn provider === null ? normalized : modelKeyFor(provider, normalized);\n}\n\n/**\n * Drop the analyzer's synthetic `#fast` suffix, leaving the id the payload\n * publishes.\n *\n * A `provider:` prefix SURVIVES this, and that is deliberate: the provider is\n * what tells `google:gemini-3-pro-preview` from\n * `github-copilot:gemini-3-pro-preview`, and the backend re-pricer needs that\n * difference at read time. Use `vendorModelId` where a human or the models\n * catalog needs the plain vendor id.\n */\nexport function baseModelId(modelKey: string): string {\n\treturn modelKey.split(\"#\")[0];\n}\n\n/**\n * The vendor-assigned id alone - no provider prefix, no `#fast`. This is the id\n * to show a reader and to match against the models catalog.\n */\nexport function vendorModelId(modelKey: string): string {\n\treturn splitModelKey(baseModelId(modelKey)).model;\n}\n\n/**\n * How this model's vendor charges for cache traffic, as ratios of the latest\n * period's input rate. Falls back to the Anthropic-shaped constants for a\n * model with no rate, so a caller that prices an unknown model still gets a\n * defined shape rather than a crash.\n */\nexport function cacheMultipliersFor(modelKey: string): CacheMultipliers {\n\tconst periods = active.periodsFor(modelKey);\n\tconst p = periods[periods.length - 1];\n\tif (!p) return DEFAULT_CACHE_MULTIPLIERS;\n\tif (p.input === 0) return { write5m: 0, write1h: 0, read: 0 };\n\t// Absolute rates back to ratios; rounded so 0.075 / 0.75 reads 0.1.\n\tconst ratio = (rate: number) => Math.round((rate / p.input) * 1e6) / 1e6;\n\treturn {\n\t\twrite5m: ratio(p.cacheWrite5m),\n\t\twrite1h: ratio(p.cacheWrite1h),\n\t\tread: ratio(p.cacheRead),\n\t};\n}\n\n/**\n * True when this key names a model that runs locally and therefore costs\n * nothing per token. A caller printing dollars uses this to say \"free\", never\n * \"unknown\".\n */\nexport function isLocalModel(modelKey: string): boolean {\n\treturn active.isLocal(modelKey);\n}\n\n/**\n * The rate in effect for `modelKey` at `atMs`, or `null` when the model is\n * unknown or the timestamp predates every period we can cite.\n *\n * A `null` timestamp also yields `null`: a record with no parseable timestamp\n * cannot be priced time-awarely, and inventing a price for it (say, today's)\n * would silently attribute the wrong rate. Its tokens surface as unpriced.\n */\nexport function priceAt(\n\tmodelKey: string,\n\tatMs: number | null,\n): PricePeriod | null {\n\treturn active.priceAt(modelKey, atMs);\n}\n\n/** True when we hold at least one citable rate for this model, at any time. */\nexport function isPricedModel(modelKey: string): boolean {\n\treturn active.isPriced(modelKey);\n}\n\n/**\n * The table id that cites this model's rates, or `null` when it has none.\n *\n * The citation belongs to the rate, not to the harness that reported it: a\n * read-time estimate is cited by the table it was drawn from, and one stack can\n * carry Anthropic and OpenAI rows at once.\n */\nexport function pricingTableFor(\n\tmodelKey: string,\n\tatMs?: number,\n): string | null {\n\treturn active.tableFor(modelKey, atMs);\n}\n\n/**\n * Every rate that applies to `modelKey` anywhere inside `[fromMs, toMs]`.\n *\n * This is the read-time counterpart of `priceAt`. A published snapshot has no\n * per-response timestamps left, so a re-pricer can only ask \"which rates could\n * this window have paid\", and then choose.\n */\nexport function pricePeriodsInWindow(\n\tmodelKey: string,\n\tfromMs: number,\n\ttoMs: number,\n): PricePeriod[] {\n\treturn active.periodsInWindow(modelKey, fromMs, toMs);\n}\n\n/** The dollars one period charges for these tokens. */\nexport function costAtPeriod(p: PricePeriod, t: TokenCounts): number {\n\tconst M = 1_000_000;\n\treturn (\n\t\t(t.input * p.input +\n\t\t\tt.output * p.output +\n\t\t\t(t.cacheWrite5m + t.cacheWriteUnsplit) * p.cacheWrite5m +\n\t\t\tt.cacheWrite1h * p.cacheWrite1h +\n\t\t\tt.cacheRead * p.cacheRead) /\n\t\tM\n\t);\n}\n\n/**\n * Cost of one response's tokens at the rate in effect at its own timestamp.\n * Returns `null` when no rate applies - the caller must surface that as\n * unpriced tokens rather than zeroing it.\n */\nexport function apiEquivalentCost(\n\tmodelKey: string,\n\tt: TokenCounts,\n\tatMs: number | null,\n): number | null {\n\tconst p = active.priceAt(modelKey, atMs);\n\tif (!p) return null;\n\treturn costAtPeriod(p, t);\n}\n","// The one place the CLI's version string lives.\n//\n// `tsup` replaces `__AISTACK_CLI_VERSION__` at build time with the version in\n// `package.json`, so a release cannot ship a stale number. The fallback covers\n// running from source (tests, `tsx src/index.ts`), where no define happens.\ndeclare const __AISTACK_CLI_VERSION__: string | undefined;\n\nexport const CLI_VERSION: string =\n\ttypeof __AISTACK_CLI_VERSION__ === \"string\"\n\t\t? __AISTACK_CLI_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { type PriceTable, parsePriceTable } from \"@aistack/pricing\";\nimport { CLI_VERSION } from \"./version.js\";\n\nexport const BASE_URL = process.env.AISTACK_URL || \"https://aistack.to\";\n\nasync function request(\n\tpath: string,\n\toptions: RequestInit = {},\n): Promise<Response> {\n\treturn fetch(`${BASE_URL}${path}`, {\n\t\t...options,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...options.headers,\n\t\t},\n\t});\n}\n\nfunction authHeaders(token: string): HeadersInit {\n\treturn { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Turn the two statuses #52 introduced into sentences.\n *\n * A bare `429` tells the user nothing they can act on, and a bare `403` reads\n * like a bug rather than a machine that is no longer allowed to do this. Every\n * other status keeps its number, because the number is all we know about it.\n */\nfunction failure(what: string, res: Response): Error {\n\tif (res.status === 429) {\n\t\tconst retry = res.headers.get(\"Retry-After\");\n\t\treturn new Error(\n\t\t\tretry\n\t\t\t\t? `${what}: too many requests. Try again in ${retry} seconds.`\n\t\t\t\t: `${what}: too many requests. Try again in a minute.`,\n\t\t);\n\t}\n\tif (res.status === 403) {\n\t\treturn new Error(\n\t\t\t`${what}: this machine is not allowed to do that. Run \\`aistack login\\` again to re-link it.`,\n\t\t);\n\t}\n\treturn new Error(`${what}: ${res.status}`);\n}\n\n/**\n * Open a device-code session.\n *\n * An automatic `machineName` is a proposal that stays editable. A label the\n * user supplied as a command parameter sets `machineNameReadOnly`, so the\n * confirmation page shows the chosen value without allowing another edit.\n */\nexport async function authStart(\n\tmachineName?: string,\n\tmachineNameReadOnly = false,\n\toptions: { replaceToken?: string; destinationRequired?: boolean } = {},\n): Promise<{\n\tsecretId: string;\n\tuserCode: string;\n\tauthUrl: string;\n}> {\n\tconst res = await request(\"/api/cli/auth/start\", {\n\t\tmethod: \"POST\",\n\t\t...(options.replaceToken\n\t\t\t? { headers: authHeaders(options.replaceToken) }\n\t\t\t: {}),\n\t\t// `cliVersion` rides along so `cli_login_completed` can report which\n\t\t// version linked the machine (#78). The server carries it on the pending\n\t\t// session and reads it at the token exchange.\n\t\tbody: JSON.stringify({\n\t\t\t...(machineName ? { machineName } : {}),\n\t\t\t...(machineNameReadOnly ? { machineNameReadOnly: true } : {}),\n\t\t\tcliVersion: CLI_VERSION,\n\t\t\t...(options.destinationRequired ? { destinationRequired: true } : {}),\n\t\t}),\n\t});\n\tif (!res.ok) throw failure(\"Auth start failed\", res);\n\treturn res.json();\n}\n\nexport async function authPoll(\n\tsecretId: string,\n): Promise<{ status: string; token?: string; userId?: string }> {\n\tconst res = await request(\n\t\t`/api/cli/auth/poll?secretId=${encodeURIComponent(secretId)}`,\n\t);\n\tif (!res.ok) throw failure(\"Auth poll failed\", res);\n\treturn res.json();\n}\n\nexport async function stackCollect(\n\ttoken: string,\n\tdata: { resources: Resource[] },\n): Promise<{ slug: string; shortId: string; url: string }> {\n\tconst res = await request(\"/api/cli/stacks/collect\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(data),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Collect failed\"));\n\t}\n\treturn res.json();\n}\n\n/** A terminal reader's budget for one error, not a log file's. */\nconst MAX_DETAIL_LINES = 4;\nconst MAX_DETAIL_LINE = 160;\nconst MAX_DETAIL = 480;\n\n/**\n * The readable part of a server error.\n *\n * A CONVEX VALIDATION ERROR CARRIES THE WHOLE OBJECT IT REFUSED. The reason and\n * the path come first and are the entire message a user can act on; after them\n * come `Object:` and `Validator:`, each holding a full dump. A real failed sync\n * printed several screens of session rows and buried the one line that said\n * what to fix. Keep the head, cut the rest, and SAY that it was cut - a message\n * silently missing its end is worse than a short one.\n */\nfunction readableDetail(detail: string): string {\n\tconst lines = detail.trim().split(\"\\n\");\n\tconst kept: string[] = [];\n\tlet cut = lines.length > MAX_DETAIL_LINES;\n\tfor (const line of lines.slice(0, MAX_DETAIL_LINES)) {\n\t\t// A dump line is one enormous line, so the cap lands mid-object. Drop it\n\t\t// entirely rather than print 160 characters of someone's session rows.\n\t\tif (line.length > MAX_DETAIL_LINE) {\n\t\t\tcut = true;\n\t\t\tcontinue;\n\t\t}\n\t\tkept.push(line);\n\t}\n\tlet text = kept.join(\"\\n\").trim();\n\tif (text.length > MAX_DETAIL) {\n\t\ttext = text.slice(0, MAX_DETAIL).trimEnd();\n\t\tcut = true;\n\t}\n\tif (!text) text = lines[0]?.slice(0, MAX_DETAIL_LINE).trimEnd() ?? \"\";\n\treturn cut ? `${text}\\n(detail truncated)` : text;\n}\n\nasync function formatHttpError(res: Response, label: string): Promise<string> {\n\tconst prefix = `${label}: ${res.status} ${res.statusText || \"\"}`.trim();\n\tconst text = await res.text().catch(() => \"\");\n\tif (!text) return prefix;\n\ttry {\n\t\tconst body = JSON.parse(text) as { error?: string; message?: string };\n\t\tconst detail = body.error || body.message;\n\t\tif (detail) return `${prefix} - ${readableDetail(detail)}`;\n\t} catch {}\n\tconst snippet = readableDetail(text);\n\treturn snippet ? `${prefix} - ${snippet}` : prefix;\n}\n\nexport type SyncPublishResult = {\n\treceivedAt: number;\n\tstackSlug: string;\n\turl: string;\n\tkeptPrivate: { stored: number; machineStored: number; refused: boolean };\n};\n\n/**\n * Publish one approved snapshot.\n *\n * Takes the staged body as an ALREADY-SERIALIZED string: the bytes the user\n * approved at the gate are the bytes on the wire, with no re-serialization\n * step between them (#35's binding constraint, #41).\n */\nexport async function syncPublish(\n\ttoken: string,\n\tbodyJson: string,\n): Promise<SyncPublishResult> {\n\tconst res = await request(\"/api/cli/sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: bodyJson,\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Sync failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Sync failed\"));\n\t}\n\treturn res.json();\n}\n\n/**\n * The day manifest (#307, ADR-0010): what the server holds for this machine,\n * date by date, each with its fingerprint, plus the retention in days.\n *\n * `null` means the server has no such route (an old backend) and the caller\n * publishes its whole window. 401 throws the same sentence a publish would,\n * so the fix is the same command either way.\n */\nexport async function fetchDayManifest(\n\tbaseUrl: string,\n\ttoken: string,\n): Promise<{\n\tretentionDays: number;\n\taggregateVersion: string;\n\tdays: { date: string; fingerprint: string }[];\n} | null> {\n\tconst res = await fetch(`${baseUrl}/api/cli/sync-manifest`, {\n\t\theaders: { \"Content-Type\": \"application/json\", ...authHeaders(token) },\n\t});\n\tif (res.status === 404) return null;\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Manifest fetch failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Manifest fetch failed\"));\n\t}\n\tconst body = (await res.json()) as {\n\t\tretentionDays?: unknown;\n\t\taggregateVersion?: unknown;\n\t\tdays?: unknown;\n\t};\n\tconst retentionDays =\n\t\ttypeof body.retentionDays === \"number\" && body.retentionDays > 0\n\t\t\t? body.retentionDays\n\t\t\t: 400;\n\tconst aggregateVersion =\n\t\ttypeof body.aggregateVersion === \"string\" ? body.aggregateVersion : \"\";\n\tconst days = Array.isArray(body.days)\n\t\t? body.days.flatMap((d: unknown) => {\n\t\t\t\tconst row = d as { date?: unknown; fingerprint?: unknown };\n\t\t\t\treturn typeof row?.date === \"string\" &&\n\t\t\t\t\ttypeof row?.fingerprint === \"string\"\n\t\t\t\t\t? [{ date: row.date, fingerprint: row.fingerprint }]\n\t\t\t\t\t: [];\n\t\t\t})\n\t\t: [];\n\treturn { retentionDays, aggregateVersion, days };\n}\n\n/**\n * The server's price table (#336): the `modelPrices` rows the CLI layers over\n * its bundled constants before pricing at ingest. Public, no bearer.\n *\n * `null` means the server has no such route (an old backend) or served a table\n * with no usable rows; the caller prices from the bundled table and says so.\n * A network failure throws and the caller treats it the same way.\n */\nexport async function fetchPriceTable(\n\tbaseUrl: string,\n): Promise<PriceTable | null> {\n\tconst res = await fetch(`${baseUrl}/api/prices`, {\n\t\theaders: { Accept: \"application/json\" },\n\t});\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw failure(\"Price table fetch failed\", res);\n\treturn parsePriceTable(await res.json());\n}\n\nexport type AutoSyncSetResult = {\n\tautoSync: { enabled: boolean; frequencyHours: number };\n\tlastAutoSyncAt: number | null;\n};\n\n/**\n * Set the auto-sync permission on the stack this machine is linked to (#103).\n *\n * The destination is the stack bound to the BEARER, exactly like a publish -\n * the body says what the permission is, never whose it is. The frequency goes\n * out only when the flag goes on: off keeps no schedule, and sending a number\n * with it would overwrite the interval the owner picked for the next enable.\n */\nexport async function setAutoSync(\n\ttoken: string,\n\tflag: { enabled: boolean; frequencyHours?: number },\n): Promise<AutoSyncSetResult> {\n\tconst res = await request(\"/api/cli/auto-sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(\n\t\t\tflag.enabled && flag.frequencyHours !== undefined\n\t\t\t\t? { enabled: true, frequencyHours: flag.frequencyHours }\n\t\t\t\t: { enabled: flag.enabled },\n\t\t),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Auto-sync update failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Auto-sync update failed\"));\n\t}\n\treturn res.json();\n}\n\nexport async function stackGet(token: string): Promise<StackData | null> {\n\tconst res = await request(\"/api/cli/stacks\", {\n\t\theaders: authHeaders(token),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw failure(\"Stack fetch failed\", res);\n\treturn res.json();\n}\n\n// Types used across the CLI\nexport interface ResourceFile {\n\tname: string;\n\tcontent: string;\n\tpath?: string;\n\ttags?: string[];\n}\n\nexport interface Resource {\n\ttype: string;\n\tname: string;\n\tdescription?: string;\n\tgroup: string;\n\tstableKey: string;\n\tfiles?: ResourceFile[];\n\tupstream?: {\n\t\trepoUrl: string;\n\t\tpath?: string;\n\t\tlicense?: string;\n\t\tstars?: number;\n\t\tlastCommitSha?: string;\n\t\tlastSyncAt?: number;\n\t};\n\tpkg?: {\n\t\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\t\tid: string;\n\t\tversion?: string;\n\t\ttransport?: \"stdio\" | \"http\" | \"sse\";\n\t};\n}\n\nexport interface StackData {\n\tname: string;\n\tslug: string;\n\tshortId: string;\n\tresources: Resource[];\n}\n","import * as p from \"@clack/prompts\";\nimport { type Resource, stackCollect, stackGet } from \"../api.js\";\nimport { classify } from \"../classifier.js\";\nimport { getExcludedPaths, getToken, saveExcludedPaths } from \"../config.js\";\nimport { buildRepoLinkResource, detectRepoUrl } from \"../git.js\";\nimport { repoNameFromCanonical } from \"../github-repo.js\";\nimport { detectHooks } from \"../hooks.js\";\nimport { detectMcpServers } from \"../mcp.js\";\nimport { detectInstalledPlugins } from \"../plugins.js\";\nimport { type ScannedFile, scanGlobal, scanLocal } from \"../scanner.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tred,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\n// Sentinel selection key for the detected repo link. NUL-prefixed so it can\n// never collide with a real ScannedFile.relativePath, letting links ride the\n// existing excluded[] selection/persistence model with no config.ts changes.\nconst REPO_LINK_KEY = \"\\0repo-link\";\n\n/**\n * A non-file resource surfaced during collect - the repo link, an installed\n * plugin, etc. Toggleable and persisted exactly like a scanned file, keyed by\n * its sentinel. Everything attaches to the single stack (global) server-side.\n */\ninterface DetectedLink {\n\tkey: string;\n\tresource: Resource;\n\tlabel: string;\n}\n\nexport async function collectCommand(options: { global: boolean }) {\n\tintro(\"collect\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst savedExcluded = getExcludedPaths(cwd);\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning...\");\n\n\tconst localFiles = scanLocal(cwd);\n\tconst globalFiles = options.global ? scanGlobal() : [];\n\ts.stop(\"Scan complete\");\n\n\tif (localFiles.length === 0 && globalFiles.length === 0) {\n\t\tp.log.warn(\"No AI configuration files found.\");\n\t\toutroSkipped(\"nothing to collect\");\n\t\treturn;\n\t}\n\n\t// Apply saved exclusions\n\tconst allFiles = [...localFiles, ...globalFiles];\n\tlet selectedFiles = allFiles.filter(\n\t\t(f) => !savedExcluded.includes(f.relativePath),\n\t);\n\tlet excluded = allFiles.filter((f) => savedExcluded.includes(f.relativePath));\n\n\t// Show file counts\n\tp.log.info(\n\t\t`${lime(String(selectedFiles.length))} included${excluded.length > 0 ? ` · ${dim(String(excluded.length) + \" excluded\")}` : \"\"}`,\n\t);\n\n\t// Detect non-file links: the repo this lives in, installed Claude Code\n\t// plugins, MCP servers, hooks. Each is toggleable and included by default\n\t// unless previously deselected. All graceful no-ops.\n\tconst detectedLinks: DetectedLink[] = [];\n\tconst repoUrl = detectRepoUrl(cwd);\n\tif (repoUrl) {\n\t\tdetectedLinks.push({\n\t\t\tkey: REPO_LINK_KEY,\n\t\t\tresource: buildRepoLinkResource(repoUrl),\n\t\t\tlabel: `repo · ${repoNameFromCanonical(repoUrl)}`,\n\t\t});\n\t}\n\tfor (const resource of detectInstalledPlugins()) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0plugin:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `plugin · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectMcpServers(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0mcp:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `mcp · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectHooks(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0hook:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `hook · ${resource.name}`,\n\t\t});\n\t}\n\n\tconst includedLinks = new Set(\n\t\tdetectedLinks\n\t\t\t.filter((l) => !savedExcluded.includes(l.key))\n\t\t\t.map((l) => l.key),\n\t);\n\tconst withLinks = (base: Resource[]): Resource[] => [\n\t\t...base,\n\t\t...detectedLinks\n\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t.map((l) => l.resource),\n\t];\n\n\t// Classify selected files\n\tlet allResources = withLinks(classify(selectedFiles));\n\n\t// Fetch the existing stack and diff against its resources.\n\tlet existingStack: Awaited<ReturnType<typeof stackGet>> = null;\n\ttry {\n\t\texistingStack = await stackGet(token);\n\t} catch (err) {\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\t// Show file list or diff\n\tif (existingStack) {\n\t\tconst diff = diffResources(allResources, existingStack.resources);\n\t\tconst changeCount = diff.added + diff.changed + diff.removed;\n\n\t\tif (changeCount === 0) {\n\t\t\tp.log.info(\"No changes since last collect.\");\n\t\t\toutroSkipped(\"nothing to upload\");\n\t\t\treturn;\n\t\t}\n\n\t\tdivider();\n\t\tsection(\"changes\");\n\t\tlines(\n\t\t\tdiff.details.map((f) => {\n\t\t\t\tif (f.status === \"added\") return lime(`+ ${f.name}`);\n\t\t\t\tif (f.status === \"changed\") return yellow(`~ ${f.name}`);\n\t\t\t\treturn red(`- ${f.name}`);\n\t\t\t}),\n\t\t);\n\t\tif (diff.unchanged > 0) {\n\t\t\tlines([dim(`${diff.unchanged} unchanged`)]);\n\t\t}\n\t\tdivider();\n\t} else {\n\t\tconst local = selectedFiles.filter((f) => f.source === \"local\");\n\t\tconst global = selectedFiles.filter((f) => f.source === \"global\");\n\n\t\tif (local.length > 0) {\n\t\t\tp.log.step(`${bold(\"LOCAL\")} ${dim(String(local.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(local)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tif (global.length > 0) {\n\t\t\tp.log.step(`${bold(\"GLOBAL\")} ${dim(String(global.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(global)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tconst shownLinks = detectedLinks.filter((l) => includedLinks.has(l.key));\n\t\tif (shownLinks.length > 0) {\n\t\t\tp.log.step(`${bold(\"LINKS\")} ${dim(String(shownLinks.length))}`);\n\t\t\tdivider();\n\t\t\tlines(shownLinks.map((l) => dim(` ${l.label}`)));\n\t\t\tdivider();\n\t\t}\n\t}\n\n\t// Action: upload, customize, or cancel\n\tconst action = await p.select({\n\t\tmessage: existingStack\n\t\t\t? \"Upload changes?\"\n\t\t\t: `Upload ${bold(String(selectedFiles.length))} files to your stack?`,\n\t\toptions: [\n\t\t\t{ value: \"upload\", label: \"Upload\" },\n\t\t\t{ value: \"customize\", label: \"Select files\" },\n\t\t\t{ value: \"cancel\", label: \"Cancel\" },\n\t\t],\n\t});\n\n\tif (p.isCancel(action) || action === \"cancel\") {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tif (action === \"customize\") {\n\t\tconst linkOptions = detectedLinks.map((l) => ({\n\t\t\tvalue: l.key,\n\t\t\tlabel: l.label,\n\t\t\thint: \"link\",\n\t\t}));\n\t\tconst selected = await p.multiselect({\n\t\t\tmessage: \"Select files to include:\",\n\t\t\toptions: [\n\t\t\t\t...linkOptions,\n\t\t\t\t...allFiles.map((f) => ({\n\t\t\t\t\tvalue: f.relativePath,\n\t\t\t\t\tlabel: f.relativePath,\n\t\t\t\t\thint: `${f.type}${f.source === \"global\" ? \" · global\" : \"\"}`,\n\t\t\t\t})),\n\t\t\t],\n\t\t\tinitialValues: [\n\t\t\t\t...detectedLinks\n\t\t\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t\t\t.map((l) => l.key),\n\t\t\t\t...selectedFiles.map((f) => f.relativePath),\n\t\t\t],\n\t\t});\n\n\t\tif (p.isCancel(selected)) {\n\t\t\toutroCancel();\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tconst selectedSet = new Set(selected as string[]);\n\t\tselectedFiles = allFiles.filter((f) => selectedSet.has(f.relativePath));\n\t\texcluded = allFiles.filter((f) => !selectedSet.has(f.relativePath));\n\t\tincludedLinks.clear();\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (selectedSet.has(l.key)) includedLinks.add(l.key);\n\t\t}\n\t\tallResources = withLinks(classify(selectedFiles));\n\n\t\tif (selectedFiles.length === 0 && includedLinks.size === 0) {\n\t\t\tp.log.warn(\"No files selected.\");\n\t\t\toutroSkipped(\"nothing to collect\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\ts.start(\"Uploading...\");\n\ttry {\n\t\tconst result = await stackCollect(token, { resources: allResources });\n\t\ts.stop(lime(\"Uploaded\"));\n\t\tconst excludedKeys = excluded.map((f) => f.relativePath);\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (!includedLinks.has(l.key)) excludedKeys.push(l.key);\n\t\t}\n\t\tsaveExcludedPaths(cwd, excludedKeys);\n\t\tp.log.success(dim(result.url));\n\t\toutro(lime(\"done\"));\n\t} catch (err) {\n\t\ts.stop(\"Upload failed\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"upload failed\");\n\t\tprocess.exit(1);\n\t}\n}\n\nconst TYPE_ORDER = [\n\t\"config\",\n\t\"prompt\",\n\t\"rule\",\n\t\"command\",\n\t\"skill\",\n\t\"subagent\",\n\t\"mcp\",\n\t\"hook\",\n\t\"custom\",\n];\n\nfunction groupByType(files: ScannedFile[]): Map<string, ScannedFile[]> {\n\tconst map = new Map<string, ScannedFile[]>();\n\tfor (const f of files) {\n\t\tconst existing = map.get(f.type) ?? [];\n\t\texisting.push(f);\n\t\tmap.set(f.type, existing);\n\t}\n\tconst sorted = new Map<string, ScannedFile[]>();\n\tfor (const type of TYPE_ORDER) {\n\t\tconst group = map.get(type);\n\t\tif (group) sorted.set(type, group);\n\t}\n\tfor (const [type, group] of map) {\n\t\tif (!sorted.has(type)) sorted.set(type, group);\n\t}\n\treturn sorted;\n}\n\ninterface DiffResult {\n\tadded: number;\n\tchanged: number;\n\tremoved: number;\n\tunchanged: number;\n\tdetails: Array<{ name: string; status: \"added\" | \"changed\" | \"removed\" }>;\n}\n\nexport function diffResources(\n\tcurrent: Resource[],\n\texisting: Resource[],\n): DiffResult {\n\tconst existingMap = new Map<string, string>();\n\tfor (const item of existing) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\texistingMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst currentMap = new Map<string, string>();\n\tfor (const item of current) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tcurrentMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst details: DiffResult[\"details\"] = [];\n\tlet added = 0;\n\tlet changed = 0;\n\tlet unchanged = 0;\n\n\tfor (const [key, content] of currentMap) {\n\t\tconst prev = existingMap.get(key);\n\t\tif (prev === undefined) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: key, status: \"added\" });\n\t\t} else if (prev !== content) {\n\t\t\tchanged++;\n\t\t\tdetails.push({ name: key, status: \"changed\" });\n\t\t} else {\n\t\t\tunchanged++;\n\t\t}\n\t}\n\n\tlet removed = 0;\n\tfor (const key of existingMap.keys()) {\n\t\tif (!currentMap.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: key, status: \"removed\" });\n\t\t}\n\t}\n\n\t// Linked resources (GitHub repos AND package refs like MCP servers) carry no\n\t// files, so the file maps above can't see them. Diff them by stableKey -\n\t// unique for both `linked:<repo>:<path>` and `linked:pkg:<registry>:<id>` -\n\t// otherwise a link-only change is invisible and collect wrongly reports\n\t// \"nothing to upload\".\n\tconst linkLabel = (item: Resource): string => {\n\t\tif (item.upstream)\n\t\t\treturn `link: ${repoNameFromCanonical(item.upstream.repoUrl)}`;\n\t\tif (item.pkg) return `link: ${item.pkg.id}`;\n\t\treturn `link: ${item.name}`;\n\t};\n\tconst linkMap = (items: Resource[]): Map<string, Resource> => {\n\t\tconst map = new Map<string, Resource>();\n\t\tfor (const item of items) {\n\t\t\tif ((item.upstream || item.pkg) && !item.files?.length) {\n\t\t\t\tmap.set(item.stableKey, item);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t};\n\tconst existingLinks = linkMap(existing);\n\tconst currentLinks = linkMap(current);\n\tfor (const [key, item] of currentLinks) {\n\t\tif (!existingLinks.has(key)) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"added\" });\n\t\t}\n\t}\n\tfor (const [key, item] of existingLinks) {\n\t\tif (!currentLinks.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"removed\" });\n\t\t}\n\t}\n\n\treturn { added, changed, removed, unchanged, details };\n}\n","import { basename, dirname } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport type { ScannedFile } from \"./scanner.js\";\nimport { computeStableKey } from \"./stableKey.js\";\n\nexport function classify(files: ScannedFile[]): Resource[] {\n\t// Group by {group, source, type, containing directory}\n\tconst groups = new Map<string, ScannedFile[]>();\n\tconst singletons: ScannedFile[] = [];\n\n\tconst singletonRoots = new Set([\n\t\t\".\",\n\t\t\"~\",\n\t\t\"~/.claude\",\n\t\t\"~/.cursor\",\n\t\t\"~/.continue\",\n\t\t\".claude\",\n\t\t\".cursor\",\n\t\t\".github\",\n\t]);\n\n\tfor (const file of files) {\n\t\tconst dir = dirname(file.relativePath);\n\t\tconst isSingleton = singletonRoots.has(dir);\n\n\t\tif (isSingleton) {\n\t\t\tsingletons.push(file);\n\t\t} else {\n\t\t\tconst key = `${file.group}:${file.source}:${file.type}:${dir}`;\n\t\t\tconst existing = groups.get(key) ?? [];\n\t\t\texisting.push(file);\n\t\t\tgroups.set(key, existing);\n\t\t}\n\t}\n\n\tconst items: Resource[] = [];\n\n\t// Singletons: one Resource per file\n\tfor (const file of singletons) {\n\t\tconst relPath = file.relativePath\n\t\t\t.replace(/^~\\/\\.[^/]+\\//, \"\")\n\t\t\t.replace(/^\\.[^/]+\\//, \"\");\n\t\titems.push({\n\t\t\ttype: file.type,\n\t\t\tname: file.relativePath,\n\t\t\tgroup: file.group,\n\t\t\tstableKey: computeStableKey(file.group, file.type, relPath),\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: basename(file.relativePath),\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\tpath: file.relativePath,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Groups: one Resource per directory group\n\tfor (const [, groupFiles] of groups) {\n\t\tconst first = groupFiles[0];\n\t\tconst dir = dirname(first.relativePath);\n\t\tconst relPath = dir\n\t\t\t.replace(/^~\\/\\.claude\\//, \"\")\n\t\t\t.replace(/^\\.claude\\//, \"\")\n\t\t\t.replace(/^~\\/\\.cursor\\//, \"\")\n\t\t\t.replace(/^\\.cursor\\//, \"\");\n\t\tconst typeLabel =\n\t\t\tfirst.type === \"subagent\" ? \"subagents\" : `${first.type}s`;\n\n\t\titems.push({\n\t\t\ttype: first.type,\n\t\t\tname: dir,\n\t\t\tdescription: `${groupFiles.length} ${typeLabel}`,\n\t\t\tgroup: first.group,\n\t\t\tstableKey: computeStableKey(first.group, first.type, relPath),\n\t\t\tfiles: groupFiles.map((f) => ({\n\t\t\t\tname: basename(f.relativePath),\n\t\t\t\tcontent: f.content,\n\t\t\t\tpath: f.relativePath,\n\t\t\t})),\n\t\t});\n\t}\n\n\treturn items;\n}\n","export function computeStableKey(\n\tgroup: string,\n\ttype: string,\n\trelPath: string,\n): string {\n\treturn `${group}:${type}:${relPath}`;\n}\n","import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { BASE_URL } from \"./api.js\";\n\nconst CONFIG_DIR = join(homedir(), \".config\", \"aistack\");\nconst CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n\ninterface ServerCredentials {\n\ttoken: string;\n\tuserId?: string;\n}\n\n/**\n * Credentials keyed by server URL (#61). Since hash-at-rest (#52) the server\n * stores only a hash, so this file holds the only plaintext copy of each\n * token. The old flat `{token, userId}` form let a localhost login overwrite\n * the prod token, which was unrecoverable. The map keeps one entry per server.\n */\ninterface CredentialsFile {\n\tservers: Record<string, ServerCredentials>;\n}\n\n/** Where a legacy flat token is assumed to come from. */\nconst DEFAULT_SERVER_URL = \"https://aistack.to\";\n\n/**\n * Read the credentials file and lift the legacy flat form into the map.\n *\n * A legacy token carries no record of which server issued it. It is assigned\n * to the default prod URL, not the caller's current server: every real flat\n * token came from prod, and keying it under a localhost caller would put it\n * exactly where the next localhost login overwrites it.\n */\nfunction readCredentials(file: string): {\n\tdata: CredentialsFile;\n\tlegacy: boolean;\n} {\n\tconst empty = { data: { servers: {} }, legacy: false };\n\tif (!existsSync(file)) return empty;\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (!raw || typeof raw !== \"object\") return empty;\n\t\tif (raw.servers && typeof raw.servers === \"object\") {\n\t\t\treturn {\n\t\t\t\tdata: { servers: raw.servers as Record<string, ServerCredentials> },\n\t\t\t\tlegacy: false,\n\t\t\t};\n\t\t}\n\t\tif (typeof raw.token === \"string\" && raw.token) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tservers: {\n\t\t\t\t\t\t[DEFAULT_SERVER_URL]: { token: raw.token, userId: raw.userId },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tlegacy: true,\n\t\t\t};\n\t\t}\n\t\t// A cleared legacy file is `{}` - empty, but safe to rewrite.\n\t\treturn { data: { servers: {} }, legacy: true };\n\t} catch {\n\t\t// Do not rewrite an unreadable file. It may still hold a token that a\n\t\t// human can recover, and this file holds the only plaintext copy.\n\t\treturn empty;\n\t}\n}\n\nfunction writeCredentials(file: string, data: CredentialsFile): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(data, null, 2));\n}\n\nexport function getToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): string | null {\n\tconst { data, legacy } = readCredentials(file);\n\tif (legacy) writeCredentials(file, data);\n\treturn data.servers[serverUrl]?.token ?? null;\n}\n\nexport function saveToken(\n\ttoken: string,\n\tuserId?: string,\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tconst { data } = readCredentials(file);\n\tdata.servers[serverUrl] = { token, userId };\n\twriteCredentials(file, data);\n}\n\n/** Remove only the current server's entry. Other servers keep their tokens. */\nexport function clearToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tif (!existsSync(file)) return;\n\tconst { data } = readCredentials(file);\n\tdelete data.servers[serverUrl];\n\twriteCredentials(file, data);\n}\n\nconst SETTINGS_FILE = join(CONFIG_DIR, \"settings.json\");\n\n/**\n * Machine-local switches (#56). A separate file from credentials.json so a\n * login overwrite never resets an answered upsell, and clearing settings never\n * touches the token.\n */\nexport interface AutoSyncConfig {\n\t/** The standing opt-in. `sync --auto` publishes nothing when false. */\n\tenabled: boolean;\n\t/** Minimum hours between auto-sync attempts. Default 6. */\n\tfrequencyHours: number;\n}\n\n/** Bookkeeping the `sync --auto` runs write. Separate from the opt-in. */\nexport interface AutoSyncState {\n\t/** Epoch ms of the last attempt (success or failure). The freshness gate. */\n\tlastRunAt?: number;\n\tlastSuccessAt?: number;\n\t/** One line about the last run, shown on the next interactive sync. */\n\tlastResult?: string;\n\tconsecutiveFailures?: number;\n\t/** The 3-failure systemMessage went out. Reset on success. */\n\tfailureWarned?: boolean;\n}\n\nexport const DEFAULT_FREQUENCY_HOURS = 6;\nexport const MAX_FREQUENCY_HOURS = 24;\n\nexport function normalizeFrequencyHours(value: number | undefined): number {\n\tif (value === undefined || !Number.isFinite(value))\n\t\treturn DEFAULT_FREQUENCY_HOURS;\n\treturn Math.min(MAX_FREQUENCY_HOURS, Math.max(1, Math.round(value)));\n}\n\nexport interface Settings {\n\t/** The post-sync connect-claude upsell was answered (either way). */\n\tconnectClaudeAnswered?: boolean;\n\t/** Legacy binary answer. Kept readable so existing settings still parse. */\n\tautoSyncAnswered?: boolean;\n\t/** The owner explicitly chose not to see the post-sync auto-sync ask again. */\n\tautoSyncNeverAskAgain?: boolean;\n\tautoSync?: AutoSyncConfig;\n\tautoSyncState?: AutoSyncState;\n}\n\nexport function getSettings(file: string = SETTINGS_FILE): Settings {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\treturn raw && typeof raw === \"object\" ? (raw as Settings) : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport function saveSettings(\n\tpatch: Partial<Settings>,\n\tfile: string = SETTINGS_FILE,\n): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(\n\t\tfile,\n\t\tJSON.stringify({ ...getSettings(file), ...patch }, null, 2),\n\t);\n}\n\nconst PROJECTS_FILE = join(CONFIG_DIR, \"projects.json\");\nconst PROJECT_WORKSPACE_ID_RE = /^[A-Za-z0-9_-]{22}$/;\n\ninterface ProjectEntry {\n\texcluded?: string[];\n\tworkspaceId?: string;\n}\n\ninterface ProjectsData {\n\t[directory: string]: ProjectEntry;\n}\n\nfunction readProjects(file: string = PROJECTS_FILE): ProjectsData {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\t// Tolerate legacy entries: string values (oldest) and objects that still\n\t\t// carry a `name` field. Only exclusions and project workspace identifiers survive.\n\t\tconst data: ProjectsData = {};\n\t\tfor (const [key, value] of Object.entries(raw)) {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\tdata[key] = {};\n\t\t\t} else if (value && typeof value === \"object\") {\n\t\t\t\tconst excluded = (value as { excluded?: string[] }).excluded;\n\t\t\t\tconst workspaceId = (value as { workspaceId?: unknown }).workspaceId;\n\t\t\t\tdata[key] = {\n\t\t\t\t\t...(Array.isArray(excluded) ? { excluded } : {}),\n\t\t\t\t\t...(typeof workspaceId === \"string\" ? { workspaceId } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeProjects(data: ProjectsData, file: string = PROJECTS_FILE): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(data, null, 2));\n}\n\nexport function getProjectWorkspaceId(\n\tdirectory: string,\n\tdeps: { file?: string; createId?: () => string } = {},\n): string {\n\tconst file = deps.file ?? PROJECTS_FILE;\n\tconst data = readProjects(file);\n\tconst held = data[directory]?.workspaceId;\n\tif (held && PROJECT_WORKSPACE_ID_RE.test(held)) return held;\n\tconst workspaceId = (\n\t\tdeps.createId ?? (() => randomBytes(16).toString(\"base64url\"))\n\t)();\n\tdata[directory] = { ...data[directory], workspaceId };\n\twriteProjects(data, file);\n\treturn workspaceId;\n}\n\nexport function getExcludedPaths(\n\tdirectory: string,\n\tfile: string = PROJECTS_FILE,\n): string[] {\n\treturn readProjects(file)[directory]?.excluded ?? [];\n}\n\nexport function saveExcludedPaths(\n\tdirectory: string,\n\texcluded: string[],\n\tfile: string = PROJECTS_FILE,\n): void {\n\tconst data = readProjects(file);\n\tdata[directory] = {\n\t\t...data[directory],\n\t\texcluded: excluded.length > 0 ? excluded : undefined,\n\t};\n\twriteProjects(data, file);\n}\n","import { execFileSync } from \"node:child_process\";\nimport type { Resource } from \"./api.js\";\nimport {\n\tcanonicalizeRepoUrl,\n\tnormalizeUpstreamPath,\n\trepoNameFromCanonical,\n} from \"./github-repo.js\";\n\n/**\n * Returns the raw `origin` remote URL for a working directory, or null when it\n * can't be determined. Injectable so `detectRepoUrl` stays unit-testable\n * without spawning git.\n */\nexport type GitRemoteRunner = (cwd: string) => string | null;\n\nexport const defaultGitRemoteRunner: GitRemoteRunner = (cwd) => {\n\ttry {\n\t\t// argv form (no shell) - git walks up to the repo root itself, and\n\t\t// stderr is swallowed so \"not a git repository\" never leaks into the\n\t\t// CLI's output. git missing / no repo / no origin all throw → null.\n\t\treturn execFileSync(\"git\", [\"-C\", cwd, \"remote\", \"get-url\", \"origin\"], {\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/**\n * Detect the canonical GitHub repo URL for `cwd`, or null. Non-GitHub origins\n * (GitLab, Bitbucket, …) canonicalize to null, so this is a graceful no-op\n * outside of GitHub repos.\n */\nexport function detectRepoUrl(\n\tcwd: string,\n\trun: GitRemoteRunner = defaultGitRemoteRunner,\n): string | null {\n\tconst raw = run(cwd);\n\tif (!raw) return null;\n\treturn canonicalizeRepoUrl(raw);\n}\n\nexport interface LinkSpec {\n\t/** Canonical GitHub URL (https://github.com/owner/repo). */\n\tcanonical: string;\n\t/** Optional subpath within the repo. */\n\tpath?: string;\n\tname: string;\n\ttype: string;\n\tgroup: string;\n\t/** Optional pinned commit, stored as upstream.lastCommitSha. */\n\tsha?: string;\n}\n\n/**\n * Build a linked-resource payload. Mirrors the web `linkResource` mutation: no\n * files (upstream presence is the storage discriminator) and the exact\n * `linked:${canonical}:${normPath}` stableKey so the web unlink UI - which\n * matches by stableKey - recognizes it. `path`/`lastCommitSha` are omitted when\n * empty so the by_upstream dedup index matches at both write and query.\n */\nexport function buildLinkResource(spec: LinkSpec): Resource {\n\tconst normPath = normalizeUpstreamPath(spec.path);\n\treturn {\n\t\ttype: spec.type,\n\t\tname: spec.name,\n\t\tgroup: spec.group,\n\t\tstableKey: `linked:${spec.canonical}:${normPath}`,\n\t\tupstream: {\n\t\t\trepoUrl: spec.canonical,\n\t\t\t...(normPath ? { path: normPath } : {}),\n\t\t\t...(spec.sha ? { lastCommitSha: spec.sha } : {}),\n\t\t},\n\t};\n}\n\n/** The repo this project lives in: a GitHub link (stack-owned server-side). */\nexport function buildRepoLinkResource(canonical: string): Resource {\n\treturn buildLinkResource({\n\t\tcanonical,\n\t\tname: repoNameFromCanonical(canonical),\n\t\ttype: \"custom\",\n\t\tgroup: \"generic\",\n\t});\n}\n","/**\n * Trimmed copy of `src/lib/github-repo.ts` - the CANONICAL parser, whose\n * `github-repo.test.ts` is the canonical test table. Copied (not imported)\n * because the CLI ships as an independent npm package and its tsconfig\n * (`rootDir: \"src\"` + `declaration: true`) forbids cross-rootDir imports.\n * Keep these functions in sync with the canonical source.\n *\n * Only the pieces the CLI needs are included: `parseRepo`,\n * `canonicalizeRepoUrl`, `repoNameFromCanonical`, and `normalizeUpstreamPath`\n * (a null from `canonicalizeRepoUrl` is the CLI's graceful-skip signal, so\n * `isGithubRepoUrl` is intentionally omitted).\n */\n\nfunction isGithubHost(host: string): boolean {\n\tconst h = host.toLowerCase();\n\treturn h === \"github.com\" || h === \"www.github.com\";\n}\n\nexport function parseRepo(\n\tinput: string,\n): { owner: string; repo: string } | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\n\n\t// SCP-like form `git@host:owner/repo`: take the host and the path after the\n\t// colon. Otherwise strip the scheme, then split the leading host off the path.\n\tconst scpMatch = trimmed.match(/^[^@]+@([^:]+):(.+)$/);\n\tlet host: string;\n\tlet withoutHost: string;\n\tif (scpMatch) {\n\t\thost = scpMatch[1];\n\t\twithoutHost = scpMatch[2];\n\t} else {\n\t\tconst withoutScheme = trimmed.replace(/^[a-z]+:\\/\\//i, \"\");\n\t\tconst slash = withoutScheme.indexOf(\"/\");\n\t\tif (slash === -1) return null;\n\t\thost = withoutScheme.slice(0, slash);\n\t\twithoutHost = withoutScheme.slice(slash + 1);\n\t}\n\tif (!isGithubHost(host)) return null;\n\n\t// Drop any query string or anchor, then split into path segments.\n\tconst pathPart = withoutHost.replace(/[?#].*$/, \"\");\n\tconst segments = pathPart.split(\"/\").filter(Boolean);\n\n\tconst owner = segments[0];\n\tconst repo = segments[1]?.replace(/\\.git$/, \"\");\n\tif (!owner || !repo) return null;\n\n\treturn { owner: owner.toLowerCase(), repo: repo.toLowerCase() };\n}\n\nexport function canonicalizeRepoUrl(input: string): string | null {\n\tconst parsed = parseRepo(input);\n\tif (!parsed) return null;\n\treturn `https://github.com/${parsed.owner}/${parsed.repo}`;\n}\n\nexport function repoNameFromCanonical(canonical: string): string {\n\treturn parseRepo(canonical)?.repo ?? \"\";\n}\n\nexport function normalizeUpstreamPath(path: string | undefined): string {\n\tif (!path) return \"\";\n\treturn path.split(\"/\").filter(Boolean).join(\"/\");\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Extract hooks defined inline in Claude Code settings as discrete `hook`\n * resources - one per event (PreToolUse, PostToolUse, …). Without this they\n * only ride inside the collected settings.json config blob and never surface as\n * first-class hooks.\n *\n * These are HOSTED resources (the event's config block is the content), so they\n * participate in the normal file-based diff. They intentionally duplicate data\n * also present in the settings.json resource; the distinct `hooks:` stableKeys\n * mean no collision, and first-class visibility was the deliberate tradeoff.\n */\n\ninterface SettingsFile {\n\thooks?: Record<string, unknown>;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction hooksFrom(\n\tpath: string,\n\tsource: \"local\" | \"global\",\n\tout: Resource[],\n\tseen: Set<string>,\n) {\n\tconst hooks = readJson<SettingsFile>(path)?.hooks;\n\tif (!hooks || typeof hooks !== \"object\") return;\n\tfor (const [event, config] of Object.entries(hooks)) {\n\t\tconst stableKey = `hooks:${source}:${event}`;\n\t\tif (seen.has(stableKey)) continue;\n\t\tseen.add(stableKey);\n\t\tout.push({\n\t\t\ttype: \"hook\",\n\t\t\tname: event,\n\t\t\tgroup: \"claude-code\",\n\t\t\tstableKey,\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: `${event}.json`,\n\t\t\t\t\tcontent: JSON.stringify(config, null, 2),\n\t\t\t\t\tpath: `hooks/${event}.json`,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n}\n\n/**\n * Detect inline hooks from project settings (`.claude/settings.json` +\n * `.claude/settings.local.json`) and global settings (`~/.claude/settings.json`).\n * Project settings win over the `.local` override on the same event.\n */\nexport function detectHooks(cwd: string, home: string = homedir()): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\thooksFrom(join(cwd, \".claude\", \"settings.json\"), \"local\", out, seen);\n\thooksFrom(join(cwd, \".claude\", \"settings.local.json\"), \"local\", out, seen);\n\thooksFrom(join(home, \".claude\", \"settings.json\"), \"global\", out, seen);\n\treturn out;\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Detect configured MCP servers and resolve each to a `pkg` reference (its\n * package identity), parsed from the launch `command`/`args` - npm/PyPI/OCI for\n * stdio servers, or a URL for remote (http/sse) servers. `env` is intentionally\n * dropped (it carries secrets), so this is a safer representation than uploading\n * the raw config file.\n */\n\nexport interface McpServerConfig {\n\tcommand?: string;\n\targs?: string[];\n\ttype?: string;\n\ttransport?: string;\n\turl?: string;\n}\n\nexport interface PkgRef {\n\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\tid: string;\n\tversion?: string;\n\ttransport?: \"stdio\" | \"http\" | \"sse\";\n}\n\nfunction commandName(cmd: string): string {\n\treturn cmd.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? cmd;\n}\n\n/** Split an npm/PyPI spec into id + version, handling scoped npm (@scope/n@v). */\nfunction splitVersion(spec: string): { id: string; version?: string } {\n\tconst at = spec.indexOf(\"@\", spec.startsWith(\"@\") ? 1 : 0);\n\tif (at <= 0) return { id: spec };\n\treturn { id: spec.slice(0, at), version: spec.slice(at + 1) || undefined };\n}\n\n/** First arg that isn't a flag (and isn't in `skip`). */\nfunction firstPositional(args: string[], skip = 0): string | undefined {\n\tfor (const a of args.slice(skip)) {\n\t\tif (!a.startsWith(\"-\")) return a;\n\t}\n\treturn undefined;\n}\n\n// docker/podman flags that consume the following token (so it isn't the image).\nconst CONTAINER_VALUE_FLAGS = new Set([\n\t\"-e\",\n\t\"--env\",\n\t\"-v\",\n\t\"--volume\",\n\t\"-p\",\n\t\"--publish\",\n\t\"-w\",\n\t\"--workdir\",\n\t\"--name\",\n\t\"--mount\",\n\t\"--network\",\n\t\"-u\",\n\t\"--user\",\n\t\"-l\",\n\t\"--label\",\n]);\n\nfunction containerImage(args: string[]): string | undefined {\n\tconst runIdx = args.indexOf(\"run\");\n\tconst rest = runIdx >= 0 ? args.slice(runIdx + 1) : args;\n\tfor (let i = 0; i < rest.length; i++) {\n\t\tconst a = rest[i];\n\t\tif (a.startsWith(\"-\")) {\n\t\t\tif (CONTAINER_VALUE_FLAGS.has(a) && !a.includes(\"=\")) i++;\n\t\t\tcontinue;\n\t\t}\n\t\treturn a; // first positional after `run` is the image\n\t}\n\treturn undefined;\n}\n\n/** Split a container image ref into id + tag (ignoring a registry host:port). */\nfunction splitImageTag(image: string): { id: string; version?: string } {\n\tconst colon = image.lastIndexOf(\":\");\n\tif (colon > 0 && !image.slice(colon + 1).includes(\"/\")) {\n\t\treturn { id: image.slice(0, colon), version: image.slice(colon + 1) };\n\t}\n\treturn { id: image };\n}\n\n/** Parse a single MCP server config into a package reference, or null. */\nexport function parseMcpPackage(server: McpServerConfig): PkgRef | null {\n\t// Remote server: a URL endpoint (http/sse).\n\tif (server.url) {\n\t\tlet safeUrl: string;\n\t\ttry {\n\t\t\tconst parsed = new URL(server.url);\n\t\t\tparsed.username = \"\";\n\t\t\tparsed.password = \"\";\n\t\t\tsafeUrl = parsed.toString();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tconst t = (server.type ?? server.transport ?? \"\").toLowerCase();\n\t\treturn {\n\t\t\tregistry: \"url\",\n\t\t\tid: safeUrl,\n\t\t\ttransport: t === \"sse\" ? \"sse\" : \"http\",\n\t\t};\n\t}\n\n\tconst command = server.command ? commandName(server.command) : \"\";\n\tif (!command) return null;\n\tconst args = server.args ?? [];\n\n\t// npm-family runners.\n\tif (command === \"npx\" || command === \"bunx\" || command === \"pnpx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif ((command === \"pnpm\" || command === \"yarn\") && args[0] === \"dlx\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\n\t// Python-family runners.\n\tif (command === \"uvx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"pipx\" && args[0] === \"run\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"uv\" && args[0] === \"tool\" && args[1] === \"run\") {\n\t\tconst spec = firstPositional(args, 2);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (/^python[0-9.]*$/.test(command)) {\n\t\tconst i = args.indexOf(\"-m\");\n\t\tconst mod = i >= 0 ? args[i + 1] : undefined;\n\t\treturn mod ? { registry: \"pypi\", id: mod, transport: \"stdio\" } : null;\n\t}\n\n\t// Container runners.\n\tif (command === \"docker\" || command === \"podman\") {\n\t\tconst image = containerImage(args);\n\t\tif (!image) return null;\n\t\treturn { registry: \"oci\", ...splitImageTag(image), transport: \"stdio\" };\n\t}\n\n\t// node/deno/bun running a local script, or an unknown command → skip.\n\treturn null;\n}\n\n/** Build a `type:\"mcp\"` linked resource from a parsed package reference. */\nexport function buildMcpResource(\n\tname: string,\n\tgroup: string,\n\tpkg: PkgRef,\n): Resource {\n\treturn {\n\t\ttype: \"mcp\",\n\t\tname,\n\t\tgroup,\n\t\tstableKey: `linked:pkg:${pkg.registry}:${pkg.id}`,\n\t\tpkg,\n\t};\n}\n\nfunction readText(path: string): string | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn readFileSync(path, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readParsed<T>(\n\tpath: string,\n\tparse: (raw: string) => unknown,\n): T | null {\n\tconst raw = readText(path);\n\tif (raw === null) return null;\n\ttry {\n\t\treturn parse(raw) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readJson<T>(path: string): T | null {\n\treturn readParsed<T>(path, JSON.parse);\n}\nfunction readYaml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseYaml);\n}\nfunction readToml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseToml);\n}\n\ntype ServerMap = Record<string, McpServerConfig> | undefined;\ninterface McpFile {\n\tmcpServers?: ServerMap;\n\tservers?: ServerMap; // VS Code uses `servers`\n}\ninterface ClaudeJson {\n\tmcpServers?: ServerMap;\n\tprojects?: Record<string, { mcpServers?: ServerMap }>;\n}\n// Continue uses a LIST under mcpServers; Codex (TOML) uses `mcp_servers`.\ninterface ContinueYaml {\n\tmcpServers?: Array<{ name?: string } & McpServerConfig>;\n}\ninterface CodexToml {\n\tmcp_servers?: Record<string, McpServerConfig>;\n}\ninterface GrokToml {\n\tmcp_servers?: Record<string, McpServerConfig & { enabled?: boolean }>;\n\tdisabled_mcp_servers?: string[];\n}\n\n/** Normalize Continue's list form to the common name→config map. */\nfunction continueListToMap(file: ContinueYaml | null): ServerMap {\n\tif (!file?.mcpServers?.length) return undefined;\n\tconst map: Record<string, McpServerConfig> = {};\n\tfile.mcpServers.forEach((s, i) => {\n\t\tmap[s.name ?? `server-${i}`] = s;\n\t});\n\treturn map;\n}\n\n/** Per-OS VS Code globalStorage bases (+ Code-OSS/VSCodium variants). */\nfunction vscodeGlobalStorageBases(home: string): string[] {\n\tconst apps = [\"Code\", \"Code - OSS\", \"VSCodium\"];\n\tlet root: string;\n\tif (platform() === \"darwin\") {\n\t\troot = join(home, \"Library\", \"Application Support\");\n\t} else if (platform() === \"win32\") {\n\t\troot = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n\t} else {\n\t\troot = process.env.XDG_CONFIG_HOME ?? join(home, \".config\");\n\t}\n\treturn apps.map((app) => join(root, app, \"User\", \"globalStorage\"));\n}\n\n/**\n * Read MCP server configs across the known tool locations and return one\n * `type:\"mcp\"` pkg-link resource per server, deduped by package identity.\n * Project-level configs win over global ones on the same identity.\n */\nexport function detectMcpServers(\n\tcwd: string,\n\thome: string = homedir(),\n): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (servers: ServerMap, group: string) => {\n\t\tfor (const [name, cfg] of Object.entries(servers ?? {})) {\n\t\t\tconst pkg = parseMcpPackage(cfg);\n\t\t\tif (!pkg) continue;\n\t\t\tconst resource = buildMcpResource(name, group, pkg);\n\t\t\tif (seen.has(resource.stableKey)) continue;\n\t\t\tseen.add(resource.stableKey);\n\t\t\tout.push(resource);\n\t\t}\n\t};\n\tconst grokHome = process.env.GROK_HOME ?? join(home, \".grok\");\n\tconst grokGlobal = readToml<GrokToml>(join(grokHome, \"config.toml\"));\n\tconst grokProject = readToml<GrokToml>(join(cwd, \".grok\", \"config.toml\"));\n\tconst disabled = new Set([\n\t\t...(grokGlobal?.disabled_mcp_servers ?? []),\n\t\t...(grokProject?.disabled_mcp_servers ?? []),\n\t]);\n\tconst effectiveGrok = {\n\t\t...(grokGlobal?.mcp_servers ?? {}),\n\t\t...(grokProject?.mcp_servers ?? {}),\n\t};\n\tadd(\n\t\tObject.fromEntries(\n\t\t\tObject.entries(effectiveGrok).filter(\n\t\t\t\t([name, config]) => config.enabled !== false && !disabled.has(name),\n\t\t\t),\n\t\t),\n\t\t\"grok-build\",\n\t);\n\n\t// Project configs first (so they win dedup over global).\n\tadd(readJson<McpFile>(join(cwd, \".mcp.json\"))?.mcpServers, \"claude-code\");\n\tadd(readJson<McpFile>(join(cwd, \"mcp.json\"))?.mcpServers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\tadd(readJson<McpFile>(join(cwd, \".vscode\", \"mcp.json\"))?.servers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \"claude_desktop_config.json\"))?.mcpServers,\n\t\t\"claude-desktop\",\n\t);\n\n\t// Continue (project): a list per YAML file under .continue/mcpServers/.\n\tfor (const file of listYamlFiles(join(cwd, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Roo (project).\n\tadd(readJson<McpFile>(join(cwd, \".roo\", \"mcp.json\"))?.mcpServers, \"roo\");\n\n\t// --- Global / stack-scoped: user-level tool configs ---\n\tconst claudeJson = readJson<ClaudeJson>(join(home, \".claude.json\"));\n\tadd(claudeJson?.projects?.[cwd]?.mcpServers, \"claude-code\");\n\tadd(claudeJson?.mcpServers, \"claude-code\");\n\tadd(\n\t\treadJson<McpFile>(join(home, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\t// Windsurf (global only).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".codeium\", \"windsurf\", \"mcp_config.json\"))\n\t\t\t?.mcpServers,\n\t\t\"windsurf\",\n\t);\n\t// Cline + Roo: VS Code extension globalStorage (OS-specific base).\n\tfor (const base of vscodeGlobalStorageBases(home)) {\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"saoudrizwan.claude-dev\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"cline_mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"cline\",\n\t\t);\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"rooveterinaryinc.roo-cline\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"roo\",\n\t\t);\n\t}\n\t// Continue (global).\n\tfor (const file of listYamlFiles(join(home, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Gemini CLI (global).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".gemini\", \"settings.json\"))?.mcpServers,\n\t\t\"gemini\",\n\t);\n\t// Codex CLI (global, TOML).\n\tadd(\n\t\treadToml<CodexToml>(join(home, \".codex\", \"config.toml\"))?.mcp_servers,\n\t\t\"codex\",\n\t);\n\n\treturn out;\n}\n\n/** List `*.yaml`/`*.yml` files in a directory (empty if absent). */\nfunction listYamlFiles(dir: string): string[] {\n\ttry {\n\t\treturn readdirSync(dir)\n\t\t\t.filter((f) => f.endsWith(\".yaml\") || f.endsWith(\".yml\"))\n\t\t\t.map((f) => join(dir, f));\n\t} catch {\n\t\treturn [];\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport { buildLinkResource } from \"./git.js\";\nimport { canonicalizeRepoUrl } from \"./github-repo.js\";\n\n/**\n * Detect installed Claude Code plugins from the on-disk registry and resolve\n * each to a GitHub link pointing at its TRUE upstream source (not the\n * aggregator marketplace), attached at stack scope (the user's toolchain).\n *\n * Registry shape (~/.claude/plugins/):\n * installed_plugins.json → { plugins: { \"<name>@<marketplace>\": [{ gitCommitSha, version, ... }] } }\n * known_marketplaces.json → { \"<marketplace>\": { source: { repo }, installLocation } }\n * <installLocation>/.claude-plugin/marketplace.json → { plugins: [{ name, source, ... }] }\n */\n\ninterface InstalledEntry {\n\tscope?: string;\n\tversion?: string;\n\tgitCommitSha?: string;\n}\n\nexport interface InstalledPlugins {\n\tplugins?: Record<string, InstalledEntry[]>;\n}\n\nexport interface KnownMarketplace {\n\tsource?: { source?: string; repo?: string; url?: string };\n\tinstallLocation?: string;\n}\n\nexport type KnownMarketplaces = Record<string, KnownMarketplace>;\n\n/** A plugin's source in marketplace.json - polymorphic. */\ntype PluginSource =\n\t| string\n\t| {\n\t\t\tsource?: string;\n\t\t\turl?: string;\n\t\t\tpath?: string;\n\t\t\tref?: string;\n\t\t\tsha?: string;\n\t };\n\ninterface ManifestPlugin {\n\tname: string;\n\tsource?: PluginSource;\n\trepository?: string;\n\thomepage?: string;\n}\n\nexport interface Manifest {\n\tplugins?: ManifestPlugin[];\n}\n\nfunction marketplaceRepoUrl(mp: KnownMarketplace | undefined): string | null {\n\tconst src = mp?.source;\n\tif (!src) return null;\n\tif (src.repo) return `https://github.com/${src.repo}`;\n\treturn src.url ?? null;\n}\n\n/** Resolve a plugin entry's source to a repo URL (+ optional subpath / sha). */\nfunction resolveSource(\n\tentry: ManifestPlugin,\n\tmpRepoUrl: string | null,\n): { url: string; path?: string; sha?: string } | null {\n\tconst src = entry.source;\n\tif (typeof src === \"string\") {\n\t\tif (!mpRepoUrl) return null;\n\t\tconst path = src.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n\t\treturn { url: mpRepoUrl, path: path || undefined };\n\t}\n\tif (src && typeof src === \"object\" && src.url) {\n\t\treturn { url: src.url, path: src.path, sha: src.sha };\n\t}\n\tconst fallback = entry.repository ?? entry.homepage ?? mpRepoUrl;\n\treturn fallback ? { url: fallback } : null;\n}\n\n/** Pure: map the parsed registry + manifests to plugin link resources. */\nexport function resolvePluginLinks(\n\tinstalled: InstalledPlugins,\n\tmarketplaces: KnownMarketplaces,\n\tmanifests: Record<string, Manifest>,\n): Resource[] {\n\tconst out: Resource[] = [];\n\tfor (const [key, entries] of Object.entries(installed.plugins ?? {})) {\n\t\tconst at = key.lastIndexOf(\"@\");\n\t\tif (at <= 0) continue;\n\t\tconst pluginName = key.slice(0, at);\n\t\tconst marketplace = key.slice(at + 1);\n\n\t\tconst mpRepoUrl = marketplaceRepoUrl(marketplaces[marketplace]);\n\t\tconst entry = manifests[marketplace]?.plugins?.find(\n\t\t\t(p) => p.name === pluginName,\n\t\t);\n\t\tif (!entry) continue;\n\n\t\tconst resolved = resolveSource(entry, mpRepoUrl);\n\t\tif (!resolved) continue;\n\n\t\tconst canonical = canonicalizeRepoUrl(resolved.url);\n\t\tif (!canonical) continue; // non-GitHub source → skip (graceful)\n\n\t\tout.push(\n\t\t\tbuildLinkResource({\n\t\t\t\tcanonical,\n\t\t\t\tpath: resolved.path,\n\t\t\t\tname: pluginName,\n\t\t\t\ttype: \"plugin\",\n\t\t\t\tgroup: \"claude-code\",\n\t\t\t\tsha: resolved.sha ?? entries[0]?.gitCommitSha,\n\t\t\t}),\n\t\t);\n\t}\n\treturn out;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** IO wrapper: read the registry + manifests, return plugin link resources. */\nexport function detectInstalledPlugins(\n\tpluginsDir: string = join(homedir(), \".claude\", \"plugins\"),\n): Resource[] {\n\tconst installed = readJson<InstalledPlugins>(\n\t\tjoin(pluginsDir, \"installed_plugins.json\"),\n\t);\n\tif (!installed?.plugins) return [];\n\n\tconst marketplaces =\n\t\treadJson<KnownMarketplaces>(join(pluginsDir, \"known_marketplaces.json\")) ??\n\t\t{};\n\n\tconst manifests: Record<string, Manifest> = {};\n\tfor (const key of Object.keys(installed.plugins)) {\n\t\tconst mp = key.slice(key.lastIndexOf(\"@\") + 1);\n\t\tif (!mp || manifests[mp]) continue;\n\t\tconst installLocation =\n\t\t\tmarketplaces[mp]?.installLocation ?? join(pluginsDir, \"marketplaces\", mp);\n\t\tconst manifest = readJson<Manifest>(\n\t\t\tjoin(installLocation, \".claude-plugin\", \"marketplace.json\"),\n\t\t);\n\t\tif (manifest) manifests[mp] = manifest;\n\t}\n\n\treturn resolvePluginLinks(installed, marketplaces, manifests);\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport ignore from \"ignore\";\n\nexport type FileType =\n\t| \"rule\"\n\t| \"mcp\"\n\t| \"skill\"\n\t| \"command\"\n\t| \"prompt\"\n\t| \"hook\"\n\t| \"subagent\"\n\t| \"config\"\n\t| \"custom\";\n\nexport interface ScannedFile {\n\tpath: string;\n\trelativePath: string;\n\tcontent: string;\n\ttype: FileType;\n\tsource: \"local\" | \"global\";\n\tgroup: string;\n}\n\nconst MAX_FILE_SIZE = 100 * 1024; // 100KB\n\ninterface FilePattern {\n\tpath: string;\n\ttype: FileType;\n\tgroup: string;\n}\n\nconst LOCAL_PATTERNS: FilePattern[] = [\n\t// Rules\n\t{ path: \"CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"AGENTS.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"GROK.md\", type: \"rule\", group: \"grok-build\" },\n\t{ path: \"GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t{ path: \".cursorrules\", type: \"rule\", group: \"cursor\" },\n\t{ path: \".windsurfrules\", type: \"rule\", group: \"windsurf\" },\n\t{ path: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ path: \".roorules\", type: \"rule\", group: \"roo\" },\n\t{ path: \".github/copilot-instructions.md\", type: \"rule\", group: \"copilot\" },\n\t// MCP servers are detected separately as pkg-reference links (see mcp.ts) -\n\t// their config files are intentionally NOT collected as content here (which\n\t// would also upload `env` secrets).\n\t// Config\n\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t{\n\t\tpath: \".claude/settings.local.json\",\n\t\ttype: \"config\",\n\t\tgroup: \"claude-code\",\n\t},\n\t// Prompts\n\t{ path: \"system-prompt.md\", type: \"prompt\", group: \"generic\" },\n];\n\nconst LOCAL_DIR_PATTERNS: { dir: string; type: FileType; group: string }[] = [\n\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t{ dir: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ dir: \".windsurf/rules\", type: \"rule\", group: \"windsurf\" },\n\t{ dir: \".roo/rules\", type: \"rule\", group: \"roo\" },\n\t{ dir: \".github/instructions\", type: \"rule\", group: \"copilot\" },\n\t{ dir: \".github/prompts\", type: \"prompt\", group: \"copilot\" },\n\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t{ dir: \".claude/skills\", type: \"skill\", group: \"claude-code\" },\n\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t{ dir: \".grok/skills\", type: \"skill\", group: \"grok-build\" },\n\t{ dir: \".grok/commands\", type: \"command\", group: \"grok-build\" },\n\t{ dir: \".grok/agents\", type: \"subagent\", group: \"grok-build\" },\n\t{ dir: \".grok/hooks\", type: \"hook\", group: \"grok-build\" },\n\t{ dir: \".cursor/skills\", type: \"skill\", group: \"cursor\" },\n\t{ dir: \".agents/skills\", type: \"skill\", group: \"generic\" },\n\t{ dir: \".agents/commands\", type: \"command\", group: \"generic\" },\n\t{ dir: \"prompts\", type: \"prompt\", group: \"generic\" },\n\t{ dir: \".ai\", type: \"custom\", group: \"generic\" },\n];\n\nfunction loadGitignore(cwd: string): ReturnType<typeof ignore> {\n\tconst ig = ignore();\n\tconst gitignorePath = join(cwd, \".gitignore\");\n\tif (existsSync(gitignorePath)) {\n\t\tig.add(readFileSync(gitignorePath, \"utf-8\"));\n\t}\n\tig.add([\"node_modules\", \".git\", \"dist\", \"build\", \".next\", \".output\"]);\n\treturn ig;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n\ttry {\n\t\tconst stat = statSync(filePath);\n\t\tif (stat.size > MAX_FILE_SIZE) return null;\n\t\treturn readFileSync(filePath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction walkDir(dir: string, maxDepth = 3, currentDepth = 0): string[] {\n\tif (currentDepth >= maxDepth || !existsSync(dir)) return [];\n\tconst results: string[] = [];\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tif (entry.isFile()) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\tresults.push(...walkDir(fullPath, maxDepth, currentDepth + 1));\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors, etc */\n\t}\n\treturn results;\n}\n\nexport function scanLocal(cwd: string): ScannedFile[] {\n\tconst ig = loadGitignore(cwd);\n\tconst results: ScannedFile[] = [];\n\n\tfor (const pattern of LOCAL_PATTERNS) {\n\t\tconst filePath = join(cwd, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (!ig.ignores(rel)) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: pattern.type,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: pattern.group,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { dir, type, group } of LOCAL_DIR_PATTERNS) {\n\t\tconst dirPath = join(cwd, dir);\n\t\tconst files = walkDir(dirPath);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Scan for skill directories (dirs with SKILL.md, 3 levels deep)\n\ttry {\n\t\tfor (const entry of readdirSync(cwd, { withFileTypes: true }).filter((e) =>\n\t\t\te.isDirectory(),\n\t\t)) {\n\t\t\tif ([\".grok\", \".claude\", \".cursor\", \".agents\"].includes(entry.name))\n\t\t\t\tcontinue;\n\t\t\tif (ig.ignores(`${entry.name}/`)) continue;\n\t\t\tscanSkillDirs(join(cwd, entry.name), cwd, ig, results, 1);\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n\n\treturn results;\n}\n\nfunction scanSkillDirs(\n\tdir: string,\n\tcwd: string,\n\tig: ReturnType<typeof ignore>,\n\tresults: ScannedFile[],\n\tdepth: number,\n) {\n\tif (depth > 3) return;\n\tconst skillMd = join(dir, \"SKILL.md\");\n\tif (existsSync(skillMd)) {\n\t\tconst files = walkDir(dir, 1);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: \"generic\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tconst rel = relative(cwd, join(dir, entry.name));\n\t\t\t\tif (!ig.ignores(`${rel}/`)) {\n\t\t\t\t\tscanSkillDirs(join(dir, entry.name), cwd, ig, results, depth + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n}\n\nexport function scanGlobal(): ScannedFile[] {\n\tconst home = homedir();\n\tconst results: ScannedFile[] = [];\n\n\tconst globalPatterns: FilePattern[] = [\n\t\t{ path: \".claude/CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t\t{ path: \".grok/GROK.md\", type: \"rule\", group: \"grok-build\" },\n\t\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t\t{ path: \".gemini/GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t\t{ path: \".gemini/settings.json\", type: \"config\", group: \"gemini\" },\n\t\t{ path: \".codex/config.toml\", type: \"config\", group: \"codex\" },\n\t\t{\n\t\t\tpath: \".codeium/windsurf/memories/global_rules.md\",\n\t\t\ttype: \"rule\",\n\t\t\tgroup: \"windsurf\",\n\t\t},\n\t];\n\n\tfor (const pattern of globalPatterns) {\n\t\tconst filePath = join(home, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tresults.push({\n\t\t\t\tpath: filePath,\n\t\t\t\trelativePath: `~/${pattern.path}`,\n\t\t\t\tcontent,\n\t\t\t\ttype: pattern.type,\n\t\t\t\tsource: \"global\",\n\t\t\t\tgroup: pattern.group,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst globalDirs: { dir: string; type: FileType; group: string }[] = [\n\t\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t\t{ dir: \".grok/skills\", type: \"skill\", group: \"grok-build\" },\n\t\t{ dir: \".grok/commands\", type: \"command\", group: \"grok-build\" },\n\t\t{ dir: \".grok/agents\", type: \"subagent\", group: \"grok-build\" },\n\t\t{ dir: \".grok/hooks\", type: \"hook\", group: \"grok-build\" },\n\t\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t\t{ dir: \".cursor/skills\", type: \"skill\", group: \"cursor\" },\n\t\t{ dir: \".agents/skills\", type: \"skill\", group: \"generic\" },\n\t\t{ dir: \".agents/commands\", type: \"command\", group: \"generic\" },\n\t];\n\n\tfor (const { dir, type, group } of globalDirs) {\n\t\tconst dirPath = join(home, dir);\n\t\tconst files = walkDir(dirPath, 2);\n\t\tfor (const filePath of files) {\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"global\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Global skills: ~/.claude/skills/<name>/SKILL.md (+ supporting files). Not\n\t// covered by globalDirs since each skill is its own dir keyed by SKILL.md.\n\tconst skillsRoot = join(home, \".claude\", \"skills\");\n\ttry {\n\t\tfor (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {\n\t\t\tif (!entry.isDirectory()) continue;\n\t\t\tconst skillDir = join(skillsRoot, entry.name);\n\t\t\tif (!existsSync(join(skillDir, \"SKILL.md\"))) continue;\n\t\t\tfor (const filePath of walkDir(skillDir, 2)) {\n\t\t\t\tconst content = readFileSafe(filePath);\n\t\t\t\tif (content !== null) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tpath: filePath,\n\t\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\t\tsource: \"global\",\n\t\t\t\t\t\tgroup: \"claude-code\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* skills dir absent / permission errors */\n\t}\n\n\treturn results;\n}\n","import * as p from \"@clack/prompts\";\n\nconst esc = (code: string) => `\\x1b[${code}m`;\nconst reset = esc(\"0\");\n\nconst LIME = \"163;230;53\";\nconst BLACK = \"0;0;0\";\nconst YELLOW = \"250;204;21\";\nconst RED = \"248;113;113\";\nconst MUTED = \"120;120;120\";\n\nexport const lime = (s: string) => `${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const limeBold = (s: string) =>\n\t`${esc(\"1\")}${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const bgLime = (s: string) =>\n\t`${esc(`48;2;${LIME}`)}${esc(`38;2;${BLACK}`)}${s}${reset}`;\nexport const yellow = (s: string) => `${esc(`38;2;${YELLOW}`)}${s}${reset}`;\nexport const red = (s: string) => `${esc(`38;2;${RED}`)}${s}${reset}`;\nexport const dim = (s: string) => `${esc(`38;2;${MUTED}`)}${s}${reset}`;\nexport const bold = (s: string) => `${esc(\"1\")}${s}${reset}`;\n\n// ■ logo square in lime + AISTACK in bold on lime bg\nexport const banner = (cmd: string) =>\n\t`${lime(\"■\")} ${bgLime(` AISTACK `)} ${bold(cmd.toUpperCase())}`;\n\n// Compact line with clack-style bar\nconst BAR = `${esc(`38;2;${MUTED}`)}│${reset}`;\n\nexport function lines(items: string[]) {\n\tfor (const item of items) {\n\t\tconsole.log(`${BAR} ${item}`);\n\t}\n}\n\nexport function section(label: string, count?: number) {\n\tconsole.log(`${BAR}`);\n\tconst countStr = count !== undefined ? ` ${dim(String(count))}` : \"\";\n\tconsole.log(`${BAR} ${bold(label.toUpperCase())}${countStr}`);\n}\n\nexport function divider() {\n\tconsole.log(`${BAR} ${dim(\"─\".repeat(40))}`);\n}\n\nexport function intro(cmd: string) {\n\tconsole.log();\n\tp.intro(banner(cmd));\n}\n\nexport function outro(msg: string) {\n\tp.outro(msg);\n\tconsole.log();\n}\n\nexport function outroError(msg: string) {\n\tp.outro(red(msg));\n\tconsole.log();\n}\n\nexport function outroCancel(msg = \"cancelled\") {\n\tp.cancel(dim(msg));\n\tconsole.log();\n}\n\nexport function outroSkipped(msg: string) {\n\tp.outro(dim(msg));\n\tconsole.log();\n}\n","// `aistack connect claude` - the opt-in in-session sync surface (#56, #57).\n//\n// Installs BOTH halves or NEITHER: the user-scoped MCP server registration and\n// the Skill copy travel together, because the Skill drives `sync_preview` /\n// `sync_publish` and has nothing to do without the server (#56 decision 3).\n// The harness argument leaves room for `connect codex` later without a rename.\n\nimport { spawnSync } from \"node:child_process\";\nimport { cpSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport { getSettings, saveSettings } from \"../config.js\";\nimport { claudeAdapter, detectionSinceMs } from \"../harness/index.js\";\nimport {\n\tdim,\n\tintro,\n\tlimeBold,\n\toutro,\n\toutroError,\n\toutroSkipped,\n} from \"../theme.js\";\n\n/** The documented manual install line, printed when we cannot run it. */\nexport const MANUAL_MCP_ADD =\n\t\"claude mcp add --scope user aistack -- npx -y @use-aistack/cli mcp\";\n\nconst MCP_ADD_ARGS = [\n\t\"mcp\",\n\t\"add\",\n\t\"--scope\",\n\t\"user\",\n\t\"aistack\",\n\t\"--\",\n\t\"npx\",\n\t\"-y\",\n\t\"@use-aistack/cli\",\n\t\"mcp\",\n];\n\nconst MCP_REMOVE_ARGS = [\"mcp\", \"remove\", \"--scope\", \"user\", \"aistack\"];\n\nexport const SKILL_DEST = join(homedir(), \".claude\", \"skills\", \"aistack-sync\");\n\nexport interface RunResult {\n\t/** The binary was not found on PATH. */\n\tnotFound: boolean;\n\tstatus: number | null;\n\toutput: string;\n}\n\nexport type Runner = (args: string[]) => RunResult;\n\nfunction runClaude(args: string[]): RunResult {\n\tconst r = spawnSync(\"claude\", args, { encoding: \"utf-8\" });\n\tconst notFound =\n\t\tr.error !== undefined &&\n\t\t(r.error as NodeJS.ErrnoException).code === \"ENOENT\";\n\treturn {\n\t\tnotFound,\n\t\tstatus: r.status,\n\t\toutput: `${r.stdout ?? \"\"}${r.stderr ?? \"\"}`,\n\t};\n}\n\n/** Is the `claude` binary reachable? Cheap check used to skip the upsell. */\nexport function claudeOnPath(run: Runner = runClaude): boolean {\n\treturn !run([\"--version\"]).notFound;\n}\n\n/**\n * The bundled Skill directory, resolved relative to this module. In the\n * published package that is `<pkg>/skills/aistack-sync` next to `dist/`; in\n * dev it is two levels up from `src/commands/`. Walking up covers both.\n */\nexport function findSkillSource(\n\tfromDir: string = dirname(fileURLToPath(import.meta.url)),\n): string | null {\n\tlet dir = fromDir;\n\tfor (let i = 0; i < 4; i++) {\n\t\tconst candidate = join(dir, \"skills\", \"aistack-sync\");\n\t\tif (existsSync(join(candidate, \"SKILL.md\"))) return candidate;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn null;\n}\n\nexport interface ConnectOutcome {\n\tok: boolean;\n\tmessage: string;\n}\n\n/**\n * Install the server registration, then the Skill. If the Skill copy fails\n * after a fresh registration, the registration is rolled back - both halves\n * or neither.\n */\nexport function installClaudeConnect(\n\trun: Runner = runClaude,\n\tcopySkill: (src: string, dest: string) => void = (src, dest) =>\n\t\tcpSync(src, dest, { recursive: true }),\n): ConnectOutcome {\n\tconst source = findSkillSource();\n\tif (source === null) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage:\n\t\t\t\t\"this install is missing its bundled Skill (skills/aistack-sync) - nothing was installed\",\n\t\t};\n\t}\n\n\tconst add = run(MCP_ADD_ARGS);\n\tif (add.notFound) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude was not found on PATH - nothing was installed. Manual install:\\n${MANUAL_MCP_ADD}`,\n\t\t};\n\t}\n\tconst alreadyRegistered =\n\t\tadd.status !== 0 && add.output.includes(\"already exists\");\n\tif (add.status !== 0 && !alreadyRegistered) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude mcp add failed - nothing was installed.\\n${add.output.trim()}`,\n\t\t};\n\t}\n\n\ttry {\n\t\tcopySkill(source, SKILL_DEST);\n\t} catch (e) {\n\t\t// Both halves or neither: a fresh registration without its Skill is\n\t\t// rolled back. A pre-existing registration is left as it was.\n\t\tif (!alreadyRegistered) run(MCP_REMOVE_ARGS);\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `copying the Skill to ${SKILL_DEST} failed - the MCP registration was ${\n\t\t\t\talreadyRegistered ? \"left as it was\" : \"rolled back\"\n\t\t\t}.\\n${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tmessage: `Installed the user-scoped aistack MCP server and Skill. Say ${limeBold('\"sync my stack\"')} in any Claude Code session. Every send still requires your confirmation. Remove it with: claude mcp remove --scope user aistack`,\n\t};\n}\n\nexport async function connectCommand(harness: string): Promise<void> {\n\tintro(\"connect\");\n\n\tif (harness !== \"claude\") {\n\t\toutroError(`unknown harness \"${harness}\" - supported: claude`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (!claudeOnPath()) {\n\t\tp.log.warn(\n\t\t\t`claude was not found on PATH. Manual install:\\n${dim(MANUAL_MCP_ADD)}\\nplus copy skills/aistack-sync from this package to ${dim(SKILL_DEST)}`,\n\t\t);\n\t\toutroSkipped(\"nothing was installed\");\n\t\treturn;\n\t}\n\n\tconst result = installClaudeConnect();\n\tif (!result.ok) {\n\t\toutroError(result.message);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tp.log.success(result.message);\n\toutro(\"done\");\n}\n\nexport interface UpsellDeps {\n\t/** Override the Claude activity check. Tests only. */\n\tclaudeActiveImpl?: () => Promise<boolean>;\n\t/** Override the PATH check. Tests only. */\n\tclaudeOnPathImpl?: () => boolean;\n\t/** Override the settings file. Tests only. */\n\tsettingsFile?: string;\n}\n\n/** Has Claude Code written a transcript inside the sync window? */\nexport function claudeRecentlyActive(): Promise<boolean> {\n\treturn claudeAdapter.detect({ sinceMs: detectionSinceMs() });\n}\n\n/**\n * The post-sync upsell (#56 decision 2), asked once per machine. Any explicit\n * answer persists to ~/.config/aistack/settings.json; ctrl-C is not an answer\n * and the question returns on the next sync.\n *\n * Two gates, both silent. The offer needs a Claude Code the user actually runs\n * (#101) - a months-old install asked a Codex-only user to connect a harness\n * they had left behind, which is what opened #100. It also needs `claude` on\n * PATH, because that binary is what installs the MCP server.\n */\nexport async function offerConnectUpsell(deps: UpsellDeps = {}): Promise<void> {\n\tif (getSettings(deps.settingsFile).connectClaudeAnswered === true) return;\n\tif (!(await (deps.claudeActiveImpl ?? claudeRecentlyActive)())) return;\n\tif (!(deps.claudeOnPathImpl ?? claudeOnPath)()) return;\n\n\tconst answer = await p.select({\n\t\tmessage:\n\t\t\t\"Add AI Stack commands to Claude Code? Every send still asks first.\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"No, don't ask again\",\n\t\t\t\thint: \"you can install later with aistack connect claude\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"install\",\n\t\t\t\tlabel: \"Install MCP + Skill\",\n\t\t\t\thint: \"user scope; enables preview and confirmed send commands\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"later\",\n\t});\n\n\tif (p.isCancel(answer)) return;\n\tsaveSettings({ connectClaudeAnswered: true }, deps.settingsFile);\n\n\tif (answer === \"install\") {\n\t\tconst result = installClaudeConnect();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn;\n\t}\n\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli connect claude\")}`,\n\t);\n}\n","// Harness-agnostic aggregate machinery: the fold target every adapter fills,\n// and the finalize step that turns it into display-ready rows.\n//\n// Extracted from the Claude analyzer by ticket #67 (map #60) so the Codex\n// adapter can reuse the same totals, name hygiene, and model rows without\n// inheriting Claude's record-dedup logic. Everything here is pure - no I/O,\n// no console.\n\nimport { isPricedModel, type TokenCounts } from \"@aistack/pricing\";\n\n// ---------------------------------------------------------------------------\n// Narrowing helpers - records are untrusted external JSON\n// ---------------------------------------------------------------------------\n\nexport type Obj = Record<string, unknown>;\n\nexport const asObj = (v: unknown): Obj | null =>\n\ttypeof v === \"object\" && v !== null && !Array.isArray(v) ? (v as Obj) : null;\nexport const asStr = (v: unknown): string | null =>\n\ttypeof v === \"string\" && v.length > 0 ? v : null;\nexport const asNum = (v: unknown): number =>\n\ttypeof v === \"number\" && Number.isFinite(v) ? v : 0;\nexport const asArr = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);\n\n/**\n * Every name that becomes a Map key or leaves this module goes through here.\n *\n * These are user-chosen strings (skill names, MCP servers, subagent types,\n * slash commands, model ids) and a hostile one is a real vector: control\n * characters move a terminal cursor, and an unterminated bidi override (U+202E)\n * reorders the rest of the rendered line - including the count and percentage\n * printed beside the name. Both survive `JSON.stringify`, which escapes C0 but\n * not bidi. See CVE-2021-42574 (\"Trojan Source\").\n *\n * Sanitizing at ingest rather than at print means the guarantee travels with\n * the module: `finalize()`'s output is safe for any consumer, not just the\n * renderer that happens to sit in front of it today.\n */\nconst NAME_UNSAFE_RE =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point\n\t/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\u2066-\\u2069\\ufeff]/g;\nconst NAME_MAX = 64;\n\nexport function cleanName(s: string): string {\n\tconst stripped = s.replace(NAME_UNSAFE_RE, \"�\").trim();\n\tif (stripped.length === 0) return \"(unnamed)\";\n\treturn stripped.length > NAME_MAX\n\t\t? `${stripped.slice(0, NAME_MAX - 1)}…`\n\t\t: stripped;\n}\n\n/**\n * The same bar as `cleanName`, asked as a question.\n *\n * Used on names arriving from the NETWORK - the per-stack opt-ins the sync\n * config carries (#44). Those are the user's own strings, so the curated list's\n * conventional charset is the wrong bar: parentheses, accents and CJK are all\n * legitimate names someone runs. What is refused is what cannot be rendered\n * safely, which is exactly what `cleanName` strips on the way in.\n */\nexport function isDisplaySafeName(s: string): boolean {\n\tif (s.length === 0 || s.trim().length === 0) return false;\n\tif (s.length > NAME_MAX) return false;\n\t// A `g`-flagged regex carries `lastIndex` across `.test` calls, so this uses\n\t// a fresh non-global copy rather than the shared literal.\n\treturn !new RegExp(NAME_UNSAFE_RE.source).test(s);\n}\n\n/** `asStr` for anything that will be used as a name. */\nexport const asName = (v: unknown): string | null => {\n\tconst s = asStr(v);\n\treturn s === null ? null : cleanName(s);\n};\n\n// ---------------------------------------------------------------------------\n// Aggregate\n// ---------------------------------------------------------------------------\n\nexport type ModelUsage = TokenCounts & {\n\tmessages: number;\n\t/**\n\t * API-equivalent cost accumulated per response at that response's own rate\n\t * (#33 decision 8). Not derivable from the token totals above once a window\n\t * straddles a repricing.\n\t */\n\tcostUSD: number;\n\t/** Tokens whose own timestamp had no citable rate. Surfaced, never zeroed. */\n\tunpricedTokens: number;\n};\n\n/**\n * The fold target. `Seen` is the adapter's own dedup bookkeeping type -\n * Claude keys responses by `message.id`, Codex needs none - kept generic so\n * the shared shape does not import any one harness's record semantics.\n */\nexport type Aggregate<Seen = unknown> = {\n\t// provenance / scan health\n\tfiles: number;\n\tlines: number;\n\tparseErrors: number;\n\trecords: number;\n\tassistantRecords: number;\n\t/** Distinct API responses actually counted. */\n\tdistinctResponses: number;\n\t/** Extra records of a response already counted (same message.id AND requestId). */\n\tcontinuationsFolded: number;\n\t/** Same message.id under a NEW requestId - a genuine replay (e.g. /btw sidechain). */\n\trealReplaysFolded: number;\n\t/** Times a later record superseded an earlier one because it carried a larger total. */\n\tsupersededByLarger: number;\n\t/** Assistant records with no message.id - counted without dedup protection. */\n\tunkeyedResponses: number;\n\tsyntheticRecords: number;\n\tsyntheticTokens: number;\n\ttoolBlocksWithoutId: number;\n\t/** Responses whose first attempt ran on a different model (#33 decision 9). */\n\tfallbackAttempts: number;\n\tuntypedMirrors: number;\n\t/** Records with no parseable timestamp - cannot be priced time-awarely. */\n\tuntimestampedResponses: number;\n\tprojectDirs: Set<string>; // held only to count - names never leave this module\n\tccVersions: Set<string>;\n\tmirroredIterationTypes: Map<string, number>;\n\n\t// tokens\n\tbyModel: Map<string, ModelUsage>;\n\tsidechainTokens: number;\n\tmainTokens: number;\n\n\t// activity\n\tsessions: Set<string>;\n\tactiveDays: Set<string>; // UTC YYYY-MM-DD\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\n\t// tools / skills / mcp / agents\n\ttoolCalls: Map<string, number>;\n\tskillCalls: Map<string, number>;\n\tmcpServerCalls: Map<string, number>;\n\tmcpToolCalls: Map<string, number>;\n\tsubagentCalls: Map<string, number>;\n\tslashCommands: Map<string, number>;\n\ttoolCallDedup: Set<string>;\n\n\t// content-block shape\n\tthinkingBlocks: number;\n\ttextBlocks: number;\n\twebSearchRequests: number;\n\twebFetchRequests: number;\n\n\t// adapter-owned dedup bookkeeping\n\tseen: Map<string, Seen>;\n\n\t// THE PER-DAY SEAM (#307, ADR-0010). The same response stream that fills\n\t// the window totals above also lands in one bucket per UTC date here, so a\n\t// fold over the days equals the window up to rounding. Nothing in these\n\t// maps is a share or a mean: `usage/days.ts` turns them into the wire.\n\tusageDays: Map<string, UsageDayAcc>;\n\t/** Session id -> earliest in-window timestamp. A session belongs to the day it STARTED. */\n\tsessionStarts: Map<string, number>;\n};\n\n/** One model's response sums inside one UTC day. */\nexport type UsageDayModel = {\n\tcounts: TokenCounts;\n\t/** Sum of the responses that had a citable rate at their own timestamp. */\n\tcostUSD: number;\n\t/** Tokens of the responses that had none. Surfaced, never zeroed. */\n\tunpricedTokens: number;\n};\n\nexport type UsageDayAcc = {\n\tmodels: Map<string, UsageDayModel>;\n\t/** Tokens of sidechain (subagent) responses, all models. */\n\tsubagentTokens: number;\n\tsyntheticTokens: number;\n\t/** Local project directories touched this day. Hashed before they leave. */\n\tprojectDirs: Set<string>;\n};\n\nexport const utcDateOf = (ms: number): string =>\n\tnew Date(ms).toISOString().slice(0, 10);\n\nfunction usageDayAcc(agg: Aggregate<never> | Aggregate<unknown>, tsMs: number) {\n\tconst date = utcDateOf(tsMs);\n\tlet day = agg.usageDays.get(date);\n\tif (!day) {\n\t\tday = {\n\t\t\tmodels: new Map(),\n\t\t\tsubagentTokens: 0,\n\t\t\tsyntheticTokens: 0,\n\t\t\tprojectDirs: new Set(),\n\t\t};\n\t\tagg.usageDays.set(date, day);\n\t}\n\treturn day;\n}\n\n/**\n * Land one response's tokens on the day of its own timestamp. `sign` is -1\n * when an adapter retracts a response it counted before (Claude's dedup). An\n * untimestamped response has no day and stays in the window totals only.\n */\nexport function noteUsageResponse(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tresponse: {\n\t\ttsMs: number | null;\n\t\tmodelKey: string;\n\t\tcounts: TokenCounts;\n\t\tcostUSD: number | null;\n\t\tsidechain?: boolean;\n\t},\n\tsign: 1 | -1 = 1,\n): void {\n\tif (response.tsMs === null) return;\n\tconst day = usageDayAcc(agg, response.tsMs);\n\tlet m = day.models.get(response.modelKey);\n\tif (!m) {\n\t\tm = {\n\t\t\tcounts: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheWrite5m: 0,\n\t\t\t\tcacheWrite1h: 0,\n\t\t\t\tcacheWriteUnsplit: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t},\n\t\t\tcostUSD: 0,\n\t\t\tunpricedTokens: 0,\n\t\t};\n\t\tday.models.set(response.modelKey, m);\n\t}\n\tconst c = response.counts;\n\tm.counts.input += sign * c.input;\n\tm.counts.output += sign * c.output;\n\tm.counts.cacheWrite5m += sign * c.cacheWrite5m;\n\tm.counts.cacheWrite1h += sign * c.cacheWrite1h;\n\tm.counts.cacheWriteUnsplit += sign * c.cacheWriteUnsplit;\n\tm.counts.cacheRead += sign * c.cacheRead;\n\tif (response.costUSD === null) m.unpricedTokens += sign * countsTotal(c);\n\telse m.costUSD += sign * response.costUSD;\n\tif (response.sidechain) day.subagentTokens += sign * countsTotal(c);\n}\n\nexport function noteSyntheticTokens(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\ttsMs: number | null,\n\ttokens: number,\n): void {\n\tif (tsMs === null) return;\n\tusageDayAcc(agg, tsMs).syntheticTokens += tokens;\n}\n\n/** Remember the earliest timestamp seen for a session: that is the day it started. */\nexport function noteSessionStart(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tsessionId: string,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null) return;\n\tconst held = agg.sessionStarts.get(sessionId);\n\tif (held === undefined || tsMs < held) agg.sessionStarts.set(sessionId, tsMs);\n}\n\n/** A project belongs to every day it was touched. */\nexport function noteProjectDay(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tdirectory: string,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null) return;\n\tusageDayAcc(agg, tsMs).projectDirs.add(directory);\n}\n\nexport function createAggregate<Seen = unknown>(): Aggregate<Seen> {\n\treturn {\n\t\tfiles: 0,\n\t\tlines: 0,\n\t\tparseErrors: 0,\n\t\trecords: 0,\n\t\tassistantRecords: 0,\n\t\tdistinctResponses: 0,\n\t\tcontinuationsFolded: 0,\n\t\trealReplaysFolded: 0,\n\t\tsupersededByLarger: 0,\n\t\tunkeyedResponses: 0,\n\t\tsyntheticRecords: 0,\n\t\tsyntheticTokens: 0,\n\t\ttoolBlocksWithoutId: 0,\n\t\tfallbackAttempts: 0,\n\t\tuntypedMirrors: 0,\n\t\tuntimestampedResponses: 0,\n\t\tprojectDirs: new Set(),\n\t\tccVersions: new Set(),\n\t\tmirroredIterationTypes: new Map(),\n\t\tbyModel: new Map(),\n\t\tsidechainTokens: 0,\n\t\tmainTokens: 0,\n\t\tsessions: new Set(),\n\t\tactiveDays: new Set(),\n\t\tfirstTs: null,\n\t\tlastTs: null,\n\t\ttoolCalls: new Map(),\n\t\tskillCalls: new Map(),\n\t\tmcpServerCalls: new Map(),\n\t\tmcpToolCalls: new Map(),\n\t\tsubagentCalls: new Map(),\n\t\tslashCommands: new Map(),\n\t\ttoolCallDedup: new Set(),\n\t\tthinkingBlocks: 0,\n\t\ttextBlocks: 0,\n\t\twebSearchRequests: 0,\n\t\twebFetchRequests: 0,\n\t\tseen: new Map(),\n\t\tusageDays: new Map(),\n\t\tsessionStarts: new Map(),\n\t};\n}\n\nexport const bump = (m: Map<string, number>, k: string, n = 1) =>\n\tm.set(k, (m.get(k) ?? 0) + n);\n\nexport function emptyUsage(): ModelUsage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: 0,\n\t\tmessages: 0,\n\t\tcostUSD: 0,\n\t\tunpricedTokens: 0,\n\t};\n}\n\nexport const countsTotal = (t: TokenCounts): number =>\n\tt.input +\n\tt.output +\n\tt.cacheWrite5m +\n\tt.cacheWrite1h +\n\tt.cacheWriteUnsplit +\n\tt.cacheRead;\n\n/**\n * Fold one priced usage delta into the per-model totals. The Claude adapter\n * has its own apply/retract pair (dedup can un-count a response); an adapter\n * whose records are already deltas - Codex - adds through here.\n */\nexport function addModelUsage(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\tcostUSD: number | null,\n\tmessages = 1,\n\t/**\n\t * Where the response sits in time, for the per-day seam (#307). An adapter\n\t * that passes nothing keeps the window totals right and lands no day.\n\t */\n\tat?: { tsMs: number | null; sidechain?: boolean },\n): void {\n\tif (at) {\n\t\tnoteUsageResponse(agg, {\n\t\t\ttsMs: at.tsMs,\n\t\t\tmodelKey,\n\t\t\tcounts,\n\t\t\tcostUSD,\n\t\t\t...(at.sidechain ? { sidechain: true } : {}),\n\t\t});\n\t}\n\tlet m = agg.byModel.get(modelKey);\n\tif (!m) {\n\t\tm = emptyUsage();\n\t\tagg.byModel.set(modelKey, m);\n\t}\n\tm.messages += messages;\n\tm.input += counts.input;\n\tm.output += counts.output;\n\tm.cacheWrite5m += counts.cacheWrite5m;\n\tm.cacheWrite1h += counts.cacheWrite1h;\n\tm.cacheWriteUnsplit += counts.cacheWriteUnsplit;\n\tm.cacheRead += counts.cacheRead;\n\tif (costUSD === null) m.unpricedTokens += countsTotal(counts);\n\telse m.costUSD += costUSD;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize - the shape the wire payload is derived from\n// ---------------------------------------------------------------------------\n\nexport type ModelRow = {\n\t/** Pricing key: normalized vendor id, plus `#fast` when speed was fast. */\n\tmodelKey: string;\n\ttokens: TokenCounts;\n\ttotalTokens: number;\n\tmessages: number;\n\tshare: number;\n\t/** Accumulated at each response's own rate. `null` when nothing was priced. */\n\tcostUSD: number | null;\n\t/** Tokens inside this row that no rate covered. */\n\tunpricedTokens: number;\n};\n\nexport type Finalized = {\n\tmodels: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n\tcacheHitShare: number;\n\tsidechainShare: number;\n\tactiveDays: number;\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\tsessions: number;\n\tprojects: number;\n\ttools: Array<[string, number]>;\n\tskills: Array<[string, number]>;\n\tmcpServers: Array<[string, number]>;\n\tsubagents: Array<[string, number]>;\n\tslashCommands: Array<[string, number]>;\n\ttotalToolCalls: number;\n\t/** Newest harness version observed, or null when none was recorded. */\n\tharnessVersion: string | null;\n};\n\nfunction buildModelRows(agg: Aggregate): {\n\trows: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n} {\n\tconst rows: ModelRow[] = [];\n\tlet totalTokens = 0;\n\tlet totalCostUSD = 0;\n\tconst unpricedModels: string[] = [];\n\tlet unpricedTokens = 0;\n\n\tfor (const [modelKey, u] of agg.byModel) {\n\t\tconst tokens: TokenCounts = {\n\t\t\tinput: u.input,\n\t\t\toutput: u.output,\n\t\t\tcacheWrite5m: u.cacheWrite5m,\n\t\t\tcacheWrite1h: u.cacheWrite1h,\n\t\t\tcacheWriteUnsplit: u.cacheWriteUnsplit,\n\t\t\tcacheRead: u.cacheRead,\n\t\t};\n\t\tconst sum = countsTotal(tokens);\n\t\ttotalTokens += sum;\n\t\tif (u.unpricedTokens > 0) {\n\t\t\tunpricedModels.push(modelKey);\n\t\t\tunpricedTokens += u.unpricedTokens;\n\t\t}\n\t\ttotalCostUSD += u.costUSD;\n\t\trows.push({\n\t\t\tmodelKey,\n\t\t\ttokens,\n\t\t\ttotalTokens: sum,\n\t\t\tmessages: u.messages,\n\t\t\tshare: 0,\n\t\t\t// A model we hold no rate for at all reports null rather than $0.00,\n\t\t\t// so \"we can't price this\" never reads as \"this was free\".\n\t\t\tcostUSD: isPricedModel(modelKey) ? u.costUSD : null,\n\t\t\tunpricedTokens: u.unpricedTokens,\n\t\t});\n\t}\n\tfor (const r of rows) r.share = totalTokens ? r.totalTokens / totalTokens : 0;\n\trows.sort(\n\t\t(a, b) =>\n\t\t\tb.totalTokens - a.totalTokens || a.modelKey.localeCompare(b.modelKey),\n\t);\n\treturn { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens };\n}\n\nfunction computeCacheHitShare(rows: ModelRow[]): number {\n\tlet cacheRead = 0;\n\tlet inputClass = 0;\n\tfor (const r of rows) {\n\t\tcacheRead += r.tokens.cacheRead;\n\t\tinputClass +=\n\t\t\tr.tokens.input +\n\t\t\tr.tokens.cacheRead +\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t}\n\treturn inputClass ? cacheRead / inputClass : 0;\n}\n\n/**\n * Newest observed harness version, compared numerically per dotted segment\n * so `2.1.9` doesn't sort above `2.1.220`.\n */\nexport function newestVersion(versions: Iterable<string>): string | null {\n\tlet best: string | null = null;\n\tlet bestParts: number[] = [];\n\tfor (const v of versions) {\n\t\tconst parts = v.split(\".\").map((p) => Number.parseInt(p, 10));\n\t\tif (parts.some((n) => !Number.isFinite(n))) continue;\n\t\tif (best === null || compareParts(parts, bestParts) > 0) {\n\t\t\tbest = v;\n\t\t\tbestParts = parts;\n\t\t}\n\t}\n\treturn best;\n}\n\nfunction compareParts(a: number[], b: number[]): number {\n\tconst len = Math.max(a.length, b.length);\n\tfor (let i = 0; i < len; i++) {\n\t\tconst d = (a[i] ?? 0) - (b[i] ?? 0);\n\t\tif (d !== 0) return d;\n\t}\n\treturn 0;\n}\n\nexport function finalize(agg: Aggregate): Finalized {\n\tconst { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens } =\n\t\tbuildModelRows(agg);\n\n\tconst byCount = (m: Map<string, number>): Array<[string, number]> =>\n\t\t[...m.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));\n\n\tlet totalToolCalls = 0;\n\tfor (const v of agg.toolCalls.values()) totalToolCalls += v;\n\tfor (const v of agg.mcpToolCalls.values()) totalToolCalls += v;\n\n\tconst sideTotal = agg.sidechainTokens + agg.mainTokens;\n\n\treturn {\n\t\tmodels: rows,\n\t\ttotalTokens,\n\t\ttotalCostUSD,\n\t\tunpricedModels,\n\t\tunpricedTokens,\n\t\tcacheHitShare: computeCacheHitShare(rows),\n\t\tsidechainShare: sideTotal ? agg.sidechainTokens / sideTotal : 0,\n\t\tactiveDays: agg.activeDays.size,\n\t\tfirstTs: agg.firstTs,\n\t\tlastTs: agg.lastTs,\n\t\tsessions: agg.sessions.size,\n\t\tprojects: agg.projectDirs.size,\n\t\ttools: byCount(agg.toolCalls),\n\t\tskills: byCount(agg.skillCalls),\n\t\tmcpServers: byCount(agg.mcpServerCalls),\n\t\tsubagents: byCount(agg.subagentCalls),\n\t\tslashCommands: byCount(agg.slashCommands),\n\t\ttotalToolCalls,\n\t\tharnessVersion: newestVersion(agg.ccVersions),\n\t};\n}\n","// The bundled curated allowlist - the fallback copy for `/api/sync-config`.\n//\n// Wayfinder ticket #37 (map #29), decision 4 of the wire-format grilling #33.\n//\n// WHAT BELONGS HERE, AND WHY IT IS SHORT\n// These four classes of name are user-chosen. A Skill called `acme-q3-pricing`,\n// an MCP server called `internal-billing`, a subagent called `client-migration`\n// - each is a real leak, and none of them is distinguishable from a public name\n// by shape.\n//\n// THE BAR (grilling #42): a name qualifies if the STRING carries no private\n// information no matter who typed it. That is a property of the string, not of\n// the user and not of the artifact.\n//\n// The bar is deliberately NOT \"the name identifies a public artifact, so\n// publishing it reveals nothing the user hasn't already published\". That was the\n// original wording and it is wrong: `stripe` is on this list, and publishing it\n// plainly does reveal something the user never published - that they use Stripe.\n// It cannot be the harm, because revealing what you use is the entire product.\n// The harm is narrower: strings drawn from the user's private vocabulary, which\n// leak a relationship (an employer, a client, a codename) rather than a\n// preference. `stripe` and `filesystem` are safe even for someone who named\n// their own server that by coincidence.\n//\n// Three sources meet that bar:\n// 1. Claude Code's own built-in subagent types and slash commands (vendor-\n// assigned, same class as a built-in tool name).\n// 2. Skills that ship with Claude Code itself.\n// 3. MCP servers with a public, documented, first-party endpoint.\n//\n// WHY THIS LIST DOES NOT NEED TO BE LONG (#42 decision 1)\n// It is no longer the only road to publishing a name. The approve gate offers\n// every kept-private name as an explicit, default-off tick, and the tick set\n// comes back down with the rest of the sync config. This list only exists to\n// spare a user from ticking boxes nobody would think twice about - so it can\n// stay strict, and every user-chosen name goes through the person who knows\n// whether it is a secret.\n//\n// The author's own `alp-river:*` plugin is deliberately NOT seeded, even though\n// it is genuinely published. This list is GLOBAL: seeding it would publish those\n// names for every user who installs the plugin without any of them ticking\n// anything, and an author adding their own names to the default everyone else\n// inherits is what would make the list untrustworthy for every other entry.\n//\n// `/api/sync-config` (ticket #38) serves the AUTHORITATIVE list. This copy only\n// covers the case where that endpoint can't be reached, which for an installed\n// user is permanent if the plugin never auto-updates. Growing the curated list\n// is server-side work; adding entries here only helps the offline case.\n\nimport type { CuratedAllowlist } from \"./allowlist.js\";\n\n/** Claude Code's own subagent types. Vendor-assigned, not user-chosen. */\nconst BUILTIN_SUBAGENTS = [\n\t\"(default)\",\n\t\"claude\",\n\t\"claude-code-guide\",\n\t\"Explore\",\n\t\"fork\",\n\t\"general-purpose\",\n\t\"Plan\",\n\t\"statusline-setup\",\n] as const;\n\n/** Skills bundled with Claude Code. */\nconst BUILTIN_SKILLS = [\n\t\"artifact-capabilities\",\n\t\"artifact-design\",\n\t\"claude-api\",\n\t\"code-review\",\n\t\"codebase-design\",\n\t\"dataviz\",\n\t\"diagnosing-bugs\",\n\t\"domain-modeling\",\n\t\"fewer-permission-prompts\",\n\t\"grilling\",\n\t\"init\",\n\t\"keybindings-help\",\n\t\"loop\",\n\t\"prototype\",\n\t\"research\",\n\t\"review\",\n\t\"run\",\n\t\"schedule\",\n\t\"security-review\",\n\t\"simplify\",\n\t\"tdd\",\n\t\"update-config\",\n] as const;\n\n/** Claude Code's own slash commands. */\nconst BUILTIN_SLASH_COMMANDS = [\n\t\"add-dir\",\n\t\"agents\",\n\t\"bug\",\n\t\"clear\",\n\t\"compact\",\n\t\"config\",\n\t\"context\",\n\t\"cost\",\n\t\"doctor\",\n\t\"effort\",\n\t\"exit\",\n\t\"export\",\n\t\"fast\",\n\t\"help\",\n\t\"hooks\",\n\t\"ide\",\n\t\"init\",\n\t\"login\",\n\t\"logout\",\n\t\"mcp\",\n\t\"memory\",\n\t\"model\",\n\t\"output-style\",\n\t\"permissions\",\n\t\"plugin\",\n\t\"privacy-settings\",\n\t\"release-notes\",\n\t\"resume\",\n\t\"review\",\n\t\"rewind\",\n\t\"security-review\",\n\t\"status\",\n\t\"statusline\",\n\t\"terminal-setup\",\n\t\"todos\",\n\t\"upgrade\",\n\t\"usage\",\n\t\"vim\",\n\t\"workflows\",\n] as const;\n\n/**\n * MCP servers with a public first-party endpoint.\n *\n * Matched against the server segment the analyzer parses out of an\n * `mcp__<server>__<tool>` name, which is the LOCAL alias the user configured -\n * so this only fires when the user kept the conventional name. A renamed server\n * is kept private, which is the correct direction to fail.\n *\n * ONE normalization applies first (#42 decision 5): a server provided by a\n * plugin is observed as `plugin_<plugin>_<server>`, a string Claude Code\n * generates rather than one the user typed. Strip that wrapper before matching,\n * and publish the NORMALIZED name. The safety property is that normalization can\n * only ever emit a string already on this list - a non-matching inner segment\n * emits nothing and the raw name falls through to the gate's review list - so a\n * bug here is bounded by an already-vetted set. If the upstream convention\n * changes, matching reverts to keeping names private: a fail-safe regression.\n */\nconst PUBLIC_MCP_SERVERS = [\n\t\"chrome-devtools\",\n\t\"context7\",\n\t\"deepwiki\",\n\t\"figma\",\n\t\"filesystem\",\n\t\"git\",\n\t\"github\",\n\t\"huggingface\",\n\t\"ide\",\n\t\"linear\",\n\t\"notion\",\n\t\"playwright\",\n\t\"puppeteer\",\n\t\"sentry\",\n\t\"slack\",\n\t\"stripe\",\n] as const;\n\nexport const BUNDLED_CURATED_ALLOWLIST: CuratedAllowlist = {\n\tmcpServers: PUBLIC_MCP_SERVERS,\n\tskills: BUILTIN_SKILLS,\n\tsubagents: BUILTIN_SUBAGENTS,\n\tslashCommands: BUILTIN_SLASH_COMMANDS,\n};\n","// Fail-closed name filtering for the measured layer.\n//\n// Wayfinder ticket #37 (map #29), decisions 2-4 of the wire-format grilling #33.\n//\n// THE INVERSION THIS FILE EXISTS TO PERFORM\n// The prototype's `toolCalls` map was a catch-all: anything that wasn't an\n// `mcp__*` tool, a Skill, or an Agent fell THROUGH into it, and from there into\n// the payload. That is denylist-shaped - a tool name nobody anticipated\n// publishes by default. Here a name publishes only if it matches a known list,\n// and everything else is withheld and published as a per-category count.\n//\n// Two classes of name, two mechanisms:\n// - Built-in Claude Code tool names are VENDOR-assigned and enumerable, so\n// they match a hardcoded literal set (BUILTIN_TOOLS below).\n// - MCP servers / Skills / subagents / slash commands are USER-chosen and can\n// carry a client name, a project codename, or an internal system's name.\n// They match a curated list fetched from aistack, with the bundled copy\n// below as the fallback.\n//\n// Model ids are exempt from all of this - see decision 3 and payload.ts.\n//\n// WHY FETCHED AND NOT ONLY BUNDLED (decision 4)\n// Third-party marketplace plugin auto-update defaults to OFF, and a\n// `plugin.json` whose `version` isn't bumped ships nothing. A bundled-only list\n// is, for an installed user, frozen forever - a Skill that becomes public next\n// month would never publish. The filtering itself still runs client-side:\n// fail-closed only means something if it happens before the send.\n\nimport { isDisplaySafeName } from \"./aggregate.js\";\nimport { BUNDLED_CURATED_ALLOWLIST } from \"./bundled-allowlist.js\";\n\n/**\n * Every built-in tool Claude Code can emit as a `tool_use` block name.\n *\n * Deliberately a literal set and not a pattern: a pattern is a denylist wearing\n * a hat. Grounded in the observed corpus (22 distinct names across 235,961\n * records) plus the documented tool surface, including tools that are deferred\n * or unavailable in most sessions - an unknown-but-real built-in withheld as a\n * count is a small loss; an unknown-and-user-named tool published verbatim is\n * the leak this whole file prevents.\n *\n * `Task` is the pre-rename spelling of `Agent`; the analyzer folds it into\n * `Agent` at ingest, so it is here only to make the set self-documenting.\n */\nexport const BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"Agent\",\n\t\"Artifact\",\n\t\"AskUserQuestion\",\n\t\"Bash\",\n\t\"BashOutput\",\n\t\"CronCreate\",\n\t\"CronDelete\",\n\t\"CronList\",\n\t\"DesignSync\",\n\t\"Edit\",\n\t\"EndConversation\",\n\t\"EnterPlanMode\",\n\t\"EnterWorktree\",\n\t\"ExitPlanMode\",\n\t\"ExitWorktree\",\n\t\"Glob\",\n\t\"Grep\",\n\t\"KillBash\",\n\t\"KillShell\",\n\t\"ListMcpResourcesTool\",\n\t\"LS\",\n\t\"Monitor\",\n\t\"MultiEdit\",\n\t\"NotebookEdit\",\n\t\"NotebookRead\",\n\t\"PushNotification\",\n\t\"Read\",\n\t\"ReadMcpResourceDirTool\",\n\t\"ReadMcpResourceTool\",\n\t\"RemoteTrigger\",\n\t\"ReportFindings\",\n\t\"ScheduleWakeup\",\n\t\"SendMessage\",\n\t\"SendUserFile\",\n\t\"Skill\",\n\t\"SlashCommand\",\n\t\"Task\",\n\t\"TaskCreate\",\n\t\"TaskGet\",\n\t\"TaskList\",\n\t\"TaskOutput\",\n\t\"TaskStop\",\n\t\"TaskUpdate\",\n\t\"TodoWrite\",\n\t\"ToolSearch\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n\t\"Workflow\",\n\t\"Write\",\n]);\n\n/** The four user-chosen atom classes that need the curated list. */\nexport type CuratedAllowlist = {\n\tmcpServers: readonly string[];\n\tskills: readonly string[];\n\tsubagents: readonly string[];\n\tslashCommands: readonly string[];\n};\n\n/** The five inventory classes the payload carries. */\nexport const NAME_CATEGORIES = [\n\t\"builtinTools\",\n\t\"mcpServers\",\n\t\"skills\",\n\t\"subagents\",\n\t\"slashCommands\",\n] as const;\n\nexport type NameCategory = (typeof NAME_CATEGORIES)[number];\n\n/**\n * Names this stack's owner has explicitly ticked for publication (#42\n * decision 1), served per-stack by the authenticated half of `/api/sync-config`.\n *\n * The curated list is a convenience default, not the coverage mechanism: every\n * user-chosen name class is unbounded and unenumerable, so a hand-curated list\n * can only ever be a rounding error against the real population. Coverage comes\n * from here - from the person who knows which of their names are secret.\n *\n * `builtinTools` is included for symmetry even though that class is\n * vendor-assigned: a built-in this version of the client has never heard of is\n * kept private like anything else, and the owner can tick it.\n */\nexport type OptInNames = Record<NameCategory, readonly string[]>;\n\nexport const EMPTY_OPT_INS: OptInNames = {\n\tbuiltinTools: [],\n\tmcpServers: [],\n\tskills: [],\n\tsubagents: [],\n\tslashCommands: [],\n};\n\nexport type SyncConfig = {\n\tallowlist: CuratedAllowlist;\n\t/**\n\t * Stack-level cost preference (decision 11). When false the payload omits\n\t * cost entirely rather than zeroing it - see payload.ts.\n\t */\n\tpublishCost: boolean;\n\t/** Per-stack ticked names, unioned into the allowlist before filtering. */\n\toptIns: OptInNames;\n\t/**\n\t * Whether this stack stages its kept-private names on the web so the owner\n\t * can tick them there (#48). Off means the machine sends the payload alone\n\t * and the names never leave it.\n\t */\n\treviewKeptPrivate: boolean;\n\t/**\n\t * Whether the measured workflow section publishes (#213).\n\t *\n\t * The third bit in this family and the same shape as the two above: a\n\t * stack-level preference, default on, applied here on the machine. Off means\n\t * `buildSyncBody` leaves the section out of the bytes entirely.\n\t */\n\tpublishWorkflow: boolean;\n\t/**\n\t * The stack the bearer token is bound to - where a publish would land.\n\t *\n\t * The approve gate must name its destination BEFORE the send (#33\n\t * decision 7, #41), and beat one points at `/stacks/{slug}/changes` (#48),\n\t * so both ride on the authenticated half of the config fetch. `null` when\n\t * the fetch was anonymous, failed, or the token resolved no stack - and a\n\t * gate that cannot name its destination must not publish.\n\t */\n\tstack: { name: string; slug: string } | null;\n\t/**\n\t * The auto-sync permission the STACK holds (#102, read by #103).\n\t *\n\t * Three states, not two, and the third is the whole point. `null` means no\n\t * owner has ever decided, and that is the one state a machine's local opt-in\n\t * may still seed. `{ enabled: false }` means the owner said no, and\n\t * `sync --auto` publishes nothing on this machine until they say otherwise.\n\t *\n\t * `frequencyHours` is absent when the value could not be read - see\n\t * `readAutoSync`.\n\t */\n\tautoSync: AutoSyncPermission | null;\n};\n\n/** What the stack allows, as the CLI reads it off the wire. */\nexport type AutoSyncPermission = {\n\tenabled: boolean;\n\tfrequencyHours?: number;\n};\n\n/**\n * Used when `/api/sync-config` can't be reached.\n *\n * `publishCost: false` is deliberate. The toggle is a stack-level preference we\n * do not hold locally, and the fail-closed default for a preference we can't\n * read is the one that transmits less. A user whose fetch failed sees cost\n * missing from the gate and can retry; the reverse - publishing cost the stack\n * had opted out of - is not recoverable, because the snapshot is immutable.\n */\nexport const BUNDLED_SYNC_CONFIG: SyncConfig = {\n\tallowlist: BUNDLED_CURATED_ALLOWLIST,\n\tpublishCost: false,\n\t// Empty for the same reason, and it is the load-bearing half of #42\n\t// decision 2: a failed config fetch reverts every ticked name to\n\t// kept-private. Losing the network publishes LESS, never more.\n\toptIns: EMPTY_OPT_INS,\n\t// Same direction again (#48): a machine that cannot read the switch does not\n\t// upload the names it is holding back. The default is ON server-side, so this\n\t// costs the owner one retry and never costs them a name.\n\treviewKeptPrivate: false,\n\t// Fail closed a third time (#213). A machine that could not read the switch\n\t// publishes measurement and no workflow section, which costs the owner one\n\t// retry and can never publish a section they had turned off.\n\tpublishWorkflow: false,\n\t// No fetch, no destination - and the gate refuses to publish without one.\n\tstack: null,\n\t// No fetch, no permission either. This costs nothing on its own: `stack` is\n\t// null in the same breath, so the stage blocks before any publish.\n\tautoSync: null,\n};\n\n// ---------------------------------------------------------------------------\n// Fetch\n// ---------------------------------------------------------------------------\n\nconst SYNC_CONFIG_PATH = \"/api/sync-config\";\nconst FETCH_TIMEOUT_MS = 5_000;\n\nexport type SyncConfigSource = \"fetched\" | \"bundled\";\n\nexport type LoadedSyncConfig = {\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Present when the fetch failed and the bundled copy was used. */\n\terror?: string;\n};\n\n/**\n * A name arriving from the network is no more trusted than one from a\n * transcript. Names are matched by exact equality, so a hostile list can widen\n * what publishes but can never smuggle a wildcard - and the approve gate\n * renders every name that will publish, which is what defuses that residual\n * trust (decision 4). Charset and length are still bounded so a pathological\n * entry can't reach a terminal or a database column.\n */\nconst CURATED_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9 ._:@/-]{0,63}$/;\n\nfunction readNameList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && CURATED_NAME_RE.test(item)) out.push(item);\n\t}\n\treturn out;\n}\n\n/**\n * Opt-ins are read against a LOOSER bar than the curated list.\n *\n * A curated entry is ours and conventional, so the tight charset costs nothing.\n * An opt-in is the user's own name - `(default)`, an accented word, a CJK skill\n * - and dropping it here would silently un-tick a decision they made at the\n * gate. The bar that survives is the one that matters for a string we print and\n * store: no control characters, no bidi overrides, bounded length.\n */\nfunction readOptInList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && isDisplaySafeName(item)) out.push(item);\n\t}\n\treturn out;\n}\n\nfunction readOptIns(v: unknown): OptInNames {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v))\n\t\treturn EMPTY_OPT_INS;\n\tconst obj = v as Record<string, unknown>;\n\treturn {\n\t\tbuiltinTools: readOptInList(obj.builtinTools),\n\t\tmcpServers: readOptInList(obj.mcpServers),\n\t\tskills: readOptInList(obj.skills),\n\t\tsubagents: readOptInList(obj.subagents),\n\t\tslashCommands: readOptInList(obj.slashCommands),\n\t};\n}\n\n/**\n * A slug becomes a URL path segment the gate prints, so it gets the tightest\n * bar of any string here. The name is display text and gets `isDisplaySafeName`.\n */\nconst STACK_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;\n\nfunction readStack(v: unknown): SyncConfig[\"stack\"] {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v)) return null;\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.name !== \"string\" || !isDisplaySafeName(obj.name)) return null;\n\tif (typeof obj.slug !== \"string\" || !STACK_SLUG_RE.test(obj.slug))\n\t\treturn null;\n\treturn { name: obj.name, slug: obj.slug };\n}\n\n/**\n * Read the stack's auto-sync permission (#103).\n *\n * ABSENT AND OFF ARE DIFFERENT ANSWERS. Absent - the key is missing or null -\n * means no owner has decided, and only that lets a local flag seed the server.\n * A value that is PRESENT but unreadable is not that state: a permission the\n * machine cannot read is a permission it does not hold, so it reads as off.\n * The frequency is left out there rather than guessed, because off keeps no\n * schedule and a made-up number would outlive the garbled value that caused it.\n */\nfunction readAutoSync(v: unknown): AutoSyncPermission | null {\n\tif (v === undefined || v === null) return null;\n\tif (typeof v !== \"object\" || Array.isArray(v)) return { enabled: false };\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.enabled !== \"boolean\") return { enabled: false };\n\tif (\n\t\ttypeof obj.frequencyHours !== \"number\" ||\n\t\t!Number.isFinite(obj.frequencyHours)\n\t)\n\t\treturn { enabled: false };\n\treturn { enabled: obj.enabled, frequencyHours: obj.frequencyHours };\n}\n\nfunction readSyncConfig(raw: unknown): SyncConfig | null {\n\tif (typeof raw !== \"object\" || raw === null || Array.isArray(raw))\n\t\treturn null;\n\tconst obj = raw as Record<string, unknown>;\n\tconst listRaw = obj.allowlist;\n\tif (typeof listRaw !== \"object\" || listRaw === null) return null;\n\tconst list = listRaw as Record<string, unknown>;\n\treturn {\n\t\tallowlist: {\n\t\t\tmcpServers: readNameList(list.mcpServers),\n\t\t\tskills: readNameList(list.skills),\n\t\t\tsubagents: readNameList(list.subagents),\n\t\t\tslashCommands: readNameList(list.slashCommands),\n\t\t},\n\t\t// Anything other than an explicit `true` fails closed.\n\t\tpublishCost: obj.publishCost === true,\n\t\t// Absent means \"no stack resolved\" - an anonymous fetch, or a token bound\n\t\t// to nothing. Both fail closed to publishing no user-chosen names.\n\t\toptIns: readOptIns(obj.optIns),\n\t\t// Anything other than an explicit `true` keeps the names on the machine.\n\t\treviewKeptPrivate: obj.reviewKeptPrivate === true,\n\t\t// And anything other than an explicit `true` keeps the workflow section\n\t\t// off the wire. An old server that has never heard of the field answers\n\t\t// without it, and that reads as off - which is right: it has no place to\n\t\t// put the section either.\n\t\tpublishWorkflow: obj.publishWorkflow === true,\n\t\tstack: readStack(obj.stack),\n\t\tautoSync: readAutoSync(obj.autoSync),\n\t};\n}\n\n/**\n * Fetch the curated allowlist and the cost preference, falling back to the\n * bundled copy on any failure. Never throws - an unreachable aistack must not\n * prevent a local analysis from running, it must only narrow what could publish.\n */\nexport async function loadSyncConfig(opts: {\n\tbaseUrl: string;\n\t/**\n\t * Bearer for the authenticated half: `publishCost`, `publishWorkflow`,\n\t * `optIns`, `reviewKeptPrivate` and the destination stack. Absent, the server answers\n\t * with the anonymous fail-closed body - same allowlist, everything else off.\n\t */\n\ttoken?: string;\n\tfetchImpl?: typeof fetch;\n\ttimeoutMs?: number;\n}): Promise<LoadedSyncConfig> {\n\tconst doFetch = opts.fetchImpl ?? fetch;\n\ttry {\n\t\tconst res = await doFetch(`${opts.baseUrl}${SYNC_CONFIG_PATH}`, {\n\t\t\tsignal: AbortSignal.timeout(opts.timeoutMs ?? FETCH_TIMEOUT_MS),\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),\n\t\t\t},\n\t\t});\n\t\tif (!res.ok) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: `sync-config returned ${res.status}`,\n\t\t\t};\n\t\t}\n\t\tconst parsed = readSyncConfig(await res.json());\n\t\tif (!parsed) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: \"sync-config response was not the expected shape\",\n\t\t\t};\n\t\t}\n\t\treturn { config: parsed, source: \"fetched\" };\n\t} catch (err) {\n\t\treturn {\n\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\tsource: \"bundled\",\n\t\t\terror: err instanceof Error ? err.message : \"sync-config fetch failed\",\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Filtering\n// ---------------------------------------------------------------------------\n\nexport type Atom = { name: string; count: number };\n\n/**\n * One observed name that will NOT publish, as the approve gate needs to render\n * it: the raw string, how often it ran, and the plugin it came from.\n *\n * Local only - this never enters the payload. It exists because the gate offers\n * every kept-private name as an explicit, default-off tick (#42 decision 1), and\n * it cannot offer what the analyzer does not hand back.\n */\nexport type KeptPrivateAtom = {\n\tname: string;\n\tcount: number;\n\t/** Plugin prefix, for the gate's grouped bulk tick. `null` when standalone. */\n\tgroup: string | null;\n};\n\nexport type FilteredAtoms = {\n\t/** Publishable names, ordered by count descending. */\n\tallowed: Atom[];\n\t/** The rest, with everything the gate needs to offer them as ticks. */\n\tkeptPrivate: KeptPrivateAtom[];\n\t/** How many DISTINCT names were kept private. */\n\twithheld: number;\n};\n\n/**\n * A server an MCP plugin provides is observed as `plugin_<plugin>_<server>`.\n *\n * That whole string is GENERATED by Claude Code - the user typed none of it -\n * which is a different class from a hand-edited `.mcp.json` alias. Strip the\n * wrapper before matching (#42 decision 5).\n *\n * The split takes the FIRST underscore-free segment as the plugin name. A plugin\n * whose own name carries an underscore therefore splits wrong, the inner segment\n * matches nothing, and the raw name stays kept private - the same direction\n * every other miss fails in.\n */\nconst PLUGIN_MCP_RE = /^plugin_([^_]+)_(.+)$/;\n\n/** `plugin:artifact` is the convention for a plugin's skills and subagents. */\nconst PLUGIN_PREFIX_RE = /^([^:\\s]+):(.+)$/;\n\n/**\n * The plugin a name came from, for the gate's grouped bulk tick.\n *\n * Grouping is a UI affordance only. What the gate STORES is every name in the\n * group, expanded (#42 decision 3): a stored `alp-river:*` would be a standing\n * grant to names that do not exist yet, and nobody can consent to a name they\n * have not thought of.\n */\nexport function pluginGroup(name: string): string | null {\n\treturn (\n\t\tPLUGIN_MCP_RE.exec(name)?.[1] ?? PLUGIN_PREFIX_RE.exec(name)?.[1] ?? null\n\t);\n}\n\nexport type FilterSets = {\n\t/** Curated list UNION this stack's opt-ins. A match here publishes verbatim. */\n\tpublishable: ReadonlySet<string>;\n\t/**\n\t * The curated list alone - the only target normalization may match.\n\t *\n\t * This is what makes the normalization safe to state in one line:\n\t * normalization can only ever emit a string that is already curated. The\n\t * blast radius of a bug in it is an already-vetted set, by construction.\n\t */\n\tcurated: ReadonlySet<string>;\n};\n\n/**\n * Resolve the name an atom would publish under, or `null` to keep it private.\n *\n * Raw match first, so a name the owner ticked publishes exactly as they saw it\n * at the gate. Only an unmatched name is normalized, and only against the\n * curated list.\n */\nfunction publishedName(name: string, sets: FilterSets): string | null {\n\tif (sets.publishable.has(name)) return name;\n\tconst inner = PLUGIN_MCP_RE.exec(name)?.[2];\n\tif (inner && sets.curated.has(inner)) return inner;\n\treturn null;\n}\n\n/**\n * Split observed atoms into what publishes and what stays on the machine.\n *\n * The withheld figure counts distinct names, not calls: it answers \"how much of\n * my inventory is not shown\", which is the honesty question, without leaking\n * how heavily any single kept-private thing is used.\n *\n * Counts are merged by PUBLISHED name, because normalization can map two\n * observed names onto one - a plugin-provided `chrome-devtools` and a directly\n * configured one both publish as `chrome-devtools`, and two rows with the same\n * name would double-count that server in the rendered inventory.\n */\nexport function filterAtoms(\n\tatoms: readonly Atom[],\n\tsets: FilterSets,\n): FilteredAtoms {\n\tconst merged = new Map<string, number>();\n\tconst keptPrivate: KeptPrivateAtom[] = [];\n\tfor (const atom of atoms) {\n\t\tconst published = publishedName(atom.name, sets);\n\t\tif (published === null) {\n\t\t\tkeptPrivate.push({\n\t\t\t\tname: atom.name,\n\t\t\t\tcount: atom.count,\n\t\t\t\tgroup: pluginGroup(atom.name),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.set(published, (merged.get(published) ?? 0) + atom.count);\n\t}\n\tconst allowed = [...merged].map(([name, count]) => ({ name, count }));\n\tallowed.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\tkeptPrivate.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\treturn { allowed, keptPrivate, withheld: keptPrivate.length };\n}\n","// \"Is this harness alive?\" - the detection primitive behind every adapter's\n// `detect()` (#101, decided in #100).\n//\n// A directory that merely exists proves nothing: a Claude Code install from\n// months ago leaves its transcript root behind forever, and detection keyed on\n// that root scanned it, published a dead snapshot next to a live one, and asked\n// its owner to connect a harness they had stopped using.\n//\n// So detection asks the same question the scan asks: did this harness write\n// anything inside the rolling window? It answers with `stat` calls only -\n// nothing is opened, nothing is parsed. A live harness answers on the first\n// recent file it meets, which is usually the first file it meets. A dead\n// harness pays a full walk of stats to say no, which is the cost the old\n// `exists()` check saved and the reason the answer was wrong.\n//\n// Directory mtimes cannot prune this walk. `claude/scan.ts` verified why:\n// appending to a file does not move its parent directory's mtime, so a resumed\n// session writes in-window records into a directory that looks untouched.\n\nimport type { Dirent, Stats } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport type RecencyOptions = {\n\t/** Override the directory reader. Tests only. */\n\treaddirImpl?: (dir: string) => Promise<Dirent[]>;\n\t/** Override the stat call. Tests only. */\n\tstatImpl?: (file: string) => Promise<Stats>;\n};\n\n/**\n * True when any file under `roots` whose basename passes `matches` was modified\n * at or after `sinceMs`. Unreadable directories and files are silence, not an\n * error - the same fail-quiet rule the scanners hold, and for the same reason:\n * the error object carries the absolute path.\n */\nexport async function hasRecentFile(\n\troots: readonly string[],\n\tmatches: (basename: string) => boolean,\n\tsinceMs: number,\n\topts: RecencyOptions = {},\n): Promise<boolean> {\n\tconst readDir =\n\t\topts.readdirImpl ??\n\t\t((dir: string) => readdir(dir, { withFileTypes: true }));\n\tconst statFile = opts.statImpl ?? stat;\n\n\tconst seen = new Set<string>();\n\tconst pending: string[] = [...roots];\n\twhile (pending.length > 0) {\n\t\tconst dir = pending.pop() as string;\n\t\tif (seen.has(dir)) continue;\n\t\tseen.add(dir);\n\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = await readDir(dir);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const e of entries) {\n\t\t\tconst full = path.join(dir, e.name);\n\t\t\tif (e.isDirectory()) {\n\t\t\t\tpending.push(full);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!e.isFile() || !matches(e.name)) continue;\n\t\t\ttry {\n\t\t\t\tconst st = await statFile(full);\n\t\t\t\tif (st.mtimeMs >= sinceMs) return true;\n\t\t\t} catch {\n\t\t\t\t/* unreadable file - it proves nothing either way */\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n","// The daily unit of the workflow wire, and the fold that turns days into a window.\n//\n// Wayfinder ticket #285 (map #200). Ticket #277 moved the wire from one 30-day\n// section to per-day rows so the page can offer 30-day, 7-day and 24-hour\n// windows and a manual sync still builds a continuous series. This module is\n// the shape of one day and the arithmetic that adds days together.\n//\n// ONLY COMBINABLE ATOMS. A day carries counts, sums, maxes and bucket\n// histograms, never a median, a share or a mean: a share of one day cannot be\n// added to a share of another, and a median of medians is nothing. Every\n// figure the page prints is computed AFTER the fold, over the window's atoms,\n// by the row rules in this package.\n//\n// THE FOLD HAS THE DAY'S SHAPE. `WorkflowDay` and the window are the same type\n// with one exception (`dates`, the days the window holds), so a rule written\n// against a window reads a single day unchanged and the tests can fold a\n// fixture of one day and compare it with itself.\n//\n// A READING IS ONE MACHINE'S, PER DAY (ADR-0009). The fold adds days of ONE\n// machine. Nothing here merges two machines: the Git day carries no commit\n// identity, so two clones of one repository would count a shared commit twice.\n\nimport type { PhaseId } from \"./types.js\";\n\nexport const WORKFLOW_AGGREGATES_V2 = \"workflow-aggregates/v2\";\n\n/**\n * `workflow-aggregates/v3` adds the optional `context` block to a harness day\n * (#358). Every other atom is unchanged, so a v2 day folds beside a v3 day and\n * an old client keeps publishing v2 days the server stores as they are.\n */\nexport const WORKFLOW_AGGREGATES_V3 = \"workflow-aggregates/v3\";\n\n/** The bucket rule both histograms cite. A bump changes what a bucket index means. */\nexport const LOG_BUCKETS_V1 = \"log-buckets/v1\";\n\n/**\n * The half-octave rule the context histograms cite (#358). Token counts span\n * 1k to 1M, and a whole octave quotes a median up to 41% off; a half octave\n * keeps the bucket median within 20% of the value.\n */\nexport const LOG_BUCKETS_V2 = \"log-buckets/v2\";\n\nexport type PhaseTotals = Record<PhaseId, number>;\n\nexport const EMPTY_PHASE_TOTALS: Readonly<PhaseTotals> = Object.freeze({\n\tscout: 0,\n\tbuild: 0,\n\tverify: 0,\n\thandoff: 0,\n\tunknown: 0,\n});\n\n/**\n * One bucket of measured session length, and the session facts that fold with\n * it. The playbook splits its two tracks at the median session, and with no\n * session rows on the wire the split is a median over these buckets.\n *\n * `merged`, `verified`, `mergedVerified` and `openedWithScout` are session\n * COUNTS inside the bucket, so a share over any subset is a ratio of sums.\n */\nexport type SessionLengthBucket = {\n\t/** `logBucket(measured minutes)`. */\n\tbucket: number;\n\tsessions: number;\n\tphaseSec: PhaseTotals;\n\t/** Sessions whose shell ran `gh pr merge`. */\n\tmerged: number;\n\t/** Sessions holding at least one verify run. */\n\tverified: number;\n\t/** Sessions that are both. */\n\tmergedVerified: number;\n\t/** Sessions whose first classified event was scout. */\n\topenedWithScout: number;\n};\n\nexport type HarnessDay = {\n\tharness: string;\n\t/** Sessions that STARTED on this day. A session spanning midnight counts once. */\n\tsessions: number;\n\t/** Start-hour histogram, UTC. The page shifts it into the owner's local time. */\n\tstartHours: readonly { hourUtc: number; sessions: number }[];\n\t/**\n\t * The phase reading. Absent when the harness failed its gate over the sync\n\t * window: the rules left more than 20% of its measured time unclassified.\n\t */\n\tphase?: {\n\t\truleVersion: string;\n\t\tsessions: number;\n\t\tphaseSec: PhaseTotals;\n\t\tphaseEvents: PhaseTotals;\n\t\twaitingSec: number;\n\t\tidleSec: number;\n\t\t/** Sessions holding at least one verify event. */\n\t\tsessionsWithVerify: number;\n\t\t/** Sessions holding at least one handoff event. */\n\t\tsessionsWithHandoff: number;\n\t\tbucketRuleVersion: string;\n\t\tlengths: readonly SessionLengthBucket[];\n\t};\n\trouting?: {\n\t\tmain: readonly { model: string; tokens: number }[];\n\t\tsubagents: readonly { model: string; tokens: number }[];\n\t};\n\tdelegation?: {\n\t\tmainToolCalls: number;\n\t\tsubagentToolCalls: number;\n\t\t/** A max: the widest concurrent fan-out any one parent reached. */\n\t\twidestFanOut: number;\n\t\t/** A max: the most children any one parent had. */\n\t\tmostSubagents: number;\n\t};\n\t/** Event cells. The weekday is the day's own; it rides along so a fold needs no calendar. */\n\tactivity: readonly { weekdayUtc: number; hourUtc: number; events: number }[];\n\t/** Responses per effort level. Absent on a harness that records no effort. */\n\teffort?: readonly { level: EffortLevel; turns: number }[];\n\t/** Absent on a harness that records no thinking tokens. */\n\tthinking?: { thinkingTokens: number; responseTokens: number };\n\t/** Turn duration histogram, `logBucket(seconds)`. Absent when the harness records no duration. */\n\tturnDurations?: {\n\t\tbucketRuleVersion: string;\n\t\tbuckets: readonly { bucket: number; turns: number }[];\n\t};\n\t/** Turns that ended with a question back, over all turns. Absent without a question marker. */\n\tquestions?: { asked: number; turns: number };\n\t/** Absent on a harness without a built-in web search tool. */\n\twebSearches?: number;\n\t/**\n\t * Per-call context (#358): the tokens each API call carried in, bucketed\n\t * under `log-buckets/v2`. Absent on a day from a client that predates the\n\t * block or a harness that logs no per-call usage.\n\t */\n\tcontext?: ContextDay;\n};\n\n/**\n * One day of per-call context for one harness. Combinable atoms only.\n *\n * A CALL is one API response; the context is what the request carried in\n * (fresh input plus cache reads plus cache writes). Main and subagent calls\n * are two histograms because a subagent has its own context window and its\n * own first call. A FIRST CALL is the first call of a main session: its cache\n * read is the harness part (system prompt and tools, cached across sessions)\n * and its cache write plus fresh input is the instructions part (project\n * instructions, memory, skills, agents, the first prompt). The two sums and\n * the count give the fold a mean over first calls, which is the one figure\n * the reading needs from them.\n */\nexport type ContextDay = {\n\tbucketRuleVersion: string;\n\tcalls: {\n\t\tmain: readonly { bucket: number; calls: number }[];\n\t\tsubagents: readonly { bucket: number; calls: number }[];\n\t};\n\t/** First calls of main sessions that started this day, by context bucket. */\n\tfirstCalls: { main: readonly { bucket: number; sessions: number }[] };\n\t/** Sum over first calls of the cross-session cached prefix. */\n\tfirstCallHarnessTokens: number;\n\t/** Sum over first calls of the per-session part. */\n\tfirstCallInstructionsTokens: number;\n\tfirstCallCount: number;\n\t/** A max over every call of the day, main and subagent alike. */\n\tmaxContext: number;\n\t/** Compaction boundaries the harness logged on this day. */\n\tcompactions: number;\n\t/** The context window the harness logged (Codex). Absent when it logs none. */\n\twindow?: number;\n};\n\nexport type EffortLevel = \"low\" | \"medium\" | \"high\" | \"other\";\n\nexport const EFFORT_LEVELS: readonly EffortLevel[] = [\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"other\",\n];\n\n/** Map a harness's own effort string onto the four public levels. */\nexport function effortLevelOf(effort: string): EffortLevel {\n\tswitch (effort.toLowerCase()) {\n\t\tcase \"low\":\n\t\tcase \"minimal\":\n\t\t\treturn \"low\";\n\t\tcase \"medium\":\n\t\t\treturn \"medium\";\n\t\tcase \"high\":\n\t\tcase \"xhigh\":\n\t\tcase \"max\":\n\t\tcase \"ultra\":\n\t\t\treturn \"high\";\n\t\tdefault:\n\t\t\treturn \"other\";\n\t}\n}\n\nexport type GitDay = {\n\ttestFileRuleVersion: string;\n\tfileTypeRuleVersion: string;\n\tcommitSetRuleVersion: string;\n\tcommits: number;\n\t/** Commits whose author hour, on the machine's clock, falls between 23:00 and 03:00. */\n\tlateNightCommits: number;\n\tadditions: number;\n\tremovals: number;\n\t/** One entry per commit, for the log-scale strip. Order carries no meaning. */\n\tchangedLinesPerCommit: readonly number[];\n\ttestFileCommits: number;\n\tchangedLinesByExtension: readonly {\n\t\textension: string;\n\t\tchangedLines: number;\n\t}[];\n\twithheldExtensionLines: number;\n\t/** UTC cells, like the harness activity cells. */\n\tweekdayHourCells: readonly {\n\t\tweekdayUtc: number;\n\t\thourUtc: number;\n\t\tcommits: number;\n\t}[];\n};\n\n/** The three Git sums of one day, dated. What the mirrored bars draw. */\nexport type GitDayTotals = {\n\tdate: string;\n\tadditions: number;\n\tremovals: number;\n\tcommits: number;\n};\n\nexport type WorkflowDay = {\n\t/** The UTC date, `YYYY-MM-DD`. Sessions belong to the day they started. */\n\tdate: string;\n\tharnesses: readonly HarnessDay[];\n\tgit: GitDay;\n\t/**\n\t * Distinct project workspaces with a session that overlapped this day, across\n\t * every harness. Absent when no session touched a workspace.\n\t */\n\tparallelProjects?: number;\n};\n\n/**\n * A window: the fold of one machine's days.\n *\n * Same shape as a day, plus the dates it holds. A window over zero days is\n * `undefined` rather than a row of zeroes, so nothing downstream prints a\n * measurement nobody made.\n */\nexport type WorkflowWindow = Omit<WorkflowDay, \"date\"> & {\n\taggregateVersion: string;\n\tdates: readonly string[];\n\t/** Minutes east of UTC on the publishing machine. */\n\tutcOffsetMinutes?: number;\n\t/** The per-day parallel-project counts, for the median over days. */\n\tparallelProjectDays: readonly number[];\n\t/**\n\t * One Git entry per stored day, sorted by date, for the per-day picture\n\t * (#288). A derived list rather than the raw days: the page reads nothing\n\t * else per day, and the raw days would carry every harness atom with them.\n\t */\n\tgitDays: readonly GitDayTotals[];\n\t/** Days on which at least one harness recorded a web search count. */\n\twebSearchDays: number;\n};\n\n/**\n * The bucket index of a positive quantity on a base-2 log scale.\n *\n * Bucket 0 holds everything under 1, bucket k holds [2^(k-1), 2^k). A session\n * of 3 minutes lands in bucket 2, one of 90 minutes in bucket 7. The scale is\n * `log-buckets/v1`; the unit is the caller's (minutes for session length,\n * seconds for turn duration) and travels in the field name.\n */\nexport function logBucket(value: number): number {\n\tif (!(value >= 1)) return 0;\n\treturn Math.floor(Math.log2(value)) + 1;\n}\n\n/** The lower and upper bound of a bucket, in the caller's unit. */\nexport function bucketRange(bucket: number): { low: number; high: number } {\n\tif (bucket <= 0) return { low: 0, high: 1 };\n\treturn { low: 2 ** (bucket - 1), high: 2 ** bucket };\n}\n\n/**\n * The geometric middle of a bucket, which is where a value drawn from it is\n * quoted. Bucket 0 quotes as 0.5.\n */\nexport function bucketMid(bucket: number): number {\n\tconst { low, high } = bucketRange(bucket);\n\treturn Math.sqrt(Math.max(low, 0.25) * high);\n}\n\n/**\n * The median over a histogram: the bucket holding the middle item, quoted at\n * its geometric middle. `undefined` on an empty histogram.\n *\n * A median over buckets is what the wire allows (#285): the exact median needs\n * every value, and every value is what the wire no longer carries.\n */\nexport function medianBucket(\n\tbuckets: readonly { bucket: number; count: number }[],\n): number | undefined {\n\tconst total = buckets.reduce((sum, row) => sum + row.count, 0);\n\tif (total <= 0) return undefined;\n\tconst sorted = [...buckets].sort((a, b) => a.bucket - b.bucket);\n\tconst middle = (total + 1) / 2;\n\tlet seen = 0;\n\tfor (const row of sorted) {\n\t\tseen += row.count;\n\t\tif (seen >= middle) return row.bucket;\n\t}\n\treturn sorted[sorted.length - 1]?.bucket;\n}\n\n/**\n * The bucket index of a positive quantity on a half-octave log scale,\n * `log-buckets/v2`. Bucket 0 holds everything under 1, bucket k holds\n * [2^((k-1)/2), 2^(k/2)). A context of 50,000 tokens lands in bucket 32, one\n * of 200,000 in bucket 36.\n */\nexport function logBucketV2(value: number): number {\n\tif (!(value >= 1)) return 0;\n\treturn Math.floor(2 * Math.log2(value)) + 1;\n}\n\n/** The lower and upper bound of a `log-buckets/v2` bucket. */\nexport function bucketRangeV2(bucket: number): { low: number; high: number } {\n\tif (bucket <= 0) return { low: 0, high: 1 };\n\treturn { low: 2 ** ((bucket - 1) / 2), high: 2 ** (bucket / 2) };\n}\n\n/** The geometric middle of a `log-buckets/v2` bucket. Bucket 0 quotes as 0.5. */\nexport function bucketMidV2(bucket: number): number {\n\tconst { low, high } = bucketRangeV2(bucket);\n\treturn Math.sqrt(Math.max(low, 0.25) * high);\n}\n\n/**\n * The bucket holding the item at quantile `q` (0..1), counting from the\n * lowest bucket: the item of rank `ceil(q * total)`, at least 1. `undefined`\n * on an empty histogram. `medianBucket` keeps its own middle rule; this one\n * is for the tails (p90).\n */\nexport function quantileBucket(\n\tbuckets: readonly { bucket: number; count: number }[],\n\tq: number,\n): number | undefined {\n\tconst total = buckets.reduce((sum, row) => sum + row.count, 0);\n\tif (total <= 0) return undefined;\n\tconst sorted = [...buckets].sort((a, b) => a.bucket - b.bucket);\n\tconst rank = Math.min(total, Math.max(1, Math.ceil(q * total)));\n\tlet seen = 0;\n\tfor (const row of sorted) {\n\t\tseen += row.count;\n\t\tif (seen >= rank) return row.bucket;\n\t}\n\treturn sorted[sorted.length - 1]?.bucket;\n}\n\n/** The median of a plain list, or `undefined` on an empty one. */\nexport function median(values: readonly number[]): number | undefined {\n\tif (values.length === 0) return undefined;\n\tconst sorted = [...values].sort((a, b) => a - b);\n\tconst mid = Math.floor(sorted.length / 2);\n\tconst midValue = sorted[mid] as number;\n\treturn sorted.length % 2 === 0\n\t\t? ((sorted[mid - 1] as number) + midValue) / 2\n\t\t: midValue;\n}\n\nfunction addPhaseTotals(into: PhaseTotals, from: PhaseTotals): void {\n\tfor (const phase of Object.keys(into) as PhaseId[]) {\n\t\tinto[phase] += from[phase] ?? 0;\n\t}\n}\n\nfunction sumBy<T>(\n\trows: readonly T[],\n\tkey: (row: T) => string,\n\tadd: (into: T, from: T) => void,\n\tclone: (row: T) => T,\n): T[] {\n\tconst merged = new Map<string, T>();\n\tfor (const row of rows) {\n\t\tconst k = key(row);\n\t\tconst held = merged.get(k);\n\t\tif (held) add(held, row);\n\t\telse merged.set(k, clone(row));\n\t}\n\treturn [...merged.values()];\n}\n\nfunction foldCells<T extends { weekdayUtc: number; hourUtc: number }>(\n\trows: readonly T[],\n\tfield: \"events\" | \"commits\",\n): T[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => `${row.weekdayUtc}:${row.hourUtc}`,\n\t\t(into, from) => {\n\t\t\t(into as Record<string, number>)[field] += (\n\t\t\t\tfrom as Record<string, number>\n\t\t\t)[field] as number;\n\t\t},\n\t\t(row) => ({ ...row }),\n\t).sort((a, b) => a.weekdayUtc - b.weekdayUtc || a.hourUtc - b.hourUtc);\n}\n\nfunction foldModels(\n\trows: readonly { model: string; tokens: number }[],\n): { model: string; tokens: number }[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => row.model,\n\t\t(into, from) => {\n\t\t\tinto.tokens += from.tokens;\n\t\t},\n\t\t(row) => ({ ...row }),\n\t).sort((a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model));\n}\n\nfunction foldCountBuckets<K extends string>(\n\trows: readonly ({ bucket: number } & Record<K, number>)[],\n\tfield: K,\n): ({ bucket: number } & Record<K, number>)[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => String(row.bucket),\n\t\t(into, from) => {\n\t\t\t(into as Record<string, number>)[field] += from[field];\n\t\t},\n\t\t(row) => ({ ...row }),\n\t).sort((a, b) => a.bucket - b.bucket);\n}\n\n/**\n * Add context days together. Histograms merge by bucket, sums and counts add,\n * the max is a max, and the window is the LAST day's: the caller hands days\n * in date order, so the last one that logged a window is the latest reading.\n */\nexport function foldContextDays(days: readonly ContextDay[]): ContextDay {\n\tconst versions = [...new Set(days.map((d) => d.bucketRuleVersion))]\n\t\t.sort()\n\t\t.join(\" · \");\n\tconst windows = days\n\t\t.map((d) => d.window)\n\t\t.filter((w): w is number => w !== undefined);\n\tconst window = windows[windows.length - 1];\n\treturn {\n\t\tbucketRuleVersion: versions,\n\t\tcalls: {\n\t\t\tmain: foldCountBuckets(\n\t\t\t\tdays.flatMap((d) => d.calls.main),\n\t\t\t\t\"calls\",\n\t\t\t),\n\t\t\tsubagents: foldCountBuckets(\n\t\t\t\tdays.flatMap((d) => d.calls.subagents),\n\t\t\t\t\"calls\",\n\t\t\t),\n\t\t},\n\t\tfirstCalls: {\n\t\t\tmain: foldCountBuckets(\n\t\t\t\tdays.flatMap((d) => d.firstCalls.main),\n\t\t\t\t\"sessions\",\n\t\t\t),\n\t\t},\n\t\tfirstCallHarnessTokens: days.reduce(\n\t\t\t(sum, d) => sum + d.firstCallHarnessTokens,\n\t\t\t0,\n\t\t),\n\t\tfirstCallInstructionsTokens: days.reduce(\n\t\t\t(sum, d) => sum + d.firstCallInstructionsTokens,\n\t\t\t0,\n\t\t),\n\t\tfirstCallCount: days.reduce((sum, d) => sum + d.firstCallCount, 0),\n\t\tmaxContext: Math.max(0, ...days.map((d) => d.maxContext)),\n\t\tcompactions: days.reduce((sum, d) => sum + d.compactions, 0),\n\t\t...(window === undefined ? {} : { window }),\n\t};\n}\n\nfunction foldLengths(\n\trows: readonly SessionLengthBucket[],\n): SessionLengthBucket[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => String(row.bucket),\n\t\t(into, from) => {\n\t\t\tinto.sessions += from.sessions;\n\t\t\taddPhaseTotals(into.phaseSec, from.phaseSec);\n\t\t\tinto.merged += from.merged;\n\t\t\tinto.verified += from.verified;\n\t\t\tinto.mergedVerified += from.mergedVerified;\n\t\t\tinto.openedWithScout += from.openedWithScout;\n\t\t},\n\t\t(row) => ({ ...row, phaseSec: { ...row.phaseSec } }),\n\t).sort((a, b) => a.bucket - b.bucket);\n}\n\n/**\n * Add one harness's days together.\n *\n * An optional block is present on the fold when ANY day carried it. A rule\n * version is the set of versions seen, joined with \" · \" when they differ, so a\n * window that straddles a rule bump says so rather than citing the newer rule\n * for days the older one classified.\n */\nexport function foldHarnessDays(days: readonly HarnessDay[]): HarnessDay {\n\tconst first = days[0];\n\tif (!first) throw new Error(\"foldHarnessDays needs at least one day\");\n\tconst versions = (values: readonly string[]): string =>\n\t\t[...new Set(values)].sort().join(\" · \");\n\n\tconst out: HarnessDay = {\n\t\tharness: first.harness,\n\t\tsessions: days.reduce((sum, day) => sum + day.sessions, 0),\n\t\tstartHours: sumBy(\n\t\t\tdays.flatMap((day) => day.startHours),\n\t\t\t(row) => String(row.hourUtc),\n\t\t\t(into, from) => {\n\t\t\t\tinto.sessions += from.sessions;\n\t\t\t},\n\t\t\t(row) => ({ ...row }),\n\t\t).sort((a, b) => a.hourUtc - b.hourUtc),\n\t\tactivity: foldCells(\n\t\t\tdays.flatMap((day) => day.activity),\n\t\t\t\"events\",\n\t\t),\n\t};\n\n\tconst phases = days.flatMap((day) => (day.phase ? [day.phase] : []));\n\tif (phases.length > 0) {\n\t\tconst phaseSec = { ...EMPTY_PHASE_TOTALS };\n\t\tconst phaseEvents = { ...EMPTY_PHASE_TOTALS };\n\t\tfor (const phase of phases) {\n\t\t\taddPhaseTotals(phaseSec, phase.phaseSec);\n\t\t\taddPhaseTotals(phaseEvents, phase.phaseEvents);\n\t\t}\n\t\tout.phase = {\n\t\t\truleVersion: versions(phases.map((phase) => phase.ruleVersion)),\n\t\t\tsessions: phases.reduce((sum, phase) => sum + phase.sessions, 0),\n\t\t\tphaseSec,\n\t\t\tphaseEvents,\n\t\t\twaitingSec: phases.reduce((sum, phase) => sum + phase.waitingSec, 0),\n\t\t\tidleSec: phases.reduce((sum, phase) => sum + phase.idleSec, 0),\n\t\t\tsessionsWithVerify: phases.reduce(\n\t\t\t\t(sum, phase) => sum + phase.sessionsWithVerify,\n\t\t\t\t0,\n\t\t\t),\n\t\t\tsessionsWithHandoff: phases.reduce(\n\t\t\t\t(sum, phase) => sum + phase.sessionsWithHandoff,\n\t\t\t\t0,\n\t\t\t),\n\t\t\tbucketRuleVersion: versions(\n\t\t\t\tphases.map((phase) => phase.bucketRuleVersion),\n\t\t\t),\n\t\t\tlengths: foldLengths(phases.flatMap((phase) => phase.lengths)),\n\t\t};\n\t}\n\n\tconst routings = days.flatMap((day) => (day.routing ? [day.routing] : []));\n\tif (routings.length > 0) {\n\t\tout.routing = {\n\t\t\tmain: foldModels(routings.flatMap((routing) => routing.main)),\n\t\t\tsubagents: foldModels(routings.flatMap((routing) => routing.subagents)),\n\t\t};\n\t}\n\n\tconst delegations = days.flatMap((day) =>\n\t\tday.delegation ? [day.delegation] : [],\n\t);\n\tif (delegations.length > 0) {\n\t\tout.delegation = {\n\t\t\tmainToolCalls: delegations.reduce((sum, d) => sum + d.mainToolCalls, 0),\n\t\t\tsubagentToolCalls: delegations.reduce(\n\t\t\t\t(sum, d) => sum + d.subagentToolCalls,\n\t\t\t\t0,\n\t\t\t),\n\t\t\twidestFanOut: Math.max(...delegations.map((d) => d.widestFanOut)),\n\t\t\tmostSubagents: Math.max(...delegations.map((d) => d.mostSubagents)),\n\t\t};\n\t}\n\n\tconst efforts = days.flatMap((day) => day.effort ?? []);\n\tif (days.some((day) => day.effort)) {\n\t\tout.effort = sumBy(\n\t\t\tefforts,\n\t\t\t(row) => row.level,\n\t\t\t(into, from) => {\n\t\t\t\tinto.turns += from.turns;\n\t\t\t},\n\t\t\t(row) => ({ ...row }),\n\t\t).sort(\n\t\t\t(a, b) => EFFORT_LEVELS.indexOf(a.level) - EFFORT_LEVELS.indexOf(b.level),\n\t\t);\n\t}\n\n\tconst thinkings = days.flatMap((day) => (day.thinking ? [day.thinking] : []));\n\tif (thinkings.length > 0) {\n\t\tout.thinking = {\n\t\t\tthinkingTokens: thinkings.reduce((sum, t) => sum + t.thinkingTokens, 0),\n\t\t\tresponseTokens: thinkings.reduce((sum, t) => sum + t.responseTokens, 0),\n\t\t};\n\t}\n\n\tconst durations = days.flatMap((day) =>\n\t\tday.turnDurations ? [day.turnDurations] : [],\n\t);\n\tif (durations.length > 0) {\n\t\tout.turnDurations = {\n\t\t\tbucketRuleVersion: versions(durations.map((d) => d.bucketRuleVersion)),\n\t\t\tbuckets: sumBy(\n\t\t\t\tdurations.flatMap((d) => d.buckets),\n\t\t\t\t(row) => String(row.bucket),\n\t\t\t\t(into, from) => {\n\t\t\t\t\tinto.turns += from.turns;\n\t\t\t\t},\n\t\t\t\t(row) => ({ ...row }),\n\t\t\t).sort((a, b) => a.bucket - b.bucket),\n\t\t};\n\t}\n\n\tconst questions = days.flatMap((day) =>\n\t\tday.questions ? [day.questions] : [],\n\t);\n\tif (questions.length > 0) {\n\t\tout.questions = {\n\t\t\tasked: questions.reduce((sum, q) => sum + q.asked, 0),\n\t\t\tturns: questions.reduce((sum, q) => sum + q.turns, 0),\n\t\t};\n\t}\n\n\tif (days.some((day) => day.webSearches !== undefined)) {\n\t\tout.webSearches = days.reduce(\n\t\t\t(sum, day) => sum + (day.webSearches ?? 0),\n\t\t\t0,\n\t\t);\n\t}\n\n\tconst contexts = days.flatMap((day) => (day.context ? [day.context] : []));\n\tif (contexts.length > 0) out.context = foldContextDays(contexts);\n\n\treturn out;\n}\n\n/** Add Git days together. Rule versions join as a set, like the harness fold. */\n/**\n * The most per-commit entries a fold keeps. One entry per commit adds up: a\n * 30-day window on a busy machine passed 17k commits, and Convex refuses an\n * array over 8192 entries. The strip that draws them shows a distribution,\n * so a quantile sample of the sorted values is the same picture.\n */\nexport const MAX_CHANGED_LINES_PER_COMMIT = 4096;\n\n/**\n * At most `max` values, evenly spaced through the SORTED input so every\n * quantile (the median included) survives the cut. Returns the values sorted.\n */\nfunction sampleSorted(values: readonly number[], max: number): number[] {\n\tconst sorted = [...values].sort((a, b) => a - b);\n\tif (sorted.length <= max) return sorted;\n\tconst out: number[] = [];\n\tfor (let i = 0; i < max; i++) {\n\t\tout.push(\n\t\t\tsorted[Math.floor((i * (sorted.length - 1)) / (max - 1))] as number,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport function foldGitDays(days: readonly GitDay[]): GitDay {\n\tconst versions = (values: readonly string[]): string =>\n\t\t[...new Set(values)].sort().join(\" · \");\n\treturn {\n\t\ttestFileRuleVersion: versions(days.map((d) => d.testFileRuleVersion)),\n\t\tfileTypeRuleVersion: versions(days.map((d) => d.fileTypeRuleVersion)),\n\t\tcommitSetRuleVersion: versions(days.map((d) => d.commitSetRuleVersion)),\n\t\tcommits: days.reduce((sum, d) => sum + d.commits, 0),\n\t\tlateNightCommits: days.reduce((sum, d) => sum + d.lateNightCommits, 0),\n\t\tadditions: days.reduce((sum, d) => sum + d.additions, 0),\n\t\tremovals: days.reduce((sum, d) => sum + d.removals, 0),\n\t\tchangedLinesPerCommit: sampleSorted(\n\t\t\tdays.flatMap((d) => [...d.changedLinesPerCommit]),\n\t\t\tMAX_CHANGED_LINES_PER_COMMIT,\n\t\t),\n\t\ttestFileCommits: days.reduce((sum, d) => sum + d.testFileCommits, 0),\n\t\tchangedLinesByExtension: sumBy(\n\t\t\tdays.flatMap((d) => d.changedLinesByExtension),\n\t\t\t(row) => row.extension,\n\t\t\t(into, from) => {\n\t\t\t\tinto.changedLines += from.changedLines;\n\t\t\t},\n\t\t\t(row) => ({ ...row }),\n\t\t).sort((a, b) => a.extension.localeCompare(b.extension)),\n\t\twithheldExtensionLines: days.reduce(\n\t\t\t(sum, d) => sum + d.withheldExtensionLines,\n\t\t\t0,\n\t\t),\n\t\tweekdayHourCells: foldCells(\n\t\t\tdays.flatMap((d) => d.weekdayHourCells),\n\t\t\t\"commits\",\n\t\t),\n\t};\n}\n\nexport type FoldOptions = {\n\taggregateVersion: string;\n\tutcOffsetMinutes?: number;\n};\n\n/**\n * Fold one machine's days into a window. `undefined` when there are no days.\n *\n * Days are keyed by date and the caller has already replaced a re-synced day,\n * so two entries with one date here would be a bug upstream; the fold takes\n * them as they come rather than guessing which is newer.\n */\nexport function foldWorkflowDays(\n\tdays: readonly WorkflowDay[],\n\toptions: FoldOptions,\n): WorkflowWindow | undefined {\n\tif (days.length === 0) return undefined;\n\t// Date order, so a \"latest\" inside the harness fold (the logged context\n\t// window) is the latest day's and not the last row's.\n\tconst dated = [...days].sort((a, b) => a.date.localeCompare(b.date));\n\tconst byHarness = new Map<string, HarnessDay[]>();\n\tfor (const day of dated) {\n\t\tfor (const harness of day.harnesses) {\n\t\t\tconst held = byHarness.get(harness.harness) ?? [];\n\t\t\theld.push(harness);\n\t\t\tbyHarness.set(harness.harness, held);\n\t\t}\n\t}\n\tconst parallelProjectDays = days.flatMap((day) =>\n\t\tday.parallelProjects === undefined ? [] : [day.parallelProjects],\n\t);\n\tconst webSearchDays = days.filter((day) =>\n\t\tday.harnesses.some((harness) => harness.webSearches !== undefined),\n\t).length;\n\treturn {\n\t\taggregateVersion: options.aggregateVersion,\n\t\t...(options.utcOffsetMinutes === undefined\n\t\t\t? {}\n\t\t\t: { utcOffsetMinutes: options.utcOffsetMinutes }),\n\t\tdates: [...new Set(days.map((day) => day.date))].sort(),\n\t\tharnesses: [...byHarness.values()]\n\t\t\t.map(foldHarnessDays)\n\t\t\t.sort((a, b) => a.harness.localeCompare(b.harness)),\n\t\tgit: foldGitDays(days.map((day) => day.git)),\n\t\t...(parallelProjectDays.length === 0\n\t\t\t? {}\n\t\t\t: { parallelProjects: Math.max(...parallelProjectDays) }),\n\t\tparallelProjectDays,\n\t\tgitDays: [...days]\n\t\t\t.sort((a, b) => a.date.localeCompare(b.date))\n\t\t\t.map((day) => ({\n\t\t\t\tdate: day.date,\n\t\t\t\tadditions: day.git.additions,\n\t\t\t\tremovals: day.git.removals,\n\t\t\t\tcommits: day.git.commits,\n\t\t\t})),\n\t\twebSearchDays,\n\t};\n}\n","// One folded workflow window, and the facts derived from it.\n//\n// Wayfinder ticket #218 (map #200), reshaped by #285: the wire is per-day rows\n// now (`daily.ts`), and a reading is the FOLD of one machine's days over a\n// window. Everything below reads the fold; nothing reads a day.\n//\n// A READING IS ONE MACHINE'S, PER DAY (ADR-0009). Every derivation is scoped to\n// a single machine's window, so nothing here has to answer what two machines'\n// figures would mean together.\n\nimport type { HarnessDay, PhaseTotals, WorkflowWindow } from \"./daily.js\";\nimport type { PhaseId } from \"./types.js\";\n\nexport type WorkflowReading = WorkflowWindow;\nexport type WorkflowHarnessReading = HarnessDay;\n\n/**\n * The kit's inputs, which are the only component fact that does NOT live in the\n * workflow wire: skills and MCP servers are inventory, and inventory travels in\n * the measured payload. One entry per harness, already name-filtered on the\n * machine.\n */\nexport type KitReading = readonly {\n\tharness: string;\n\tskills: readonly { name: string; callShare: number }[];\n\tmcpServers: readonly { name: string; callShare: number }[];\n}[];\n\nconst EMPTY_TOTALS: PhaseTotals = {\n\tscout: 0,\n\tbuild: 0,\n\tverify: 0,\n\thandoff: 0,\n\tunknown: 0,\n};\n\n/** Every harness that shipped a phase reading, i.e. passed its own gate. */\nexport function playbookHarnesses(\n\treading: WorkflowReading,\n): WorkflowHarnessReading[] {\n\treturn reading.harnesses.filter((harness) => harness.phase !== undefined);\n}\n\n/** Measured seconds per phase, summed over the harnesses that shipped a phase reading. */\nexport function totalPhaseSec(reading: WorkflowReading): PhaseTotals {\n\tconst totals = { ...EMPTY_TOTALS };\n\tfor (const harness of playbookHarnesses(reading)) {\n\t\tfor (const phase of Object.keys(totals) as PhaseId[]) {\n\t\t\ttotals[phase] += harness.phase?.phaseSec[phase] ?? 0;\n\t\t}\n\t}\n\treturn totals;\n}\n\n/** Share of TOTAL measured time per phase, `unknown` included, summing to 1. */\nexport function phaseShare(reading: WorkflowReading): PhaseTotals | undefined {\n\tconst totals = totalPhaseSec(reading);\n\tconst measured = Object.values(totals).reduce((sum, sec) => sum + sec, 0);\n\tif (measured <= 0) return undefined;\n\tconst shares = { ...totals };\n\tfor (const phase of Object.keys(totals) as PhaseId[]) {\n\t\tshares[phase] = totals[phase] / measured;\n\t}\n\treturn shares;\n}\n\n/**\n * The unknown share of one harness's measured time over the window.\n *\n * Derived here rather than carried: a share cannot fold, so the day ships the\n * seconds and the window computes the ratio.\n */\nexport function unknownShareOf(harness: WorkflowHarnessReading): number {\n\tconst phase = harness.phase;\n\tif (!phase) return 0;\n\tconst measured = Object.values(phase.phaseSec).reduce((a, b) => a + b, 0);\n\treturn measured <= 0 ? 0 : phase.phaseSec.unknown / measured;\n}\n\n/** Sessions across every harness that shipped a phase reading. */\nexport function phaseSessionCount(reading: WorkflowReading): number {\n\treturn playbookHarnesses(reading).reduce(\n\t\t(sum, harness) => sum + (harness.phase?.sessions ?? 0),\n\t\t0,\n\t);\n}\n\n/**\n * Share of sessions holding at least one event of `phase`.\n *\n * The denominator is the PLAYBOOK sessions, not every synced session: a harness\n * held back by the gate ships no phase reading at all, so it can neither raise\n * nor lower this share. The scope line above it counts every session, which is\n * the number a reader doing arithmetic would use, and the two denominators are\n * why the lead names its scope before it prints a share.\n */\nexport function sessionShareWith(\n\treading: WorkflowReading,\n\tphase: \"verify\" | \"handoff\",\n): number | undefined {\n\tconst sessions = phaseSessionCount(reading);\n\tif (sessions === 0) return undefined;\n\tconst key = phase === \"verify\" ? \"sessionsWithVerify\" : \"sessionsWithHandoff\";\n\tconst hits = playbookHarnesses(reading).reduce(\n\t\t(sum, harness) => sum + (harness.phase?.[key] ?? 0),\n\t\t0,\n\t);\n\treturn hits / sessions;\n}\n\n/** The start-hour histogram over every harness, in UTC. */\nexport function startHoursUtc(reading: WorkflowReading): Map<number, number> {\n\tconst counts = new Map<number, number>();\n\tfor (const harness of reading.harnesses) {\n\t\tfor (const cell of harness.startHours) {\n\t\t\tcounts.set(cell.hourUtc, (counts.get(cell.hourUtc) ?? 0) + cell.sessions);\n\t\t}\n\t}\n\treturn counts;\n}\n\n/** A UTC hour on the owner's clock. */\nexport function ownerLocalHour(hourUtc: number, offsetMinutes: number): number {\n\treturn Math.floor(((((hourUtc * 60 + offsetMinutes) / 60) % 24) + 24) % 24);\n}\n\n/**\n * The hour most sessions start, in the OWNER's local time.\n *\n * Undefined without an offset. A reader's own clock would put a stranger's habit\n * at the wrong hour and describe nobody (spec), and UTC would do the same to\n * every owner outside London.\n */\nexport function modalStartHour(reading: WorkflowReading): number | undefined {\n\tconst offsetMinutes = reading.utcOffsetMinutes;\n\tif (offsetMinutes === undefined) return undefined;\n\tconst counts = new Map<number, number>();\n\tfor (const [hourUtc, sessions] of startHoursUtc(reading)) {\n\t\tconst hour = ownerLocalHour(hourUtc, offsetMinutes);\n\t\tcounts.set(hour, (counts.get(hour) ?? 0) + sessions);\n\t}\n\tif (counts.size === 0) return undefined;\n\t// Ties go to the earlier hour, so the same reading always names the same one.\n\treturn [...counts.entries()].sort(\n\t\t(a, b) => b[1] - a[1] || a[0] - b[0],\n\t)[0]?.[0];\n}\n\n/** Distinct phase rule versions in this reading, in the order the page should print them. */\nexport function phaseRuleVersions(reading: WorkflowReading): string[] {\n\treturn [\n\t\t...new Set(\n\t\t\tplaybookHarnesses(reading).flatMap((harness) =>\n\t\t\t\t(harness.phase?.ruleVersion ?? \"\").split(\" · \").filter(Boolean),\n\t\t\t),\n\t\t),\n\t].sort();\n}\n\n/**\n * True when one reading carries aggregates from more than one phase rule set.\n *\n * \"A rule-set bump reclassifies old sessions from local raw records at the next\n * sync. A session whose raw records are gone keeps its old aggregate, and the\n * page shows a mixed-version tag\" (spec). With daily rows the same thing happens\n * to a window that straddles a bump: the older days keep the older rule.\n */\nexport function hasMixedRuleVersions(reading: WorkflowReading): boolean {\n\treturn phaseRuleVersions(reading).length > 1;\n}\n\nexport type LeadFactsInput = {\n\treading: WorkflowReading;\n\t/** Every synced session on this machine, including harnesses held back by the gate. */\n\tsessionCount: number;\n\t/** Every synced harness on this machine, for the same reason. */\n\tharnessCount: number;\n};\n\n/**\n * The five figures `lead-templates/v1` prints, derived from one window.\n *\n * Absent inputs stay absent: the lead drops a sentence it cannot fill, and this\n * function never substitutes a default for a measurement that does not exist.\n */\nexport function buildLeadFacts(input: LeadFactsInput): {\n\tsessionCount: number;\n\tharnessCount: number;\n\tplaybookHarnessCount: number;\n\tphaseShare?: PhaseTotals;\n\tverifySessionShare?: number;\n\thandoffSessionShare?: number;\n\tmodalStartHourOwnerLocal?: number;\n\truleVersion?: string;\n} {\n\tconst { reading, sessionCount, harnessCount } = input;\n\tconst versions = phaseRuleVersions(reading);\n\tconst shares = phaseShare(reading);\n\tconst verify = sessionShareWith(reading, \"verify\");\n\tconst handoff = sessionShareWith(reading, \"handoff\");\n\tconst hour = modalStartHour(reading);\n\treturn {\n\t\tsessionCount,\n\t\tharnessCount,\n\t\tplaybookHarnessCount: playbookHarnesses(reading).length,\n\t\t...(shares ? { phaseShare: shares } : {}),\n\t\t...(verify === undefined ? {} : { verifySessionShare: verify }),\n\t\t...(handoff === undefined ? {} : { handoffSessionShare: handoff }),\n\t\t...(hour === undefined ? {} : { modalStartHourOwnerLocal: hour }),\n\t\t// Mixed versions print as the set. One reading classified by two rule sets\n\t\t// has no single rule id to cite, and citing the newer one would claim the\n\t\t// older sessions were reclassified when they were not.\n\t\t...(versions.length === 0 ? {} : { ruleVersion: versions.join(\" · \") }),\n\t};\n}\n","// The versioned component rule pool: `component-rules/v2`.\n//\n// Wayfinder ticket #218 (map #200) declared v1 so a component could compete\n// for a podium slot beside a pool metric. Ticket #277 took fit off the page,\n// and #285 folded the wire into windows, so v2 is a smaller claim: each\n// component names ONE headline figure over the folded window, with a band the\n// API carries and nothing ranks by. The order on the page is fixed\n// (`workflowRows.ts`).\n//\n// A COMPONENT RULE MEASURES NOTHING NEW. Every value below is arithmetic over\n// atoms the machine already shipped, which keeps the CLI the only source of\n// measured atoms.\n//\n// BAND VALUES ARE DEFAULTS, NOT PROVEN DATA, the caveat `metric-rules/v2`\n// carries.\n\nimport { bucketMid, medianBucket } from \"./daily.js\";\nimport type { MetricUnit } from \"./metricRules.js\";\nimport type { KitReading, WorkflowReading } from \"./reading.js\";\nimport { modalStartHour, playbookHarnesses } from \"./reading.js\";\n\nexport const COMPONENT_RULES_V2 = \"component-rules/v2\";\n\nexport type ComponentInput = {\n\treading: WorkflowReading;\n\t/** Inventory for the same machine. Absent when no payload carried one. */\n\tkit?: KitReading;\n};\n\nexport type ComponentRule = {\n\tid: string;\n\tversion: string;\n\t/** Sentence fragment completing \"<value> <label>\", like a metric rule's. */\n\tlabel: string;\n\tunit: MetricUnit;\n\tband: { low: number; high: number };\n\t/** The measurement, or `undefined` when this reading cannot support the row. */\n\tevaluate: (input: ComponentInput) => number | undefined;\n\t/** Share of the machine's synced harnesses the row counts, 0..1. */\n\tcoverage: (input: ComponentInput) => number;\n};\n\n/** Git history counts every synced harness, whatever the harness itself records (spec). */\nconst gitCoverage = (): number => 1;\n\nfunction harnessShare(\n\tinput: ComponentInput,\n\tcounts: (input: ComponentInput) => number,\n): number {\n\tconst synced = input.reading.harnesses.length;\n\tif (synced === 0) return 0;\n\treturn counts(input) / synced;\n}\n\nfunction topShare(entries: readonly { value: number }[]): number | undefined {\n\tconst total = entries.reduce((sum, entry) => sum + entry.value, 0);\n\tif (total <= 0) return undefined;\n\tconst top = Math.max(...entries.map((entry) => entry.value));\n\treturn top / total;\n}\n\nexport const COMPONENT_RULES: readonly ComponentRule[] = [\n\t{\n\t\tid: \"activity-heatmap\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of events fall in the three busiest hours of the day\",\n\t\tunit: \"share\",\n\t\t// Three of twenty-four hours is an eighth of the clock. A day spread evenly\n\t\t// lands near it; a night owl runs far above it.\n\t\tband: { low: 0.125, high: 0.35 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst byHour = new Map<number, number>();\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tfor (const cell of harness.activity) {\n\t\t\t\t\tbyHour.set(\n\t\t\t\t\t\tcell.hourUtc,\n\t\t\t\t\t\t(byHour.get(cell.hourUtc) ?? 0) + cell.events,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst total = [...byHour.values()].reduce((sum, n) => sum + n, 0);\n\t\t\tif (total <= 0) return undefined;\n\t\t\tconst busiest = [...byHour.values()].sort((a, b) => b - a).slice(0, 3);\n\t\t\treturn busiest.reduce((sum, n) => sum + n, 0) / total;\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.activity.length > 0)\n\t\t\t\t\t\t.length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"start-hours\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"is the most common start hour\",\n\t\tunit: \"hour\",\n\t\t// A band on a clock face means little; the row is never ranked, and the\n\t\t// figure is a position rather than a size. Kept for shape.\n\t\tband: { low: 9, high: 18 },\n\t\tevaluate: ({ reading }) => modalStartHour(reading),\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.startHours.length > 0)\n\t\t\t\t\t\t.length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"phase-playbook\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"median measured session\",\n\t\tunit: \"minutes\",\n\t\tband: { low: 10, high: 60 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst bucket = medianBucket(\n\t\t\t\tplaybookHarnesses(reading).flatMap((harness) =>\n\t\t\t\t\t(harness.phase?.lengths ?? []).map((row) => ({\n\t\t\t\t\t\tbucket: row.bucket,\n\t\t\t\t\t\tcount: row.sessions,\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn bucket === undefined ? undefined : bucketMid(bucket);\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(input, ({ reading }) => playbookHarnesses(reading).length),\n\t},\n\t{\n\t\tid: \"git-ledger\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of changed lines are removals\",\n\t\tunit: \"share\",\n\t\t// Most work adds more than it takes away. A ledger that removes as much as\n\t\t// it adds is the surprising one, and so is one that never removes.\n\t\tband: { low: 0.15, high: 0.35 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst changed = reading.git.additions + reading.git.removals;\n\t\t\treturn changed > 0 ? reading.git.removals / changed : undefined;\n\t\t},\n\t\tcoverage: gitCoverage,\n\t},\n\t{\n\t\tid: \"coding-languages\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of changed lines are one file type\",\n\t\tunit: \"share\",\n\t\tband: { low: 0.35, high: 0.7 },\n\t\tevaluate: ({ reading }) => {\n\t\t\t// The withheld lines are a real bucket, not a rounding loss: they belong\n\t\t\t// in the denominator, or a stack whose top language is unapproved would\n\t\t\t// read as more concentrated than it is.\n\t\t\tconst named = reading.git.changedLinesByExtension.map((row) => ({\n\t\t\t\tvalue: row.changedLines,\n\t\t\t}));\n\t\t\tconst total =\n\t\t\t\tnamed.reduce((sum, row) => sum + row.value, 0) +\n\t\t\t\treading.git.withheldExtensionLines;\n\t\t\tif (total <= 0 || named.length === 0) return undefined;\n\t\t\treturn Math.max(...named.map((row) => row.value)) / total;\n\t\t},\n\t\tcoverage: gitCoverage,\n\t},\n\t{\n\t\tid: \"kit\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of skill and MCP calls go to one artifact\",\n\t\tunit: \"share\",\n\t\tband: { low: 0.15, high: 0.4 },\n\t\tevaluate: ({ kit }) => {\n\t\t\tif (!kit) return undefined;\n\t\t\tconst byName = new Map<string, number>();\n\t\t\tfor (const harness of kit) {\n\t\t\t\tfor (const atom of [...harness.skills, ...harness.mcpServers]) {\n\t\t\t\t\tbyName.set(atom.name, (byName.get(atom.name) ?? 0) + atom.callShare);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn topShare([...byName.values()].map((share) => ({ value: share })));\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ kit }) =>\n\t\t\t\t\t(kit ?? []).filter(\n\t\t\t\t\t\t(harness) =>\n\t\t\t\t\t\t\tharness.skills.length > 0 || harness.mcpServers.length > 0,\n\t\t\t\t\t).length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"model-routing\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of main-loop tokens run on one model\",\n\t\tunit: \"share\",\n\t\tband: { low: 0.4, high: 0.85 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst main = reading.harnesses.flatMap((harness) => [\n\t\t\t\t...(harness.routing?.main ?? []),\n\t\t\t]);\n\t\t\tconst byModel = new Map<string, number>();\n\t\t\tfor (const row of main) {\n\t\t\t\tbyModel.set(row.model, (byModel.get(row.model) ?? 0) + row.tokens);\n\t\t\t}\n\t\t\treturn topShare(\n\t\t\t\t[...byModel.values()].map((tokens) => ({ value: tokens })),\n\t\t\t);\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.routing).length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"delegation\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of tool calls run inside a subagent\",\n\t\tunit: \"share\",\n\t\tband: { low: 0, high: 0.3 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tlet main = 0;\n\t\t\tlet subagents = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tmain += harness.delegation?.mainToolCalls ?? 0;\n\t\t\t\tsubagents += harness.delegation?.subagentToolCalls ?? 0;\n\t\t\t}\n\t\t\tconst total = main + subagents;\n\t\t\treturn total > 0 ? subagents / total : undefined;\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.delegation).length,\n\t\t\t),\n\t},\n];\n\nexport function componentRule(id: string): ComponentRule | undefined {\n\treturn COMPONENT_RULES.find((rule) => rule.id === id);\n}\n","// The versioned metric rule pool: `metric-rules/v2`.\n//\n// Wayfinder ticket #214 (map #200) declared v1 over per-session facts the CLI\n// reduced on the machine. Ticket #285 moved the wire to daily rows of\n// combinable atoms and moved EVERY evaluation to the server, over the folded\n// window (`daily.ts`). That is v2: the same pool, minus the two rows #277\n// dropped (model switches, effort switches), with the effort and turn rows\n// reshaped to what a histogram can say.\n//\n// A rule declares what it measures (`evaluate`), which harnesses can supply\n// the signal (`counts`, for coverage and the coverage tag), and the typical\n// band the value sits against. The band is DATA THE PAGE DOES NOT RANK BY\n// (#277): fit stays in the API as a number, and the order on the page is the\n// fixed editorial one in `workflowRows.ts`.\n//\n// BAND VALUES ARE DEFAULTS, NOT PROVEN DATA. No calibration run has happened,\n// and a rule version bump corrects one once real synced readings are in.\n\nimport type { HarnessDay } from \"./daily.js\";\nimport { bucketMid, median, medianBucket } from \"./daily.js\";\nimport type { WorkflowReading } from \"./reading.js\";\n\nexport const METRIC_RULES_V2 = \"metric-rules/v2\";\n\nexport type MetricUnit = \"share\" | \"count\" | \"minutes\" | \"hour\";\n\n/** The typical range surprise is measured against, in the metric's own unit. */\nexport type Band = { low: number; high: number };\n\nexport type MetricRule = {\n\tid: string;\n\tversion: string;\n\t/** Sentence fragment completing \"<value> <label>\", e.g. \"of commits land between 23:00 and 03:00\". */\n\tlabel: string;\n\tkind: \"exact\" | \"proxy\";\n\tunit: MetricUnit;\n\tband: Band;\n\t/**\n\t * True for a harness whose fold carries this metric's signal, or `\"all\"`\n\t * when the signal comes from Git history, which counts every synced harness\n\t * regardless of what the harness itself records (spec, \"Fit\").\n\t */\n\tcounts: ((harness: HarnessDay) => boolean) | \"all\";\n\tevaluate: (reading: WorkflowReading) => number | undefined;\n};\n\nexport const METRIC_RULES: readonly MetricRule[] = [\n\t{\n\t\tid: \"late-night-commits\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of commits land between 23:00 and 03:00\",\n\t\tkind: \"exact\",\n\t\tunit: \"share\",\n\t\tcounts: \"all\",\n\t\tband: { low: 0, high: 0.15 },\n\t\tevaluate: (reading) => {\n\t\t\tconst git = reading.git;\n\t\t\tif (git.commits === 0) return undefined;\n\t\t\treturn git.lateNightCommits / git.commits;\n\t\t},\n\t},\n\t{\n\t\tid: \"parallel-projects\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"projects run in parallel on a median active day\",\n\t\tkind: \"proxy\",\n\t\tunit: \"count\",\n\t\tcounts: \"all\",\n\t\tband: { low: 1, high: 1.5 },\n\t\tevaluate: (reading) => median(reading.parallelProjectDays),\n\t},\n\t{\n\t\tid: \"thinking-share\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of response tokens are thinking\",\n\t\tkind: \"proxy\",\n\t\tunit: \"share\",\n\t\tcounts: (harness) => harness.thinking !== undefined,\n\t\tband: { low: 0.1, high: 0.3 },\n\t\tevaluate: (reading) => {\n\t\t\tlet thinking = 0;\n\t\t\tlet response = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tthinking += harness.thinking?.thinkingTokens ?? 0;\n\t\t\t\tresponse += harness.thinking?.responseTokens ?? 0;\n\t\t\t}\n\t\t\treturn response > 0 ? thinking / response : undefined;\n\t\t},\n\t},\n\t{\n\t\tid: \"effort-levels\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of turns run at high effort\",\n\t\tkind: \"exact\",\n\t\tunit: \"share\",\n\t\tcounts: (harness) => harness.effort !== undefined,\n\t\tband: { low: 0.2, high: 0.5 },\n\t\tevaluate: (reading) => {\n\t\t\tlet high = 0;\n\t\t\tlet total = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tfor (const row of harness.effort ?? []) {\n\t\t\t\t\ttotal += row.turns;\n\t\t\t\t\tif (row.level === \"high\") high += row.turns;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total > 0 ? high / total : undefined;\n\t\t},\n\t},\n\t{\n\t\tid: \"turn-duration\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"median turn duration\",\n\t\tkind: \"exact\",\n\t\tunit: \"minutes\",\n\t\tcounts: (harness) => harness.turnDurations !== undefined,\n\t\tband: { low: 0.25, high: 2 },\n\t\tevaluate: (reading) => {\n\t\t\tconst bucket = medianBucket(\n\t\t\t\treading.harnesses.flatMap((harness) =>\n\t\t\t\t\t(harness.turnDurations?.buckets ?? []).map((row) => ({\n\t\t\t\t\t\tbucket: row.bucket,\n\t\t\t\t\t\tcount: row.turns,\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn bucket === undefined ? undefined : bucketMid(bucket) / 60;\n\t\t},\n\t},\n\t{\n\t\tid: \"question-back-share\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of turns end with a question back to the human\",\n\t\tkind: \"proxy\",\n\t\tunit: \"share\",\n\t\tcounts: (harness) => harness.questions !== undefined,\n\t\tband: { low: 0, high: 0.15 },\n\t\tevaluate: (reading) => {\n\t\t\tlet asked = 0;\n\t\t\tlet turns = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tasked += harness.questions?.asked ?? 0;\n\t\t\t\tturns += harness.questions?.turns ?? 0;\n\t\t\t}\n\t\t\treturn turns > 0 ? asked / turns : undefined;\n\t\t},\n\t},\n\t{\n\t\tid: \"web-searches-per-active-day\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"web searches per active day, inside the harness\",\n\t\tkind: \"proxy\",\n\t\tunit: \"count\",\n\t\tcounts: (harness) => harness.webSearches !== undefined,\n\t\tband: { low: 0, high: 4 },\n\t\tevaluate: (reading) => {\n\t\t\tif (reading.webSearchDays === 0) return undefined;\n\t\t\tconst total = reading.harnesses.reduce(\n\t\t\t\t(sum, harness) => sum + (harness.webSearches ?? 0),\n\t\t\t\t0,\n\t\t\t);\n\t\t\treturn total / reading.webSearchDays;\n\t\t},\n\t},\n];\n\nexport function metricRule(id: string): MetricRule | undefined {\n\treturn METRIC_RULES.find((m) => m.id === id);\n}\n","// Shared vocabulary for the measured workflow surface's rule core.\n//\n// Wayfinder ticket #214 (map #200). Terms follow CONTEXT.md's \"Workflow\n// surface\" section. This package holds the DETERMINISTIC rules only - no I/O,\n// no harness-specific file reading (that is ticket #219's local reducers) and\n// no server-side fit ranking or rotation state (that is ticket #218).\n\n/** Matches the four `*_HARNESS_NAME` constants in packages/cli/src/harness/*\\/adapter.ts. */\nexport type HarnessName =\n\t| \"claude-code\"\n\t| \"codex\"\n\t| \"grok-build\"\n\t| \"opencode\"\n\t| \"pi-mono\";\n\nexport const HARNESS_NAMES: readonly HarnessName[] = [\n\t\"claude-code\",\n\t\"codex\",\n\t\"grok-build\",\n\t\"opencode\",\n\t\"pi-mono\",\n];\n\n/**\n * Display label for a coverage tag. Mirrors `harnessLabel` in\n * packages/cli/src/harness/index.ts - kept as a small local copy rather than\n * an import so this package stays dependency-free of the CLI (the web app\n * imports it too, see #217).\n */\nexport function harnessLabel(name: HarnessName): string {\n\tswitch (name) {\n\t\tcase \"claude-code\":\n\t\t\treturn \"Claude Code\";\n\t\tcase \"codex\":\n\t\t\treturn \"Codex\";\n\t\tcase \"grok-build\":\n\t\t\treturn \"Grok Build\";\n\t\tcase \"opencode\":\n\t\t\treturn \"opencode\";\n\t\tcase \"pi-mono\":\n\t\t\treturn \"Pi\";\n\t}\n}\n\n/**\n * The public phase set (spec: docs/specs/workflow-surface.md, \"Phases\").\n * Scout is reading and searching before the change, handoff is the exchange\n * at a blocking human gate. `unknown` is a visible fifth bucket, never hidden.\n */\nexport type PhaseId = \"scout\" | \"build\" | \"verify\" | \"handoff\" | \"unknown\";\n\nexport const PHASES: readonly PhaseId[] = [\n\t\"scout\",\n\t\"build\",\n\t\"verify\",\n\t\"handoff\",\n\t\"unknown\",\n];\n\n/**\n * One recorded tool call, already reduced to the three fields a phase rule\n * needs: when it happened, which tool fired, and its sanitized argument (a\n * skill or subagent name, or a shell command string). Producing this tuple\n * from a harness's own transcript format is the harness reducer's job\n * (ticket #219) - this package only classifies it.\n */\nexport type HarnessEvent = readonly [tsMs: number, tool: string, arg: string];\n","// The versioned phase classifier: `phase-rules/v1`.\n//\n// Wayfinder ticket #214 (map #200), the shipping rule set proven in ticket\n// #196 (`prototypes/phase-extraction/extract.mjs`, PR #197) and specced in\n// docs/specs/workflow-surface.md (\"Phases\"). Ported here as the production\n// rule core - the harness reducers (ticket #219) call `classifyEvent` and\n// `deriveSessionPhases` over their own reduced event lists; this file makes\n// no assumption about how those events were read off disk.\n//\n// First match wins. A rule-set bump reclassifies old sessions from local raw\n// records at the next sync (spec); a session whose raw records are gone\n// keeps its old aggregate tagged with the rule id it was computed under.\n\nimport type { HarnessEvent, HarnessName, PhaseId } from \"./types.js\";\nimport { PHASES } from \"./types.js\";\n\nexport const PHASE_RULES_V1 = \"phase-rules/v1\";\n\n/**\n * The gate a harness's playbook must clear to ship (owner decision, #196,\n * 2026-08-22): 20% or less of a harness's measured time left unclassified.\n * Per harness, so one unreadable harness (opencode measured 28%) holds back\n * only its own playbook.\n */\nexport const UNKNOWN_GATE = 0.2;\n\nexport type PhaseClassification = { ruleId: string; phase: PhaseId };\n\n/**\n * Handoff markers are per adapter, not one global list (#196): Claude Code\n * records `AskUserQuestion` and `ExitPlanMode`, Codex records\n * `request_user_input`, opencode records `question`. Pi records no tool\n * calls at all, so it has none to name.\n */\nexport const HANDOFF_MARKERS: Record<HarnessName, readonly string[]> = {\n\t\"claude-code\": [\n\t\t\"AskUserQuestion\",\n\t\t\"ExitPlanMode\",\n\t\t\"mcp__curia__ask_human\",\n\t\t\"mcp__curia__request_review\",\n\t],\n\tcodex: [\"request_user_input\"],\n\t\"grok-build\": [\"ask_user_question\", \"request_user_input\"],\n\topencode: [\"question\"],\n\t\"pi-mono\": [],\n};\n\n/** Every handoff marker across every adapter, for classifying an event whose harness isn't known yet. */\nconst ALL_HANDOFF_MARKERS = new Set(\n\tObject.values(HANDOFF_MARKERS).flat() as string[],\n);\n\n/**\n * Tools that surface a handoff's result rather than opening one - the curia\n * MCP layer sits above every harness, so these spellings don't vary by adapter.\n */\nconst HANDOFF_SURFACE_TOOLS = [\n\t\"mcp__curia__open_pull_request\",\n\t\"mcp__curia__publish_preview\",\n\t\"mcp__curia__report_result\",\n\t\"mcp__curia__notify\",\n];\n\nconst SCOUT_TOOLS = [\n\t\"Read\",\n\t\"Grep\",\n\t\"Glob\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n\t\"ToolSearch\",\n\t// cross-harness spellings of the same read/search tools\n\t\"read\",\n\t\"grep\",\n\t\"glob\",\n\t\"list\",\n\t\"ls\",\n\t\"webfetch\",\n\t\"websearch\",\n\t\"web_search\",\n\t\"tool_search\",\n\t\"codebase_search\",\n\t\"find\",\n\t\"search_tool\",\n\t\"search_files\",\n\t\"list_dir\",\n\t\"read_file\",\n];\n\nconst EDIT_TOOLS = [\n\t\"Edit\",\n\t\"Write\",\n\t\"NotebookEdit\",\n\t// cross-harness spellings\n\t\"edit\",\n\t\"write\",\n\t\"patch\",\n\t\"multiedit\",\n\t\"apply_patch\",\n\t\"write_file\",\n\t\"edit_file\",\n];\n\nconst REVIEW_SKILLS = [\"code-review\", \"security-review\", \"review\"];\nconst SCOUT_AGENTS = [\"Explore\", \"Plan\", \"research\"];\nconst SHELL_TOOLS = [\n\t\"Bash\",\n\t\"bash\",\n\t\"shell\",\n\t\"local_shell\",\n\t\"exec_command\",\n\t\"run_terminal_command\",\n];\nconst SKILL_TOOLS = [\"Skill\", \"skill\"];\nconst AGENT_TOOLS = [\"Agent\", \"Task\", \"task\", \"agent\"];\n\n/**\n * A todo/task tool carries no information about the work itself - the\n * neighbor data decided this (#196): against a base rate of scout 72% / build\n * 23%, `TaskCreate` sits at build 1%, `TaskStop` at 4%, `Monitor` at 7%.\n * Filing the whole family as build added 4 points on the strength of tools\n * that build nothing, so it inherits the phase of the event before it.\n */\nconst BOOKKEEPING_TOOLS = [\n\t\"TaskCreate\",\n\t\"TaskUpdate\",\n\t\"TaskStop\",\n\t\"TaskOutput\",\n\t\"todowrite\",\n\t\"TodoWrite\",\n\t\"Monitor\",\n\t\"SendUserFile\",\n\t\"SendMessage\",\n\t\"ListAgents\",\n];\n\nfunction isShell(tool: string): boolean {\n\treturn SHELL_TOOLS.includes(tool);\n}\n\nconst skillLeaf = (arg: string): string => arg.split(\":\").pop() ?? arg;\n\n// ---------------------------------------------------------------------------\n// Chain-segment command matching (#196, structural defect 1: 85% of recorded\n// shell commands hold a chain or a pipe, so a whole-string prefix match reads\n// only the first command and misses the rest).\n// ---------------------------------------------------------------------------\n\n/** Splits a shell command into its `&&`/`||`/`;`/`|` segments, normalized. */\nexport function chainSegments(arg: string): string[] {\n\treturn arg\n\t\t.split(/(?:&&|\\|\\||;|\\|)/)\n\t\t.map((s) =>\n\t\t\ts\n\t\t\t\t.trim()\n\t\t\t\t.replace(/^(?:\\w+=\\S*\\s+)+/, \"\")\n\t\t\t\t.replace(/^(?:cd\\s+\\S+\\s*)$/, \"\")\n\t\t\t\t// `git -C <path> log` is the same rule as `git log`. 650 real uses\n\t\t\t\t// matched nothing before this normalization (#196).\n\t\t\t\t.replace(/^git\\s+-C\\s+\\S+\\s+/, \"git \"),\n\t\t)\n\t\t.filter(Boolean);\n}\n\nfunction cmdIs(seg: string, heads: readonly string[]): boolean {\n\tfor (const h of heads) if (seg === h || seg.startsWith(`${h} `)) return true;\n\treturn false;\n}\n\n// Head lists measured off the owner's real history, not guessed (#196,\n// structural defect 2: the first draft's guessed heads left verify at 0%\n// across 464 sessions).\nconst TEST_HEADS = [\n\t\"pnpm test\",\n\t\"vitest\",\n\t\"tsc\",\n\t\"biome\",\n\t\"pnpm build\",\n\t\"pnpm lint\",\n\t\"pnpm typecheck\",\n\t\"npm test\",\n\t\"npm run test\",\n\t\"pnpm vitest\",\n\t\"npx vitest\",\n\t\"npx tsc\",\n\t\"npx biome\",\n\t\"pnpm exec\",\n\t\"pytest\",\n\t\"cargo test\",\n\t\"go test\",\n\t\"node --test\",\n\t\"make test\",\n\t\"pnpm check\",\n\t\"npm run build\",\n\t\"npm run lint\",\n];\nconst PUBLISH_HEADS = [\n\t\"git push\",\n\t\"gh pr create\",\n\t\"gh pr merge\",\n\t\"npm publish\",\n\t\"pnpm publish\",\n\t\"gh release\",\n];\nconst CHANGE_HEADS = [\n\t\"git add\",\n\t\"git commit\",\n\t\"mkdir\",\n\t\"cp\",\n\t\"mv\",\n\t\"rm\",\n\t\"touch\",\n\t\"sed\",\n\t\"pnpm add\",\n\t\"npm install\",\n\t\"git checkout\",\n\t\"git restore\",\n\t\"git stash\",\n\t\"git mv\",\n\t\"git rm\",\n\t\"git rebase\",\n\t\"git merge\",\n\t\"git cherry-pick\",\n\t\"git init\",\n\t\"git branch\",\n\t\"git worktree\",\n\t\"pnpm install\",\n\t\"pnpm remove\",\n\t\"npm i\",\n\t\"npm ci\",\n\t\"yarn add\",\n\t\"chmod\",\n\t\"ln\",\n\t\"tee\",\n\t\"echo\",\n\t\"printf\",\n\t\"npx convex\",\n\t\"pnpm convex\",\n\t\"pnpm dlx\",\n\t\"npx create\",\n];\nconst READ_HEADS = [\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"wc\",\n\t\"grep\",\n\t\"rg\",\n\t\"find\",\n\t\"git log\",\n\t\"git show\",\n\t\"git diff\",\n\t\"git status\",\n\t\"gh issue view\",\n\t\"gh issue list\",\n\t\"gh pr view\",\n\t\"gh pr list\",\n\t\"curl\",\n\t\"pwd\",\n\t\"which\",\n\t\"whoami\",\n\t\"echo $\",\n\t\"env\",\n\t\"printenv\",\n\t\"node --version\",\n\t\"node -v\",\n\t\"pnpm --version\",\n\t\"df\",\n\t\"du\",\n\t\"ps\",\n\t\"top\",\n\t\"file\",\n\t\"stat\",\n\t\"tree\",\n\t\"jq\",\n\t\"sort\",\n\t\"uniq\",\n\t\"cut\",\n\t\"awk\",\n\t\"diff\",\n\t\"gh api\",\n\t\"gh run\",\n\t\"gh workflow\",\n\t\"gh search\",\n\t\"gh issue\",\n\t\"gh pr\",\n\t\"git remote\",\n\t\"git fetch\",\n\t\"git ls-files\",\n\t\"git blame\",\n\t\"git describe\",\n\t\"git rev-parse\",\n\t\"sqlite3\",\n\t\"date\",\n\t\"uname\",\n\t\"man\",\n\t\"history\",\n\t\"type\",\n\t\"nl\",\n\t\"strings\",\n\t\"pgrep\",\n\t\"basename\",\n\t\"dirname\",\n\t\"realpath\",\n];\n\n/**\n * Flag-aware rules for dual-use commands (#196, structural defect 3): the\n * head alone files these wrong. 2,374 of 2,733 `sed` calls are `sed -n`, a\n * read filed as a change; only 199 of 7,758 `echo` calls redirect to a file.\n * These run BEFORE the head lists.\n */\nconst DUAL_USE_RULES: ReadonlyArray<{\n\tid: string;\n\tmatch: RegExp;\n\tbuild: RegExp;\n}> = [\n\t{ id: \"sed\", match: /^sed\\b/, build: /^sed\\s+(-[a-zA-Z]*i|--in-place)\\b/ },\n\t{ id: \"echo\", match: /^(echo|printf)\\b/, build: />>?\\s*\\S/ },\n\t{ id: \"cat\", match: /^cat\\b/, build: /^cat\\s*(>>?\\s*\\S|<<)/ },\n];\n\nfunction dualUse(seg: string): PhaseId | null {\n\tfor (const rule of DUAL_USE_RULES) {\n\t\tif (!rule.match.test(seg)) continue;\n\t\treturn rule.build.test(seg) ? \"build\" : \"scout\";\n\t}\n\treturn null;\n}\n\nconst CHAIN_PHASE_RANK: Record<PhaseId, number> = {\n\tverify: 4,\n\thandoff: 3,\n\tbuild: 2,\n\tscout: 1,\n\tunknown: 0,\n};\n\n/**\n * Classifies a full shell command by splitting it into chain segments,\n * classifying each, and letting the strongest phase in the chain win -\n * ordered verify, handoff, build, scout (spec, rule family 2).\n */\nfunction classifyShellChain(arg: string): PhaseId | null {\n\tlet best: PhaseId | null = null;\n\tfor (const seg of chainSegments(arg)) {\n\t\tlet p = dualUse(seg);\n\t\tif (!p) {\n\t\t\tif (cmdIs(seg, TEST_HEADS)) p = \"verify\";\n\t\t\telse if (cmdIs(seg, PUBLISH_HEADS)) p = \"handoff\";\n\t\t\telse if (cmdIs(seg, CHANGE_HEADS)) p = \"build\";\n\t\t\telse if (cmdIs(seg, READ_HEADS)) p = \"scout\";\n\t\t}\n\t\tif (p && (!best || CHAIN_PHASE_RANK[p] > CHAIN_PHASE_RANK[best])) best = p;\n\t}\n\treturn best;\n}\n\n// ---------------------------------------------------------------------------\n// The rule table. First match wins. A rule with `phase: null` derives its\n// phase from `test`'s return value instead of a fixed id.\n// ---------------------------------------------------------------------------\n\ntype Rule = {\n\tid: string;\n\t/** `null` means the phase comes from `test`'s return value. `\"@prev\"` means \"inherit\". */\n\tphase: PhaseId | \"@prev\" | null;\n\ttest: (tool: string, arg: string) => PhaseId | boolean;\n};\n\nconst RULES_V1: readonly Rule[] = [\n\t{\n\t\tid: \"handoff.surface\",\n\t\tphase: \"handoff\",\n\t\ttest: (t) => HANDOFF_SURFACE_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"verify.review-skill\",\n\t\tphase: \"verify\",\n\t\ttest: (t, a) => SKILL_TOOLS.includes(t) && REVIEW_SKILLS.includes(a),\n\t},\n\t// Forge stage markers (#166 round 3): named rules where the harness records\n\t// the skill call, matched on the last path segment so the\n\t// plugin-namespaced spelling `forge:crossfire` counts too.\n\t{\n\t\tid: \"verify.crossfire-skill\",\n\t\tphase: \"verify\",\n\t\ttest: (t, a) => SKILL_TOOLS.includes(t) && skillLeaf(a) === \"crossfire\",\n\t},\n\t{\n\t\tid: \"build.forge-skill\",\n\t\tphase: \"build\",\n\t\ttest: (t, a) => SKILL_TOOLS.includes(t) && skillLeaf(a) === \"forge\",\n\t},\n\t{\n\t\tid: \"build.edit-tool\",\n\t\tphase: \"build\",\n\t\ttest: (t) => EDIT_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"scout.read-tool\",\n\t\tphase: \"scout\",\n\t\ttest: (t) => SCOUT_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"scout.skill-load\",\n\t\tphase: \"scout\",\n\t\ttest: (t) => SKILL_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"scout.scout-agent\",\n\t\tphase: \"scout\",\n\t\ttest: (t, a) => AGENT_TOOLS.includes(t) && SCOUT_AGENTS.includes(a),\n\t},\n\t// Plan bookkeeping inherits the phase of the event before it (#196).\n\t// `phase: null` here means \"look at prevPhase\", signaled via the\n\t// sentinel below rather than a boolean/PhaseId return.\n\t{\n\t\tid: \"inherit.bookkeeping\",\n\t\tphase: \"@prev\",\n\t\ttest: (t) => BOOKKEEPING_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"chain-cmd\",\n\t\tphase: null,\n\t\ttest: (t, a) => (isShell(t) ? (classifyShellChain(a) ?? false) : false),\n\t},\n\t{ id: \"unknown.shell\", phase: \"unknown\", test: (t) => isShell(t) },\n\t{\n\t\tid: \"unknown.agent\",\n\t\tphase: \"unknown\",\n\t\ttest: (t) => AGENT_TOOLS.includes(t),\n\t},\n\t{ id: \"unknown.tool\", phase: \"unknown\", test: () => true },\n];\n\nconst RULE_SETS: Record<string, readonly Rule[]> = {\n\t[PHASE_RULES_V1]: RULES_V1,\n};\n\n/**\n * Classifies one event under the named rule set. `prevPhase` resolves plan\n * bookkeeping's inherited phase (`null` before the first non-bookkeeping\n * event of a session, which classifies as `unknown`).\n */\nexport function classifyEvent(\n\ttool: string,\n\targ: string,\n\tprevPhase: PhaseId | null,\n\truleSet: string = PHASE_RULES_V1,\n\tharness?: HarnessName,\n): PhaseClassification {\n\tconst handoffMarkers = harness\n\t\t? new Set(HANDOFF_MARKERS[harness])\n\t\t: ALL_HANDOFF_MARKERS;\n\tif (handoffMarkers.has(tool)) {\n\t\treturn { ruleId: \"handoff.blocking-call\", phase: \"handoff\" };\n\t}\n\tconst rules = RULE_SETS[ruleSet];\n\tif (!rules) throw new Error(`unknown phase rule set: ${ruleSet}`);\n\tfor (const rule of rules) {\n\t\tconst res = rule.test(tool, arg);\n\t\tif (res === false) continue;\n\t\tif (rule.phase === null) {\n\t\t\t// `chain-cmd`: the phase IS the result.\n\t\t\treturn { ruleId: `${rule.id}.${res}`, phase: res as PhaseId };\n\t\t}\n\t\tif (rule.phase === \"@prev\") {\n\t\t\treturn { ruleId: rule.id, phase: prevPhase ?? \"unknown\" };\n\t\t}\n\t\treturn { ruleId: rule.id, phase: rule.phase };\n\t}\n\treturn { ruleId: \"unknown.tool\", phase: \"unknown\" };\n}\n\n// ---------------------------------------------------------------------------\n// Session time attribution: each event owns the gap to the next event,\n// capped at 5 minutes. The tail (after the last event) is capped at 60s.\n// The wait at a blocking handoff call renders as a striped waiting slice.\n// ---------------------------------------------------------------------------\n\nconst CAP_SEC = 300;\nconst TAIL_SEC = 60;\n\n/** Residual families for the unknown bucket, weighed by time before any rule is proposed for them (#196). */\nexport type ResidualFamily =\n\t| \"mcp server\"\n\t| \"harness bookkeeping\"\n\t| \"delegation\"\n\t| \"other harness tool\"\n\t| \"interpreter run\"\n\t| \"remote or container\"\n\t| \"shell construct\"\n\t| \"other shell command\";\n\nconst INTERPRETER_HEADS = [\n\t\"python3\",\n\t\"python\",\n\t\"node\",\n\t\"bun\",\n\t\"deno\",\n\t\"ruby\",\n\t\"perl\",\n\t\"php\",\n\t\"tsx\",\n];\nconst REMOTE_HEADS = [\"ssh\", \"tmux\", \"docker\", \"scp\", \"rsync\", \"kubectl\"];\n\nfunction residualFamily(tool: string, arg: string): ResidualFamily {\n\tif (tool.startsWith(\"mcp__\") || /chrome-devtools/.test(tool))\n\t\treturn \"mcp server\";\n\tif (BOOKKEEPING_TOOLS.includes(tool)) return \"harness bookkeeping\";\n\tif (AGENT_TOOLS.includes(tool)) return \"delegation\";\n\tif (!isShell(tool)) return \"other harness tool\";\n\tconst seg = chainSegments(arg)[0] ?? arg;\n\tconst head = (seg.trim().split(/\\s+/)[0] ?? \"\").split(\"/\").pop() ?? \"\";\n\tif (INTERPRETER_HEADS.includes(head)) return \"interpreter run\";\n\tif (REMOTE_HEADS.includes(head)) return \"remote or container\";\n\tif (/^(for|while|until|if|timeout|sleep|true|bash|sh|zsh)$/.test(head))\n\t\treturn \"shell construct\";\n\treturn \"other shell command\";\n}\n\nexport type SessionPhaseDerivation = {\n\t/** Seconds of measured time per phase, including the visible `unknown` bucket. */\n\tphaseSec: Record<PhaseId, number>;\n\t/** Event counts per phase. */\n\tphaseEvents: Record<PhaseId, number>;\n\t/** Seconds spent waiting at a blocking handoff call (a striped slice, spec). */\n\twaitingSec: number;\n\t/** Seconds spent idle between events for any other reason, above the cap. */\n\tidleSec: number;\n\t/** Rule id -> event count, for auditing which rule fired how often. */\n\truleTally: Record<string, number>;\n\t/** Unknown seconds by residual family, for weighing the next rule proposal. */\n\tresidualSec: Partial<Record<ResidualFamily, number>>;\n\truleSet: string;\n};\n\nfunction emptyPhaseRecord(): Record<PhaseId, number> {\n\treturn { scout: 0, build: 0, verify: 0, handoff: 0, unknown: 0 };\n}\n\n/**\n * Derives one session's phase mix from its raw events. Each event owns the\n * gap to the next event (capped at 5 minutes); the last event owns a fixed\n * 60s tail. A gap following a blocking handoff call counts as waiting rather\n * than idle.\n */\nexport function deriveSessionPhases(\n\tevents: readonly HarnessEvent[],\n\truleSet: string = PHASE_RULES_V1,\n\tharness?: HarnessName,\n): SessionPhaseDerivation {\n\tconst phaseSec = emptyPhaseRecord();\n\tconst phaseEvents = emptyPhaseRecord();\n\tconst ruleTally: Record<string, number> = {};\n\tconst residualSec: Partial<Record<ResidualFamily, number>> = {};\n\tlet waitingSec = 0;\n\tlet idleSec = 0;\n\tlet prevPhase: PhaseId | null = null;\n\n\tfor (let i = 0; i < events.length; i++) {\n\t\tconst event = events[i];\n\t\tif (!event) continue;\n\t\tconst [ts, tool, arg] = event;\n\t\tconst next = events[i + 1];\n\t\tconst gapSec = next ? (next[0] - ts) / 1000 : TAIL_SEC;\n\t\tconst ownSec = Math.min(gapSec, CAP_SEC);\n\n\t\tconst { ruleId, phase } = classifyEvent(\n\t\t\ttool,\n\t\t\targ,\n\t\t\tprevPhase,\n\t\t\truleSet,\n\t\t\tharness,\n\t\t);\n\t\tif (phase !== \"unknown\") prevPhase = phase;\n\t\truleTally[ruleId] = (ruleTally[ruleId] ?? 0) + 1;\n\t\tphaseSec[phase] += ownSec;\n\t\tphaseEvents[phase] += 1;\n\n\t\tif (phase === \"unknown\") {\n\t\t\tconst family = residualFamily(tool, arg);\n\t\t\tresidualSec[family] = (residualSec[family] ?? 0) + ownSec;\n\t\t}\n\n\t\tconst overflow = gapSec - ownSec;\n\t\tif (overflow > 0) {\n\t\t\tconst handoffMarkers = harness\n\t\t\t\t? new Set(HANDOFF_MARKERS[harness])\n\t\t\t\t: ALL_HANDOFF_MARKERS;\n\t\t\tif (handoffMarkers.has(tool)) waitingSec += overflow;\n\t\t\telse idleSec += overflow;\n\t\t}\n\t}\n\n\treturn {\n\t\tphaseSec,\n\t\tphaseEvents,\n\t\twaitingSec,\n\t\tidleSec,\n\t\truleTally,\n\t\tresidualSec,\n\t\truleSet,\n\t};\n}\n\n/** Share of a session's measured (non-unknown) attribution that landed in `unknown`, 0..1. */\nexport function unknownShare(derivation: SessionPhaseDerivation): number {\n\tconst total = PHASES.reduce((sum, p) => sum + derivation.phaseSec[p], 0);\n\treturn total > 0 ? derivation.phaseSec.unknown / total : 0;\n}\n","// The daily unit of the usage wire, and the fold that turns days into a window.\n//\n// Tickets #305, #306 and #315 (ADR-0010, ADR-0011). The snapshot payload the\n// CLI sends today is one 30-day block with its shares already computed. This\n// module is its per-day successor: one `measuredDays` row per (stack, machine,\n// date) holds `{ date, usage?, workflow? }` under ONE version, `measured-days/v1`,\n// with ONE fingerprint over both blocks.\n//\n// ONLY COMBINABLE ATOMS. A usage day carries token sums, session counts, project\n// keys and exact dollars, never a share or a mean. Shares, active days and the\n// dollar total come out of the fold, over the window's atoms. A day that lacks\n// the cache-write split folds its whole `cacheWrite` into `unsplit`.\n//\n// THE FOLD HAS THE DAY'S SHAPE. A window over one day prints the day's own\n// figures, and the tests fold a fixture of one day and compare it with itself.\n//\n// A READING IS ONE MACHINE'S, PER DAY (ADR-0009). Nothing here merges machines.\n\nimport type { WorkflowDay } from \"./daily.js\";\n\nexport const MEASURED_DAYS_V1 = \"measured-days/v1\";\n\nexport type UsageTokens = {\n\tinput: number;\n\toutput: number;\n\tcacheWrite: number;\n\tcacheRead: number;\n\t/**\n\t * The cache-write split by TTL. `unsplit` holds writes from payloads that\n\t * predate the split. Absent when the day recorded no split at all.\n\t */\n\tcacheWriteTtl?: { fiveMinute: number; oneHour: number; unsplit: number };\n};\n\nexport type UsageModelDay = {\n\tmodel: string;\n\ttokens: UsageTokens;\n\t/** Exact dollars the CLI priced at ingest; absent when unpriced or publishCost off. */\n\tusd?: number;\n\tpricingTable?: string;\n};\n\nexport type UsageHarnessDay = {\n\tharness: string;\n\t/** Sessions that STARTED this day. */\n\tsessions: number;\n\t/** Hashed project keys touched this day, sorted unique. */\n\tprojectKeys: readonly string[];\n\tmodels: readonly UsageModelDay[];\n\t/** Tokens spent inside subagent turns, all models. */\n\tsubagentTokens: number;\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\nexport type UsageDay = { harnesses: readonly UsageHarnessDay[] };\n\n/** One stored row. When `workflow` is present, `workflow.date === date`. */\nexport type MeasuredDay = {\n\tdate: string;\n\tusage?: UsageDay;\n\tworkflow?: WorkflowDay;\n};\n\nexport type UsageWindowModel = {\n\tmodel: string;\n\ttokens: UsageTokens;\n\ttotalTokens: number;\n\t/** `totalTokens / window totalTokens`, rounded to 4 places, like the CLI's `tokenShare`. */\n\ttokenShare: number;\n\t/** Exact sum over days that carried usd; `undefined` when no day did. */\n\tusd: number | undefined;\n\t/** Tokens of the days that carried no usd, for the server's per-day fill. */\n\tunpricedTokens: UsageTokens;\n\t/** Dates lacking usd for this model, sorted unique. */\n\tunpricedDates: readonly string[];\n\tpricingTables: readonly string[];\n};\n\nexport type UsageWindowHarness = {\n\tharness: string;\n\tsessions: number;\n\ttotalTokens: number;\n\ttokenShare: number;\n};\n\nexport type UsageWindow = {\n\taggregateVersion: string;\n\tdates: readonly string[];\n\t/** Days with at least one session. */\n\tactiveDays: number;\n\tsessions: number;\n\tprojectKeys: readonly string[];\n\ttokens: UsageTokens;\n\ttotalTokens: number;\n\t/**\n\t * `cacheRead / (input + cacheRead + cacheWrite)`: cache reads over the\n\t * input side, as the CLI's `computeCacheHitShare`. Output is not in the\n\t * denominator. Rounded to 4 places.\n\t */\n\tcacheHitShare: number;\n\t/** `subagentTokens / totalTokens`, rounded to 4 places. */\n\tsubagentShare: number;\n\tmodels: readonly UsageWindowModel[];\n\tharnesses: readonly UsageWindowHarness[];\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\n/** The CLI's `round4`: four decimal places on every share. */\nconst round4 = (n: number): number => Math.round(n * 10_000) / 10_000;\n\nexport function emptyUsageTokens(): UsageTokens {\n\treturn { input: 0, output: 0, cacheWrite: 0, cacheRead: 0 };\n}\n\n/**\n * Mutating add. The TTL split folds when either side carries one; a side\n * without the split adds its whole `cacheWrite` to `unsplit`, so the split's\n * three parts always sum to `cacheWrite` on the result.\n */\nexport function addUsageTokens(into: UsageTokens, from: UsageTokens): void {\n\tif (into.cacheWriteTtl || from.cacheWriteTtl) {\n\t\tconst a = into.cacheWriteTtl ?? {\n\t\t\tfiveMinute: 0,\n\t\t\toneHour: 0,\n\t\t\tunsplit: into.cacheWrite,\n\t\t};\n\t\tconst b = from.cacheWriteTtl ?? {\n\t\t\tfiveMinute: 0,\n\t\t\toneHour: 0,\n\t\t\tunsplit: from.cacheWrite,\n\t\t};\n\t\tinto.cacheWriteTtl = {\n\t\t\tfiveMinute: a.fiveMinute + b.fiveMinute,\n\t\t\toneHour: a.oneHour + b.oneHour,\n\t\t\tunsplit: a.unsplit + b.unsplit,\n\t\t};\n\t}\n\tinto.input += from.input;\n\tinto.output += from.output;\n\tinto.cacheWrite += from.cacheWrite;\n\tinto.cacheRead += from.cacheRead;\n}\n\n/**\n * The processed-token total. The wire normalizes every provider to disjoint\n * buckets, so cache reads are added exactly once. This matches Codex account\n * activity, OpenAI's cache-inclusive `input_tokens + output_tokens`, and the\n * CLI snapshot total.\n */\nexport function totalOfTokens(t: UsageTokens): number {\n\treturn t.input + t.output + t.cacheWrite + t.cacheRead;\n}\n\ntype ModelAcc = {\n\tmodel: string;\n\ttokens: UsageTokens;\n\tusd: number | undefined;\n\tunpricedTokens: UsageTokens;\n\tunpricedDates: Set<string>;\n\tpricingTables: Set<string>;\n};\n\n/**\n * Fold one machine's usage days into a window.\n *\n * `dates` is sorted unique. Models sort by `totalTokens` desc then model id,\n * harnesses likewise, matching the CLI's `groupModels` order. Empty input gives\n * a zeroed window with every share 0.\n */\nexport function foldUsageDays(\n\tdays: readonly { date: string; usage: UsageDay }[],\n): UsageWindow {\n\tconst tokens = emptyUsageTokens();\n\tconst projectKeys = new Set<string>();\n\tconst activeDates = new Set<string>();\n\tconst models = new Map<string, ModelAcc>();\n\tconst harnesses = new Map<\n\t\tstring,\n\t\t{ harness: string; sessions: number; tokens: UsageTokens }\n\t>();\n\tlet sessions = 0;\n\tlet subagentTokens = 0;\n\tconst excludedTokens = { unpriced: 0, synthetic: 0 };\n\n\tfor (const day of days) {\n\t\tfor (const h of day.usage.harnesses) {\n\t\t\tsessions += h.sessions;\n\t\t\tif (h.sessions > 0) activeDates.add(day.date);\n\t\t\tsubagentTokens += h.subagentTokens;\n\t\t\texcludedTokens.unpriced += h.excludedTokens.unpriced;\n\t\t\texcludedTokens.synthetic += h.excludedTokens.synthetic;\n\t\t\tfor (const key of h.projectKeys) projectKeys.add(key);\n\t\t\tconst held = harnesses.get(h.harness) ?? {\n\t\t\t\tharness: h.harness,\n\t\t\t\tsessions: 0,\n\t\t\t\ttokens: emptyUsageTokens(),\n\t\t\t};\n\t\t\theld.sessions += h.sessions;\n\t\t\tharnesses.set(h.harness, held);\n\t\t\tfor (const m of h.models) {\n\t\t\t\taddUsageTokens(tokens, m.tokens);\n\t\t\t\taddUsageTokens(held.tokens, m.tokens);\n\t\t\t\tconst acc = models.get(m.model) ?? {\n\t\t\t\t\tmodel: m.model,\n\t\t\t\t\ttokens: emptyUsageTokens(),\n\t\t\t\t\tusd: undefined,\n\t\t\t\t\tunpricedTokens: emptyUsageTokens(),\n\t\t\t\t\tunpricedDates: new Set<string>(),\n\t\t\t\t\tpricingTables: new Set<string>(),\n\t\t\t\t};\n\t\t\t\taddUsageTokens(acc.tokens, m.tokens);\n\t\t\t\tif (m.usd === undefined) {\n\t\t\t\t\taddUsageTokens(acc.unpricedTokens, m.tokens);\n\t\t\t\t\tacc.unpricedDates.add(day.date);\n\t\t\t\t} else {\n\t\t\t\t\tacc.usd = (acc.usd ?? 0) + m.usd;\n\t\t\t\t}\n\t\t\t\tif (m.pricingTable) acc.pricingTables.add(m.pricingTable);\n\t\t\t\tmodels.set(m.model, acc);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst totalTokens = totalOfTokens(tokens);\n\tconst share = (n: number): number =>\n\t\ttotalTokens ? round4(n / totalTokens) : 0;\n\tconst inputSide = tokens.input + tokens.cacheRead + tokens.cacheWrite;\n\treturn {\n\t\taggregateVersion: MEASURED_DAYS_V1,\n\t\tdates: [...new Set(days.map((day) => day.date))].sort(),\n\t\tactiveDays: activeDates.size,\n\t\tsessions,\n\t\tprojectKeys: [...projectKeys].sort(),\n\t\ttokens,\n\t\ttotalTokens,\n\t\tcacheHitShare: inputSide ? round4(tokens.cacheRead / inputSide) : 0,\n\t\tsubagentShare: totalTokens ? round4(subagentTokens / totalTokens) : 0,\n\t\tmodels: [...models.values()]\n\t\t\t.map((acc) => ({\n\t\t\t\tmodel: acc.model,\n\t\t\t\ttokens: acc.tokens,\n\t\t\t\ttotalTokens: totalOfTokens(acc.tokens),\n\t\t\t\ttokenShare: share(totalOfTokens(acc.tokens)),\n\t\t\t\tusd: acc.usd,\n\t\t\t\tunpricedTokens: acc.unpricedTokens,\n\t\t\t\tunpricedDates: [...acc.unpricedDates].sort(),\n\t\t\t\tpricingTables: [...acc.pricingTables].sort(),\n\t\t\t}))\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tb.totalTokens - a.totalTokens || a.model.localeCompare(b.model),\n\t\t\t),\n\t\tharnesses: [...harnesses.values()]\n\t\t\t.map((h) => ({\n\t\t\t\tharness: h.harness,\n\t\t\t\tsessions: h.sessions,\n\t\t\t\ttotalTokens: totalOfTokens(h.tokens),\n\t\t\t\ttokenShare: share(totalOfTokens(h.tokens)),\n\t\t\t}))\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tb.totalTokens - a.totalTokens || a.harness.localeCompare(b.harness),\n\t\t\t),\n\t\texcludedTokens,\n\t};\n}\n\n/** JSON with object keys sorted at every depth, so key order never changes the hash. */\nfunction canonicalJson(value: unknown): string {\n\tif (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n\tif (value && typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>)\n\t\t\t.filter(([, v]) => v !== undefined)\n\t\t\t.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n\t\treturn `{${entries\n\t\t\t.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`)\n\t\t\t.join(\",\")}}`;\n\t}\n\treturn JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * FNV-1a, 64-bit, over the UTF-8 bytes of `text`. Pure JS with no imports so\n * it runs in the Convex runtime and in Node alike. Returns 16 hex characters.\n */\nfunction fnv1a64(text: string): string {\n\tconst prime = 0x100000001b3n;\n\tconst mask = 0xffffffffffffffffn;\n\tlet hash = 0xcbf29ce484222325n;\n\tconst bytes = new TextEncoder().encode(text);\n\tfor (const byte of bytes) {\n\t\thash ^= BigInt(byte);\n\t\thash = (hash * prime) & mask;\n\t}\n\treturn hash.toString(16).padStart(16, \"0\");\n}\n\n/**\n * The content identity of one stored day: a hex hash over `MEASURED_DAYS_V1`\n * and both blocks, stable across key order. Two days that differ only in key\n * order hash equal; a version bump changes every hash. This is identity for\n * skipping an unchanged re-sync, not a security primitive.\n */\nexport function dayFingerprint(day: MeasuredDay): string {\n\treturn fnv1a64(\n\t\tcanonicalJson({\n\t\t\tversion: MEASURED_DAYS_V1,\n\t\t\tdate: day.date,\n\t\t\tusage: day.usage,\n\t\t\tworkflow: day.workflow,\n\t\t}),\n\t);\n}\n\nexport type RangeId = \"30d\" | \"7d\" | \"24h\";\n\nexport const RANGES: readonly RangeId[] = [\"30d\", \"7d\", \"24h\"];\n\nconst DAY_MS = 86_400_000;\n\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\n/** Midnight UTC of the day holding `ms`. */\nconst dayStart = (ms: number): number => Math.floor(ms / DAY_MS) * DAY_MS;\n\nconst rangeLength = (range: RangeId): number =>\n\trange === \"30d\" ? 30 : range === \"7d\" ? 7 : 1;\n\n/**\n * The inclusive `YYYY-MM-DD` UTC dates a range covers, ending today.\n * 24h is today only; 7d is today-6..today; 30d is today-29..today.\n */\nexport function rangeDates(\n\trange: RangeId,\n\tnowMs: number,\n): { from: string; to: string } {\n\tconst today = dayStart(nowMs);\n\treturn {\n\t\tfrom: utcDate(today - (rangeLength(range) - 1) * DAY_MS),\n\t\tto: utcDate(today),\n\t};\n}\n\n/**\n * The same-length range immediately before `rangeDates`: 24h is yesterday, 7d\n * is the 7 days before, 30d is 60 to 30 days ago.\n */\nexport function previousRangeDates(\n\trange: RangeId,\n\tnowMs: number,\n): { from: string; to: string } {\n\tconst length = rangeLength(range);\n\treturn rangeDates(range, dayStart(nowMs) - length * DAY_MS);\n}\n\n/** Inclusive on both ends. Dates are `YYYY-MM-DD`, so string order is date order. */\nexport function inDateRange(\n\tdate: string,\n\tr: { from: string; to: string },\n): boolean {\n\treturn date >= r.from && date <= r.to;\n}\n\n/**\n * `(current - previous) / previous`. `null` when `previous` is 0 or either\n * side is not finite: a change from nothing has no ratio.\n */\nexport function ratioChange(current: number, previous: number): number | null {\n\tif (!Number.isFinite(current) || !Number.isFinite(previous)) return null;\n\tif (previous === 0) return null;\n\treturn (current - previous) / previous;\n}\n","// The rows of one reading, in the fixed order the page prints them.\n//\n// Wayfinder ticket #218 (map #200) built the rows for a fit ranking. Ticket\n// #277 took fit off the page: on the first prod reading 15 of 16 rows sat under\n// the fit line and every row the owner wanted scored zero, so the section\n// moved to a fixed editorial order with a picture on every row. Ticket #285\n// dropped the ranking state from the server. This file is now the join\n// between the two rule pools and that order, and it is where the CLI, the\n// server and the page agree on which rows exist, what they are called, and\n// which of them are flat.\n//\n// FIT STAYS IN THE API AS A NUMBER NOTHING RANKS BY. `surprise` and `fit` are\n// still computed per row because the band is part of the versioned rule and a\n// reader of the API may want them. No caller sorts by them.\n\nimport type { ComponentInput } from \"./componentRules.js\";\nimport { COMPONENT_RULES } from \"./componentRules.js\";\nimport type { Band, MetricUnit } from \"./metricRules.js\";\nimport { METRIC_RULES } from \"./metricRules.js\";\nimport { type HarnessName, harnessLabel } from \"./types.js\";\n\n/** The podium: the first three rows in the fixed order. */\nexport const HIGHLIGHT_SLOTS = 3;\n\nexport type RowKind = \"metric\" | \"component\";\n\n/** `metric:late-night-commits`, `component:git-ledger`. Stable across rule versions. */\nexport function metricRowId(metricId: string): string {\n\treturn `metric:${metricId}`;\n}\n\nexport function componentRowId(componentId: string): string {\n\treturn `component:${componentId}`;\n}\n\nexport type WorkflowRowOrder = {\n\trowId: string;\n\t/** The plain name the page prints (#284). */\n\tname: string;\n\t/**\n\t * True when the row's head holds its whole picture, so the row never\n\t * expands (#284): no chevron, no body.\n\t */\n\tflat: boolean;\n};\n\n/**\n * The fixed editorial order (#284, decision 2), one entry per row either pool\n * can produce. A row absent from the reading is skipped, and the order of the\n * rest does not change.\n */\nexport const WORKFLOW_ROW_ORDER: readonly WorkflowRowOrder[] = [\n\t{\n\t\trowId: \"component:activity-heatmap\",\n\t\tname: \"When work happens\",\n\t\tflat: false,\n\t},\n\t{ rowId: \"component:start-hours\", name: \"Session start times\", flat: false },\n\t{\n\t\trowId: \"metric:late-night-commits\",\n\t\tname: \"Late-night commits\",\n\t\tflat: true,\n\t},\n\t{ rowId: \"component:phase-playbook\", name: \"Session length\", flat: false },\n\t{ rowId: \"component:git-ledger\", name: \"Lines changed\", flat: false },\n\t{ rowId: \"component:coding-languages\", name: \"Languages\", flat: false },\n\t{ rowId: \"component:kit\", name: \"Skills and MCP\", flat: false },\n\t{ rowId: \"component:model-routing\", name: \"Models used\", flat: false },\n\t{ rowId: \"component:delegation\", name: \"Subagents\", flat: false },\n\t{ rowId: \"metric:effort-levels\", name: \"Effort levels\", flat: false },\n\t{ rowId: \"metric:thinking-share\", name: \"Thinking tokens\", flat: false },\n\t{ rowId: \"metric:turn-duration\", name: \"Turn length\", flat: false },\n\t{ rowId: \"metric:question-back-share\", name: \"Questions asked\", flat: true },\n\t{\n\t\trowId: \"metric:web-searches-per-active-day\",\n\t\tname: \"Web searches\",\n\t\tflat: true,\n\t},\n\t{ rowId: \"metric:parallel-projects\", name: \"Parallel projects\", flat: true },\n];\n\nconst ORDER_INDEX = new Map(\n\tWORKFLOW_ROW_ORDER.map((row, index) => [row.rowId, index]),\n);\n\nexport function rowOrder(rowId: string): WorkflowRowOrder | undefined {\n\treturn WORKFLOW_ROW_ORDER.find((row) => row.rowId === rowId);\n}\n\n/** Every row id either rule pool can produce. */\nexport const KNOWN_ROW_IDS: ReadonlySet<string> = new Set([\n\t...METRIC_RULES.map((rule) => metricRowId(rule.id)),\n\t...COMPONENT_RULES.map((rule) => componentRowId(rule.id)),\n]);\n\n/**\n * One row of the reading.\n *\n * Both pools produce the same shape: a metric row's value is the rule's\n * evaluation over the folded window, and a component row's value is arithmetic\n * over the same window. Both are computed on the server.\n */\nexport type WorkflowRow = {\n\t/** Stable across syncs and rule versions: what a pin or a hide is keyed on. */\n\trowId: string;\n\tkind: RowKind;\n\truleId: string;\n\truleVersion: string;\n\tlabel: string;\n\tname: string;\n\tflat: boolean;\n\tunit: MetricUnit;\n\tvalue: number;\n\tband: Band;\n\t/** Share of this reading's synced harnesses the row counts, 0..1. */\n\tcoverage: number;\n\tcoverageTag?: string;\n\t/** Distance outside the typical band, 0..1. Nothing ranks by it. */\n\tsurprise: number;\n\t/** Coverage times surprise. Nothing ranks by it. */\n\tfit: number;\n};\n\n/**\n * How far outside its typical band a value sits, as 0..1.\n *\n * `d / (d + width)`, so one band width outside reads as 0.5 and the scale never\n * reaches 1. A value inside the band scores 0.\n */\nexport function surpriseOf(value: number, band: Band): number {\n\tconst width = Math.max(band.high - band.low, Number.EPSILON);\n\tconst distance =\n\t\tvalue < band.low\n\t\t\t? band.low - value\n\t\t\t: value > band.high\n\t\t\t\t? value - band.high\n\t\t\t\t: 0;\n\tif (distance === 0) return 0;\n\treturn distance / (distance + width);\n}\n\n/** Fit is coverage times surprise (spec, CONTEXT.md). */\nexport function fitOf(coverage: number, surprise: number): number {\n\treturn coverage * surprise;\n}\n\n/** The coverage tag naming the counted harnesses, or `undefined` when every synced harness counts. */\nexport function coverageTag(\n\tcounted: readonly string[],\n\tsynced: readonly string[],\n): string | undefined {\n\tif (counted.length === 0 || counted.length === synced.length)\n\t\treturn undefined;\n\treturn `counts: ${counted.map((name) => harnessLabel(name as HarnessName)).join(\" · \")}`;\n}\n\n/**\n * Build the row set for one reading, in the fixed order.\n *\n * \"A row ships when its measurement exists. A missing measurement stays absent,\n * so no separate first-ship list exists\" (spec). Both pools follow it: a rule\n * returns undefined for a window that cannot support its row, and the row is\n * skipped rather than printed as a zero.\n */\nexport function buildWorkflowRows(input: ComponentInput): WorkflowRow[] {\n\tconst rows: WorkflowRow[] = [];\n\tconst synced = input.reading.harnesses.map((harness) => harness.harness);\n\n\tfor (const rule of METRIC_RULES) {\n\t\tconst value = rule.evaluate(input.reading);\n\t\tif (value === undefined) continue;\n\t\tconst counted =\n\t\t\trule.counts === \"all\"\n\t\t\t\t? synced\n\t\t\t\t: input.reading.harnesses\n\t\t\t\t\t\t.filter(rule.counts)\n\t\t\t\t\t\t.map((harness) => harness.harness);\n\t\tconst coverage =\n\t\t\trule.counts === \"all\"\n\t\t\t\t? 1\n\t\t\t\t: synced.length === 0\n\t\t\t\t\t? 0\n\t\t\t\t\t: counted.length / synced.length;\n\t\tconst tag =\n\t\t\trule.counts === \"all\" ? undefined : coverageTag(counted, synced);\n\t\trows.push(\n\t\t\tfinishRow({\n\t\t\t\trowId: metricRowId(rule.id),\n\t\t\t\tkind: \"metric\",\n\t\t\t\truleId: rule.id,\n\t\t\t\truleVersion: rule.version,\n\t\t\t\tlabel: rule.label,\n\t\t\t\tunit: rule.unit,\n\t\t\t\tvalue,\n\t\t\t\tband: rule.band,\n\t\t\t\tcoverage,\n\t\t\t\t...(tag === undefined ? {} : { coverageTag: tag }),\n\t\t\t}),\n\t\t);\n\t}\n\n\tfor (const rule of COMPONENT_RULES) {\n\t\tconst value = rule.evaluate(input);\n\t\tif (value === undefined) continue;\n\t\trows.push(\n\t\t\tfinishRow({\n\t\t\t\trowId: componentRowId(rule.id),\n\t\t\t\tkind: \"component\",\n\t\t\t\truleId: rule.id,\n\t\t\t\truleVersion: rule.version,\n\t\t\t\tlabel: rule.label,\n\t\t\t\tunit: rule.unit,\n\t\t\t\tvalue,\n\t\t\t\tband: rule.band,\n\t\t\t\tcoverage: rule.coverage(input),\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn rows.sort(\n\t\t(a, b) =>\n\t\t\t(ORDER_INDEX.get(a.rowId) ?? Number.MAX_SAFE_INTEGER) -\n\t\t\t\t(ORDER_INDEX.get(b.rowId) ?? Number.MAX_SAFE_INTEGER) ||\n\t\t\ta.rowId.localeCompare(b.rowId),\n\t);\n}\n\nfunction finishRow(\n\trow: Omit<WorkflowRow, \"surprise\" | \"fit\" | \"name\" | \"flat\">,\n): WorkflowRow {\n\tconst order = rowOrder(row.rowId);\n\tconst surprise = surpriseOf(row.value, row.band);\n\treturn {\n\t\t...row,\n\t\tname: order?.name ?? row.label,\n\t\tflat: order?.flat ?? false,\n\t\tsurprise,\n\t\tfit: fitOf(row.coverage, surprise),\n\t};\n}\n\nexport type Placement = \"highlight\" | \"normal\";\n\nexport type PlacedRow = WorkflowRow & {\n\tplacement: Placement;\n};\n\n/**\n * Place one reading's rows in the fixed order. The first three rows on the\n * page are the podium. There are no pins and no hides (#303, #321): the owner\n * has no per-row control, so placement is a function of the order alone.\n */\nexport function placeRows(rows: readonly WorkflowRow[]): PlacedRow[] {\n\tconst ordered = [...rows].sort(\n\t\t(a, b) =>\n\t\t\t(ORDER_INDEX.get(a.rowId) ?? Number.MAX_SAFE_INTEGER) -\n\t\t\t(ORDER_INDEX.get(b.rowId) ?? Number.MAX_SAFE_INTEGER),\n\t);\n\treturn ordered.map((row, index) => ({\n\t\t...row,\n\t\tplacement: index < HIGHLIGHT_SLOTS ? \"highlight\" : \"normal\",\n\t}));\n}\n","// The rolling window and the scan-health shape every adapter reports.\n// Shared so the payload builder and each harness scanner agree by import\n// rather than by convention (#67).\n\n/** Rolling window locked by the owner in the #32 prototype resolution. */\nexport const DEFAULT_WINDOW_DAYS = 30;\n\n/**\n * UTC midnight opening a rolling window of `days` calendar days ending on the\n * day containing `now` (inclusive). `days = 30` therefore spans today plus the\n * 29 preceding days.\n *\n * Defined once and shared by the scan filter and the payload's `window.from`, so\n * the reported window and the records actually counted cannot drift apart.\n */\nexport function windowStartMs(now: number, days: number): number {\n\tconst startOfToday = Date.UTC(\n\t\tnew Date(now).getUTCFullYear(),\n\t\tnew Date(now).getUTCMonth(),\n\t\tnew Date(now).getUTCDate(),\n\t);\n\treturn startOfToday - (days - 1) * 86_400_000;\n}\n\nexport type ScanStats = {\n\t/** Files found on disk before any window filter. */\n\tfilesFound: number;\n\t/** Files actually opened and read. */\n\tfilesRead: number;\n\t/** Files skipped because their mtime predates the window. */\n\tfilesSkippedByMtime: number;\n\t/** Files skipped because a resolved path was already scanned (overlapping roots). */\n\tfilesSkippedAsDuplicate: number;\n\t/**\n\t * Files that could not be read (permissions, or pruned mid-scan). Counted\n\t * rather than thrown: an unhandled read error would surface the absolute path\n\t * AND the munged project directory in the crash output, which is exactly what\n\t * this tool promises never to emit.\n\t */\n\tfilesUnreadable: number;\n\t/**\n\t * Files excluded because they fail the genuine-rollout fingerprint (#73):\n\t * another tool wrote them into the harness's log directory, so their usage\n\t * would distort the numbers. Codex sets this; Claude has no known impostors.\n\t */\n\tfilesForeign: number;\n\t/**\n\t * LOCAL-ONLY detail behind the counts above. `buildPayload` copies coverage\n\t * fields one by one, so nothing below can reach the wire.\n\t *\n\t * `session_meta.originator` values seen on foreign files, value → file count.\n\t */\n\tforeignOriginators: Map<string, number>;\n\t/** LOCAL-ONLY: one `{path relative to the scan root, error class}` per unreadable file. */\n\tunreadableFiles: Array<{ path: string; reason: string }>;\n\t/** Subset of `filesUnreadable`: `.zst` rollouts this Node runtime cannot decompress. */\n\tfilesZstdUnsupported: number;\n};\n\nexport function emptyScanStats(): ScanStats {\n\treturn {\n\t\tfilesFound: 0,\n\t\tfilesRead: 0,\n\t\tfilesSkippedByMtime: 0,\n\t\tfilesSkippedAsDuplicate: 0,\n\t\tfilesUnreadable: 0,\n\t\tfilesForeign: 0,\n\t\tforeignOriginators: new Map(),\n\t\tunreadableFiles: [],\n\t\tfilesZstdUnsupported: 0,\n\t};\n}\n","// The wire payload builder - the only thing in this module that decides what\n// leaves the machine.\n//\n// Wayfinder ticket #37 (map #29). Shape fixed by the wire-format grilling #33;\n// nothing here is open design.\n//\n// Two invariants this file is responsible for:\n// 1. FAIL-CLOSED NAMES. Every freeform name is matched against an allowlist\n// before it can reach the payload; unmatched names publish only as\n// per-category counts (#33 decisions 2-4). Model ids are the sole exempt\n// class (decision 3) and are charset/length sanitized instead.\n// 2. COST IS ABSENT, NOT ZEROED. With `publishCost` off, the cost fields are\n// not in the payload at all (#33 decision 11) - there is nothing to\n// \"reveal\" server-side, because nothing was transmitted.\n\nimport { baseModelId, pricingTableFor } from \"@aistack/pricing\";\nimport type { MeasuredDay, WorkflowDay } from \"@aistack/workflow-rules\";\nimport { MEASURED_DAYS_V1 } from \"@aistack/workflow-rules\";\nimport type { WorkflowExtraction } from \"../../workflow/index.js\";\nimport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\ttype ModelRow,\n} from \"./aggregate.js\";\nimport {\n\ttype Atom,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"./allowlist.js\";\nimport { type ScanStats, windowStartMs } from \"./window.js\";\n\nexport const SCHEMA_VERSION = 2;\n\nexport type PayloadModel = {\n\t/** Vendor-assigned id, sanitized. `catalogSlug` is resolved SERVER-side at read time. */\n\tid: string;\n\ttokenShare: number;\n\ttokens: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheWrite: number;\n\t\tcacheRead: number;\n\t\t/**\n\t\t * The cache-write TTL breakdown (#213). The three sum to `cacheWrite`,\n\t\t * which stays the total.\n\t\t *\n\t\t * The analyzer has held this split since #126 and the wire merged it, so\n\t\t * the backend's read-time repricer had to charge every write at the cheap\n\t\t * 5-minute rate - about 8% low for Claude Code. Absent only when this\n\t\t * harness reports no cache writes at all.\n\t\t */\n\t\tcacheWriteTtl?: {\n\t\t\tfiveMinute: number;\n\t\t\toneHour: number;\n\t\t\t/** Writes the harness reported with no TTL. Priced at the 5-minute rate. */\n\t\t\tunsplit: number;\n\t\t};\n\t};\n\tapiEquivalentUSD?: number;\n\t/**\n\t * The table that produced `apiEquivalentUSD` - present exactly when the\n\t * dollars are (#136). Per model, not per payload: one opencode payload mixes\n\t * vendors, so a single top-level id would cite one table for dollars drawn\n\t * from two.\n\t */\n\tpricingTable?: string;\n};\n\nexport type PayloadAtom = {\n\tname: string;\n\tcallShare: number;\n\t/**\n\t * The absolute invocation count behind the share (#213). Its denominator is\n\t * `inventory.calls` for the same category, NOT the sum of the published\n\t * atoms: shares are computed over every observed call, withheld ones\n\t * included, and the count keeps that property.\n\t */\n\tcalls: number;\n};\n\nexport type PayloadInventory = {\n\tbuiltinTools: PayloadAtom[];\n\tmcpServers: PayloadAtom[];\n\tskills: PayloadAtom[];\n\tsubagents: PayloadAtom[];\n\tslashCommands: PayloadAtom[];\n\t/** DISTINCT names withheld per category, so the gap in the shares is explained. */\n\twithheld: {\n\t\tbuiltinTools: number;\n\t\tmcpServers: number;\n\t\tskills: number;\n\t\tsubagents: number;\n\t\tslashCommands: number;\n\t};\n\t/**\n\t * Every observed call per category, withheld names included (#213) - the\n\t * denominator the shares were computed over. Withheld calls are this total\n\t * minus the published counts, so the absolute figures explain their own gap\n\t * the way `withheld` explains the shares'.\n\t */\n\tcalls: {\n\t\tbuiltinTools: number;\n\t\tmcpServers: number;\n\t\tskills: number;\n\t\tsubagents: number;\n\t\tslashCommands: number;\n\t};\n};\n\nexport type MeasuredPayload = {\n\tschemaVersion: 2;\n\t/** Client clock. The server stamps its own `receivedAt` (#33 decision 6). */\n\tcapturedAt: number;\n\twindow: { days: number; from: string; to: string };\n\tharness: { name: string; version: string | null };\n\t/**\n\t * The one table the models' citations agree on, or `null` - when\n\t * `publishCost` is off, when nothing priced, and when a mixed-vendor payload\n\t * cites several tables (the per-model `pricingTable` fields carry the truth,\n\t * and joining them here would blow the server's 64-character name bound).\n\t */\n\tpricingTable: string | null;\n\tactivity: {\n\t\tsessions: number;\n\t\t/** Sorted UTC dates inside the declared window. */\n\t\tactiveDayDates: string[];\n\t\t/** Sorted project workspace identifiers. Project paths never travel. */\n\t\tprojectKeys: string[];\n\t\ttotalTokens: number;\n\t\tcacheHitShare: number;\n\t\tsubagentShare: number;\n\t};\n\tmodels: PayloadModel[];\n\tinventory: PayloadInventory;\n\tcoverage: {\n\t\tfilesScanned: number;\n\t\tfilesUnreadable: number;\n\t\tlinesParsed: number;\n\t\tlinesFailed: number;\n\t};\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\n// ---------------------------------------------------------------------------\n// Sanitization\n// ---------------------------------------------------------------------------\n\n/**\n * Model ids are exempt from the allowlist (#33 decision 3) precisely because\n * they are vendor-assigned: on the day a new Claude model ships, fail-closing it\n * would make its tokens silently vanish from every sync and understate cost with\n * no visible cause. Exempt is not unchecked, though - the id still becomes a\n * database key and a rendered string, so charset and length are bounded here.\n */\nconst MODEL_ID_UNSAFE_RE = /[^A-Za-z0-9._:-]+/g;\nconst MODEL_ID_MAX = 64;\n\nexport function sanitizeModelId(id: string): string {\n\tconst collapsed = cleanName(id)\n\t\t.replace(MODEL_ID_UNSAFE_RE, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\tif (collapsed.length === 0) return \"unknown\";\n\treturn collapsed.length > MODEL_ID_MAX\n\t\t? collapsed.slice(0, MODEL_ID_MAX)\n\t\t: collapsed;\n}\n\nconst round4 = (n: number): number => Math.round(n * 10_000) / 10_000;\nconst round2 = (n: number): number => Math.round(n * 100) / 100;\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\n// ---------------------------------------------------------------------------\n// Inventory\n// ---------------------------------------------------------------------------\n\nconst toAtoms = (pairs: ReadonlyArray<readonly [string, number]>): Atom[] =>\n\tpairs.map(([name, count]) => ({ name, count }));\n\n/**\n * Shares are computed over ALL observed calls, including withheld ones.\n *\n * Renormalizing over only the allowlisted atoms would make the published shares\n * sum to 1.0 and read as a complete inventory - a withheld MCP server carrying\n * 90% of the calls would leave no trace. Keeping the true denominator means the\n * shares sum to less than 1 exactly when something was withheld, and the\n * `withheld` counts say how many things.\n */\nfunction buildCategory(\n\tobserved: ReadonlyArray<readonly [string, number]>,\n\tcurated: ReadonlySet<string>,\n\toptIns: readonly string[],\n\tdenominator: number,\n): { atoms: PayloadAtom[]; withheld: number; keptPrivate: KeptPrivateAtom[] } {\n\t// The union is where #42 decision 1 lands: a name publishes if it is curated\n\t// OR the owner ticked it. Filtering itself is unchanged - still client-side,\n\t// still fail-closed, still before the send. What moves is who judged the name.\n\tconst publishable = new Set([...curated, ...optIns]);\n\tconst {\n\t\tallowed: kept,\n\t\tkeptPrivate,\n\t\twithheld,\n\t} = filterAtoms(toAtoms(observed), { publishable, curated });\n\treturn {\n\t\tatoms: kept.map((a) => ({\n\t\t\tname: a.name,\n\t\t\tcallShare: denominator ? round4(a.count / denominator) : 0,\n\t\t\tcalls: a.count,\n\t\t})),\n\t\twithheld,\n\t\tkeptPrivate,\n\t};\n}\n\nconst sumCounts = (pairs: ReadonlyArray<readonly [string, number]>): number => {\n\tlet n = 0;\n\tfor (const [, c] of pairs) n += c;\n\treturn n;\n};\n\n// ---------------------------------------------------------------------------\n// Models\n// ---------------------------------------------------------------------------\n\ntype ModelGroup = {\n\tid: string;\n\ttotalTokens: number;\n\tinput: number;\n\toutput: number;\n\tcacheWrite: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\tcacheWriteUnsplit: number;\n\tcacheRead: number;\n\tcostUSD: number;\n\tunpricedTokens: number;\n\tanyUnpriceable: boolean;\n\t/** The table citing this group's rates; every row shares it (one base model). */\n\ttable: string | null;\n};\n\n/**\n * Collapse the analyzer's pricing keys into vendor-assigned ids.\n *\n * The analyzer prices fast mode under a synthetic `claude-opus-5#fast` key\n * because it bills at a different rate ($10/$50 vs $5/$25). That suffix is OURS,\n * not the vendor's, so publishing it would hand the server an id that cannot\n * resolve against the models catalog - the exact silent-disappearance failure\n * decision 3 exists to prevent. The rows are therefore merged back onto the base\n * id here. Cost stays exact because it was already accumulated per response at\n * the fast rate; what is lost is the fast-mode share itself, which the payload\n * has no field for and which is a candidate for a later schema bump.\n */\nfunction groupModels(rows: readonly ModelRow[]): ModelGroup[] {\n\tconst groups = new Map<string, ModelGroup>();\n\tfor (const r of rows) {\n\t\tconst id = sanitizeModelId(baseModelId(r.modelKey));\n\t\tlet g = groups.get(id);\n\t\tif (!g) {\n\t\t\tg = {\n\t\t\t\tid,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\tcacheWrite5m: 0,\n\t\t\t\tcacheWrite1h: 0,\n\t\t\t\tcacheWriteUnsplit: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcostUSD: 0,\n\t\t\t\tunpricedTokens: 0,\n\t\t\t\tanyUnpriceable: false,\n\t\t\t\ttable: null,\n\t\t\t};\n\t\t\tgroups.set(id, g);\n\t\t}\n\t\tg.table ??= pricingTableFor(r.modelKey);\n\t\tg.totalTokens += r.totalTokens;\n\t\tg.input += r.tokens.input;\n\t\tg.output += r.tokens.output;\n\t\tg.cacheWrite5m += r.tokens.cacheWrite5m;\n\t\tg.cacheWrite1h += r.tokens.cacheWrite1h;\n\t\tg.cacheWriteUnsplit += r.tokens.cacheWriteUnsplit;\n\t\tg.cacheWrite +=\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t\tg.cacheRead += r.tokens.cacheRead;\n\t\tg.costUSD += r.costUSD ?? 0;\n\t\tg.unpricedTokens += r.unpricedTokens;\n\t\tif (r.costUSD === null) g.anyUnpriceable = true;\n\t}\n\treturn [...groups.values()].sort(\n\t\t(a, b) => b.totalTokens - a.totalTokens || a.id.localeCompare(b.id),\n\t);\n}\n\nfunction buildModels(\n\trows: readonly ModelRow[],\n\ttotalTokens: number,\n\tpublishCost: boolean,\n): PayloadModel[] {\n\treturn groupModels(rows).map((g) => {\n\t\tconst model: PayloadModel = {\n\t\t\tid: g.id,\n\t\t\ttokenShare: totalTokens ? round4(g.totalTokens / totalTokens) : 0,\n\t\t\ttokens: {\n\t\t\t\tinput: g.input,\n\t\t\t\toutput: g.output,\n\t\t\t\tcacheWrite: g.cacheWrite,\n\t\t\t\tcacheRead: g.cacheRead,\n\t\t\t\t// All three or none (#213), so a reader never sees half a\n\t\t\t\t// breakdown. Omitted when there were no cache writes at all -\n\t\t\t\t// three zeros state nothing the total does not already state.\n\t\t\t\t...(g.cacheWrite > 0\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tcacheWriteTtl: {\n\t\t\t\t\t\t\t\tfiveMinute: g.cacheWrite5m,\n\t\t\t\t\t\t\t\toneHour: g.cacheWrite1h,\n\t\t\t\t\t\t\t\tunsplit: g.cacheWriteUnsplit,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t};\n\t\t// Absent, not zero: a partially-priced model reporting a dollar figure\n\t\t// would understate without saying so. `excludedTokens.unpriced` carries\n\t\t// the tokens that were left out. Dollars and their citation travel\n\t\t// together (#136) - a figure without its table may not render anywhere.\n\t\tif (\n\t\t\tpublishCost &&\n\t\t\t!g.anyUnpriceable &&\n\t\t\tg.unpricedTokens === 0 &&\n\t\t\tg.table !== null\n\t\t) {\n\t\t\tmodel.apiEquivalentUSD = round2(g.costUSD);\n\t\t\tmodel.pricingTable = g.table;\n\t\t}\n\t\treturn model;\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Build\n// ---------------------------------------------------------------------------\n\nexport type BuildPayloadInput = {\n\taggregate: Aggregate;\n\tstats: ScanStats;\n\tsyncConfig: SyncConfig;\n\t/** Client clock, epoch ms. The same value used to derive the scan window. */\n\tnow: number;\n\twindowDays: number;\n\t/** The adapter's payload discriminator, e.g. `\"claude-code\"` (#66). */\n\tharnessName: string;\n\t/** The adapter's fail-closed vendor tool set (#66 decision 3). */\n\tbuiltinTools: ReadonlySet<string>;\n\t/** Resolve one local project directory to its persistent opaque id. */\n\tprojectWorkspaceId: (directory: string) => string;\n};\n\nexport type BuiltPayload = {\n\tpayload: MeasuredPayload;\n\t/** The same numbers unfiltered, for the local report and the approve gate. */\n\tfinalized: Finalized;\n\t/**\n\t * Every observed name that will NOT publish, by category - the gate's review\n\t * list (#42 decision 1, wired in #44).\n\t *\n\t * This is the one thing here that is deliberately NOT in the payload. It is\n\t * the list of names the user has not agreed to publish, so it stays on the\n\t * machine; the payload carries only the per-category COUNT.\n\t */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n};\n\nexport function buildPayload(input: BuildPayloadInput): BuiltPayload {\n\tconst {\n\t\taggregate: agg,\n\t\tstats,\n\t\tsyncConfig,\n\t\tnow,\n\t\twindowDays,\n\t\tharnessName,\n\t\tbuiltinTools,\n\t\tprojectWorkspaceId,\n\t} = input;\n\tconst finalized = finalize(agg);\n\tconst { publishCost, allowlist, optIns } = syncConfig;\n\n\tconst fromMs = windowStartMs(now, windowDays);\n\tconst from = utcDate(fromMs);\n\tconst to = utcDate(now);\n\n\t// Limited to the reported window rather than copied from the aggregate: a\n\t// clock-skewed, imported, or restored transcript dated in the future would\n\t// otherwise put an impossible date in a deterministic payload.\n\tconst activeDayDates = [...agg.activeDays]\n\t\t.filter((d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d) && d >= from && d <= to)\n\t\t.sort();\n\tconst projectKeys = [\n\t\t...new Set(\n\t\t\t[...agg.projectDirs].map((directory) => projectWorkspaceId(directory)),\n\t\t),\n\t].sort();\n\tif (\n\t\tprojectKeys.length > 1_000 ||\n\t\tprojectKeys.some((key) => !/^[A-Za-z0-9_-]{22}$/.test(key))\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Project workspace identifiers must be 22-character base64url strings\",\n\t\t);\n\t}\n\n\t// One denominator per category, held rather than inlined: each is both the\n\t// divisor for its shares and the total the payload publishes (#213), and the\n\t// two must be the same number or the absolute counts would not add up.\n\tconst observedCalls = {\n\t\tbuiltinTools: finalized.totalToolCalls,\n\t\tmcpServers: sumCounts(finalized.mcpServers),\n\t\tskills: sumCounts(finalized.skills),\n\t\tsubagents: sumCounts(finalized.subagents),\n\t\tslashCommands: sumCounts(finalized.slashCommands),\n\t};\n\tconst builtins = buildCategory(\n\t\tfinalized.tools,\n\t\tbuiltinTools,\n\t\toptIns.builtinTools,\n\t\tobservedCalls.builtinTools,\n\t);\n\tconst mcp = buildCategory(\n\t\tfinalized.mcpServers,\n\t\tnew Set(allowlist.mcpServers),\n\t\toptIns.mcpServers,\n\t\tobservedCalls.mcpServers,\n\t);\n\tconst skills = buildCategory(\n\t\tfinalized.skills,\n\t\tnew Set(allowlist.skills),\n\t\toptIns.skills,\n\t\tobservedCalls.skills,\n\t);\n\tconst subagents = buildCategory(\n\t\tfinalized.subagents,\n\t\tnew Set(allowlist.subagents),\n\t\toptIns.subagents,\n\t\tobservedCalls.subagents,\n\t);\n\tconst slash = buildCategory(\n\t\tfinalized.slashCommands,\n\t\tnew Set(allowlist.slashCommands),\n\t\toptIns.slashCommands,\n\t\tobservedCalls.slashCommands,\n\t);\n\n\tconst models = buildModels(\n\t\tfinalized.models,\n\t\tfinalized.totalTokens,\n\t\tpublishCost,\n\t);\n\t// The citation lives on each model (#136). The top-level field survives for\n\t// readers of the old shape and states the one table everything agrees on -\n\t// never a false single citation over a mixed payload.\n\tconst citedTables = [\n\t\t...new Set(models.flatMap((m) => (m.pricingTable ? [m.pricingTable] : []))),\n\t];\n\n\tconst payload: MeasuredPayload = {\n\t\tschemaVersion: SCHEMA_VERSION,\n\t\tcapturedAt: now,\n\t\twindow: { days: windowDays, from, to },\n\t\tharness: {\n\t\t\tname: harnessName,\n\t\t\tversion:\n\t\t\t\tfinalized.harnessVersion === null\n\t\t\t\t\t? null\n\t\t\t\t\t: sanitizeModelId(finalized.harnessVersion),\n\t\t},\n\t\tpricingTable: citedTables.length === 1 ? citedTables[0] : null,\n\t\tactivity: {\n\t\t\tsessions: finalized.sessions,\n\t\t\tactiveDayDates,\n\t\t\tprojectKeys,\n\t\t\ttotalTokens: finalized.totalTokens,\n\t\t\tcacheHitShare: round4(finalized.cacheHitShare),\n\t\t\tsubagentShare: round4(finalized.sidechainShare),\n\t\t},\n\t\tmodels,\n\t\tinventory: {\n\t\t\tbuiltinTools: builtins.atoms,\n\t\t\tmcpServers: mcp.atoms,\n\t\t\tskills: skills.atoms,\n\t\t\tsubagents: subagents.atoms,\n\t\t\tslashCommands: slash.atoms,\n\t\t\twithheld: {\n\t\t\t\tbuiltinTools: builtins.withheld,\n\t\t\t\tmcpServers: mcp.withheld,\n\t\t\t\tskills: skills.withheld,\n\t\t\t\tsubagents: subagents.withheld,\n\t\t\t\tslashCommands: slash.withheld,\n\t\t\t},\n\t\t\tcalls: observedCalls,\n\t\t},\n\t\tcoverage: {\n\t\t\tfilesScanned: stats.filesRead,\n\t\t\tfilesUnreadable: stats.filesUnreadable,\n\t\t\tlinesParsed: agg.lines - agg.parseErrors,\n\t\t\tlinesFailed: agg.parseErrors,\n\t\t},\n\t\texcludedTokens: {\n\t\t\tunpriced: finalized.unpricedTokens,\n\t\t\tsynthetic: agg.syntheticTokens,\n\t\t},\n\t};\n\n\treturn {\n\t\tpayload,\n\t\tfinalized,\n\t\tkeptPrivate: {\n\t\t\tbuiltinTools: builtins.keptPrivate,\n\t\t\tmcpServers: mcp.keptPrivate,\n\t\t\tskills: skills.keptPrivate,\n\t\t\tsubagents: subagents.keptPrivate,\n\t\t\tslashCommands: slash.keptPrivate,\n\t\t},\n\t};\n}\n\n/**\n * What `POST /api/cli/sync` takes: one sealed payload PER DETECTED HARNESS,\n * one unsealed half shared across them (#66 decision 5). The batch is atomic\n * server-side, so two harnesses cannot wipe each other's staged names - which\n * is what two sequential per-harness publishes would have done, because the\n * staged list is a whole-list replace per stack.\n */\nexport type SyncBody = {\n\tpayloads: MeasuredPayload[];\n\tkeptPrivate?: Record<NameCategory, KeptPrivateAtom[]>;\n\t/**\n\t * The machine's standing auto-sync opt-in (#78). Not measurement and not a\n\t * name - it is the one bit of local state the backend cannot otherwise see,\n\t * and `auto_sync_enabled` has nothing to fire on without it.\n\t *\n\t * It rides BESIDE the payloads, never inside one: the payload validator is\n\t * closed, and that closedness is the privacy claim.\n\t */\n\tautoSync?: { enabled: boolean; frequencyHours: number };\n\t/**\n\t * How this sync fired (#102, sent by #103). `auto` means a SessionStart hook\n\t * ran it with nobody watching; `manual` means a human typed the command.\n\t *\n\t * The server stamps `lastAutoSyncAt` from it, so the web switch can tell\n\t * on-and-working from on-but-never-fired. It rides beside the payloads for\n\t * the same reason `autoSync` does: the payload validator is closed.\n\t */\n\ttrigger?: SyncTrigger;\n\t/**\n\t * The measured days (#307, ADR-0010): one row per UTC date holding the\n\t * usage half and the workflow half under ONE version, `measured-days/v1`.\n\t * Only the dates the server lacks or holds differently ride here, plus\n\t * today; the manifest decides.\n\t *\n\t * It rides BESIDE the payloads for the reasons the workflow section did: the\n\t * Git half is per machine, not per harness, and the closed payload\n\t * validator is the privacy claim (#33). Each consent bit strips its own\n\t * half on the machine: `publishWorkflow` off means no day carries a\n\t * `workflow` block, `publishCost` off means no model carries `usd`.\n\t */\n\tmeasuredDays?: PayloadMeasuredDays;\n\t/**\n\t * The workflow section of the wire before #307. This CLI no longer sets it;\n\t * the workflow blocks ride inside `measuredDays`. The field stays so an old\n\t * body still type-checks.\n\t */\n\tworkflow?: PayloadWorkflow;\n\t/**\n\t * The version of this CLI (#213).\n\t *\n\t * It answers one operational question that had no answer: how many machines\n\t * are still on an old wire. `cliVersion` reached PostHog and nothing else,\n\t * so no query over published rows could count them - which is the exact\n\t * question a wire bump asks.\n\t */\n\tcliVersion?: string;\n};\n\n/**\n * The workflow section as it goes on the wire (#285): per-day rows of\n * combinable atoms, plus the machine's clock. `WorkflowExtraction` is already\n * that shape, and nothing local survives it, so the wire type restates it\n * rather than deriving it: the two ends must describe the same bytes.\n */\nexport type PayloadWorkflow = {\n\taggregateVersion: string;\n\tutcOffsetMinutes: number;\n\tdays: WorkflowDay[];\n};\n\n/**\n * Put an extraction on the legacy `workflow` body field. The CLI no longer\n * sends that field (#307); this stays for the server-side contract test that\n * checks the day shape against the validator.\n */\nexport function toPayloadWorkflow(\n\textraction: WorkflowExtraction,\n): PayloadWorkflow {\n\treturn {\n\t\taggregateVersion: extraction.aggregateVersion,\n\t\tutcOffsetMinutes: extraction.utcOffsetMinutes,\n\t\tdays: extraction.days,\n\t};\n}\n\n/** The day rows on the wire (#307), plus the machine's clock. */\nexport type PayloadMeasuredDays = {\n\taggregateVersion: typeof MEASURED_DAYS_V1;\n\t/** Minutes EAST of UTC, as `PayloadWorkflow` carried it (#218). */\n\tutcOffsetMinutes: number;\n\tdays: MeasuredDay[];\n};\n\n/**\n * Apply both consent bits to the day rows. Idempotent and pure, so the stage\n * runs it BEFORE fingerprinting (the fingerprint must hash the bytes that go)\n * and `buildSyncBody` runs it again as the last line of defense.\n *\n * `publishWorkflow` off drops every `workflow` block. `publishCost` off drops\n * `usd` and `pricingTable` from every model. A config the machine could not\n * fetch reads as both off.\n */\nexport function applyDayConsent(\n\tdays: readonly MeasuredDay[],\n\tsyncConfig: Pick<SyncConfig, \"publishCost\" | \"publishWorkflow\">,\n): MeasuredDay[] {\n\treturn days.map((day) => {\n\t\tconst { workflow, usage, ...rest } = day;\n\t\tconst out: MeasuredDay = { ...rest };\n\t\tif (usage) {\n\t\t\tout.usage = syncConfig.publishCost\n\t\t\t\t? usage\n\t\t\t\t: {\n\t\t\t\t\t\tharnesses: usage.harnesses.map((h) => ({\n\t\t\t\t\t\t\t...h,\n\t\t\t\t\t\t\tmodels: h.models.map(\n\t\t\t\t\t\t\t\t({ usd: _usd, pricingTable: _table, ...model }) => model,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t}\n\t\tif (workflow && syncConfig.publishWorkflow) out.workflow = workflow;\n\t\treturn out;\n\t});\n}\n\n/** The two ways a sync can fire. Absent on an old CLI, and that reads as manual. */\nexport type SyncTrigger = \"manual\" | \"auto\";\n\n/**\n * Union the per-harness kept-private lists into the one list the wire carries.\n *\n * One list, not one per harness, because consent is per NAME (#66 decision 5):\n * the owner ticks \"alp-river\", not \"alp-river as seen by Codex\". Counts merge\n * by (category, name); the group survives from whichever harness saw it first.\n */\nexport function mergeKeptPrivate(\n\thalves: ReadonlyArray<Record<NameCategory, KeptPrivateAtom[]>>,\n): Record<NameCategory, KeptPrivateAtom[]> {\n\tconst out = {} as Record<NameCategory, KeptPrivateAtom[]>;\n\tfor (const category of NAME_CATEGORIES) {\n\t\tconst merged = new Map<string, KeptPrivateAtom>();\n\t\tfor (const half of halves) {\n\t\t\tfor (const atom of half[category]) {\n\t\t\t\tconst held = merged.get(atom.name);\n\t\t\t\tif (held) held.count += atom.count;\n\t\t\t\telse merged.set(atom.name, { ...atom });\n\t\t\t}\n\t\t}\n\t\tout[category] = [...merged.values()].sort(\n\t\t\t(a, b) => b.count - a.count || a.name.localeCompare(b.name),\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * Assemble the request body from the built payloads, one per detected harness.\n *\n * The two halves ride in ONE request (#48): a second call would let them drift\n * against a newer snapshot. They stay SEPARATE objects because the payload's\n * validator is closed and rejects any extra key - that closedness is the privacy\n * claim, so a kept-private name may sit beside the payloads and never inside one.\n *\n * The switch is read from the sync config the server just served. Off - or a\n * config the machine could not fetch, which reads as off - sends the payloads\n * alone and the names stay on the machine.\n */\nexport function buildSyncBody(\n\tbuilt: readonly BuiltPayload[],\n\tsyncConfig: SyncConfig,\n\tautoSync?: { enabled: boolean; frequencyHours: number },\n\ttrigger: SyncTrigger = \"manual\",\n\tmeasuredDays?: PayloadMeasuredDays,\n\tcliVersion?: string,\n): SyncBody {\n\tconst payloads = built.map((b) => b.payload);\n\tconst base: SyncBody = autoSync\n\t\t? { payloads, autoSync, trigger }\n\t\t: { payloads, trigger };\n\t// THE CONSENT GATES, APPLIED HERE TOO (#213, #307). The stage already\n\t// stripped what the owner declined before it fingerprinted the days; this\n\t// pass is idempotent and keeps the promise even for a caller that did not.\n\tconst withDays: SyncBody = measuredDays\n\t\t? {\n\t\t\t\t...base,\n\t\t\t\tmeasuredDays: {\n\t\t\t\t\taggregateVersion: MEASURED_DAYS_V1,\n\t\t\t\t\tutcOffsetMinutes: measuredDays.utcOffsetMinutes,\n\t\t\t\t\tdays: applyDayConsent(measuredDays.days, syncConfig),\n\t\t\t\t},\n\t\t\t}\n\t\t: base;\n\tconst withVersion: SyncBody = cliVersion\n\t\t? { ...withDays, cliVersion }\n\t\t: withDays;\n\tif (!syncConfig.reviewKeptPrivate) return withVersion;\n\treturn {\n\t\t...withVersion,\n\t\tkeptPrivate: mergeKeptPrivate(built.map((b) => b.keptPrivate)),\n\t};\n}\n","import {\n\tclassifyEvent,\n\tderiveSessionPhases,\n\tEFFORT_LEVELS,\n\ttype EffortLevel,\n\teffortLevelOf,\n\ttype HarnessDay,\n\ttype HarnessEvent,\n\ttype HarnessName,\n\tLOG_BUCKETS_V1,\n\tLOG_BUCKETS_V2,\n\tlogBucket,\n\tlogBucketV2,\n\tPHASE_RULES_V1,\n\tPHASES,\n\ttype PhaseId,\n\ttype SessionLengthBucket,\n\tUNKNOWN_GATE,\n\tWORKFLOW_AGGREGATES_V3,\n} from \"@aistack/workflow-rules\";\nimport { sanitizeModelId } from \"../harness/shared/payload.js\";\n\nexport const WORKFLOW_AGGREGATE_VERSION = WORKFLOW_AGGREGATES_V3;\n\n/**\n * The first call of a session, split (#358). `harnessTokens` is what the\n * call read from a cache another session had already filled: the system\n * prompt and the tool definitions. `instructionsTokens` is what it wrote or\n * sent fresh: project instructions, memory, skills, agents, the first prompt.\n */\nexport type FirstCallSplit = {\n\tharnessTokens: number;\n\tinstructionsTokens: number;\n};\n\nexport type WorkflowObservation = {\n\tsession: string;\n\tprojectWorkspace?: string;\n\ttsMs: number;\n\tparentSession?: string;\n\tsidechain?: boolean;\n} & (\n\t| { type: \"event\"; tool: string; arg?: string; batchId?: string }\n\t| {\n\t\t\ttype: \"response\";\n\t\t\tresponseId?: string;\n\t\t\tmodel?: string;\n\t\t\tthinkingTokens?: number;\n\t\t\tresponseTokens?: number;\n\t\t\troutingTokens?: number;\n\t\t\teffort?: string;\n\t\t\tdurationSec?: number;\n\t\t\t/** What the request carried in: fresh input plus cache reads and writes. */\n\t\t\tcontextTokens?: number;\n\t\t\t/** The window the harness logged for this call, when it logs one. */\n\t\t\tcontextWindow?: number;\n\t\t\t/** Present on the first call of the session only. */\n\t\t\tfirstCall?: FirstCallSplit;\n\t }\n\t| { type: \"turn\"; turnId?: string; questionBack: boolean }\n\t/** A compaction boundary the harness logged. Lands on the day of the event. */\n\t| { type: \"compaction\" }\n);\n\n/** One harness's reading for one UTC day, with the day it belongs to. */\nexport type HarnessDayRow = HarnessDay & { date: string };\n\n/**\n * One harness's workflow reading over the sync window: one row per UTC day\n * that saw a session start, an event, or a response (#285).\n *\n * THE GATE IS OVER THE WHOLE WINDOW. \"`phase-rules/v1` ships only when a\n * harness has 20 percent unknown time or less\" (map notes), and a day is too\n * small a sample to judge that on: a quiet day with one unclassified command\n * would fail alone and pass inside its month. The extraction strips `phase`\n * from every day of a harness that fails, so the wire carries no phase atoms\n * a window could fold into a playbook the gate refused.\n */\nexport type HarnessWorkflowAggregate = {\n\taggregateVersion: typeof WORKFLOW_AGGREGATE_VERSION;\n\tharness: HarnessName;\n\tgate: {\n\t\truleVersion: typeof PHASE_RULES_V1;\n\t\tpublishable: boolean;\n\t\tsessions: number;\n\t\tunknownShare: number;\n\t};\n\tdays: HarnessDayRow[];\n};\n\n/** Raw local keys used to join harness activity to Git. Never serialize this value. */\nexport type WorkflowLocalSources = {\n\tprojectWorkspaces: Set<string>;\n\tactiveProjectDays: Map<string, Set<string>>;\n};\n\nexport function createWorkflowLocalSources(): WorkflowLocalSources {\n\treturn { projectWorkspaces: new Set(), activeProjectDays: new Map() };\n}\n\ntype SessionState = {\n\tevents: Array<{ event: HarnessEvent; batchId?: string }>;\n\tresponses: Map<\n\t\tstring,\n\t\t{\n\t\t\tmodel?: string;\n\t\t\tthinkingTokens?: number;\n\t\t\tresponseTokens?: number;\n\t\t\troutingTokens?: number;\n\t\t\teffort?: string;\n\t\t\tdurationSec?: number;\n\t\t\tcontextTokens?: number;\n\t\t\tcontextWindow?: number;\n\t\t\tfirstCall?: FirstCallSplit;\n\t\t\ttsMs: number;\n\t\t}\n\t>;\n\tnextAnonymousResponse: number;\n\tturns: Map<string, boolean>;\n\tnextAnonymousTurn: number;\n\tprojectWorkspaces: Set<string>;\n\tparentSession: string | undefined;\n\tsidechain: boolean;\n\tfirstTs: number | undefined;\n\tlastTs: number | undefined;\n};\n\nconst emptyPhase = (): Record<PhaseId, number> => ({\n\tscout: 0,\n\tbuild: 0,\n\tverify: 0,\n\thandoff: 0,\n\tunknown: 0,\n});\n\nconst finiteNonnegative = (value: number | undefined): number =>\n\tvalue !== undefined && Number.isFinite(value) && value > 0 ? value : 0;\n\nconst bump = <K>(map: Map<K, number>, key: K, amount = 1): void => {\n\tmap.set(key, (map.get(key) ?? 0) + amount);\n};\n\nexport const utcDateOf = (ms: number): string =>\n\tnew Date(ms).toISOString().slice(0, 10);\n\n/** A bucket histogram as sorted rows, the count under the caller's field name. */\nfunction asBuckets<K extends string>(\n\tmap: Map<number, number>,\n\tfield: K,\n): ({ bucket: number } & Record<K, number>)[] {\n\treturn [...map]\n\t\t.map(\n\t\t\t([bucket, count]) =>\n\t\t\t\t({ bucket, [field]: count }) as { bucket: number } & Record<K, number>,\n\t\t)\n\t\t.sort((a, b) => a.bucket - b.bucket);\n}\n\nconst PHASE_RANK: Record<PhaseId, number> = {\n\tverify: 4,\n\thandoff: 3,\n\tbuild: 2,\n\tscout: 1,\n\tunknown: 0,\n};\n\nfunction reduceEventBatches(\n\trecorded: SessionState[\"events\"],\n\tharness: HarnessName,\n): HarnessEvent[] {\n\tconst sorted = [...recorded].sort((a, b) => a.event[0] - b.event[0]);\n\tconst output: HarnessEvent[] = [];\n\tconst batchIndexes = new Map<string, number>();\n\tfor (const row of sorted) {\n\t\tif (!row.batchId) {\n\t\t\toutput.push(row.event);\n\t\t\tcontinue;\n\t\t}\n\t\tconst existingIndex = batchIndexes.get(row.batchId);\n\t\tif (existingIndex === undefined) {\n\t\t\tbatchIndexes.set(row.batchId, output.length);\n\t\t\toutput.push(row.event);\n\t\t\tcontinue;\n\t\t}\n\t\tconst existing = output[existingIndex];\n\t\tif (!existing) continue;\n\t\tconst existingPhase = classifyEvent(\n\t\t\texisting[1],\n\t\t\texisting[2],\n\t\t\tnull,\n\t\t\tPHASE_RULES_V1,\n\t\t\tharness,\n\t\t).phase;\n\t\tconst candidatePhase = classifyEvent(\n\t\t\trow.event[1],\n\t\t\trow.event[2],\n\t\t\tnull,\n\t\t\tPHASE_RULES_V1,\n\t\t\tharness,\n\t\t).phase;\n\t\tif (PHASE_RANK[candidatePhase] > PHASE_RANK[existingPhase]) {\n\t\t\toutput[existingIndex] = [existing[0], row.event[1], row.event[2]];\n\t\t}\n\t}\n\treturn output;\n}\n\nconst hasVerifyRun = (\n\tevents: readonly HarnessEvent[],\n\tharness: HarnessName,\n): boolean =>\n\tevents.some(\n\t\t(event) =>\n\t\t\tderiveSessionPhases([event], PHASE_RULES_V1, harness).phaseEvents.verify >\n\t\t\t0,\n\t);\n\nfunction shellIncludes(arg: string, head: string): boolean {\n\treturn arg\n\t\t.split(/(?:&&|\\|\\||;|\\|)/)\n\t\t.some((part) => part.trim() === head || part.trim().startsWith(`${head} `));\n}\n\nfunction sessionState(): SessionState {\n\treturn {\n\t\tevents: [],\n\t\tresponses: new Map(),\n\t\tnextAnonymousResponse: 0,\n\t\tturns: new Map(),\n\t\tnextAnonymousTurn: 0,\n\t\tprojectWorkspaces: new Set(),\n\t\tparentSession: undefined,\n\t\tsidechain: false,\n\t\tfirstTs: undefined,\n\t\tlastTs: undefined,\n\t};\n}\n\n/** The accumulators behind one day's row, before they become plain arrays. */\ntype DayState = {\n\tsessions: number;\n\tstartHours: Map<number, number>;\n\tphase: {\n\t\tsessions: number;\n\t\tphaseSec: Record<PhaseId, number>;\n\t\tphaseEvents: Record<PhaseId, number>;\n\t\twaitingSec: number;\n\t\tidleSec: number;\n\t\tsessionsWithVerify: number;\n\t\tsessionsWithHandoff: number;\n\t\tlengths: Map<number, SessionLengthBucket>;\n\t};\n\trouting: { main: Map<string, number>; subagents: Map<string, number> };\n\thasRouting: boolean;\n\tdelegation: {\n\t\tmainToolCalls: number;\n\t\tsubagentToolCalls: number;\n\t\twidestFanOut: number;\n\t\tmostSubagents: number;\n\t};\n\thasDelegation: boolean;\n\tactivity: Map<string, number>;\n\teffort: Map<EffortLevel, number>;\n\thasEffort: boolean;\n\tthinking: { thinkingTokens: number; responseTokens: number };\n\thasThinking: boolean;\n\tturnDurations: Map<number, number>;\n\thasDurations: boolean;\n\tquestions: { asked: number; turns: number };\n\thasQuestions: boolean;\n\twebSearches: number;\n\thasWebSearches: boolean;\n\tcontext: {\n\t\tcalls: { main: Map<number, number>; subagents: Map<number, number> };\n\t\tfirstCalls: Map<number, number>;\n\t\tfirstCallHarnessTokens: number;\n\t\tfirstCallInstructionsTokens: number;\n\t\tfirstCallCount: number;\n\t\tmaxContext: number;\n\t\tcompactions: number;\n\t\twindow: { tsMs: number; window: number } | undefined;\n\t};\n\thasContext: boolean;\n};\n\nfunction dayState(): DayState {\n\treturn {\n\t\tsessions: 0,\n\t\tstartHours: new Map(),\n\t\tphase: {\n\t\t\tsessions: 0,\n\t\t\tphaseSec: emptyPhase(),\n\t\t\tphaseEvents: emptyPhase(),\n\t\t\twaitingSec: 0,\n\t\t\tidleSec: 0,\n\t\t\tsessionsWithVerify: 0,\n\t\t\tsessionsWithHandoff: 0,\n\t\t\tlengths: new Map(),\n\t\t},\n\t\trouting: { main: new Map(), subagents: new Map() },\n\t\thasRouting: false,\n\t\tdelegation: {\n\t\t\tmainToolCalls: 0,\n\t\t\tsubagentToolCalls: 0,\n\t\t\twidestFanOut: 0,\n\t\t\tmostSubagents: 0,\n\t\t},\n\t\thasDelegation: false,\n\t\tactivity: new Map(),\n\t\teffort: new Map(),\n\t\thasEffort: false,\n\t\tthinking: { thinkingTokens: 0, responseTokens: 0 },\n\t\thasThinking: false,\n\t\tturnDurations: new Map(),\n\t\thasDurations: false,\n\t\tquestions: { asked: 0, turns: 0 },\n\t\thasQuestions: false,\n\t\twebSearches: 0,\n\t\thasWebSearches: false,\n\t\tcontext: {\n\t\t\tcalls: { main: new Map(), subagents: new Map() },\n\t\t\tfirstCalls: new Map(),\n\t\t\tfirstCallHarnessTokens: 0,\n\t\t\tfirstCallInstructionsTokens: 0,\n\t\t\tfirstCallCount: 0,\n\t\t\tmaxContext: 0,\n\t\t\tcompactions: 0,\n\t\t\twindow: undefined,\n\t\t},\n\t\thasContext: false,\n\t};\n}\n\nexport type HarnessWorkflowReducer = {\n\tingest(observation: WorkflowObservation): void;\n\tfinish(): HarnessWorkflowAggregate;\n};\n\n/**\n * Reduce one harness's observations into per-day rows of combinable atoms.\n *\n * A SESSION BELONGS TO THE UTC DAY IT STARTED. Its phase seconds, its length\n * bucket, its model tokens, its effort and thinking and turn figures all land\n * on that day, so a session spanning midnight counts once. Event cells and web\n * searches land on the day of the event, so the heatmap stays exact.\n *\n * Nothing that names a path, a session, a command or a timestamp survives\n * `finish()`: the wire carries counts, sums, maxes and bucket indexes.\n */\nexport function createHarnessWorkflowReducer(\n\tharness: HarnessName,\n\tlocalSources: WorkflowLocalSources = createWorkflowLocalSources(),\n): HarnessWorkflowReducer {\n\tconst sessions = new Map<string, SessionState>();\n\tconst eventCells = new Map<string, Map<string, number>>();\n\tconst webSearchesByDate = new Map<string, number>();\n\tconst compactionsByDate = new Map<string, number>();\n\tconst eventDates = new Set<string>();\n\tlet finished: HarnessWorkflowAggregate | undefined;\n\n\tconst getSession = (key: string): SessionState => {\n\t\tlet state = sessions.get(key);\n\t\tif (!state) {\n\t\t\tstate = sessionState();\n\t\t\tsessions.set(key, state);\n\t\t}\n\t\treturn state;\n\t};\n\n\treturn {\n\t\tingest(observation): void {\n\t\t\tif (finished) return;\n\t\t\tif (!Number.isFinite(observation.tsMs)) return;\n\t\t\tconst state = getSession(observation.session);\n\t\t\tstate.firstTs =\n\t\t\t\tstate.firstTs === undefined\n\t\t\t\t\t? observation.tsMs\n\t\t\t\t\t: Math.min(state.firstTs, observation.tsMs);\n\t\t\tstate.lastTs =\n\t\t\t\tstate.lastTs === undefined\n\t\t\t\t\t? observation.tsMs\n\t\t\t\t\t: Math.max(state.lastTs, observation.tsMs);\n\t\t\tstate.parentSession ??= observation.parentSession;\n\t\t\tstate.sidechain ||= observation.sidechain === true;\n\t\t\tconst at = new Date(observation.tsMs);\n\t\t\tconst date = utcDateOf(observation.tsMs);\n\t\t\tif (observation.projectWorkspace) {\n\t\t\t\tstate.projectWorkspaces.add(observation.projectWorkspace);\n\t\t\t\tlocalSources.projectWorkspaces.add(observation.projectWorkspace);\n\t\t\t}\n\n\t\t\tif (observation.type === \"event\") {\n\t\t\t\tconst arg = observation.arg ?? \"\";\n\t\t\t\tstate.events.push({\n\t\t\t\t\tevent: [observation.tsMs, observation.tool, arg],\n\t\t\t\t\t...(observation.batchId ? { batchId: observation.batchId } : {}),\n\t\t\t\t});\n\t\t\t\teventDates.add(date);\n\t\t\t\tconst cells = eventCells.get(date) ?? new Map<string, number>();\n\t\t\t\tbump(cells, `${at.getUTCDay()}:${at.getUTCHours()}`);\n\t\t\t\teventCells.set(date, cells);\n\t\t\t\tif ([\"WebSearch\", \"web_search\", \"websearch\"].includes(observation.tool))\n\t\t\t\t\tbump(webSearchesByDate, date);\n\t\t\t} else if (observation.type === \"response\") {\n\t\t\t\tconst responseId =\n\t\t\t\t\tobservation.responseId ??\n\t\t\t\t\t`anonymous:${state.nextAnonymousResponse++}`;\n\t\t\t\tconst duration = finiteNonnegative(observation.durationSec);\n\t\t\t\tconst contextTokens = finiteNonnegative(observation.contextTokens);\n\t\t\t\tconst contextWindow = finiteNonnegative(observation.contextWindow);\n\t\t\t\tconst response = {\n\t\t\t\t\ttsMs: observation.tsMs,\n\t\t\t\t\t...(observation.model ? { model: observation.model } : {}),\n\t\t\t\t\t...(observation.thinkingTokens !== undefined\n\t\t\t\t\t\t? { thinkingTokens: finiteNonnegative(observation.thinkingTokens) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(observation.responseTokens !== undefined\n\t\t\t\t\t\t? { responseTokens: finiteNonnegative(observation.responseTokens) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(observation.routingTokens !== undefined\n\t\t\t\t\t\t? { routingTokens: finiteNonnegative(observation.routingTokens) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(observation.effort ? { effort: observation.effort } : {}),\n\t\t\t\t\t...(duration > 0 ? { durationSec: duration } : {}),\n\t\t\t\t\t...(observation.contextTokens !== undefined ? { contextTokens } : {}),\n\t\t\t\t\t...(contextWindow > 0 ? { contextWindow } : {}),\n\t\t\t\t\t...(observation.firstCall\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tfirstCall: {\n\t\t\t\t\t\t\t\t\tharnessTokens: finiteNonnegative(\n\t\t\t\t\t\t\t\t\t\tobservation.firstCall.harnessTokens,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tinstructionsTokens: finiteNonnegative(\n\t\t\t\t\t\t\t\t\t\tobservation.firstCall.instructionsTokens,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t};\n\t\t\t\tconst existing = state.responses.get(responseId);\n\t\t\t\tconst magnitude = (value: typeof response): number =>\n\t\t\t\t\tvalue.routingTokens ??\n\t\t\t\t\t(value.thinkingTokens ?? 0) + (value.responseTokens ?? 0);\n\t\t\t\tif (!existing || magnitude(response) > magnitude(existing)) {\n\t\t\t\t\tstate.responses.set(responseId, response);\n\t\t\t\t}\n\t\t\t} else if (observation.type === \"turn\") {\n\t\t\t\tconst turnId =\n\t\t\t\t\tobservation.turnId ?? `anonymous:${state.nextAnonymousTurn++}`;\n\t\t\t\tstate.turns.set(turnId, observation.questionBack);\n\t\t\t} else {\n\t\t\t\tbump(compactionsByDate, date);\n\t\t\t}\n\t\t},\n\n\t\tfinish(): HarnessWorkflowAggregate {\n\t\t\tif (finished) return finished;\n\t\t\tconst days = new Map<string, DayState>();\n\t\t\tconst dayOf = (date: string): DayState => {\n\t\t\t\tlet state = days.get(date);\n\t\t\t\tif (!state) {\n\t\t\t\t\tstate = dayState();\n\t\t\t\t\tdays.set(date, state);\n\t\t\t\t}\n\t\t\t\treturn state;\n\t\t\t};\n\n\t\t\tconst windowPhaseSec = emptyPhase();\n\t\t\tlet phaseSessionCount = 0;\n\n\t\t\tfor (const state of sessions.values()) {\n\t\t\t\tif (state.firstTs === undefined) continue;\n\t\t\t\tconst day = dayOf(utcDateOf(state.firstTs));\n\t\t\t\tconst events = reduceEventBatches(state.events, harness);\n\t\t\t\tconst responses = [...state.responses.values()];\n\n\t\t\t\tday.sessions++;\n\t\t\t\tconst startHour = new Date(state.firstTs).getUTCHours();\n\t\t\t\tday.startHours.set(startHour, (day.startHours.get(startHour) ?? 0) + 1);\n\n\t\t\t\t// The phase reading of this session.\n\t\t\t\tconst phases = deriveSessionPhases(events, PHASE_RULES_V1, harness);\n\t\t\t\tif (state.events.length > 0) phaseSessionCount++;\n\t\t\t\tday.phase.sessions++;\n\t\t\t\tfor (const phase of PHASES) {\n\t\t\t\t\tday.phase.phaseSec[phase] += phases.phaseSec[phase];\n\t\t\t\t\tday.phase.phaseEvents[phase] += phases.phaseEvents[phase];\n\t\t\t\t\twindowPhaseSec[phase] += phases.phaseSec[phase];\n\t\t\t\t}\n\t\t\t\tday.phase.waitingSec += phases.waitingSec;\n\t\t\t\tday.phase.idleSec += phases.idleSec;\n\t\t\t\tif (phases.phaseEvents.verify > 0) day.phase.sessionsWithVerify++;\n\t\t\t\tif (phases.phaseEvents.handoff > 0) day.phase.sessionsWithHandoff++;\n\n\t\t\t\tconst measuredSec = PHASES.reduce(\n\t\t\t\t\t(sum, phase) => sum + phases.phaseSec[phase],\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\tconst bucket = logBucket(measuredSec / 60);\n\t\t\t\tconst merged = events.some(\n\t\t\t\t\t([, tool, arg]) =>\n\t\t\t\t\t\t[\"Bash\", \"bash\", \"shell\", \"local_shell\", \"exec_command\"].includes(\n\t\t\t\t\t\t\ttool,\n\t\t\t\t\t\t) && shellIncludes(arg, \"gh pr merge\"),\n\t\t\t\t);\n\t\t\t\tconst verified = hasVerifyRun(events, harness);\n\t\t\t\tconst openedWithScout =\n\t\t\t\t\t(events[0]\n\t\t\t\t\t\t? deriveSessionPhases([events[0]], PHASE_RULES_V1, harness)\n\t\t\t\t\t\t\t\t.phaseEvents.scout\n\t\t\t\t\t\t: 0) > 0;\n\t\t\t\tconst length = day.phase.lengths.get(bucket) ?? {\n\t\t\t\t\tbucket,\n\t\t\t\t\tsessions: 0,\n\t\t\t\t\tphaseSec: emptyPhase(),\n\t\t\t\t\tmerged: 0,\n\t\t\t\t\tverified: 0,\n\t\t\t\t\tmergedVerified: 0,\n\t\t\t\t\topenedWithScout: 0,\n\t\t\t\t};\n\t\t\t\tlength.sessions++;\n\t\t\t\tfor (const phase of PHASES)\n\t\t\t\t\tlength.phaseSec[phase] += phases.phaseSec[phase];\n\t\t\t\tif (merged) length.merged++;\n\t\t\t\tif (verified) length.verified++;\n\t\t\t\tif (merged && verified) length.mergedVerified++;\n\t\t\t\tif (openedWithScout) length.openedWithScout++;\n\t\t\t\tday.phase.lengths.set(bucket, length);\n\n\t\t\t\t// Routing and delegation.\n\t\t\t\tconst routing =\n\t\t\t\t\tstate.sidechain || state.parentSession ? \"subagents\" : \"main\";\n\t\t\t\tfor (const response of responses) {\n\t\t\t\t\tif (!response.model) continue;\n\t\t\t\t\tday.hasRouting = true;\n\t\t\t\t\tbump(\n\t\t\t\t\t\tday.routing[routing],\n\t\t\t\t\t\tresponse.model,\n\t\t\t\t\t\tresponse.routingTokens ?? response.responseTokens ?? 0,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (routing === \"subagents\") {\n\t\t\t\t\tday.delegation.subagentToolCalls += state.events.length;\n\t\t\t\t\tday.hasDelegation ||= state.events.length > 0;\n\t\t\t\t} else day.delegation.mainToolCalls += state.events.length;\n\n\t\t\t\t// Effort, thinking, turn durations and questions.\n\t\t\t\tfor (const response of responses) {\n\t\t\t\t\tif (response.effort) {\n\t\t\t\t\t\tday.hasEffort = true;\n\t\t\t\t\t\tconst level = effortLevelOf(response.effort);\n\t\t\t\t\t\tday.effort.set(level, (day.effort.get(level) ?? 0) + 1);\n\t\t\t\t\t}\n\t\t\t\t\tif (response.thinkingTokens !== undefined) {\n\t\t\t\t\t\tday.hasThinking = true;\n\t\t\t\t\t\tday.thinking.thinkingTokens += response.thinkingTokens;\n\t\t\t\t\t\tday.thinking.responseTokens += response.responseTokens ?? 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (response.durationSec !== undefined) {\n\t\t\t\t\t\tday.hasDurations = true;\n\t\t\t\t\t\tconst durationBucket = logBucket(response.durationSec);\n\t\t\t\t\t\tday.turnDurations.set(\n\t\t\t\t\t\t\tdurationBucket,\n\t\t\t\t\t\t\t(day.turnDurations.get(durationBucket) ?? 0) + 1,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (harness !== \"pi-mono\") {\n\t\t\t\t\tday.hasQuestions = true;\n\t\t\t\t\tday.questions.turns += state.turns.size;\n\t\t\t\t\tday.questions.asked += [...state.turns.values()].filter(\n\t\t\t\t\t\tBoolean,\n\t\t\t\t\t).length;\n\t\t\t\t}\n\n\t\t\t\t// Per-call context (#358), on the session's start day like every\n\t\t\t\t// other response figure. First calls count on main sessions only:\n\t\t\t\t// the reading splits the MAIN median call, and a subagent's first\n\t\t\t\t// call carries a different fixed part.\n\t\t\t\tfor (const response of responses) {\n\t\t\t\t\tif (response.contextTokens === undefined) continue;\n\t\t\t\t\tday.hasContext = true;\n\t\t\t\t\tconst context = day.context;\n\t\t\t\t\tbump(context.calls[routing], logBucketV2(response.contextTokens));\n\t\t\t\t\tcontext.maxContext = Math.max(\n\t\t\t\t\t\tcontext.maxContext,\n\t\t\t\t\t\tresponse.contextTokens,\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\tresponse.contextWindow !== undefined &&\n\t\t\t\t\t\t(context.window === undefined ||\n\t\t\t\t\t\t\tresponse.tsMs >= context.window.tsMs)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcontext.window = {\n\t\t\t\t\t\t\ttsMs: response.tsMs,\n\t\t\t\t\t\t\twindow: response.contextWindow,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (response.firstCall && routing === \"main\") {\n\t\t\t\t\t\tbump(context.firstCalls, logBucketV2(response.contextTokens));\n\t\t\t\t\t\tcontext.firstCallHarnessTokens += response.firstCall.harnessTokens;\n\t\t\t\t\t\tcontext.firstCallInstructionsTokens +=\n\t\t\t\t\t\t\tresponse.firstCall.instructionsTokens;\n\t\t\t\t\t\tcontext.firstCallCount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Compactions, on the day of the boundary.\n\t\t\tfor (const [date, compactions] of compactionsByDate) {\n\t\t\t\tconst day = dayOf(date);\n\t\t\t\tday.hasContext = true;\n\t\t\t\tday.context.compactions += compactions;\n\t\t\t}\n\n\t\t\t// Event cells and web searches, on the day of the event.\n\t\t\tfor (const date of eventDates) {\n\t\t\t\tconst day = dayOf(date);\n\t\t\t\tfor (const [key, events] of eventCells.get(date) ?? []) {\n\t\t\t\t\tbump(day.activity, key, events);\n\t\t\t\t}\n\t\t\t\tif (harness !== \"pi-mono\") {\n\t\t\t\t\tday.hasWebSearches = true;\n\t\t\t\t\tday.webSearches = webSearchesByDate.get(date) ?? 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fan-out, on the parent's start day.\n\t\t\tconst childrenByParent = new Map<string, SessionState[]>();\n\t\t\tfor (const state of sessions.values()) {\n\t\t\t\tif (!state.parentSession) continue;\n\t\t\t\tconst children = childrenByParent.get(state.parentSession) ?? [];\n\t\t\t\tchildren.push(state);\n\t\t\t\tchildrenByParent.set(state.parentSession, children);\n\t\t\t}\n\t\t\tfor (const [parentKey, children] of childrenByParent) {\n\t\t\t\tconst parent = sessions.get(parentKey);\n\t\t\t\tconst anchor =\n\t\t\t\t\tparent?.firstTs ??\n\t\t\t\t\tMath.min(...children.map((child) => child.firstTs ?? Infinity));\n\t\t\t\tif (!Number.isFinite(anchor)) continue;\n\t\t\t\tconst day = dayOf(utcDateOf(anchor));\n\t\t\t\tday.hasDelegation = true;\n\t\t\t\tday.delegation.mostSubagents = Math.max(\n\t\t\t\t\tday.delegation.mostSubagents,\n\t\t\t\t\tchildren.length,\n\t\t\t\t);\n\t\t\t\tconst boundaries = children.flatMap((child) => [\n\t\t\t\t\t{ ts: child.firstTs ?? 0, delta: 1 },\n\t\t\t\t\t{ ts: child.lastTs ?? child.firstTs ?? 0, delta: -1 },\n\t\t\t\t]);\n\t\t\t\tboundaries.sort((a, b) => a.ts - b.ts || b.delta - a.delta);\n\t\t\t\tlet active = 0;\n\t\t\t\tfor (const boundary of boundaries) {\n\t\t\t\t\tactive += boundary.delta;\n\t\t\t\t\tday.delegation.widestFanOut = Math.max(\n\t\t\t\t\t\tday.delegation.widestFanOut,\n\t\t\t\t\t\tactive,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// The workspace-day marks Git and the parallel-project count read.\n\t\t\tlocalSources.activeProjectDays.clear();\n\t\t\tfor (const state of sessions.values()) {\n\t\t\t\tif (state.firstTs === undefined || state.lastTs === undefined) continue;\n\t\t\t\tlet day = Date.parse(`${utcDateOf(state.firstTs)}T00:00:00Z`);\n\t\t\t\tconst lastDay = Date.parse(`${utcDateOf(state.lastTs)}T00:00:00Z`);\n\t\t\t\twhile (day <= lastDay) {\n\t\t\t\t\tconst date = utcDateOf(day);\n\t\t\t\t\tconst projects =\n\t\t\t\t\t\tlocalSources.activeProjectDays.get(date) ?? new Set();\n\t\t\t\t\tfor (const project of state.projectWorkspaces) projects.add(project);\n\t\t\t\t\tif (projects.size > 0)\n\t\t\t\t\t\tlocalSources.activeProjectDays.set(date, projects);\n\t\t\t\t\tday += 86_400_000;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst attributed = PHASES.reduce(\n\t\t\t\t(sum, phase) => sum + windowPhaseSec[phase],\n\t\t\t\t0,\n\t\t\t);\n\t\t\tconst unknown =\n\t\t\t\tattributed === 0 ? 0 : windowPhaseSec.unknown / attributed;\n\t\t\tconst routesModels =\n\t\t\t\tharness === \"claude-code\" ||\n\t\t\t\tharness === \"opencode\" ||\n\t\t\t\tharness === \"grok-build\";\n\n\t\t\tconst asRows = (map: Map<string, number>) => {\n\t\t\t\tconst safe = new Map<string, number>();\n\t\t\t\tfor (const [model, tokens] of map) {\n\t\t\t\t\tbump(safe, sanitizeModelId(model), tokens);\n\t\t\t\t}\n\t\t\t\treturn [...safe]\n\t\t\t\t\t.map(([model, tokens]) => ({ model, tokens }))\n\t\t\t\t\t.sort(\n\t\t\t\t\t\t(a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model),\n\t\t\t\t\t);\n\t\t\t};\n\n\t\t\tfinished = {\n\t\t\t\taggregateVersion: WORKFLOW_AGGREGATE_VERSION,\n\t\t\t\tharness,\n\t\t\t\tgate: {\n\t\t\t\t\truleVersion: PHASE_RULES_V1,\n\t\t\t\t\tpublishable: phaseSessionCount > 0 && unknown <= UNKNOWN_GATE,\n\t\t\t\t\tsessions: sessions.size,\n\t\t\t\t\tunknownShare: unknown,\n\t\t\t\t},\n\t\t\t\tdays: [...days]\n\t\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t\t.map(([date, day]) => ({\n\t\t\t\t\t\tdate,\n\t\t\t\t\t\tharness,\n\t\t\t\t\t\tsessions: day.sessions,\n\t\t\t\t\t\tstartHours: [...day.startHours]\n\t\t\t\t\t\t\t.map(([hourUtc, count]) => ({ hourUtc, sessions: count }))\n\t\t\t\t\t\t\t.sort((a, b) => a.hourUtc - b.hourUtc),\n\t\t\t\t\t\t...(day.phase.sessions > 0\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tphase: {\n\t\t\t\t\t\t\t\t\t\truleVersion: PHASE_RULES_V1,\n\t\t\t\t\t\t\t\t\t\tsessions: day.phase.sessions,\n\t\t\t\t\t\t\t\t\t\tphaseSec: day.phase.phaseSec,\n\t\t\t\t\t\t\t\t\t\tphaseEvents: day.phase.phaseEvents,\n\t\t\t\t\t\t\t\t\t\twaitingSec: day.phase.waitingSec,\n\t\t\t\t\t\t\t\t\t\tidleSec: day.phase.idleSec,\n\t\t\t\t\t\t\t\t\t\tsessionsWithVerify: day.phase.sessionsWithVerify,\n\t\t\t\t\t\t\t\t\t\tsessionsWithHandoff: day.phase.sessionsWithHandoff,\n\t\t\t\t\t\t\t\t\t\tbucketRuleVersion: LOG_BUCKETS_V1,\n\t\t\t\t\t\t\t\t\t\tlengths: [...day.phase.lengths.values()].sort(\n\t\t\t\t\t\t\t\t\t\t\t(a, b) => a.bucket - b.bucket,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(routesModels && day.hasRouting\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\t\t\t\tmain: asRows(day.routing.main),\n\t\t\t\t\t\t\t\t\t\tsubagents: asRows(day.routing.subagents),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(day.hasDelegation ? { delegation: day.delegation } : {}),\n\t\t\t\t\t\tactivity: [...day.activity]\n\t\t\t\t\t\t\t.map(([key, events]) => {\n\t\t\t\t\t\t\t\tconst [weekdayUtc, hourUtc] = key.split(\":\").map(Number);\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tweekdayUtc: weekdayUtc ?? 0,\n\t\t\t\t\t\t\t\t\thourUtc: hourUtc ?? 0,\n\t\t\t\t\t\t\t\t\tevents,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t\t(a, b) => a.weekdayUtc - b.weekdayUtc || a.hourUtc - b.hourUtc,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t...(day.hasEffort\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\teffort: EFFORT_LEVELS.flatMap((level) => {\n\t\t\t\t\t\t\t\t\t\tconst turns = day.effort.get(level) ?? 0;\n\t\t\t\t\t\t\t\t\t\treturn turns > 0 ? [{ level, turns }] : [];\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(day.hasThinking ? { thinking: day.thinking } : {}),\n\t\t\t\t\t\t...(day.hasDurations\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tturnDurations: {\n\t\t\t\t\t\t\t\t\t\tbucketRuleVersion: LOG_BUCKETS_V1,\n\t\t\t\t\t\t\t\t\t\tbuckets: [...day.turnDurations]\n\t\t\t\t\t\t\t\t\t\t\t.map(([bucket, turns]) => ({ bucket, turns }))\n\t\t\t\t\t\t\t\t\t\t\t.sort((a, b) => a.bucket - b.bucket),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(day.hasQuestions ? { questions: day.questions } : {}),\n\t\t\t\t\t\t...(day.hasWebSearches ? { webSearches: day.webSearches } : {}),\n\t\t\t\t\t\t...(day.hasContext\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\t\t\t\tbucketRuleVersion: LOG_BUCKETS_V2,\n\t\t\t\t\t\t\t\t\t\tcalls: {\n\t\t\t\t\t\t\t\t\t\t\tmain: asBuckets(day.context.calls.main, \"calls\"),\n\t\t\t\t\t\t\t\t\t\t\tsubagents: asBuckets(\n\t\t\t\t\t\t\t\t\t\t\t\tday.context.calls.subagents,\n\t\t\t\t\t\t\t\t\t\t\t\t\"calls\",\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tfirstCalls: {\n\t\t\t\t\t\t\t\t\t\t\tmain: asBuckets(day.context.firstCalls, \"sessions\"),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tfirstCallHarnessTokens: day.context.firstCallHarnessTokens,\n\t\t\t\t\t\t\t\t\t\tfirstCallInstructionsTokens:\n\t\t\t\t\t\t\t\t\t\t\tday.context.firstCallInstructionsTokens,\n\t\t\t\t\t\t\t\t\t\tfirstCallCount: day.context.firstCallCount,\n\t\t\t\t\t\t\t\t\t\tmaxContext: day.context.maxContext,\n\t\t\t\t\t\t\t\t\t\tcompactions: day.context.compactions,\n\t\t\t\t\t\t\t\t\t\t...(day.context.window\n\t\t\t\t\t\t\t\t\t\t\t? { window: day.context.window.window }\n\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t})),\n\t\t\t};\n\t\t\tsessions.clear();\n\t\t\teventCells.clear();\n\t\t\twebSearchesByDate.clear();\n\t\t\tcompactionsByDate.clear();\n\t\t\teventDates.clear();\n\t\t\treturn finished;\n\t\t},\n\t};\n}\n","// Pure fold over parsed Claude Code transcript records. No I/O, no console.\n//\n// Wayfinder ticket #37 (map #29), productizing the #32 prototype. Field\n// semantics come from docs/research/claude-code-transcripts-2026-07.md (#30),\n// as corrected by #32 and #33. Every field is treated as untrusted and\n// optional: records arrive as `unknown` and are narrowed here.\n//\n// The shared aggregate/finalize machinery lives in ../shared/aggregate.ts\n// (#67); this file owns what is CLAUDE-specific - the record shapes, and the\n// response dedup below.\n//\n// THE LOAD-BEARING SUBTLETY - read before touching `ingestAssistant`.\n// Claude Code writes ONE API response as SEVERAL JSONL records: each carries a\n// distinct content block (thinking, then tool_use, then tool_use...) and a\n// *cumulative* `usage` snapshot that grows with each record. Measured on a real\n// corpus: 20,073 of 44,280 response groups have differing usage across their\n// records, 20,071 of them monotonically increasing.\n//\n// So there are three wrong ways to count and one right way:\n// - sum every record -> ~2x over\n// - keep the first record -> ~2.1x under\n// - keep the last record -> right, but relies on file order\n// - keep the largest total -> right, order-independent <- this\n// Keeping the largest total is also ccusage's documented rule\n// (`should_replace_deduped_entry`).\n//\n// THE SECOND SUBTLETY - cost accumulates HERE, not in `finalize`.\n// Decision 8 of #33 made pricing time-aware, so a response is priced at the\n// rate in effect at its own timestamp. Summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change, so each response's\n// cost is computed as it is ingested and un-applied on replace.\n\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\temptyUsage,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\tnoteSyntheticTokens,\n\tnoteUsageResponse,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n// Re-exported for the existing import sites (tests, stage, summary); the\n// definitions moved to ../shared/aggregate.ts in #67.\nexport {\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"../shared/aggregate.js\";\n\ntype Entry = {\n\tmodelKey: string;\n\tcounts: TokenCounts;\n\t/** `null` = no rate applied at this response's timestamp. */\n\tcostUSD: number | null;\n};\n\n/** One API response's full contribution, kept so it can be un-applied on replace. */\ntype Contribution = {\n\tentries: Entry[];\n\ttotal: number;\n\tsidechain: boolean;\n\t/** The response's own timestamp; the day its tokens belong to (#307). */\n\ttsMs: number | null;\n\twebSearch: number;\n\twebFetch: number;\n\t/** Iteration types that mirrored top-level usage, for the diagnostics line. */\n\tmirroredIterationTypes: Array<[string, number]>;\n\t/** Iterations naming a different model, attributed to that model (#33 dec. 9). */\n\tfallbackAttempts: number;\n\t/** Mirror-suspected iterations with no `model` field - skipped, not billed. */\n\tuntypedMirrors: number;\n};\n\ntype SeenEntry = { requestId: string | null; contribution: Contribution };\n\n/**\n * The Claude adapter's aggregate: the shared fold target, with `seen` keyed\n * by `message.id` holding this adapter's replay/continuation bookkeeping.\n */\nexport type Aggregate = SharedAggregate<SeenEntry> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n\tworkflowSeenCalls: Set<string>;\n\tworkflowSeenTurns: Set<string>;\n\t/**\n\t * The `message.id` of the first API call seen per workflow session key\n\t * (#358), so every record of that response carries the first-call split\n\t * and no later response does. `null` marks a session whose first call was\n\t * seen without an id, or before the window opened (`noteRecordBeforeWindow`).\n\t */\n\tcontextFirstCall: Map<string, string | null>;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<SeenEntry>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"claude-code\", workflowLocal),\n\t\tworkflowLocal,\n\t\tworkflowSeenCalls: new Set<string>(),\n\t\tworkflowSeenTurns: new Set<string>(),\n\t\tcontextFirstCall: new Map<string, string | null>(),\n\t});\n}\n\n/** The session key the workflow reducer sees: subagents get their own. */\nfunction workflowSessionKey(rec: Obj): string | null {\n\tconst baseSession = asStr(rec.sessionId);\n\tif (!baseSession) return null;\n\tif (rec.isSidechain !== true) return baseSession;\n\treturn `${baseSession}:agent:${asStr(rec.agentId) ?? \"unknown\"}`;\n}\n\n/** True for a record that is one API call: assistant, with usage, not the harness's own pseudo-model. */\nfunction isApiCall(rec: Obj): boolean {\n\tif (asStr(rec.type) !== \"assistant\") return false;\n\tconst msg = asObj(rec.message);\n\tif (!msg || !asObj(msg.usage)) return false;\n\treturn !(asName(msg.model) ?? \"\").startsWith(\"<\");\n}\n\n/**\n * Note a record the scan skipped because it predates the window. The only\n * fact that matters here is that the session already made a call, so the\n * first call the window does see is not the session's first (#358). Without\n * this a session resumed inside the window would file a mid-conversation\n * call as its startup overhead.\n */\nexport function noteRecordBeforeWindow(agg: Aggregate, raw: unknown): void {\n\tconst rec = asObj(raw);\n\tif (!rec || !isApiCall(rec)) return;\n\tconst session = workflowSessionKey(rec);\n\tif (session && !agg.contextFirstCall.has(session)) {\n\t\tagg.contextFirstCall.set(session, null);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Ingest\n// ---------------------------------------------------------------------------\n\nexport type IngestContext = { projectDir: string };\n\n/** Read the local project workspace directory from one untrusted record. */\nexport function projectWorkspaceDirectory(raw: unknown): string | null {\n\tconst rec = asObj(raw);\n\treturn rec ? asStr(rec.cwd) : null;\n}\n\n/** Fold one parsed JSONL record into the aggregate. */\nexport function ingestRecord(\n\tagg: Aggregate,\n\traw: unknown,\n\tctx: IngestContext,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\n\tagg.records++;\n\tconst projectDir = projectWorkspaceDirectory(rec) ?? ctx.projectDir;\n\tagg.projectDirs.add(projectDir);\n\n\tconst version = asStr(rec.version);\n\tif (version) agg.ccVersions.add(cleanName(version));\n\tconst sessionId = asStr(rec.sessionId);\n\tif (sessionId) agg.sessions.add(sessionId);\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) {\n\t\t\ttsMs = ts;\n\t\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\t\tagg.firstTs = agg.firstTs === null ? ts : Math.min(agg.firstTs, ts);\n\t\t\tagg.lastTs = agg.lastTs === null ? ts : Math.max(agg.lastTs, ts);\n\t\t}\n\t}\n\tif (sessionId) noteSessionStart(agg, sessionId, tsMs);\n\tnoteProjectDay(agg, projectDir, tsMs);\n\n\tconst type = asStr(rec.type);\n\tif (type === \"assistant\") {\n\t\tingestClaudeWorkflow(agg, rec, ctx, tsMs);\n\t\tingestAssistant(agg, rec, tsMs);\n\t} else if (type === \"user\") ingestUser(agg, rec);\n\telse if (type === \"system\") {\n\t\tingestClaudeTurnDuration(agg, rec, ctx, tsMs);\n\t\tingestClaudeCompaction(agg, rec, ctx, tsMs);\n\t}\n}\n\n/**\n * A compaction boundary: `{type: \"system\", subtype: \"compact_boundary\"}`\n * (#358). Counted on the day of the boundary; the sizes it carries are not\n * on the wire.\n */\nfunction ingestClaudeCompaction(\n\tagg: Aggregate,\n\trec: Obj,\n\tctx: IngestContext,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null || asStr(rec.subtype) !== \"compact_boundary\") return;\n\tconst session = workflowSessionKey(rec);\n\tif (!session) return;\n\tagg.workflow.ingest({\n\t\ttype: \"compaction\",\n\t\tsession,\n\t\tprojectWorkspace: projectWorkspaceDirectory(rec) ?? ctx.projectDir,\n\t\ttsMs,\n\t\t...(rec.isSidechain === true ? { sidechain: true } : {}),\n\t});\n}\n\n/** What the request carried in: fresh input plus every cache write and read. */\nfunction contextOf(counts: TokenCounts): number {\n\treturn (\n\t\tcounts.input +\n\t\tcounts.cacheWrite5m +\n\t\tcounts.cacheWrite1h +\n\t\tcounts.cacheWriteUnsplit +\n\t\tcounts.cacheRead\n\t);\n}\n\nfunction ingestClaudeTurnDuration(\n\tagg: Aggregate,\n\trec: Obj,\n\tctx: IngestContext,\n\ttsMs: number | null,\n): void {\n\tif (\n\t\ttsMs === null ||\n\t\tasStr(rec.subtype) !== \"turn_duration\" ||\n\t\tasNum(rec.durationMs) <= 0\n\t) {\n\t\treturn;\n\t}\n\tconst session = asStr(rec.sessionId);\n\tif (!session) return;\n\tagg.workflow.ingest({\n\t\ttype: \"response\",\n\t\tsession,\n\t\tprojectWorkspace: projectWorkspaceDirectory(rec) ?? ctx.projectDir,\n\t\ttsMs,\n\t\t...(asStr(rec.uuid) ? { responseId: `duration:${asStr(rec.uuid)}` } : {}),\n\t\tdurationSec: asNum(rec.durationMs) / 1000,\n\t});\n}\n\nfunction ingestClaudeWorkflow(\n\tagg: Aggregate,\n\trec: Obj,\n\tctx: IngestContext,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null) return;\n\tconst baseSession = asStr(rec.sessionId);\n\tconst msg = asObj(rec.message);\n\tif (!baseSession || !msg) return;\n\tconst projectWorkspace = projectWorkspaceDirectory(rec) ?? ctx.projectDir;\n\tconst sidechain = rec.isSidechain === true;\n\tconst agentId = asStr(rec.agentId);\n\tconst session = sidechain\n\t\t? `${baseSession}:agent:${agentId ?? \"unknown\"}`\n\t\t: baseSession;\n\tconst parentSession = sidechain ? baseSession : undefined;\n\tconst usage = asObj(msg.usage);\n\tconst counts = usage ? readCounts(usage) : null;\n\tconst messageId = asStr(msg.id);\n\tconst newTurn = messageId ? !agg.workflowSeenTurns.has(messageId) : true;\n\tif (messageId) agg.workflowSeenTurns.add(messageId);\n\n\t// Per-call context (#358). The first call of a session is the first API\n\t// call seen under its key; every record of that response carries the split\n\t// (the records of one response share one context), and a response seen\n\t// before the window opened has already claimed the slot with `null`.\n\tlet context: {\n\t\tcontextTokens: number;\n\t\tfirstCall?: { harnessTokens: number; instructionsTokens: number };\n\t} | null = null;\n\tif (counts && isApiCall(rec)) {\n\t\tconst held = agg.contextFirstCall.get(session);\n\t\tlet first = false;\n\t\tif (held === undefined) {\n\t\t\tagg.contextFirstCall.set(session, messageId);\n\t\t\tfirst = true;\n\t\t} else if (held !== null && held === messageId) first = true;\n\t\tcontext = {\n\t\t\tcontextTokens: contextOf(counts),\n\t\t\t...(first\n\t\t\t\t? {\n\t\t\t\t\t\tfirstCall: {\n\t\t\t\t\t\t\tharnessTokens: counts.cacheRead,\n\t\t\t\t\t\t\tinstructionsTokens: contextOf(counts) - counts.cacheRead,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tagg.workflow.ingest({\n\t\ttype: \"response\",\n\t\tsession,\n\t\tprojectWorkspace,\n\t\ttsMs,\n\t\tsidechain,\n\t\t...(parentSession ? { parentSession } : {}),\n\t\t...(messageId ? { responseId: messageId } : {}),\n\t\t...(asName(msg.model) ? { model: asName(msg.model) as string } : {}),\n\t\t...(counts ? { responseTokens: counts.output } : {}),\n\t\t...(counts ? { routingTokens: countsTotal(counts) } : {}),\n\t\t...((asStr(rec.effort) ?? asStr(msg.effort))\n\t\t\t? { effort: (asStr(rec.effort) ?? asStr(msg.effort)) as string }\n\t\t\t: {}),\n\t\t...(context ?? {}),\n\t});\n\n\tconst tools: Obj[] = [];\n\tfor (const rawBlock of asArr(msg.content)) {\n\t\tconst block = asObj(rawBlock);\n\t\tif (block && asStr(block.type) === \"tool_use\") tools.push(block);\n\t}\n\tfor (const block of tools) {\n\t\tconst id = asStr(block.id);\n\t\tif (id) {\n\t\t\tif (agg.workflowSeenCalls.has(id)) continue;\n\t\t\tagg.workflowSeenCalls.add(id);\n\t\t}\n\t\tconst name = asName(block.name);\n\t\tif (!name) continue;\n\t\tconst input = asObj(block.input) ?? {};\n\t\tlet arg = \"\";\n\t\tif (name === \"Skill\") arg = asStr(input.skill) ?? \"\";\n\t\telse if (name === \"Agent\" || name === \"Task\")\n\t\t\targ = asStr(input.subagent_type) ?? \"\";\n\t\telse if (name === \"Bash\") arg = asStr(input.command) ?? \"\";\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"event\",\n\t\t\tsession,\n\t\t\tprojectWorkspace,\n\t\t\ttsMs,\n\t\t\tsidechain,\n\t\t\t...(parentSession ? { parentSession } : {}),\n\t\t\ttool: name === \"Task\" ? \"Agent\" : name,\n\t\t\targ,\n\t\t\t...(messageId ? { batchId: messageId } : {}),\n\t\t});\n\t}\n\tif (tools.length > 0 || newTurn) {\n\t\tconst lastTool = tools.at(-1);\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession,\n\t\t\tprojectWorkspace,\n\t\t\ttsMs,\n\t\t\tsidechain,\n\t\t\t...(parentSession ? { parentSession } : {}),\n\t\t\t...(messageId ? { turnId: messageId } : {}),\n\t\t\tquestionBack: [\"AskUserQuestion\", \"ExitPlanMode\"].includes(\n\t\t\t\tasName(lastTool?.name) ?? \"\",\n\t\t\t),\n\t\t});\n\t}\n}\n\nfunction ingestAssistant(agg: Aggregate, rec: Obj, tsMs: number | null): void {\n\tagg.assistantRecords++;\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\n\tconst messageId = asStr(msg.id);\n\tconst requestId = asStr(rec.requestId);\n\tconst existing = messageId === null ? undefined : agg.seen.get(messageId);\n\t// A genuine replay is the same message.id under a NEW requestId. Its records\n\t// repeat content already counted; a continuation's records do not.\n\tconst isReplay = existing !== undefined && existing.requestId !== requestId;\n\n\t// Content blocks are counted per RECORD, deliberately outside the token\n\t// fold: the records of ONE response carry disjoint blocks (verified across\n\t// 44,478 groups - zero overlap), so folding them would drop real blocks.\n\t// Replays are the exception and must be skipped, because `tool_use` has\n\t// `block.id` to dedup on but thinking/text blocks have no identity at all.\n\tif (!isReplay) ingestContentBlocks(agg, msg.content);\n\n\tconst usage = asObj(msg.usage);\n\tif (!usage) return;\n\n\tconst model = asName(msg.model) ?? \"(unknown)\";\n\t// `<synthetic>` is the harness's own pseudo-model for records it generates\n\t// itself. Not a tool the user chose - excluded from inventory and pricing,\n\t// but its tokens are surfaced rather than silently dropped.\n\tif (model.startsWith(\"<\")) {\n\t\tagg.syntheticRecords++;\n\t\tconst synthetic = countsTotal(readCounts(usage));\n\t\tagg.syntheticTokens += synthetic;\n\t\tnoteSyntheticTokens(agg, tsMs, synthetic);\n\t\treturn;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst sidechain = rec.isSidechain === true;\n\tconst contribution = buildContribution(usage, model, sidechain, tsMs);\n\n\tif (messageId === null) {\n\t\t// No dedup key available - count it and record that we were unprotected.\n\t\tagg.unkeyedResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\treturn;\n\t}\n\n\tif (existing === undefined) {\n\t\tagg.distinctResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\tagg.seen.set(messageId, { requestId, contribution });\n\t\treturn;\n\t}\n\n\tif (isReplay) agg.realReplaysFolded++;\n\telse agg.continuationsFolded++;\n\n\tif (!supersedes(contribution, existing.contribution)) return;\n\n\tagg.supersededByLarger++;\n\tretractContribution(agg, existing.contribution);\n\tacceptContribution(agg, contribution);\n\t// Keep the FIRST-seen requestId, not this record's. If a genuine replay wins\n\t// on tokens, overwriting it would make the replay's own later records compare\n\t// equal to the stored id, read as continuations, and get their thinking/text\n\t// blocks counted a second time - reopening exactly what the `isReplay` gate\n\t// above exists to close. (tool_use survives either way via `block.id`.)\n\tagg.seen.set(messageId, { requestId: existing.requestId, contribution });\n}\n\n/**\n * Apply a contribution and tally its diagnostics. Paired with\n * `retractContribution` so every per-response census stays per-RESPONSE rather\n * than per-record - these used to be bumped while merely *building* a\n * contribution, which counted every folded continuation too.\n */\nfunction acceptContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, +1);\n}\n\nfunction retractContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, -1);\n}\n\n/**\n * ccusage's collision rule: a non-sidechain copy beats a sidechain one;\n * otherwise the larger token total wins. Order-independent by construction,\n * so the result does not depend on filesystem traversal order.\n */\nfunction supersedes(next: Contribution, prev: Contribution): boolean {\n\tif (prev.sidechain !== next.sidechain)\n\t\treturn prev.sidechain && !next.sidechain;\n\treturn next.total > prev.total;\n}\n\nfunction readCounts(usage: Obj): TokenCounts {\n\tconst t: TokenCounts = {\n\t\tinput: asNum(usage.input_tokens),\n\t\toutput: asNum(usage.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: asNum(usage.cache_read_input_tokens),\n\t};\n\tconst cacheWriteTotal = asNum(usage.cache_creation_input_tokens);\n\tconst cc = asObj(usage.cache_creation);\n\tif (cc) {\n\t\tt.cacheWrite5m = asNum(cc.ephemeral_5m_input_tokens);\n\t\tt.cacheWrite1h = asNum(cc.ephemeral_1h_input_tokens);\n\t\tconst residual = cacheWriteTotal - (t.cacheWrite5m + t.cacheWrite1h);\n\t\tif (residual > 0) t.cacheWriteUnsplit = residual;\n\t} else {\n\t\tt.cacheWriteUnsplit = cacheWriteTotal;\n\t}\n\treturn t;\n}\n\n/** `usage.speed === \"fast\"` prices under a separate, higher rate. */\nfunction modelKeyFor(model: string, speed: string | null): string {\n\treturn normalizeModel(speed === \"fast\" ? `${model}#fast` : model);\n}\n\nfunction makeEntry(\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\ttsMs: number | null,\n): Entry {\n\treturn {\n\t\tmodelKey,\n\t\tcounts,\n\t\tcostUSD: apiEquivalentCost(modelKey, counts, tsMs),\n\t};\n}\n\nfunction buildContribution(\n\tusage: Obj,\n\tmodel: string,\n\tsidechain: boolean,\n\ttsMs: number | null,\n): Contribution {\n\tconst modelKey = modelKeyFor(model, asStr(usage.speed));\n\tconst entries: Entry[] = [makeEntry(modelKey, readCounts(usage), tsMs)];\n\tconst mirrored = new Map<string, number>();\n\tlet fallbackAttempts = 0;\n\tlet untypedMirrors = 0;\n\n\tfor (const rawIt of asArr(usage.iterations)) {\n\t\tconst it = asObj(rawIt);\n\t\tif (!it) continue;\n\t\tconst itType = asName(it.type) ?? \"(untyped)\";\n\t\tconst itModel = asName(it.model);\n\t\tconst itKey =\n\t\t\titModel === null ? null : modelKeyFor(itModel, asStr(it.speed));\n\n\t\t// Advisor iterations are a genuinely separate billed call under their own\n\t\t// model, never a mirror of top-level usage (ccusage prices them apart).\n\t\tif (itType === \"advisor_message\") {\n\t\t\tentries.push(makeEntry(itKey ?? modelKey, readCounts(it), tsMs));\n\t\t\tcontinue;\n\t\t}\n\n\t\t// #33 decision 9, SHARPENED - read the whole comment before touching this.\n\t\t//\n\t\t// The prototype skipped EVERY `type: \"message\"` iteration as a mirror of\n\t\t// top-level usage, which was correct by luck rather than construction: a\n\t\t// real `fallback_message` record showed top-level usage equal to the\n\t\t// fallback iteration EXACTLY, while a sibling `type: message` iteration\n\t\t// named a DIFFERENT model and carried tokens recorded nowhere else. So\n\t\t// `message.model` is already the serving model, and the mirror test is the\n\t\t// MODEL, not the type.\n\t\t//\n\t\t// #33 phrased the fix as \"skip it only when `iter.model === message.model`\".\n\t\t// Taken literally that is a ~2x overcount, because the corpus says the\n\t\t// `model` field is almost never there: of 63,638 non-advisor iterations,\n\t\t// 63,634 carry NO `model` at all - and all 63,634 are byte-exact mirrors of\n\t\t// their record's top-level usage (measured: zero differ). They carry 7.24\n\t\t// BILLION tokens, nearly double the corpus total, so attributing them as\n\t\t// separate entries would roughly double both tokens and cost. Only 8\n\t\t// iterations name a model: 4 matching (the `fallback_message` entries) and\n\t\t// 4 differing (the real first attempts).\n\t\t//\n\t\t// So the operative rule is: SKIP UNLESS THE ITERATION NAMES A DIFFERENT\n\t\t// MODEL. Absent is treated as matching - mis-attributing is a double-bill,\n\t\t// skipping is at worst an undercount, and the measurement above says it is\n\t\t// not even that.\n\t\tif (itKey === null) {\n\t\t\tuntypedMirrors++;\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tif (itKey === modelKey) {\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tentries.push(makeEntry(itKey, readCounts(it), tsMs));\n\t\tfallbackAttempts++;\n\t}\n\n\tconst serverTools = asObj(usage.server_tool_use);\n\treturn {\n\t\tentries,\n\t\ttotal: entries.reduce((a, e) => a + countsTotal(e.counts), 0),\n\t\tsidechain,\n\t\ttsMs,\n\t\twebSearch: serverTools ? asNum(serverTools.web_search_requests) : 0,\n\t\twebFetch: serverTools ? asNum(serverTools.web_fetch_requests) : 0,\n\t\tmirroredIterationTypes: [...mirrored],\n\t\tfallbackAttempts,\n\t\tuntypedMirrors,\n\t};\n}\n\n/** Add (sign +1) or remove (sign -1) a response's contribution from the totals. */\nfunction applyContribution(\n\tagg: Aggregate,\n\tc: Contribution,\n\tsign: 1 | -1,\n): void {\n\tc.entries.forEach(({ modelKey, counts, costUSD }, i) => {\n\t\tlet m = agg.byModel.get(modelKey);\n\t\tif (!m) {\n\t\t\tm = emptyUsage();\n\t\t\tagg.byModel.set(modelKey, m);\n\t\t}\n\t\t// One response is one message, even when a fallback attempt or an advisor\n\t\t// iteration attributes tokens to a second model - counting per entry would\n\t\t// inflate the response total past distinctResponses.\n\t\tif (i === 0) m.messages += sign;\n\t\tm.input += sign * counts.input;\n\t\tm.output += sign * counts.output;\n\t\tm.cacheWrite5m += sign * counts.cacheWrite5m;\n\t\tm.cacheWrite1h += sign * counts.cacheWrite1h;\n\t\tm.cacheWriteUnsplit += sign * counts.cacheWriteUnsplit;\n\t\tm.cacheRead += sign * counts.cacheRead;\n\t\tif (costUSD === null) m.unpricedTokens += sign * countsTotal(counts);\n\t\telse m.costUSD += sign * costUSD;\n\t\tnoteUsageResponse(\n\t\t\tagg,\n\t\t\t{ tsMs: c.tsMs, modelKey, counts, costUSD, sidechain: c.sidechain },\n\t\t\tsign,\n\t\t);\n\t});\n\tif (c.sidechain) agg.sidechainTokens += sign * c.total;\n\telse agg.mainTokens += sign * c.total;\n\tagg.webSearchRequests += sign * c.webSearch;\n\tagg.webFetchRequests += sign * c.webFetch;\n\tagg.fallbackAttempts += sign * c.fallbackAttempts;\n\tagg.untypedMirrors += sign * c.untypedMirrors;\n\tfor (const [type, count] of c.mirroredIterationTypes) {\n\t\tbump(agg.mirroredIterationTypes, type, sign * count);\n\t}\n}\n\nfunction ingestContentBlocks(agg: Aggregate, content: unknown): void {\n\tfor (const rawBlock of asArr(content)) {\n\t\tconst block = asObj(rawBlock);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") agg.thinkingBlocks++;\n\t\telse if (type === \"text\") agg.textBlocks++;\n\t\telse if (type === \"tool_use\") ingestToolUse(agg, block);\n\t}\n}\n\nfunction ingestToolUse(agg: Aggregate, block: Obj): void {\n\tconst name = asName(block.name);\n\tif (!name) return;\n\n\t// `toolu_...` block ids are globally unique, which makes this key both\n\t// collision-proof and replay-proof without a record-level prefix. A block\n\t// with no id is skipped rather than folded under a name-only key, which\n\t// would silently collapse every call to that tool into one.\n\tconst blockId = asStr(block.id);\n\tif (!blockId) {\n\t\tagg.toolBlocksWithoutId++;\n\t\treturn;\n\t}\n\tif (agg.toolCallDedup.has(blockId)) return;\n\tagg.toolCallDedup.add(blockId);\n\n\tconst input = asObj(block.input) ?? {};\n\n\tif (name.startsWith(\"mcp__\")) {\n\t\tconst parts = name.slice(\"mcp__\".length).split(\"__\");\n\t\tbump(agg.mcpServerCalls, parts[0] || \"(unknown)\");\n\t\tbump(agg.mcpToolCalls, name);\n\t\treturn;\n\t}\n\tif (name === \"Skill\") {\n\t\tbump(agg.skillCalls, asName(input.skill) ?? \"(unnamed)\");\n\t\tbump(agg.toolCalls, \"Skill\");\n\t\treturn;\n\t}\n\t// `Task` is the pre-rename spelling of `Agent`.\n\tif (name === \"Agent\" || name === \"Task\") {\n\t\tbump(agg.subagentCalls, asName(input.subagent_type) ?? \"(default)\");\n\t\tbump(agg.toolCalls, \"Agent\");\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, name);\n}\n\nconst SLASH_RE = /<command-name>\\/?([^<\\n\\r]{1,64})<\\/command-name>/g;\n\nfunction ingestUser(agg: Aggregate, rec: Obj): void {\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\tconst content = msg.content;\n\n\tlet text = \"\";\n\tif (typeof content === \"string\") text = content;\n\telse {\n\t\tfor (const rawBlock of asArr(content)) {\n\t\t\tconst block = asObj(rawBlock);\n\t\t\tif (!block) continue;\n\t\t\tif (asStr(block.type) === \"text\") text += asStr(block.text) ?? \"\";\n\t\t}\n\t}\n\tif (!text.includes(\"<command-name>\")) return;\n\n\t// `matchAll` over `exec` in a loop: the regex is module-level and `g`-flagged,\n\t// so an `exec` loop carries a shared `lastIndex` that a forgotten reset turns\n\t// into records being skipped at random.\n\tfor (const match of text.matchAll(SLASH_RE)) {\n\t\tbump(agg.slashCommands, cleanName(match[1]));\n\t}\n}\n","// I/O shell around the pure analyzer: find transcript roots, stream JSONL, hand\n// each parsed record to ingestRecord. Nothing leaves this machine.\n//\n// Wayfinder ticket #37 (map #29).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. Two consequences visible here:\n// project directories are counted but their names never escape this module, and\n// read errors are swallowed rather than thrown, because the error object carries\n// the absolute path and the munged project directory.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport {\n\temptyScanStats,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tingestRecord,\n\tnoteRecordBeforeWindow,\n\tprojectWorkspaceDirectory,\n} from \"./analyzer.js\";\n\nexport { type ScanStats, windowStartMs };\n\n/** Discovery order mirrors ccusage's adapter: CLAUDE_CONFIG_DIR, then the defaults. */\nexport function transcriptRoots(): string[] {\n\tconst env = process.env.CLAUDE_CONFIG_DIR;\n\tif (env) {\n\t\treturn env\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((s) => path.join(s, \"projects\"));\n\t}\n\tconst roots = [path.join(homedir(), \".claude\", \"projects\")];\n\tconst xdg = process.env.XDG_CONFIG_HOME ?? path.join(homedir(), \".config\");\n\troots.push(path.join(xdg, \"claude\", \"projects\"));\n\treturn roots;\n}\n\n/** What counts as a Claude Code transcript. Shared with `detect` (#101). */\nexport function isTranscriptFile(basename: string): boolean {\n\treturn basename.endsWith(\".jsonl\");\n}\n\n/** Recursive *.jsonl walk - the nested `<sessionId>/subagents/` layout is real. */\nasync function* walkJsonl(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkJsonl(full);\n\t\telse if (e.isFile() && isTranscriptFile(e.name)) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only ingest records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\n/**\n * KNOWN PERFORMANCE FLOOR - measured, decided, deliberately not fixed.\n *\n * Enumeration walks every project directory and `realpath`+`stat`s every file\n * BEFORE the mtime filter can skip anything, so a narrow window still pays a\n * floor proportional to TOTAL history (~60 ms over 3,206 files today, growing\n * linearly). The obvious fix - prune whole project directories by directory\n * mtime - is UNSOUND, and this was verified rather than assumed: appending to a\n * file does not update its parent directory's mtime (only adding, removing, or\n * renaming an entry does). A session resumed with `--resume` appends to a\n * transcript created before the window opened, inside a directory whose mtime\n * never moves, so dir-mtime pruning would silently drop live in-window records\n * - a wrong number, which is worse than a slow one for a tool whose whole claim\n * is measured-not-claimed.\n *\n * The sound version is a persisted enumeration cache, which cuts against #33\n * decision 1 (a sync is a stateless snapshot-replace, no durable client scan\n * state). So: DEFERRED. 60 ms is two orders of magnitude under the ~3 s full\n * scan and invisible next to the send round-trip; it becomes worth revisiting\n * only when total history reaches a scale where the floor dominates.\n */\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\t// Roots can overlap (CLAUDE_CONFIG_DIR may repeat a dir; ~/.claude and\n\t// ~/.config/claude may be symlinked together). Without this guard the same\n\t// file is ingested twice and the record/line/block counters silently double.\n\tconst visited = new Set<string>();\n\tconst projectWorkspaces = new Map<string, string>();\n\n\tfor (const root of opts.roots ?? transcriptRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkJsonl(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Transcripts are append-only and chronological, so a file untouched\n\t\t\t// since the window opened cannot hold an in-window record. This is what\n\t\t\t// makes a narrow window actually cheaper rather than merely narrower.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat - fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project dir = first path segment under projects/ (privacy-sensitive:\n\t\t\t// it is a munged absolute path, so it is only ever counted, never shown).\n\t\t\tconst rel = path.relative(root, file);\n\t\t\tconst projectDir = rel.split(path.sep)[0] ?? \"(root)\";\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tawait ingestFile(\n\t\t\t\t\tagg,\n\t\t\t\t\tfile,\n\t\t\t\t\tprojectDir,\n\t\t\t\t\tprojectWorkspaces,\n\t\t\t\t\topts.sinceMs,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\tprojectDir: string,\n\tprojectWorkspaces: Map<string, string>,\n\tsinceMs?: number,\n): Promise<void> {\n\tlet projectWorkspace = projectWorkspaces.get(projectDir) ?? projectDir;\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tfor await (const line of rl) {\n\t\tif (!line) continue;\n\t\tagg.lines++;\n\t\tlet rec: unknown;\n\t\ttry {\n\t\t\trec = JSON.parse(line);\n\t\t} catch {\n\t\t\tagg.parseErrors++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst cwd = projectWorkspaceDirectory(rec);\n\t\tif (cwd) {\n\t\t\tagg.projectDirs.delete(projectDir);\n\t\t\tprojectWorkspace = cwd;\n\t\t\tprojectWorkspaces.set(projectDir, cwd);\n\t\t}\n\t\tif (sinceMs !== undefined) {\n\t\t\tconst ts =\n\t\t\t\trec &&\n\t\t\t\ttypeof rec === \"object\" &&\n\t\t\t\t\"timestamp\" in rec &&\n\t\t\t\ttypeof (rec as { timestamp?: unknown }).timestamp === \"string\"\n\t\t\t\t\t? Date.parse((rec as { timestamp: string }).timestamp)\n\t\t\t\t\t: Number.NaN;\n\t\t\tif (Number.isNaN(ts) || ts < sinceMs) {\n\t\t\t\t// Skipped, but a call before the window still means the session's\n\t\t\t\t// first call is not the one the window will see (#358).\n\t\t\t\tnoteRecordBeforeWindow(agg, rec);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tingestRecord(agg, rec, { projectDir: projectWorkspace });\n\t}\n}\n","// The Claude Code harness behind the seam (#67). The parsing lives in\n// analyzer.ts/scan.ts, unchanged from the single-harness era; this file only\n// gives it the adapter shape.\n\nimport { BUILTIN_TOOLS } from \"../shared/allowlist.js\";\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isTranscriptFile, scan, transcriptRoots } from \"./scan.js\";\n\nexport const CLAUDE_HARNESS_NAME = \"claude-code\";\n\nexport const claudeAdapter: HarnessAdapter = {\n\tname: CLAUDE_HARNESS_NAME,\n\tbuiltinTools: BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? transcriptRoots(),\n\t\t\tisTranscriptFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","// Pure fold over parsed Codex CLI rollout lines. No I/O, no console.\n//\n// Wayfinder ticket #67 (map #60). Field semantics come from\n// docs/research/codex-session-log-anatomy-2026-08.md (#65) as locked by the\n// wire-format grilling #66. Every field is untrusted and optional: lines\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING SUBTLETY - Claude's cumulative gotcha, INVERTED.\n// Claude Code logs per-message usage that can repeat across snapshot records,\n// so its analyzer dedups by message id. Codex logs a `token_count` event whose\n// `total_token_usage` is the CUMULATIVE session sum - summing it across a\n// session's 20+ events overcounts by orders of magnitude. The rule locked in\n// #66: sum `last_token_usage` (the per-response delta) and never read the\n// totals. Deltas also carry the cached/non-cached split each response's cost\n// needs, which the cumulative figure re-counts every turn.\n//\n// Attribution: `token_count` events carry no model. Each delta is attributed\n// to the model of the nearest preceding `turn_context` in the same file.\n//\n// TokenCounts mapping (#66 decision 6): `cached_input_tokens` is a SUBSET of\n// `input_tokens`, so `input = input_tokens - cached_input`, `cacheRead =\n// cached_input`, and `cacheWrite = 0` - Codex reports no cache writes, and a\n// zero write prices correctly with zero pricing-code changes.\n\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/** Codex needs no response dedup bookkeeping - deltas count once by construction. */\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n\tworkflowSeenCalls: Set<string>;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"codex\", workflowLocal),\n\t\tworkflowLocal,\n\t\tworkflowSeenCalls: new Set<string>(),\n\t});\n}\n\n/**\n * Per-file fold state. A rollout file is one session; the session id, CLI\n * version, cwd and current model are context lines that may sit BEFORE the\n * window opens, so they update state unconditionally and are only counted\n * when an in-window line lands (`noteActivity`).\n */\nexport type FileState = {\n\tsessionId: string | null;\n\tcliVersion: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding `turn_context`. */\n\tmodelKey: string | null;\n\teffort: string | null;\n\tresponseIndex: number;\n\tcurrentQuestionBack: boolean;\n\t/** True once any in-window line was counted for this file. */\n\tcounted: boolean;\n\t/** Timestamp of the file's `session_meta` line, for the replay guard. */\n\tmetaTsMs: number | null;\n\t/**\n\t * True on a rollout forked from another (`forked_from_id`). Its first\n\t * genuine call already carries the parent's history, so it is no first\n\t * call for the context split (#358); its calls still count.\n\t */\n\tforked: boolean;\n\t/** True once a genuine (non-replayed, nonzero) response was seen, in window or not. */\n\tsawResponse: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn {\n\t\tsessionId: null,\n\t\tcliVersion: null,\n\t\tcwd: null,\n\t\tmodelKey: null,\n\t\teffort: null,\n\t\tresponseIndex: 0,\n\t\tcurrentQuestionBack: false,\n\t\tcounted: false,\n\t\tmetaTsMs: null,\n\t\tforked: false,\n\t\tsawResponse: false,\n\t};\n}\n\n/**\n * The per-response delta of a `token_count` event, or null when the line is\n * a rate-limit-only refresh (zero delta) or replayed parent history (see\n * `FORK_REPLAY_WINDOW_MS`). Shared by the window-independent first-call\n * bookkeeping and the in-window fold, so both see the same responses.\n */\nfunction genuineDelta(\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n): { counts: TokenCounts; last: Obj; contextWindow: number } | null {\n\tif (asStr(payload.type) !== \"token_count\") return null;\n\tconst info = asObj(payload.info);\n\tconst last = info ? asObj(info.last_token_usage) : null;\n\tif (!last) return null;\n\n\tconst inputTotal = asNum(last.input_tokens);\n\tconst cached = Math.min(asNum(last.cached_input_tokens), inputTotal);\n\tconst counts: TokenCounts = {\n\t\tinput: inputTotal - cached,\n\t\toutput: asNum(last.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: cached,\n\t};\n\t// A zero delta is a rate-limit-only refresh, not a response.\n\tif (countsTotal(counts) === 0) return null;\n\n\t// Replayed parent history (see FORK_REPLAY_WINDOW_MS): a `token_count`\n\t// stamped within the fork's write burst was counted by the parent rollout.\n\t// The replay often carries the parent's `turn_context` lines too, so the\n\t// timestamp is the guard; the `modelKey` check in `ingestEvent` only\n\t// backstops a replay whose head carried usage before any `turn_context`.\n\tif (\n\t\ttsMs !== null &&\n\t\tstate.metaTsMs !== null &&\n\t\ttsMs - state.metaTsMs < FORK_REPLAY_WINDOW_MS\n\t)\n\t\treturn null;\n\treturn {\n\t\tcounts,\n\t\tlast,\n\t\tcontextWindow: info ? asNum(info.model_context_window) : 0,\n\t};\n}\n\n/**\n * THE FORK-REPLAY GUARD. Codex forked threads (observed in codex-tui 0.151.0,\n * `forked_from_id` in `session_meta`) replay the parent's history into the new\n * rollout - `token_count` events included - re-stamped at fork creation. The\n * parent rollout already counted those deltas, so counting the replay double\n * counts them; on one machine the replays held 763M fresh tokens over 30 days.\n *\n * The replay is a synchronous write burst: across 166 local files every\n * replayed `token_count` sat within 144ms of the `session_meta` timestamp\n * (median 3ms), while the earliest GENUINE response of any session landed\n * 1,035ms after (5th percentile 5.3s) - a real response needs a network round\n * trip. 500ms splits the two populations with a wide margin on both sides.\n */\nconst FORK_REPLAY_WINDOW_MS = 500;\n\n/**\n * Fold one parsed rollout line into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context lines\n * (session_meta, turn_context) must update `state` even when they predate the\n * window - a session resumed today attributes today's deltas to a model named\n * last week.\n */\nexport function ingestLine(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) tsMs = ts;\n\t}\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\n\tconst type = asStr(rec.type);\n\tconst payload = asObj(rec.payload);\n\n\tif (type === \"session_meta\" && payload) {\n\t\tstate.sessionId =\n\t\t\tasStr(payload.id) ?? asStr(payload.session_id) ?? state.sessionId;\n\t\tstate.cliVersion = asStr(payload.cli_version) ?? state.cliVersion;\n\t\tstate.cwd = asStr(payload.cwd) ?? state.cwd;\n\t\tstate.metaTsMs = tsMs ?? state.metaTsMs;\n\t\tstate.forked =\n\t\t\tpayload.forked_from_id !== undefined && payload.forked_from_id !== null;\n\t} else if (type === \"turn_context\" && payload) {\n\t\tconst model = asName(payload.model);\n\t\tif (model) state.modelKey = normalizeModel(model);\n\t\tstate.effort = asStr(payload.effort) ?? state.effort;\n\t}\n\n\t// The first genuine response of a file is a fact about the whole file, so\n\t// it is noted before the window filter: a session resumed inside the\n\t// window must not file a mid-conversation call as its first (#358).\n\tlet firstCall = false;\n\tif (type === \"event_msg\" && payload && genuineDelta(payload, state, tsMs)) {\n\t\tfirstCall = !state.sawResponse;\n\t\tstate.sawResponse = true;\n\t}\n\n\tif (!inWindow) return;\n\n\tif (tsMs !== null && timestamp) {\n\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state, tsMs);\n\tnoteProjectDay(agg, state.cwd ?? \"(unknown)\", tsMs);\n\n\tif (type === \"event_msg\" && payload)\n\t\tingestEvent(agg, payload, state, tsMs, firstCall);\n\telse if (type === \"response_item\" && payload)\n\t\tingestItem(agg, payload, state, tsMs);\n\telse if (type === \"compacted\" && tsMs !== null && state.sessionId) {\n\t\t// A `compacted` rollout line is one compaction boundary (#358). The\n\t\t// zero-delta `token_count` that follows it stays skipped as usage.\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"compaction\",\n\t\t\tsession: state.sessionId,\n\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\ttsMs,\n\t\t});\n\t}\n}\n\n/** Count the file's session/version/cwd once, on its first in-window line. */\nfunction noteActivity(\n\tagg: Aggregate,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (state.sessionId) noteSessionStart(agg, state.sessionId, tsMs);\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\tif (state.cliVersion) agg.ccVersions.add(cleanName(state.cliVersion));\n\t// Counted, never published - same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n// ---------------------------------------------------------------------------\n// Usage - token_count deltas\n// ---------------------------------------------------------------------------\n\nfunction ingestEvent(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n\tfirstCall: boolean,\n): void {\n\tconst delta = genuineDelta(payload, state, tsMs);\n\tif (!delta) return;\n\tconst { counts, last, contextWindow } = delta;\n\tconst total = countsTotal(counts);\n\tif (state.modelKey === null) return;\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\n\tconst modelKey = state.modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t\t1,\n\t\t{ tsMs },\n\t);\n\t// Codex rollouts carry no sidechain flag; everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 rather than a guess.\n\tagg.mainTokens += total;\n\tif (tsMs !== null && state.sessionId) {\n\t\tconst responseId = `${state.sessionId}:response:${state.responseIndex++}`;\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"response\",\n\t\t\tsession: state.sessionId,\n\t\t\tresponseId,\n\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\ttsMs,\n\t\t\tmodel: modelKey,\n\t\t\tresponseTokens: counts.output,\n\t\t\troutingTokens: total,\n\t\t\tthinkingTokens: asNum(last.reasoning_output_tokens),\n\t\t\t...(state.effort ? { effort: state.effort } : {}),\n\t\t\t// The context at this call is `input_tokens` (cached is a subset).\n\t\t\t// On the first call the cached part is the harness (base\n\t\t\t// instructions and tool specs, cached by an earlier session) and\n\t\t\t// the fresh part is the instructions: AGENTS.md, environment,\n\t\t\t// skills and the first prompt. Same method as Claude Code (#358).\n\t\t\tcontextTokens: counts.input + counts.cacheRead,\n\t\t\t...(contextWindow > 0 ? { contextWindow } : {}),\n\t\t\t...(firstCall && !state.forked\n\t\t\t\t? {\n\t\t\t\t\t\tfirstCall: {\n\t\t\t\t\t\t\tharnessTokens: counts.cacheRead,\n\t\t\t\t\t\t\tinstructionsTokens: counts.input,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession: state.sessionId,\n\t\t\tturnId: responseId,\n\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\ttsMs,\n\t\t\tquestionBack: state.currentQuestionBack,\n\t\t});\n\t\tstate.currentQuestionBack = false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Inventory - response_item tool calls\n// ---------------------------------------------------------------------------\n\n/**\n * MCP tools reach the model as `<server>__<tool>` (MCP_TOOL_NAME_DELIMITER in\n * the Codex source); split on the FIRST `__` to recover the server. Over-long\n * names get a hash suffix on the TOOL side, so the server segment survives.\n */\nfunction ingestCall(agg: Aggregate, name: string, callId: string | null): void {\n\tif (callId) {\n\t\tif (agg.toolCallDedup.has(callId)) return;\n\t\tagg.toolCallDedup.add(callId);\n\t}\n\tconst sep = name.indexOf(\"__\");\n\tif (sep > 0) {\n\t\tbump(agg.mcpServerCalls, cleanName(name.slice(0, sep)));\n\t\tbump(agg.mcpToolCalls, cleanName(name));\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, cleanName(name));\n}\n\nfunction ingestItem(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tconst type = asStr(payload.type);\n\tif (type === \"function_call\" || type === \"custom_tool_call\") {\n\t\tconst name = asName(payload.name);\n\t\tif (!name) return;\n\t\tconst callId = asStr(payload.call_id) ?? asStr(payload.id);\n\t\tingestCall(agg, name, callId);\n\t\tingestWorkflowCall(agg, payload, name, callId, state, tsMs);\n\t\treturn;\n\t}\n\t// Non-function tool items publish under stable synthetic names that live in\n\t// CODEX_BUILTIN_TOOLS, so they survive the fail-closed filter.\n\tif (type === \"local_shell_call\") {\n\t\tconst callId = asStr(payload.call_id) ?? asStr(payload.id);\n\t\tingestCall(agg, \"local_shell\", callId);\n\t\tingestWorkflowCall(agg, payload, \"local_shell\", callId, state, tsMs);\n\t} else if (type === \"web_search_call\") {\n\t\tagg.webSearchRequests++;\n\t\tingestCall(agg, \"web_search\", asStr(payload.id));\n\t\tingestWorkflowCall(\n\t\t\tagg,\n\t\t\tpayload,\n\t\t\t\"web_search\",\n\t\t\tasStr(payload.id),\n\t\t\tstate,\n\t\t\ttsMs,\n\t\t);\n\t} else if (type === \"tool_search_call\") {\n\t\tingestCall(agg, \"tool_search\", asStr(payload.id));\n\t\tingestWorkflowCall(\n\t\t\tagg,\n\t\t\tpayload,\n\t\t\t\"tool_search\",\n\t\t\tasStr(payload.id),\n\t\t\tstate,\n\t\t\ttsMs,\n\t\t);\n\t}\n}\n\nfunction ingestWorkflowCall(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tname: string,\n\tcallId: string | null,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null || !state.sessionId) return;\n\tif (callId) {\n\t\tif (agg.workflowSeenCalls.has(callId)) return;\n\t\tagg.workflowSeenCalls.add(callId);\n\t}\n\tstate.currentQuestionBack = name === \"request_user_input\";\n\tlet arg = \"\";\n\tif (\n\t\t[\"exec_command\", \"shell\", \"container.exec\", \"local_shell\"].includes(name)\n\t) {\n\t\tconst raw =\n\t\t\tname === \"local_shell\"\n\t\t\t\t? asObj(payload.action)?.command\n\t\t\t\t: (payload.arguments ?? payload.input);\n\t\tif (Array.isArray(raw)) arg = unwrapShellCommand(raw.map(String));\n\t\telse if (typeof raw === \"string\" && !raw.trim().startsWith(\"{\")) arg = raw;\n\t\telse {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(String(raw ?? \"{}\")) as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tunknown\n\t\t\t\t>;\n\t\t\t\tconst command = parsed.cmd ?? parsed.command;\n\t\t\t\targ = Array.isArray(command)\n\t\t\t\t\t? unwrapShellCommand(command.map(String))\n\t\t\t\t\t: typeof command === \"string\"\n\t\t\t\t\t\t? command\n\t\t\t\t\t\t: \"\";\n\t\t\t} catch {\n\t\t\t\targ = \"\";\n\t\t\t}\n\t\t}\n\t}\n\tagg.workflow.ingest({\n\t\ttype: \"event\",\n\t\tsession: state.sessionId,\n\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\ttsMs,\n\t\ttool: name,\n\t\targ,\n\t});\n}\n\nfunction unwrapShellCommand(command: string[]): string {\n\tif (\n\t\tcommand.length >= 3 &&\n\t\t/^(bash|sh|zsh)$/.test(command[0] ?? \"\") &&\n\t\t/^-l?c$/.test(command[1] ?? \"\")\n\t)\n\t\treturn command.slice(2).join(\" \");\n\treturn command.join(\" \");\n}\n\n/**\n * Static MCP inventory from `~/.codex/config.toml` (#66 decision 3): a\n * configured server the window never called still exists. Zero-count entries\n * ride into the inventory (callShare 0) without inventing calls.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(raw);\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t}\n}\n","// I/O shell around the pure Codex analyzer: find rollout files, stream JSONL\n// (plain or zstd), hand each parsed line to ingestLine. Nothing leaves this\n// machine.\n//\n// Wayfinder ticket #67 (map #60), semantics from #65/#66.\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. `~/.codex/history.jsonl` holds raw\n// prompt text and is NEVER opened here; read errors are swallowed rather than\n// thrown, because the error object carries the absolute path.\n\nimport { type Dirent, readFileSync } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport * as zlib from \"node:zlib\";\n\nimport { parse as parseToml } from \"smol-toml\";\n\nimport { asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tingestLine,\n\tnoteConfiguredMcpServers,\n} from \"./analyzer.js\";\n\n/** `$CODEX_HOME` honored, `~/.codex` the default - mirrors the Codex source. */\nexport function codexHome(): string {\n\treturn process.env.CODEX_HOME || path.join(homedir(), \".codex\");\n}\n\n/**\n * Only `sessions/` is read. `archived_sessions/` is deliberately excluded: an\n * archived session was removed from the user's working set, and the rolling\n * window makes old ones irrelevant anyway. `history.jsonl` is raw prompts and\n * is out of bounds entirely.\n */\nexport function rolloutRoots(): string[] {\n\treturn [path.join(codexHome(), \"sessions\")];\n}\n\nconst ROLLOUT_RE = /^rollout-.*\\.jsonl(\\.zst)?$/;\n\n/** What counts as a Codex rollout. Shared with `detect` (#101). */\nexport function isRolloutFile(basename: string): boolean {\n\treturn ROLLOUT_RE.test(basename);\n}\n\n/** Recursive rollout walk - the YYYY/MM/DD nesting is real. */\nasync function* walkRollouts(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkRollouts(full);\n\t\telse if (e.isFile() && isRolloutFile(e.name)) yield full;\n\t}\n}\n\n/**\n * zstd support landed in node:zlib after the CLI's original Node 18 floor,\n * so it is feature-detected. On an old runtime a `.zst` rollout counts as\n * unreadable - a visible coverage figure, never a silent skip.\n */\nconst zstdDecompress: ((buf: Buffer) => Buffer) | null =\n\ttypeof (zlib as { zstdDecompressSync?: unknown }).zstdDecompressSync ===\n\t\"function\"\n\t\t? (buf) =>\n\t\t\t\t(\n\t\t\t\t\tzlib as unknown as { zstdDecompressSync: (b: Buffer) => Buffer }\n\t\t\t\t).zstdDecompressSync(buf)\n\t\t: null;\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n\t/** Override the config.toml path. Tests only. */\n\tconfigFile?: string;\n\t/** Override the file reader. Tests only. */\n\treadFileImpl?: (file: string) => Buffer | string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\n\tfor (const root of opts.roots ?? rolloutRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkRollouts(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\t// Dedup key = resolved path with `.zst` stripped. Codex's compression\n\t\t\t// worker leaves `foo.jsonl` and `foo.jsonl.zst` coexisting for a moment\n\t\t\t// (rename before unlink, #73 §4) - one session, two names. Keying on\n\t\t\t// the stem makes the second listing a duplicate, not a double count.\n\t\t\tconst dedupKey = resolved.endsWith(\".zst\")\n\t\t\t\t? resolved.slice(0, -\".zst\".length)\n\t\t\t\t: resolved;\n\t\t\tif (visited.has(dedupKey)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(dedupKey);\n\n\t\t\t// Rollouts are append-only and chronological, so a file untouched since\n\t\t\t// the window opened cannot hold an in-window record.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat - fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\tconst outcome = ingestWithRetry(agg, file, opts);\n\t\t\tif (!outcome.ok) {\n\t\t\t\t// Never rethrown: the error object carries the absolute path. The\n\t\t\t\t// stats keep a relative path and a bare error class instead (#75).\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tif (outcome.reason === \"zstd-unsupported\") stats.filesZstdUnsupported++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: outcome.reason,\n\t\t\t\t});\n\t\t\t} else if (!outcome.genuine) {\n\t\t\t\t// Fingerprint failure (#73): another tool wrote this file. Its usage\n\t\t\t\t// stayed out of the aggregate entirely.\n\t\t\t\tstats.filesForeign++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tconst seen = stats.foreignOriginators.get(outcome.originator) ?? 0;\n\t\t\t\tstats.foreignOriginators.set(outcome.originator, seen + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\treadConfiguredMcpServers(agg, opts.configFile);\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\ntype IngestOutcome =\n\t| { ok: true; genuine: true }\n\t| { ok: true; genuine: false; originator: string }\n\t| { ok: false; reason: string };\n\n/**\n * A read failure classified WITHOUT the error object's message or stack -\n * both carry the absolute path, which never leaves this module. `code` is a\n * bare class name (`ENOENT`, `EACCES`, `zstd-unsupported`, `zstd-corrupt`).\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n/**\n * The compression race (#73 §4): codex's background worker compresses a\n * rollout to `.zst` and then unlinks the plain `.jsonl`, so a file listed by\n * the walk can be gone at read time. Mirror codex's own reader: on `ENOENT`,\n * try the `.zst` sibling once before counting the file unreadable.\n */\nfunction ingestWithRetry(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\ttry {\n\t\treturn ingestFile(agg, file, opts);\n\t} catch (e) {\n\t\tif (errorClass(e) === \"ENOENT\" && !file.endsWith(\".zst\")) {\n\t\t\ttry {\n\t\t\t\treturn ingestFile(agg, `${file}.zst`, opts);\n\t\t\t} catch (e2) {\n\t\t\t\treturn { ok: false, reason: errorClass(e2) };\n\t\t\t}\n\t\t}\n\t\treturn { ok: false, reason: errorClass(e) };\n\t}\n}\n\n/**\n * Whole-file read rather than a stream: a `.zst` rollout must be decompressed\n * as one buffer anyway, and rollout files are single sessions - megabytes,\n * not gigabytes. The lines are parsed BEFORE any of them folds into the\n * aggregate, because the fingerprint verdict (#73) arrives only at end of\n * file: a foreign file must leave the aggregate untouched.\n */\nfunction ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\tconst readFile = opts.readFileImpl ?? readFileSync;\n\tlet text: string;\n\tif (file.endsWith(\".zst\")) {\n\t\tif (zstdDecompress === null) throw readError(\"zstd-unsupported\");\n\t\tconst raw = readFile(file);\n\t\ttry {\n\t\t\ttext = zstdDecompress(\n\t\t\t\tBuffer.isBuffer(raw) ? raw : Buffer.from(raw),\n\t\t\t).toString(\"utf8\");\n\t\t} catch {\n\t\t\tthrow readError(\"zstd-corrupt\");\n\t\t}\n\t} else {\n\t\ttext = readFile(file).toString(\"utf8\");\n\t}\n\n\tconst records: unknown[] = [];\n\tlet nonEmptyLines = 0;\n\tlet parseErrors = 0;\n\tfor (const line of text.split(\"\\n\")) {\n\t\tif (!line) continue;\n\t\tnonEmptyLines++;\n\t\ttry {\n\t\t\trecords.push(JSON.parse(line));\n\t\t} catch {\n\t\t\tparseErrors++;\n\t\t}\n\t}\n\n\tconst verdict = classifyRollout(records);\n\tif (!verdict.genuine) return { ok: true, ...verdict };\n\n\tagg.lines += nonEmptyLines;\n\tagg.parseErrors += parseErrors;\n\tconst state = createFileState();\n\tfor (const rec of records) ingestLine(agg, rec, state, opts.sinceMs);\n\treturn { ok: true, genuine: true };\n}\n\n/**\n * The genuine-rollout fingerprint (#73, source-pinned at rust-v0.146.0): the\n * codex-rs recorder always writes `session_meta` first, and every real user\n * turn persists a `turn_context` before its usage lands. Newer Codex (observed\n * in 0.151.0) broke the ORDER half of that invariant: a forked thread replays\n * the parent's history - `token_count` events included - ahead of its first\n * new turn, so usage may sit before any `turn_context`. The analyzer skips\n * that replayed head; here the rule weakens to presence: a file that carries\n * a `token_count` but no `turn_context` ANYWHERE was not written by Codex\n * CLI. Negative by construction - it detects \"not genuine\", never \"written by\n * tool X\"; the originator label is diagnostic only.\n */\nfunction classifyRollout(\n\trecords: readonly unknown[],\n): { genuine: true } | { genuine: false; originator: string } {\n\tlet originator: string | null = null;\n\tlet sawTurnContext = false;\n\tlet sawTokenCount = false;\n\tlet genuine = records.length > 0;\n\tfor (const [i, raw] of records.entries()) {\n\t\tconst rec = asObj(raw);\n\t\tconst type = rec ? asStr(rec.type) : null;\n\t\tconst payload = rec ? asObj(rec.payload) : null;\n\t\tif (i === 0 && type !== \"session_meta\") genuine = false;\n\t\tif (type === \"session_meta\" && payload && originator === null) {\n\t\t\toriginator = asStr(payload.originator);\n\t\t} else if (type === \"turn_context\") {\n\t\t\tsawTurnContext = true;\n\t\t} else if (\n\t\t\ttype === \"event_msg\" &&\n\t\t\tpayload &&\n\t\t\tasStr(payload.type) === \"token_count\"\n\t\t) {\n\t\t\tsawTokenCount = true;\n\t\t}\n\t}\n\tif (sawTokenCount && !sawTurnContext) genuine = false;\n\tif (genuine) return { genuine: true };\n\treturn { genuine: false, originator: originator ?? \"(none)\" };\n}\n\n/**\n * The static half of the MCP inventory (#66 decision 3): `[mcp_servers.*]`\n * in `~/.codex/config.toml`. Unreadable or absent config is silence, not an\n * error - the observed half stands on its own.\n */\nfunction readConfiguredMcpServers(agg: Aggregate, configFile?: string): void {\n\tconst file = configFile ?? path.join(codexHome(), \"config.toml\");\n\tlet names: string[] = [];\n\ttry {\n\t\tconst parsed = parseToml(readFileSync(file, \"utf8\"));\n\t\tconst servers = parsed.mcp_servers;\n\t\tif (servers && typeof servers === \"object\" && !Array.isArray(servers)) {\n\t\t\tnames = Object.keys(servers);\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n\tnoteConfiguredMcpServers(agg, names);\n}\n","// The Codex CLI harness behind the seam (#66 decision 6, built in #67).\n\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isRolloutFile, rolloutRoots, scan } from \"./scan.js\";\n\nexport const CODEX_HARNESS_NAME = \"codex\";\n\n/**\n * Codex's vendor-assigned tool surface, as observed in rollouts and pinned in\n * the Codex source (#65 §4). Same fail-closed mechanism as Claude's\n * BUILTIN_TOOLS: a literal set, never a pattern - an unknown-but-real\n * built-in withheld as a count is a small loss; an unknown-and-user-named\n * tool published verbatim is the leak this prevents. The last four are the\n * stable synthetic names the analyzer assigns to non-`function_call` items.\n *\n * Codex v1 publishes builtinTools and mcpServers ONLY (#66 decision 3):\n * slash commands verifiably never reach rollouts, and the skill/subagent\n * surfaces are unverified - those categories ship as empty arrays, absorbed\n * with no schema change if a later Codex version logs them.\n */\nexport const CODEX_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"exec_command\",\n\t\"grep_command\",\n\t\"list_dir\",\n\t\"read_file\",\n\t\"request_user_input\",\n\t\"shell\",\n\t\"unified_exec\",\n\t\"update_plan\",\n\t\"view_image\",\n\t\"write_stdin\",\n\t// synthetic names for non-function_call response items\n\t\"local_shell\",\n\t\"web_search\",\n\t\"tool_search\",\n]);\n\nexport const codexAdapter: HarnessAdapter = {\n\tname: CODEX_HARNESS_NAME,\n\tbuiltinTools: CODEX_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? rolloutRoots(),\n\t\t\tisRolloutFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","import {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"grok-build\", workflowLocal),\n\t\tworkflowLocal,\n\t});\n}\n\nexport type UsageContribution = {\n\tsessionId: string;\n\tprojectDir: string;\n\ttsMs: number;\n\tdurationMs?: number;\n\tmodels: Array<{ model: string; counts: TokenCounts }>;\n};\n\ntype GrokEventState = {\n\ttools: Map<string, { name: string; arg?: string; tsMs: number }>;\n\tcompletedTools: Set<string>;\n\tparentSession?: string;\n};\n\nexport const createGrokEventState = (\n\tparentSession?: string,\n): GrokEventState => ({\n\ttools: new Map(),\n\tcompletedTools: new Set(),\n\t...(parentSession ? { parentSession } : {}),\n});\n\nconst bump = (map: Map<string, number>, key: string): void => {\n\tmap.set(key, (map.get(key) ?? 0) + 1);\n};\n\nconst toolMetadata = (update: Record<string, unknown>) => {\n\tconst meta = asObj(update._meta);\n\treturn meta && asObj(meta[\"x.ai/tool\"]);\n};\n\n/** Project one persisted Grok update without retaining prompts or raw arguments. */\nexport function ingestUpdate(\n\tagg: Aggregate,\n\tstate: GrokEventState,\n\tvalue: unknown,\n\tprojectDir: string,\n\tsinceMs?: number,\n): void {\n\tconst root = asObj(value);\n\tconst params = root && asObj(root.params);\n\tconst update = params && asObj(params.update);\n\tconst session = params && asStr(params.sessionId);\n\tconst tsMs = timestampMs(\n\t\tasObj(params?._meta)?.agentTimestampMs ?? root?.timestamp,\n\t);\n\tif (!update || !session || tsMs === null) return;\n\tconst kind = asStr(update.sessionUpdate);\n\tif (kind === \"tool_call\") {\n\t\tconst id = asStr(update.toolCallId);\n\t\tconst metadata = toolMetadata(update);\n\t\tconst name = asName(metadata?.name ?? update.toolName);\n\t\tif (!id || !name || state.tools.has(id)) return;\n\t\tconst raw = asObj(update.input);\n\t\tconst arg = asStr(raw?.command ?? raw?.query ?? raw?.skill ?? raw?.name);\n\t\tstate.tools.set(id, { name, ...(arg ? { arg } : {}), tsMs });\n\t\treturn;\n\t}\n\tif (sinceMs !== undefined && tsMs < sinceMs) return;\n\tif (kind === \"tool_call_update\") {\n\t\tconst id = asStr(update.toolCallId);\n\t\tif (!id || asStr(update.status) !== \"completed\") return;\n\t\tcompleteTool(agg, state, id, session, projectDir, tsMs);\n\t\treturn;\n\t}\n\tif (kind !== \"turn_completed\") return;\n\tconst usage = asObj(update.usage);\n\tif (!usage) return;\n\tconst prompt = asStr(update.prompt_id) ?? `turn:${tsMs}`;\n\tfor (const [model, raw] of Object.entries(asObj(usage.modelUsage) ?? {})) {\n\t\tconst row = asObj(raw);\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"response\",\n\t\t\tsession,\n\t\t\tprojectWorkspace: projectDir,\n\t\t\tparentSession: state.parentSession,\n\t\t\ttsMs,\n\t\t\tresponseId: `${prompt}:${model}`,\n\t\t\tmodel,\n\t\t\tthinkingTokens: asNum(row?.reasoningTokens),\n\t\t\tresponseTokens: asNum(row?.outputTokens),\n\t\t\troutingTokens: asNum(row?.outputTokens),\n\t\t\t...(asNum(row?.apiDurationMs) > 0\n\t\t\t\t? { durationSec: asNum(row?.apiDurationMs) / 1000 }\n\t\t\t\t: asNum(update.elapsed_ms) > 0\n\t\t\t\t\t? { durationSec: asNum(update.elapsed_ms) / 1000 }\n\t\t\t\t\t: {}),\n\t\t});\n\t}\n\tagg.workflow.ingest({\n\t\ttype: \"turn\",\n\t\tsession,\n\t\tprojectWorkspace: projectDir,\n\t\tparentSession: state.parentSession,\n\t\ttsMs,\n\t\tturnId: prompt,\n\t\tquestionBack: asStr(update.stop_reason) === \"question\",\n\t});\n}\n\nfunction completeTool(\n\tagg: Aggregate,\n\tstate: GrokEventState,\n\tid: string,\n\tsession: string,\n\tprojectDir: string,\n\ttsMs: number,\n): void {\n\tif (state.completedTools.has(id)) return;\n\tconst tool = state.tools.get(id);\n\tif (!tool) return;\n\tstate.completedTools.add(id);\n\tbump(agg.toolCalls, tool.name);\n\tif ([\"web_search\", \"websearch\", \"search_web\"].includes(tool.name))\n\t\tagg.webSearchRequests++;\n\tif ([\"skill\", \"use_skill\"].includes(tool.name) && tool.arg)\n\t\tbump(agg.skillCalls, tool.arg);\n\tconst mcp = /^(?:mcp__|mcp:)([^_:]+)[_:](.+)$/.exec(tool.name);\n\tif (mcp) {\n\t\tbump(agg.mcpServerCalls, mcp[1] as string);\n\t\tbump(agg.mcpToolCalls, tool.name);\n\t} else if (tool.name === \"use_tool\" && tool.arg?.includes(\"__\")) {\n\t\tconst [server] = tool.arg.split(\"__\", 1);\n\t\tif (server) {\n\t\t\tbump(agg.mcpServerCalls, server);\n\t\t\tbump(agg.mcpToolCalls, tool.arg);\n\t\t}\n\t}\n\tagg.workflow.ingest({\n\t\ttype: \"event\",\n\t\tsession,\n\t\tprojectWorkspace: projectDir,\n\t\tparentSession: state.parentSession,\n\t\ttsMs: tool.tsMs || tsMs,\n\t\ttool: tool.name,\n\t\t...(tool.arg ? { arg: tool.arg } : {}),\n\t\tbatchId: id,\n\t});\n}\n\n/** Complete a tool from the durable event stream when the ACP update is absent. */\nexport function ingestEvent(\n\tagg: Aggregate,\n\tstate: GrokEventState,\n\tvalue: unknown,\n\tsessionFallback: string,\n\tprojectDir: string,\n\tsinceMs?: number,\n): void {\n\tconst row = asObj(value);\n\tif (!row) return;\n\tconst session = asStr(row.session_id) ?? sessionFallback;\n\tconst tsMs = timestampMs(row.ts);\n\tif (!session || tsMs === null) return;\n\tif (asStr(row.type) === \"tool_started\") {\n\t\tconst id = asStr(row.tool_call_id);\n\t\tconst name = asName(row.tool_name);\n\t\tif (id && name && !state.tools.has(id)) state.tools.set(id, { name, tsMs });\n\t} else if (sinceMs !== undefined && tsMs < sinceMs) {\n\t\treturn;\n\t} else if (asStr(row.type) === \"tool_completed\") {\n\t\tconst id = asStr(row.tool_call_id);\n\t\tif (id) completeTool(agg, state, id, session, projectDir, tsMs);\n\t} else if (asStr(row.type) === \"compaction\") {\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"compaction\",\n\t\t\tsession,\n\t\t\tprojectWorkspace: projectDir,\n\t\t\tparentSession: state.parentSession,\n\t\t\ttsMs,\n\t\t});\n\t}\n}\n\nconst timestampMs = (value: unknown): number | null => {\n\tif (typeof value === \"string\") {\n\t\tconst parsed = Date.parse(value);\n\t\treturn Number.isFinite(parsed) ? parsed : null;\n\t}\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return null;\n\treturn value < 10_000_000_000 ? value * 1000 : value;\n};\n\nfunction counts(value: unknown): TokenCounts | null {\n\tconst row = asObj(value);\n\tif (!row) return null;\n\tconst totalInput = asNum(row.inputTokens);\n\tconst cacheRead = asNum(row.cachedReadTokens);\n\tconst cacheWrite = asNum(row.cacheCreationTokens);\n\tif (totalInput < cacheRead + cacheWrite) return null;\n\tconst result = {\n\t\tinput: totalInput - cacheRead - cacheWrite,\n\t\toutput: asNum(row.outputTokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: cacheWrite,\n\t\tcacheRead,\n\t};\n\treturn countsTotal(result) > 0 ? result : null;\n}\n\nexport function sidecarContributions(\n\tvalue: unknown,\n\tprojectDir: string,\n): UsageContribution[] {\n\tconst root = asObj(value);\n\tconst sessionId = root && asStr(root.sessionId);\n\tif (!root || !sessionId) return [];\n\tconst turns = Array.isArray(root.turns) ? root.turns : [];\n\tconst out: UsageContribution[] = [];\n\tfor (const raw of turns) {\n\t\tconst turn = asObj(raw);\n\t\tconst tsMs = turn && timestampMs(turn.endedAt);\n\t\tif (!turn || tsMs === null) continue;\n\t\tconst models: UsageContribution[\"models\"] = [];\n\t\tconst perModel = asObj(turn.modelUsage);\n\t\tfor (const [model, usage] of Object.entries(perModel ?? {})) {\n\t\t\tconst c = counts(usage);\n\t\t\tif (c) models.push({ model, counts: c });\n\t\t}\n\t\tif (models.length === 0) {\n\t\t\tconst c = counts(turn);\n\t\t\tconst model = asStr(turn.primaryModelId);\n\t\t\tif (c && model) models.push({ model, counts: c });\n\t\t}\n\t\tif (models.length > 0)\n\t\t\tout.push({\n\t\t\t\tsessionId,\n\t\t\t\tprojectDir,\n\t\t\t\ttsMs,\n\t\t\t\t...(asNum(turn.apiDurationMs) > 0\n\t\t\t\t\t? { durationMs: asNum(turn.apiDurationMs) }\n\t\t\t\t\t: {}),\n\t\t\t\tmodels,\n\t\t\t});\n\t}\n\treturn out;\n}\n\nexport function terminalContribution(\n\tvalue: unknown,\n\tprojectDir: string,\n): UsageContribution | null {\n\tconst root = asObj(value);\n\tconst params = root && asObj(root.params);\n\tconst update = params && asObj(params.update);\n\tconst meta = params && asObj(params._meta);\n\tif (!update || asStr(update.sessionUpdate) !== \"turn_completed\") return null;\n\tconst usage = asObj(update.usage);\n\tconst sessionId = params && asStr(params.sessionId);\n\tconst tsMs = timestampMs(meta?.agentTimestampMs ?? root?.timestamp);\n\tif (!usage || !sessionId || tsMs === null) return null;\n\tconst models: UsageContribution[\"models\"] = [];\n\tfor (const [model, row] of Object.entries(asObj(usage.modelUsage) ?? {})) {\n\t\tconst c = counts(row);\n\t\tif (c) models.push({ model, counts: c });\n\t}\n\treturn models.length === 0\n\t\t? null\n\t\t: {\n\t\t\t\tsessionId,\n\t\t\t\tprojectDir,\n\t\t\t\ttsMs,\n\t\t\t\t...(asNum(update.elapsed_ms) > 0\n\t\t\t\t\t? { durationMs: asNum(update.elapsed_ms) }\n\t\t\t\t\t: {}),\n\t\t\t\tmodels,\n\t\t\t};\n}\n\nexport function ingestContribution(\n\tagg: Aggregate,\n\trow: UsageContribution,\n): void {\n\tagg.records++;\n\tagg.assistantRecords++;\n\tagg.distinctResponses++;\n\tagg.sessions.add(row.sessionId);\n\tagg.activeDays.add(new Date(row.tsMs).toISOString().slice(0, 10));\n\tagg.projectDirs.add(row.projectDir);\n\tagg.firstTs =\n\t\tagg.firstTs === null ? row.tsMs : Math.min(agg.firstTs, row.tsMs);\n\tagg.lastTs = agg.lastTs === null ? row.tsMs : Math.max(agg.lastTs, row.tsMs);\n\tnoteSessionStart(agg, row.sessionId, row.tsMs);\n\tnoteProjectDay(agg, row.projectDir, row.tsMs);\n\tfor (const { model, counts: tokenCounts } of row.models) {\n\t\tconst key = normalizeModel(model);\n\t\taddModelUsage(\n\t\t\tagg,\n\t\t\tkey,\n\t\t\ttokenCounts,\n\t\t\tapiEquivalentCost(key, tokenCounts, row.tsMs),\n\t\t\t1,\n\t\t\t{\n\t\t\t\ttsMs: row.tsMs,\n\t\t\t},\n\t\t);\n\t}\n}\n","import { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\nimport { parse as parseToml } from \"smol-toml\";\nimport { asObj, asStr, countsTotal } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport type { Aggregate } from \"./analyzer.js\";\nimport {\n\tcreateGrokEventState,\n\tingestContribution,\n\tingestEvent,\n\tingestUpdate,\n\tsidecarContributions,\n\tterminalContribution,\n\ttype UsageContribution,\n} from \"./analyzer.js\";\n\nexport function sessionRoots(): string[] {\n\treturn [\n\t\tpath.join(\n\t\t\tprocess.env.GROK_HOME || path.join(homedir(), \".grok\"),\n\t\t\t\"sessions\",\n\t\t),\n\t];\n}\n\nexport const isGrokEvidenceFile = (name: string): boolean =>\n\tname === \"usage.json\" || name === \"updates.jsonl\" || name === \"events.jsonl\";\n\ntype FileRead = { lines?: unknown[]; json?: unknown; complete: boolean };\nconst delays = [0, 100, 300];\nconst pause = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nasync function stableRead(file: string, jsonl: boolean): Promise<FileRead> {\n\tfor (const delay of delays) {\n\t\tif (delay) await pause(delay);\n\t\ttry {\n\t\t\tconst before = await stat(file);\n\t\t\tif (!before.isFile()) return { complete: false };\n\t\t\tif (jsonl) {\n\t\t\t\tconst lines: unknown[] = [];\n\t\t\t\tconst input = readline.createInterface({\n\t\t\t\t\tinput: createReadStream(file),\n\t\t\t\t\tcrlfDelay: Infinity,\n\t\t\t\t});\n\t\t\t\tfor await (const line of input) {\n\t\t\t\t\tif (!line.trim()) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlines.push(JSON.parse(line));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tlines.push(null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst after = await stat(file);\n\t\t\t\tif (before.size === after.size && before.mtimeMs === after.mtimeMs)\n\t\t\t\t\treturn { lines, complete: true };\n\t\t\t} else {\n\t\t\t\tconst raw = await readFile(file, \"utf8\");\n\t\t\t\tconst after = await stat(file);\n\t\t\t\tif (before.size === after.size && before.mtimeMs === after.mtimeMs)\n\t\t\t\t\treturn { json: JSON.parse(raw), complete: true };\n\t\t\t}\n\t\t} catch {\n\t\t\t/* retry a transient read or parse failure */\n\t\t}\n\t}\n\treturn { complete: false };\n}\n\nasync function directories(root: string): Promise<string[] | null> {\n\ttry {\n\t\tconst workspaces = await readdir(root, { withFileTypes: true });\n\t\tconst out: string[] = [];\n\t\tfor (const workspace of workspaces) {\n\t\t\tif (!workspace.isDirectory() || workspace.isSymbolicLink()) continue;\n\t\t\tconst workspacePath = path.join(root, workspace.name);\n\t\t\tfor (const session of await readdir(workspacePath, {\n\t\t\t\twithFileTypes: true,\n\t\t\t})) {\n\t\t\t\tif (session.isDirectory() && !session.isSymbolicLink())\n\t\t\t\t\tout.push(path.join(workspacePath, session.name));\n\t\t\t}\n\t\t}\n\t\treturn out.sort();\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"ENOENT\" ? [] : null;\n\t}\n}\n\n/**\n * Read only aliases that still use xAI's normal endpoint. Routing overrides\n * remain under their recorded alias because no vendor rate is established.\n */\nexport async function modelAliasesForRoot(\n\troot: string,\n): Promise<ReadonlyMap<string, string>> {\n\tif (process.env.GROK_MODELS_BASE_URL) return new Map();\n\ttry {\n\t\tconst parsed = asObj(\n\t\t\tparseToml(await readFile(path.join(root, \"..\", \"config.toml\"), \"utf8\")),\n\t\t);\n\t\tif (!parsed || asObj(parsed.endpoints)?.models_base_url) return new Map();\n\t\tconst models = asObj(parsed.model);\n\t\tconst aliases = new Map<string, string>();\n\t\tfor (const [alias, raw] of Object.entries(models ?? {})) {\n\t\t\tconst entry = asObj(raw);\n\t\t\tconst model = entry && asStr(entry.model);\n\t\t\tif (!model || entry?.base_url || entry?.model_provider) continue;\n\t\t\taliases.set(alias, model);\n\t\t}\n\t\treturn aliases;\n\t} catch {\n\t\treturn new Map();\n\t}\n}\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: {\n\t\tsinceMs?: number;\n\t\troots?: string[];\n\t\tonProgress?: (files: number) => void;\n\t} = {},\n): Promise<{\n\tstats: ScanStats;\n\tcomplete: boolean;\n\tsessionDates: Map<string, Set<string>>;\n}> {\n\tconst stats = emptyScanStats();\n\tconst sessionDates = new Map<string, Set<string>>();\n\tconst candidates = new Map<\n\t\tstring,\n\t\t{ precedence: number; rows: UsageContribution[] }\n\t>();\n\tlet complete = true;\n\tfor (const root of opts.roots ?? sessionRoots()) {\n\t\tconst aliases = await modelAliasesForRoot(root);\n\t\tconst dirs = await directories(root);\n\t\tif (dirs === null) {\n\t\t\tcomplete = false;\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const dir of dirs) {\n\t\t\tlet entries: Dirent[];\n\t\t\ttry {\n\t\t\t\tentries = await readdir(dir, { withFileTypes: true });\n\t\t\t} catch {\n\t\t\t\tcomplete = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst files = new Map(\n\t\t\t\tentries\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(e) =>\n\t\t\t\t\t\t\te.isFile() &&\n\t\t\t\t\t\t\t(isGrokEvidenceFile(e.name) || e.name === \"summary.json\"),\n\t\t\t\t\t)\n\t\t\t\t\t.map((e) => [e.name, path.join(dir, e.name)]),\n\t\t\t);\n\t\t\tlet projectDir = dir;\n\t\t\tlet sessionFallback = path.basename(dir);\n\t\t\tlet child = false;\n\t\t\tlet parentSession: string | undefined;\n\t\t\tconst summary = files.get(\"summary.json\");\n\t\t\tif (summary) {\n\t\t\t\tconst read = await stableRead(summary, false);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst value = asObj(read.json);\n\t\t\t\tconst info = value && asObj(value.info);\n\t\t\t\tprojectDir = asStr(info?.cwd) ?? asStr(value?.cwd) ?? dir;\n\t\t\t\tsessionFallback = asStr(value?.sessionId) ?? sessionFallback;\n\t\t\t\tparentSession =\n\t\t\t\t\tasStr(value?.parentSessionId) ??\n\t\t\t\t\tasStr(value?.parent_session_id) ??\n\t\t\t\t\tasStr(info?.parentSessionId) ??\n\t\t\t\t\tasStr(info?.parent_session_id) ??\n\t\t\t\t\tundefined;\n\t\t\t\tchild = parentSession !== undefined;\n\t\t\t}\n\t\t\tlet rows = [] as ReturnType<typeof sidecarContributions>;\n\t\t\tlet precedence = 0;\n\t\t\tconst usage = files.get(\"usage.json\");\n\t\t\tif (usage) {\n\t\t\t\tstats.filesFound++;\n\t\t\t\tconst read = await stableRead(usage, false);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstats.filesRead++;\n\t\t\t\trows = sidecarContributions(read.json, projectDir);\n\t\t\t\tif (rows.length > 0) precedence = 2;\n\t\t\t}\n\t\t\tif (child) {\n\t\t\t\trows = [];\n\t\t\t\tprecedence = 0;\n\t\t\t}\n\t\t\tconst eventState = createGrokEventState(parentSession);\n\t\t\tconst updates = files.get(\"updates.jsonl\");\n\t\t\tif (updates) {\n\t\t\t\tconst useTerminalUsage = rows.length === 0 && !child;\n\t\t\t\tstats.filesFound++;\n\t\t\t\tconst read = await stableRead(updates, true);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstats.filesRead++;\n\t\t\t\tfor (const value of read.lines ?? []) {\n\t\t\t\t\tif (value === null) {\n\t\t\t\t\t\tagg.parseErrors++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tingestUpdate(agg, eventState, value, projectDir, opts.sinceMs);\n\t\t\t\t\tif (useTerminalUsage) {\n\t\t\t\t\t\tconst row = terminalContribution(value, projectDir);\n\t\t\t\t\t\tif (row) rows.push(row);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (rows.length > 0) precedence = 1;\n\t\t\t}\n\t\t\tconst events = files.get(\"events.jsonl\");\n\t\t\tif (events) {\n\t\t\t\tstats.filesFound++;\n\t\t\t\tconst read = await stableRead(events, true);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstats.filesRead++;\n\t\t\t\tfor (const value of read.lines ?? []) {\n\t\t\t\t\tif (value === null) agg.parseErrors++;\n\t\t\t\t\telse\n\t\t\t\t\t\tingestEvent(\n\t\t\t\t\t\t\tagg,\n\t\t\t\t\t\t\teventState,\n\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\tsessionFallback,\n\t\t\t\t\t\t\tprojectDir,\n\t\t\t\t\t\t\topts.sinceMs,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\trows = rows.filter(\n\t\t\t\t(row) => opts.sinceMs === undefined || row.tsMs >= opts.sinceMs,\n\t\t\t);\n\t\t\trows = rows.map((row) => ({\n\t\t\t\t...row,\n\t\t\t\tmodels: row.models.map(({ model, counts }) => ({\n\t\t\t\t\tmodel: aliases.get(model) ?? model,\n\t\t\t\t\tcounts,\n\t\t\t\t})),\n\t\t\t}));\n\t\t\tif (rows.length > 0) {\n\t\t\t\tconst sessionId = rows[0]?.sessionId as string;\n\t\t\t\tconst held = candidates.get(sessionId);\n\t\t\t\tconst total = (values: UsageContribution[]) =>\n\t\t\t\t\tvalues\n\t\t\t\t\t\t.flatMap((value) => value.models)\n\t\t\t\t\t\t.reduce((sum, model) => sum + countsTotal(model.counts), 0);\n\t\t\t\tif (\n\t\t\t\t\t!held ||\n\t\t\t\t\tprecedence > held.precedence ||\n\t\t\t\t\t(precedence === held.precedence && total(rows) > total(held.rows))\n\t\t\t\t)\n\t\t\t\t\tcandidates.set(sessionId, { precedence, rows });\n\t\t\t}\n\t\t\topts.onProgress?.(stats.filesFound);\n\t\t}\n\t}\n\tfor (const { rows } of candidates.values()) {\n\t\tfor (const row of rows) {\n\t\t\tingestContribution(agg, row);\n\t\t\tconst dates = sessionDates.get(row.sessionId) ?? new Set<string>();\n\t\t\tdates.add(new Date(row.tsMs).toISOString().slice(0, 10));\n\t\t\tsessionDates.set(row.sessionId, dates);\n\t\t}\n\t}\n\treturn { stats, complete, sessionDates };\n}\n","import { hasRecentFile } from \"../shared/recency.js\";\nimport type { HarnessAdapter } from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isGrokEvidenceFile, scan, sessionRoots } from \"./scan.js\";\n\nexport const GROK_HARNESS_NAME = \"grok-build\";\nexport const GROK_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"run_terminal_command\",\n\t\"read_file\",\n\t\"write_file\",\n\t\"search\",\n\t\"web_search\",\n]);\n\nexport const grokAdapter: HarnessAdapter = {\n\tname: GROK_HARNESS_NAME,\n\tbuiltinTools: GROK_BUILTIN_TOOLS,\n\tdetect: (opts) =>\n\t\thasRecentFile(\n\t\t\topts.roots ?? sessionRoots(),\n\t\t\tisGrokEvidenceFile,\n\t\t\topts.sinceMs,\n\t\t),\n\tasync scan(opts) {\n\t\tconst aggregate = createAggregate();\n\t\tconst result = await scan(aggregate, opts);\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats: result.stats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t\tscanComplete: result.complete,\n\t\t\tsessionDates: result.sessionDates,\n\t\t};\n\t},\n};\n","// Pure fold over rows projected out of opencode's SQLite store. No I/O, no\n// console - the scanner owns the database and hands this module plain values.\n//\n// Wayfinder ticket #124 (map #121). Field semantics come from\n// docs/research/harness-adapters-2026-08.md (§opencode), keyed by #123's\n// binding rule: every pricing key carries the harness's own provider id\n// (`modelKeyFor(providerID, modelID)`), because one opencode install routes\n// several providers and a gateway re-serving a vendor's model must not price\n// at that vendor's list rate.\n//\n// THE LOAD-BEARING FACTS (research §2):\n// - the four token counters are DISJOINT deltas: input, output, cache.read,\n// cache.write map straight onto TokenCounts with no arithmetic;\n// - `tokens.total` is absent on some rows and wrong on Google rows - never\n// read it; `reasoning` is a subset of `output` for two vendors and\n// additive for one - never add it;\n// - opencode's own `cost` is 0.0 on every record (subscription/OAuth auth);\n// a zero is not a measurement, so cost comes from @aistack/pricing only.\n\nimport {\n\tapiEquivalentCost,\n\tmodelKeyFor,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasNum,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/**\n * opencode's vendor-assigned tool surface, as observed in the probe DB and\n * pinned against the opencode source. Same fail-closed mechanism as the other\n * adapters: a literal set, never a pattern. It doubles as the MCP guard -\n * MCP tool names are `sanitize(server) + \"_\" + sanitize(tool)` and built-in\n * names contain `_` too, so a name must miss this set AND carry a configured\n * server's prefix before any split is attempted.\n */\nexport const OPENCODE_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"bash\",\n\t\"edit\",\n\t\"glob\",\n\t\"grep\",\n\t\"list\",\n\t\"patch\",\n\t\"question\",\n\t\"read\",\n\t\"skill\",\n\t\"task\",\n\t\"todoread\",\n\t\"todowrite\",\n\t\"webfetch\",\n\t\"websearch\",\n\t\"write\",\n]);\n\n/** Message-id dedup lives in DbFoldState, not the aggregate's `seen`. */\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"opencode\", workflowLocal),\n\t\tworkflowLocal,\n\t});\n}\n\n/** What the scanner knows about one session row, named columns only. */\nexport type SessionInfo = {\n\tid: unknown;\n\tparentId: unknown;\n\tversion: unknown;\n};\n\n/**\n * Per-database fold state. Sessions are loaded up front (a message's session\n * may predate the window); message ids are deduped across the v1 and v2\n * tables because v2 is described in the opencode source as a projection -\n * reading a row from both would double count.\n */\nexport type DbFoldState = {\n\tsessions: Map<string, { parentId: string | null; version: string | null }>;\n\tseenMessageIds: Set<string>;\n\t/** Configured MCP server names, sanitized the way opencode builds tool names. */\n\tmcpServers: string[];\n};\n\nexport function createDbFoldState(): DbFoldState {\n\treturn { sessions: new Map(), seenMessageIds: new Set(), mcpServers: [] };\n}\n\nexport function noteSessions(\n\tstate: DbFoldState,\n\trows: Iterable<SessionInfo>,\n): void {\n\tfor (const row of rows) {\n\t\tconst id = asStr(row.id);\n\t\tif (!id) continue;\n\t\tstate.sessions.set(id, {\n\t\t\tparentId: asStr(row.parentId),\n\t\t\tversion: asStr(row.version),\n\t\t});\n\t}\n}\n\n/**\n * One projected message row. Every value is untrusted - `json_extract` returns\n * whatever the blob holds - so the fold narrows each field itself.\n */\nexport type MessageRow = {\n\tid: unknown;\n\tsessionId: unknown;\n\trole: unknown;\n\tproviderId: unknown;\n\tmodelId: unknown;\n\t/** `data.time.created`, epoch ms. */\n\ttsMs: unknown;\n\tinput: unknown;\n\toutput: unknown;\n\tcacheRead: unknown;\n\tcacheWrite: unknown;\n\tcwd: unknown;\n\treasoning?: unknown;\n\tcompletedTsMs?: unknown;\n};\n\nexport function ingestMessageRow(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\trow: MessageRow,\n): void {\n\tagg.records++;\n\n\tconst id = asStr(row.id);\n\tif (id) {\n\t\tif (state.seenMessageIds.has(id)) return;\n\t\tstate.seenMessageIds.add(id);\n\t}\n\n\tconst tsMs =\n\t\ttypeof row.tsMs === \"number\" && Number.isFinite(row.tsMs) ? row.tsMs : null;\n\tif (tsMs !== null) {\n\t\tagg.activeDays.add(new Date(tsMs).toISOString().slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\n\tconst sessionId = asStr(row.sessionId);\n\tconst session = sessionId ? state.sessions.get(sessionId) : undefined;\n\tif (sessionId) {\n\t\tagg.sessions.add(sessionId);\n\t\tnoteSessionStart(agg, sessionId, tsMs);\n\t\tif (session?.version) agg.ccVersions.add(cleanName(session.version));\n\t}\n\t// Counted, never published - same standing non-goal as Claude project dirs.\n\tconst cwd = asStr(row.cwd);\n\tif (cwd) {\n\t\tagg.projectDirs.add(cwd);\n\t\tnoteProjectDay(agg, cwd, tsMs);\n\t}\n\n\tif (asStr(row.role) !== \"assistant\") return;\n\tagg.assistantRecords++;\n\tagg.distinctResponses++;\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst counts: TokenCounts = {\n\t\tinput: asNum(row.input),\n\t\toutput: asNum(row.output),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: asNum(row.cacheWrite),\n\t\tcacheRead: asNum(row.cacheRead),\n\t};\n\n\t// A session with a parent is a subagent's - an honest measurement here,\n\t// unlike Codex's structural 0 (research §inventory: 21.4% on the probe DB).\n\tconst total =\n\t\tcounts.input + counts.output + counts.cacheWriteUnsplit + counts.cacheRead;\n\tif (session?.parentId) agg.sidechainTokens += total;\n\telse agg.mainTokens += total;\n\n\tconst provider = asStr(row.providerId);\n\tconst model = asStr(row.modelId);\n\tconst modelKey =\n\t\tprovider && model\n\t\t\t? normalizeModel(modelKeyFor(provider, cleanName(model)))\n\t\t\t: \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t\t1,\n\t\t{ tsMs, sidechain: Boolean(session?.parentId) },\n\t);\n\tif (sessionId && tsMs !== null) {\n\t\tconst completed = asNum(row.completedTsMs);\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"response\",\n\t\t\tsession: sessionId,\n\t\t\t...(id ? { responseId: id } : {}),\n\t\t\tprojectWorkspace: cwd ?? undefined,\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\t...(provider && model ? { model: `${provider}:${model}` } : {}),\n\t\t\tthinkingTokens: asNum(row.reasoning),\n\t\t\tresponseTokens: counts.output,\n\t\t\troutingTokens: total,\n\t\t\t...(completed > tsMs ? { durationSec: (completed - tsMs) / 1000 } : {}),\n\t\t});\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession: sessionId,\n\t\t\t...(id ? { turnId: id } : {}),\n\t\t\tprojectWorkspace: cwd ?? undefined,\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\tquestionBack: false,\n\t\t});\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Inventory - `part` rows of type \"tool\"\n// ---------------------------------------------------------------------------\n\n/**\n * opencode's own `sanitize` as applied when composing MCP tool names: the\n * observed names (`chrome-devtools_click`) show `-` surviving, so everything\n * outside the tool-name charset collapses to `_`.\n */\nconst sanitizeMcpName = (name: string): string =>\n\tname.replace(/[^A-Za-z0-9_-]/g, \"_\");\n\n/**\n * One projected `part` row. The scanner extracts these named paths and\n * nothing else - `part.data.state.output` holds full command output and never\n * materializes in JS.\n */\nexport type ToolPartRow = {\n\tid: unknown;\n\tpartType: unknown;\n\ttool: unknown;\n\tcallId: unknown;\n\t/** `$.state.input.name` - the skill tool's skill. */\n\tinputName: unknown;\n\t/** `$.state.input.subagent_type` - the task tool's agent. */\n\tsubagentType: unknown;\n\tsessionId?: unknown;\n\ttsMs?: unknown;\n\tcommand?: unknown;\n\tmessageId?: unknown;\n};\n\nexport function ingestToolPart(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\trow: ToolPartRow,\n): void {\n\t// step-finish parts repeat the message's tokens and text parts carry prose;\n\t// only tool executions count (research §2).\n\tif (asStr(row.partType) !== \"tool\") return;\n\tconst rawName = asStr(row.tool);\n\tif (!rawName) return;\n\tconst name = cleanName(rawName);\n\n\tconst dedupKey = asStr(row.callId) ?? asStr(row.id);\n\tif (dedupKey) {\n\t\tif (agg.toolCallDedup.has(dedupKey)) return;\n\t\tagg.toolCallDedup.add(dedupKey);\n\t}\n\tconst sessionId = asStr(row.sessionId);\n\tconst tsMs = asNum(row.tsMs);\n\tconst session = sessionId ? state.sessions.get(sessionId) : undefined;\n\tif (sessionId && tsMs > 0) {\n\t\tconst messageId = asStr(row.messageId);\n\t\tlet arg = \"\";\n\t\tif (name === \"skill\") arg = asStr(row.inputName) ?? \"\";\n\t\telse if (name === \"task\") arg = asStr(row.subagentType) ?? \"\";\n\t\telse if (name === \"bash\") arg = asStr(row.command) ?? \"\";\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"event\",\n\t\t\tsession: sessionId,\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\ttool: name,\n\t\t\targ,\n\t\t});\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession: sessionId,\n\t\t\t...(messageId ? { turnId: messageId } : {}),\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\tquestionBack: name === \"question\",\n\t\t});\n\t}\n\n\t// Fail-closed order (research §inventory): the literal built-in set first,\n\t// then configured MCP server prefixes, then a plain count the shared\n\t// payload filter withholds - never a split that invents a server name.\n\tif (OPENCODE_BUILTIN_TOOLS.has(name)) {\n\t\tbump(agg.toolCalls, name);\n\t\tif (name === \"skill\") {\n\t\t\tconst skill = asStr(row.inputName);\n\t\t\tif (skill) bump(agg.skillCalls, cleanName(skill));\n\t\t} else if (name === \"task\") {\n\t\t\tconst agent = asStr(row.subagentType);\n\t\t\tif (agent) bump(agg.subagentCalls, cleanName(agent));\n\t\t}\n\t\treturn;\n\t}\n\tfor (const server of state.mcpServers) {\n\t\tif (name.startsWith(`${server}_`)) {\n\t\t\tbump(agg.mcpServerCalls, server);\n\t\t\tbump(agg.mcpToolCalls, name);\n\t\t\treturn;\n\t\t}\n\t}\n\tbump(agg.toolCalls, name);\n}\n\n/**\n * Static MCP inventory from `~/.config/opencode/opencode.json` (JSONC - the\n * scanner owns the tolerant parse). A configured server the window never\n * called still exists: zero-count entries ride into the inventory. The\n * sanitized form is what tool-name prefixes are matched against, longest\n * first so `foo-bar` wins over `foo` for `foo-bar_tool`.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(sanitizeMcpName(raw));\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t\tif (!state.mcpServers.includes(name)) state.mcpServers.push(name);\n\t}\n\tstate.mcpServers.sort((a, b) => b.length - a.length);\n}\n","// I/O shell around the pure opencode analyzer: find `opencode*.db`, open it\n// read-only with node:sqlite, project NAMED COLUMNS through json_extract, and\n// hand plain values to the fold. Nothing leaves this machine.\n//\n// Wayfinder ticket #124 (map #121), semantics from\n// docs/research/harness-adapters-2026-08.md (§opencode).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths\n// and repo names never leave the machine. The SAME FILE this module opens\n// also holds `account.refresh_token`, `credential.value`,\n// `session_input.prompt` and full file contents in `session.summary_diffs`\n// and `part.data.state.output`. The rule that keeps them out: never\n// `SELECT *` - every query names its columns, and `part.data` reaches JS only\n// as four json_extract'ed scalars. Errors are swallowed, not thrown, because\n// a node:sqlite error message carries the DB path.\n\nimport { readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateDbFoldState,\n\ttype DbFoldState,\n\tingestMessageRow,\n\tingestToolPart,\n\tnoteConfiguredMcpServers,\n\tnoteSessions,\n} from \"./analyzer.js\";\n\n/**\n * Newest migration id this build understands, from the probe DB (research\n * §1). A DB migrated past it may hold the same table names with different\n * semantics, so it counts as UNREADABLE - a visible coverage figure - rather\n * than being read on the guess that nothing moved.\n */\nexport const OPENCODE_MIGRATION_CEILING = 20260622202450;\n\n/** `$XDG_DATA_HOME/opencode` or `~/.local/share/opencode` - opencode's own rule. */\nexport function opencodeDataDirs(): string[] {\n\tconst xdg = process.env.XDG_DATA_HOME;\n\tconst base = xdg || path.join(homedir(), \".local\", \"share\");\n\treturn [path.join(base, \"opencode\")];\n}\n\n/**\n * The store is `opencode.db` on release channels, `opencode-<channel>.db`\n * otherwise, and `$OPENCODE_DB` overrides both. WAL siblings (`-wal`,\n * `-shm`) are opened by SQLite itself, never listed as stores.\n */\nfunction isStoreFile(basename: string): boolean {\n\treturn basename === \"opencode.db\" || /^opencode-[^/]+\\.db$/.test(basename);\n}\n\nasync function dbFilesIn(root: string): Promise<string[]> {\n\tconst override = process.env.OPENCODE_DB;\n\tif (override) return [override];\n\ttry {\n\t\tconst entries = await readdir(root, { withFileTypes: true });\n\t\treturn entries\n\t\t\t.filter((e) => e.isFile() && isStoreFile(e.name))\n\t\t\t.map((e) => path.join(root, e.name))\n\t\t\t.sort();\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// node:sqlite\n// ---------------------------------------------------------------------------\n\ntype SqliteDb = {\n\tprepare(sql: string): {\n\t\tall(...params: unknown[]): Record<string, unknown>[];\n\t\tget(...params: unknown[]): Record<string, unknown> | undefined;\n\t};\n\tclose(): void;\n};\n\n/**\n * `node:sqlite` landed in Node 22.5, which is also this CLI's runtime floor.\n * Keep the import guarded so a damaged or nonstandard runtime reports the DB\n * as unreadable instead of crashing sync.\n */\nasync function loadSqlite(): Promise<((file: string) => SqliteDb) | null> {\n\ttry {\n\t\tconst mod = (await import(\"node:sqlite\")) as {\n\t\t\tDatabaseSync: new (file: string, opts: { readOnly: boolean }) => SqliteDb;\n\t\t};\n\t\tif (typeof mod.DatabaseSync !== \"function\") return null;\n\t\treturn (file) => new mod.DatabaseSync(file, { readOnly: true });\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * A read failure classified WITHOUT the error object's message or stack -\n * both can carry the absolute DB path, which never leaves this module.\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n// ---------------------------------------------------------------------------\n// Scan\n// ---------------------------------------------------------------------------\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered data dirs. Tests only. */\n\troots?: string[];\n\t/** Override the opencode.json path. Tests only. */\n\tconfigFile?: string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst open = await loadSqlite();\n\tconst sinceMs = opts.sinceMs ?? 0;\n\n\tconst state = createDbFoldState();\n\treadConfiguredMcpServers(agg, state, opts.configFile);\n\n\tfor (const root of opts.roots ?? opencodeDataDirs()) {\n\t\tfor (const file of await dbFilesIn(root)) {\n\t\t\tstats.filesFound++;\n\t\t\tagg.files++;\n\t\t\ttry {\n\t\t\t\tif (open === null) throw readError(\"sqlite-unsupported\");\n\t\t\t\treadDb(agg, state, open, file, sinceMs);\n\t\t\t\tstats.filesRead++;\n\t\t\t} catch (e) {\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.basename(file),\n\t\t\t\t\treason: errorClass(e),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (opts.onProgress) opts.onProgress(stats.filesFound);\n\t\t}\n\t}\n\treturn stats;\n}\n\n/** Refuse a DB whose newest migration this build has never seen. */\nfunction checkMigrationCeiling(db: SqliteDb): void {\n\tlet newest: unknown;\n\ttry {\n\t\tnewest = db.prepare(\"select max(id) as id from migration\").get()?.id;\n\t} catch {\n\t\tthrow readError(\"schema-unversioned\");\n\t}\n\tconst prefix = Number.parseInt(String(newest ?? \"\"), 10);\n\tif (!Number.isFinite(prefix)) throw readError(\"schema-unversioned\");\n\tif (prefix > OPENCODE_MIGRATION_CEILING) throw readError(\"schema-too-new\");\n}\n\nfunction readDb(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\topen: (file: string) => SqliteDb,\n\tfile: string,\n\tsinceMs: number,\n): void {\n\tconst db = open(file);\n\ttry {\n\t\tcheckMigrationCeiling(db);\n\n\t\tnoteSessions(\n\t\t\tstate,\n\t\t\tdb\n\t\t\t\t.prepare(\"select id, parent_id, version from session\")\n\t\t\t\t.all()\n\t\t\t\t.map((r) => ({ id: r.id, parentId: r.parent_id, version: r.version })),\n\t\t);\n\n\t\t// v1 messages. `time.created` (epoch ms, in the blob) prices the\n\t\t// response; the indexed integer column runs the window filter and backs\n\t\t// a blob whose clock field is missing or malformed.\n\t\tconst v1 = db.prepare(\n\t\t\t`select id, session_id, time_created,\n\t\t\t\tjson_extract(data, '$.role') as role,\n\t\t\t\tjson_extract(data, '$.providerID') as provider_id,\n\t\t\t\tjson_extract(data, '$.modelID') as model_id,\n\t\t\t\tjson_extract(data, '$.time.created') as ts_ms,\n\t\t\t\tjson_extract(data, '$.tokens.input') as tok_input,\n\t\t\t\tjson_extract(data, '$.tokens.output') as tok_output,\n\t\t\t\tjson_extract(data, '$.tokens.reasoning') as tok_reasoning,\n\t\t\t\tjson_extract(data, '$.tokens.cache.read') as tok_cache_read,\n\t\t\t\tjson_extract(data, '$.tokens.cache.write') as tok_cache_write,\n\t\t\t\tjson_extract(data, '$.time.completed') as completed_ts_ms,\n\t\t\t\tjson_extract(data, '$.path.cwd') as cwd\n\t\t\tfrom message where time_created >= ?`,\n\t\t);\n\t\tfor (const r of v1.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestMessageRow(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\trole: r.role,\n\t\t\t\tproviderId: r.provider_id,\n\t\t\t\tmodelId: r.model_id,\n\t\t\t\ttsMs: pickTs(r.ts_ms, r.time_created),\n\t\t\t\tinput: r.tok_input,\n\t\t\t\toutput: r.tok_output,\n\t\t\t\tcacheRead: r.tok_cache_read,\n\t\t\t\tcacheWrite: r.tok_cache_write,\n\t\t\t\tcwd: r.cwd,\n\t\t\t\treasoning: r.tok_reasoning,\n\t\t\t\tcompletedTsMs: r.completed_ts_ms,\n\t\t\t});\n\t\t}\n\n\t\t// v2 (`session_message`) - the other live generation (research §1: which\n\t\t// one current opencode writes is unproven, so both are read and message\n\t\t// ids dedup across them). The assistant shape differs: `model: {id,\n\t\t// providerID}`, role in the `type` column.\n\t\tconst v2 = db.prepare(\n\t\t\t`select id, session_id, type, time_created,\n\t\t\t\tjson_extract(data, '$.model.providerID') as provider_id,\n\t\t\t\tjson_extract(data, '$.model.id') as model_id,\n\t\t\t\tjson_extract(data, '$.time.created') as ts_ms,\n\t\t\t\tjson_extract(data, '$.tokens.input') as tok_input,\n\t\t\t\tjson_extract(data, '$.tokens.output') as tok_output,\n\t\t\t\tjson_extract(data, '$.tokens.reasoning') as tok_reasoning,\n\t\t\t\tjson_extract(data, '$.tokens.cache.read') as tok_cache_read,\n\t\t\t\tjson_extract(data, '$.tokens.cache.write') as tok_cache_write,\n\t\t\t\tjson_extract(data, '$.time.completed') as completed_ts_ms,\n\t\t\t\tjson_extract(data, '$.path.cwd') as cwd\n\t\t\tfrom session_message where time_created >= ?`,\n\t\t);\n\t\tfor (const r of v2.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestMessageRow(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\trole: r.type,\n\t\t\t\tproviderId: r.provider_id,\n\t\t\t\tmodelId: r.model_id,\n\t\t\t\ttsMs: pickTs(r.ts_ms, r.time_created),\n\t\t\t\tinput: r.tok_input,\n\t\t\t\toutput: r.tok_output,\n\t\t\t\tcacheRead: r.tok_cache_read,\n\t\t\t\tcacheWrite: r.tok_cache_write,\n\t\t\t\tcwd: r.cwd,\n\t\t\t\treasoning: r.tok_reasoning,\n\t\t\t\tcompletedTsMs: r.completed_ts_ms,\n\t\t\t});\n\t\t}\n\n\t\t// v1 tool parts. Only named scalar paths reach JavaScript.\n\t\t// `$.state.output` holds full command output and stays in SQLite.\n\t\tconst parts = db.prepare(\n\t\t\t`select id, message_id, session_id, time_created,\n\t\t\t\tjson_extract(data, '$.type') as part_type,\n\t\t\t\tjson_extract(data, '$.tool') as tool,\n\t\t\t\tjson_extract(data, '$.callID') as call_id,\n\t\t\t\tjson_extract(data, '$.state.input.name') as input_name,\n\t\t\t\tjson_extract(data, '$.state.input.subagent_type') as subagent_type,\n\t\t\t\tjson_extract(data, '$.state.input.command') as command\n\t\t\tfrom part\n\t\t\twhere time_created >= ? and json_extract(data, '$.type') = 'tool'\n\t\t\torder by time_created, message_id, id`,\n\t\t);\n\t\tfor (const r of parts.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestToolPart(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tpartType: r.part_type,\n\t\t\t\ttool: r.tool,\n\t\t\t\tcallId: r.call_id,\n\t\t\t\tinputName: r.input_name,\n\t\t\t\tsubagentType: r.subagent_type,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\ttsMs: pickTs(null, r.time_created),\n\t\t\t\tcommand: r.command,\n\t\t\t\tmessageId: r.message_id,\n\t\t\t});\n\t\t}\n\n\t\t// v2 inline tool content, same named-scalar rule via json_each. The v2\n\t\t// content shape is unverified on any real machine, so a query error here\n\t\t// is tolerated - the tokens above are the load-bearing read.\n\t\ttry {\n\t\t\tconst v2parts = db.prepare(\n\t\t\t\t`select sm.id || ':' || je.key as id, sm.id as message_id,\n\t\t\t\t\tsm.session_id, sm.time_created,\n\t\t\t\t\tjson_extract(je.value, '$.type') as part_type,\n\t\t\t\t\tjson_extract(je.value, '$.tool') as tool,\n\t\t\t\t\tjson_extract(je.value, '$.callID') as call_id,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.name') as input_name,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.subagent_type') as subagent_type,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.command') as command\n\t\t\t\tfrom session_message sm, json_each(sm.data, '$.content') je\n\t\t\t\twhere sm.time_created >= ? and sm.type = 'assistant'\n\t\t\t\torder by sm.time_created, sm.seq, sm.id, cast(je.key as integer)`,\n\t\t\t);\n\t\t\tfor (const r of v2parts.all(sinceMs)) {\n\t\t\t\tingestToolPart(agg, state, {\n\t\t\t\t\tid: r.id,\n\t\t\t\t\tpartType: r.part_type,\n\t\t\t\t\ttool: r.tool,\n\t\t\t\t\tcallId: r.call_id,\n\t\t\t\t\tinputName: r.input_name,\n\t\t\t\t\tsubagentType: r.subagent_type,\n\t\t\t\t\tsessionId: r.session_id,\n\t\t\t\t\ttsMs: pickTs(null, r.time_created),\n\t\t\t\t\tcommand: r.command,\n\t\t\t\t\tmessageId: r.message_id,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t/* v2 content unreadable - the message tokens already counted */\n\t\t}\n\t} finally {\n\t\ttry {\n\t\t\tdb.close();\n\t\t} catch {\n\t\t\t/* already closed or never opened fully */\n\t\t}\n\t}\n}\n\n/** The blob's own clock when it is a finite number, else the indexed column. */\nfunction pickTs(jsonTs: unknown, columnTs: unknown): number | null {\n\tif (typeof jsonTs === \"number\" && Number.isFinite(jsonTs)) return jsonTs;\n\tif (typeof columnTs === \"number\" && Number.isFinite(columnTs))\n\t\treturn columnTs;\n\t// node:sqlite may hand integers back as bigint depending on flags.\n\tif (typeof columnTs === \"bigint\") return Number(columnTs);\n\tif (typeof jsonTs === \"bigint\") return Number(jsonTs);\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Config - the static MCP inventory\n// ---------------------------------------------------------------------------\n\n/** `$XDG_CONFIG_HOME/opencode/opencode.json` or `~/.config/opencode/opencode.json`. */\nexport function opencodeConfigFile(): string {\n\tconst xdg = process.env.XDG_CONFIG_HOME;\n\tconst base = xdg || path.join(homedir(), \".config\");\n\treturn path.join(base, \"opencode\", \"opencode.json\");\n}\n\n/**\n * The real config is JSONC - comments and trailing commas (research\n * §inventory) - so `JSON.parse` alone throws on it. The strip below is\n * string-aware: a `//` inside a quoted URL survives. Any remaining parse\n * failure is silence, not an error: the observed half of the MCP inventory\n * stands on its own.\n */\nfunction readConfiguredMcpServers(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\tconfigFile?: string,\n): void {\n\tconst file = configFile ?? opencodeConfigFile();\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(stripJsonc(readFileSync(file, \"utf8\")));\n\t\tconst mcp = (parsed as { mcp?: unknown } | null)?.mcp;\n\t\tif (mcp && typeof mcp === \"object\" && !Array.isArray(mcp)) {\n\t\t\tnoteConfiguredMcpServers(agg, state, Object.keys(mcp));\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n}\n\nexport function stripJsonc(text: string): string {\n\tlet out = \"\";\n\tlet i = 0;\n\tlet inString = false;\n\twhile (i < text.length) {\n\t\tconst ch = text[i];\n\t\tconst next = text[i + 1];\n\t\tif (inString) {\n\t\t\tout += ch;\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tout += next ?? \"\";\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === '\"') inString = false;\n\t\t\ti++;\n\t\t} else if (ch === '\"') {\n\t\t\tinString = true;\n\t\t\tout += ch;\n\t\t\ti++;\n\t\t} else if (ch === \"/\" && next === \"/\") {\n\t\t\twhile (i < text.length && text[i] !== \"\\n\") i++;\n\t\t} else if (ch === \"/\" && next === \"*\") {\n\t\t\ti += 2;\n\t\t\twhile (i < text.length && !(text[i] === \"*\" && text[i + 1] === \"/\")) i++;\n\t\t\ti += 2;\n\t\t} else {\n\t\t\tout += ch;\n\t\t\ti++;\n\t\t}\n\t}\n\t// Trailing commas: `, }` and `, ]` with any whitespace between.\n\treturn out.replace(/,(\\s*[}\\]])/g, \"$1\");\n}\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detection is a QUERY, not a stat walk (#101, research §6): every opencode\n * start - including `opencode --version` - touches the DB file, and the probe\n * machine showed a four-month gap between the file's mtime and the newest\n * real message. The indexed probe costs 0.02 ms.\n */\nexport async function detectOpencode(opts: {\n\tsinceMs: number;\n\troots?: string[];\n}): Promise<boolean> {\n\tconst open = await loadSqlite();\n\tif (open === null) return false;\n\n\tfor (const root of opts.roots ?? opencodeDataDirs()) {\n\t\tfor (const file of await dbFilesIn(root)) {\n\t\t\tif (!(await exists(file))) continue;\n\t\t\tlet db: SqliteDb | null = null;\n\t\t\ttry {\n\t\t\t\tdb = open(file);\n\t\t\t\tcheckMigrationCeiling(db);\n\t\t\t\tconst probe = (table: string) =>\n\t\t\t\t\tdb\n\t\t\t\t\t\t?.prepare(\n\t\t\t\t\t\t\t`select 1 as hit from ${table} where time_created >= ? limit 1`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get(opts.sinceMs) !== undefined;\n\t\t\t\tif (probe(\"message\") || probe(\"session_message\")) return true;\n\t\t\t} catch {\n\t\t\t\t/* unreadable or foreign DB - not detection */\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tdb?.close();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","// The opencode harness behind the seam (#66 decision 6) - wayfinder ticket\n// #124 (map #121).\n\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate, OPENCODE_BUILTIN_TOOLS } from \"./analyzer.js\";\nimport { detectOpencode, scan } from \"./scan.js\";\n\nexport const OPENCODE_HARNESS_NAME = \"opencode\";\n\nexport { OPENCODE_BUILTIN_TOOLS } from \"./analyzer.js\";\n\nexport const opencodeAdapter: HarnessAdapter = {\n\tname: OPENCODE_HARNESS_NAME,\n\tbuiltinTools: OPENCODE_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn detectOpencode({\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.roots ? { roots: opts.roots } : {}),\n\t\t});\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","// Pure fold over parsed pi session-file entries. No I/O, no console.\n//\n// Wayfinder ticket #126 (map #121). Field semantics come from\n// docs/research/harness-adapters-2026-08.md (§pi-mono), read off the vendor\n// doc set shipped in /opt/pi-coding-agent/docs and verified against the real\n// files in ~/.pi/agent/sessions. Every field is untrusted and optional: entries\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING FACTS (research §2-§3):\n// - `usage.input` already EXCLUDES cache traffic - no subtraction (Codex,\n// inverted); `reasoning` is a subset of `output` - never add it;\n// - `cacheWrite1h` is a SUBSET of `cacheWrite`, so the TTL split maps onto\n// TokenCounts exactly: pi is the only harness that hands the re-pricer\n// the split instead of a lower bound;\n// - /fork and /clone copy entries into a second file KEEPING entry ids, so\n// usage dedup is cross-file, and the 8-hex id alone collides at corpus\n// scale - the key is `${id}:${timestamp}:${totalTokens}`;\n// - `compaction.retainedTail` embeds assistant messages that already appear\n// as their own entries earlier in the same file - never descend into it;\n// - pi's own `usage.cost` is computed against an unpinned network-refreshed\n// table - uncitable, so cost comes from @aistack/pricing only.\n//\n// Pricing keys follow #123's binding rule: every row is keyed\n// `modelKeyFor(provider, model)` with pi's own provider id verbatim. Only\n// `anthropic`/`openai`/`google` reach vendor rates; a router-billed response\n// (`openrouter:anthropic/claude-opus-4.6`) stays unpriced, which is the safe\n// direction - it did not pay vendor list price.\n\nimport {\n\tapiEquivalentCost,\n\tmodelKeyFor,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/** Cross-file dedup lives in FoldState, not the aggregate's `seen`. */\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"pi-mono\", workflowLocal),\n\t\tworkflowLocal,\n\t});\n}\n\n/**\n * Scan-level fold state, shared across every file in one scan: /fork and\n * /clone duplicate entries into a second file keeping their ids, so the dedup\n * set cannot be per-file.\n */\nexport type FoldState = {\n\tseenUsage: Set<string>;\n};\n\nexport function createFoldState(): FoldState {\n\treturn { seenUsage: new Set() };\n}\n\n/** Per-file fold state. */\nexport type FileState = {\n\tsessionId: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding assistant message or model_change. */\n\tmodelKey: string | null;\n\t/** True once any in-window entry was counted for this file. */\n\tcounted: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn {\n\t\tsessionId: null,\n\t\tcwd: null,\n\t\tmodelKey: null,\n\t\tcounted: false,\n\t};\n}\n\n/**\n * Fold one parsed session-file entry into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context entries\n * (the header, `model_change`) must update `state` even when they predate the\n * window - a session resumed today bills today's usage to a model named last\n * week. The window filter reads the entry's ISO timestamp first and falls back\n * to the message's Unix-ms timestamp, the same order pricing uses.\n */\nexport function ingestEntry(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tfold: FoldState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tconst type = asStr(rec.type);\n\tconst message = type === \"message\" ? asObj(rec.message) : null;\n\tconst role = message ? asStr(message.role) : null;\n\n\t// Context updates happen regardless of the window.\n\tif (type === \"session\") {\n\t\tstate.sessionId = asStr(rec.id) ?? state.sessionId;\n\t\tstate.cwd = asStr(rec.cwd) ?? state.cwd;\n\t\treturn;\n\t}\n\tif (type === \"model_change\") {\n\t\tconst provider = asStr(rec.provider);\n\t\tconst model = asStr(rec.modelId);\n\t\tif (provider && model) state.modelKey = toPricingKey(provider, model);\n\t}\n\tif (role === \"assistant\" && message) {\n\t\tconst provider = asStr(message.provider);\n\t\tconst model = asStr(message.model);\n\t\tif (provider && model) state.modelKey = toPricingKey(provider, model);\n\t}\n\n\t// Entry-level ISO timestamp first, message-level Unix ms as the fallback.\n\tconst entryTs = Date.parse(asStr(rec.timestamp) ?? \"\");\n\tconst msgTs = message ? asNum(message.timestamp) : 0;\n\tconst tsMs = !Number.isNaN(entryTs) ? entryTs : msgTs > 0 ? msgTs : null;\n\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\tif (!inWindow) return;\n\n\tif (tsMs !== null) {\n\t\tagg.activeDays.add(new Date(tsMs).toISOString().slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state, tsMs);\n\tnoteProjectDay(agg, state.cwd ?? \"(unknown)\", tsMs);\n\n\tif (role === \"assistant\" && message) {\n\t\tagg.assistantRecords++;\n\t\t// `model` is what pi asked for, `responseModel` what the API says it\n\t\t// served. Routers make them differ, and then the rate for `model`\n\t\t// cannot be cited - the tokens surface as unpriced instead.\n\t\tconst served = asStr(message.responseModel);\n\t\tconst priceable = served === null || served === asStr(message.model);\n\t\tconst outcome = countUsage(\n\t\t\tagg,\n\t\t\tfold,\n\t\t\trec,\n\t\t\tmsgTs,\n\t\t\tmessage.usage,\n\t\t\tstate.modelKey,\n\t\t\ttsMs,\n\t\t\tpriceable,\n\t\t);\n\t\t// A /fork duplicate repeats the content blocks too; the call-id dedup\n\t\t// already covers tool calls, but the thinking/text tallies have no ids.\n\t\tif (outcome !== \"duplicate\") {\n\t\t\tif (state.sessionId && tsMs !== null) {\n\t\t\t\tconst usage = asObj(message.usage);\n\t\t\t\tconst counts = readCounts(message.usage);\n\t\t\t\tagg.workflow.ingest({\n\t\t\t\t\ttype: \"response\",\n\t\t\t\t\tsession: state.sessionId,\n\t\t\t\t\t...(asStr(rec.id) ? { responseId: asStr(rec.id) as string } : {}),\n\t\t\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\t\t\ttsMs,\n\t\t\t\t\t...(state.modelKey ? { model: state.modelKey } : {}),\n\t\t\t\t\tthinkingTokens: usage ? asNum(usage.reasoning) : 0,\n\t\t\t\t\tresponseTokens: counts?.output ?? 0,\n\t\t\t\t\troutingTokens: counts ? countsTotal(counts) : 0,\n\t\t\t\t});\n\t\t\t\tingestContent(agg, message.content, state.sessionId, state.cwd, tsMs);\n\t\t\t\tagg.workflow.ingest({\n\t\t\t\t\ttype: \"turn\",\n\t\t\t\t\tsession: state.sessionId,\n\t\t\t\t\t...(asStr(rec.id) ? { turnId: asStr(rec.id) as string } : {}),\n\t\t\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\t\t\ttsMs,\n\t\t\t\t\tquestionBack: false,\n\t\t\t\t});\n\t\t\t} else ingestContent(agg, message.content);\n\t\t}\n\t} else if (role === \"toolResult\" && message) {\n\t\t// \"Nested LLM work performed by the tool\" - real spend, counted by\n\t\t// pi's own footer. No model of its own, so it bills to the model in\n\t\t// effect, the way Codex deltas bill to the nearest turn_context.\n\t\tcountUsage(agg, fold, rec, msgTs, message.usage, state.modelKey, tsMs);\n\t} else if (type === \"compaction\" || type === \"branch_summary\") {\n\t\t// Summary generation is real spend (optional `usage` on the entry). The\n\t\t// materialized `retainedTail` embeds assistant messages that already\n\t\t// appear as their own entries - deliberately never walked.\n\t\tcountUsage(agg, fold, rec, 0, rec.usage, state.modelKey, tsMs);\n\t}\n}\n\n/** Count the file's session and cwd once, on its first in-window entry. */\nfunction noteActivity(\n\tagg: Aggregate,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (state.sessionId) noteSessionStart(agg, state.sessionId, tsMs);\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\t// Counted, never published - same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n/** Fold one usage block into the totals, behind the cross-file dedup. */\nfunction countUsage(\n\tagg: Aggregate,\n\tfold: FoldState,\n\trec: Record<string, unknown>,\n\tmsgTsMs: number,\n\tusageRaw: unknown,\n\tmodelKey: string | null,\n\ttsMs: number | null,\n\tpriceable = true,\n): \"counted\" | \"duplicate\" | \"none\" {\n\tconst counts = readCounts(usageRaw);\n\tif (!counts) return \"none\";\n\tconst total = countsTotal(counts);\n\tif (total === 0) return \"none\";\n\n\t// /fork and /clone write the same entry into a second file with its id\n\t// intact, so dedup is cross-file. The 8-hex id alone has a real birthday\n\t// collision at corpus scale, so the timestamps and the token total ride\n\t// along - two genuinely different responses sharing an id stay two.\n\tconst id = asStr(rec.id);\n\tif (id) {\n\t\tconst key = `${id}:${asStr(rec.timestamp) ?? \"\"}:${msgTsMs}:${total}`;\n\t\tif (fold.seenUsage.has(key)) {\n\t\t\tagg.continuationsFolded++;\n\t\t\treturn \"duplicate\";\n\t\t}\n\t\tfold.seenUsage.add(key);\n\t} else {\n\t\tagg.unkeyedResponses++;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\tconst key = modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tkey,\n\t\tcounts,\n\t\tpriceable ? apiEquivalentCost(key, counts, tsMs) : null,\n\t\t1,\n\t\t{ tsMs },\n\t);\n\t// pi has no subagents by vendor design - everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 (same case as Codex).\n\tagg.mainTokens += total;\n\treturn \"counted\";\n}\n\n/**\n * Compose the pricing key from pi's own provider and model ids (#123's\n * binding rule). pi spells fast mode as an id SUFFIX (`claude-opus-5-fast`)\n * and has no `usage.speed` field, so the suffix is translated into the price\n * table's `#fast` marker here. A model genuinely named `-fast` by a provider\n * the table does not cover stays unpriced either way, so the translation\n * cannot invent a rate.\n */\nfunction toPricingKey(provider: string, model: string): string {\n\tconst fast = model.endsWith(\"-fast\");\n\tconst marked = fast ? `${model.slice(0, -\"-fast\".length)}#fast` : model;\n\treturn normalizeModel(modelKeyFor(provider, marked));\n}\n\n/**\n * Assistant content blocks: tool calls plus the thinking/text tallies.\n *\n * `bashExecution` entries are deliberately NOT counted as tool calls - they\n * are user-typed `!` commands, not something the model chose. A name outside\n * PI_BUILTIN_TOOLS is a user extension's tool; it stays a plain count and the\n * shared fail-closed payload filter withholds the name. pi has no MCP, no\n * subagents and no skill tool by vendor design, so those maps stay EMPTY -\n * absent from the payload, never zero (#40).\n */\nfunction ingestContent(\n\tagg: Aggregate,\n\tcontentRaw: unknown,\n\tsession?: string,\n\tprojectWorkspace?: string | null,\n\ttsMs?: number,\n): void {\n\tfor (const blockRaw of asArr(contentRaw)) {\n\t\tconst block = asObj(blockRaw);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") {\n\t\t\tagg.thinkingBlocks++;\n\t\t} else if (type === \"text\") {\n\t\t\tagg.textBlocks++;\n\t\t} else if (type === \"toolCall\") {\n\t\t\tconst name = asName(block.name);\n\t\t\tif (!name) continue;\n\t\t\t// /fork duplicates keep call ids, so the shared dedup set makes the\n\t\t\t// copy a repeat rather than a double count.\n\t\t\tconst callId = asStr(block.id);\n\t\t\tif (callId) {\n\t\t\t\tif (agg.toolCallDedup.has(callId)) continue;\n\t\t\t\tagg.toolCallDedup.add(callId);\n\t\t\t} else {\n\t\t\t\tagg.toolBlocksWithoutId++;\n\t\t\t}\n\t\t\tbump(agg.toolCalls, name);\n\t\t\tif (session && tsMs !== undefined) {\n\t\t\t\tconst args = asObj(block.arguments) ?? asObj(block.args) ?? {};\n\t\t\t\tagg.workflow.ingest({\n\t\t\t\t\ttype: \"event\",\n\t\t\t\t\tsession,\n\t\t\t\t\tprojectWorkspace: projectWorkspace ?? undefined,\n\t\t\t\t\ttsMs,\n\t\t\t\t\ttool: name,\n\t\t\t\t\targ: asStr(args.command) ?? \"\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The Usage shape shared by assistant/toolResult messages and summary entries. */\nfunction readCounts(usageRaw: unknown): TokenCounts | null {\n\tconst u = asObj(usageRaw);\n\tif (!u) return null;\n\tconst cacheWrite = asNum(u.cacheWrite);\n\tconst split =\n\t\ttypeof u.cacheWrite1h === \"number\" && Number.isFinite(u.cacheWrite1h);\n\tconst cacheWrite1h = split\n\t\t? Math.min(Math.max(u.cacheWrite1h as number, 0), cacheWrite)\n\t\t: 0;\n\treturn {\n\t\t// Already exclusive of cache traffic - no subtraction (research §2).\n\t\tinput: asNum(u.input),\n\t\t// `reasoning` is a subset of `output` - never added.\n\t\toutput: asNum(u.output),\n\t\tcacheWrite5m: split ? cacheWrite - cacheWrite1h : 0,\n\t\tcacheWrite1h,\n\t\tcacheWriteUnsplit: split ? 0 : cacheWrite,\n\t\tcacheRead: asNum(u.cacheRead),\n\t};\n}\n","// I/O shell around the pure pi analyzer: find session files, stream JSONL,\n// hand each parsed entry to ingestEntry. Nothing leaves this machine.\n//\n// Wayfinder ticket #126 (map #121), semantics from\n// docs/research/harness-adapters-2026-08.md (§pi-mono).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. pi's files are MORE sensitive than\n// the other harnesses': there is no separate history file, so raw prompts,\n// bashExecution output, base64 screenshots, provider error bodies and the\n// munged-absolute-path directory names all sit on the same lines the scanner\n// parses. The analyzer reads named fields only; the directory name is never\n// even counted (the header's `cwd` is, as an opaque key). Streaming line by\n// line keeps a pasted screenshot from pulling megabytes into memory.\n// `~/.pi/agent/auth.json` holds credentials - only `sessions/` is ever walked.\n// Read errors are swallowed, not thrown: the error object carries the path.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport { asNum, asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tcreateFoldState,\n\ttype FoldState,\n\tingestEntry,\n} from \"./analyzer.js\";\n\n/**\n * The newest session-format version this scanner understands. The vendor doc\n * states the ladder (v1 linear, v2 tree, v3 renamed hookMessage to custom);\n * none of the changes touched `usage`, `model` or `timestamp`, so v1-v3 all\n * read with one fold. A file ABOVE the ceiling may have reshaped those fields,\n * so it counts as unreadable - a visible coverage figure - rather than being\n * misread as zeros.\n */\nexport const MAX_SESSION_VERSION = 3;\n\n/** `PI_CODING_AGENT_DIR` honored, `~/.pi/agent` the default - mirrors pi. */\nexport function piAgentDir(): string {\n\treturn (\n\t\tprocess.env.PI_CODING_AGENT_DIR || path.join(homedir(), \".pi\", \"agent\")\n\t);\n}\n\n/**\n * Only `sessions/` is read - `auth.json` (credentials), `settings.json` and\n * the ACP session map live beside it and are out of bounds. A run started\n * with `--session-dir` writes outside every discoverable root and is\n * invisible, which is silence - the direction #40 permits.\n */\nexport function sessionRoots(): string[] {\n\tconst override = process.env.PI_CODING_AGENT_SESSION_DIR;\n\treturn [override || path.join(piAgentDir(), \"sessions\")];\n}\n\n/**\n * pi names every session file `<munged-ISO-start>_<uuid>.jsonl`\n * (`2026-07-23T16-54-00-149Z_019f8fe5-….jsonl`). Shared with `detect` (#101).\n */\nconst SESSION_FILE_RE =\n\t/^\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}-\\d{3}Z_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.jsonl$/;\n\n/** What counts as a pi session file. */\nexport function isSessionFile(basename: string): boolean {\n\treturn SESSION_FILE_RE.test(basename);\n}\n\n/** Recursive walk - the real layout is two levels (one directory per cwd). */\nasync function* walkSessions(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkSessions(full);\n\t\telse if (e.isFile() && isSessionFile(e.name)) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only count entries with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\t// /fork and /clone duplicate entries ACROSS files, so the dedup state is\n\t// one per scan, not one per file.\n\tconst fold = createFoldState();\n\n\tfor (const root of opts.roots ?? sessionRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkSessions(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Session files are append-only, so a file untouched since the window\n\t\t\t// opened cannot hold an in-window entry.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat - fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tconst verdict = await ingestFile(agg, fold, file, opts.sinceMs);\n\t\t\t\tif (verdict === \"foreign\") {\n\t\t\t\t\t// The first line is not a pi session header: another tool wrote\n\t\t\t\t\t// this file. Its usage stayed out of the aggregate entirely.\n\t\t\t\t\tstats.filesForeign++;\n\t\t\t\t\tstats.filesRead--;\n\t\t\t\t\tconst seen = stats.foreignOriginators.get(\"(no-pi-header)\") ?? 0;\n\t\t\t\t\tstats.foreignOriginators.set(\"(no-pi-header)\", seen + 1);\n\t\t\t\t} else if (verdict === \"version-too-new\") {\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tstats.filesRead--;\n\t\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\t\treason: \"version-too-new\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: \"read-error\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Stream one session file through the fold. The vendor put the fingerprint on\n * line 1 - `{\"type\":\"session\"}` with a numeric `version` - so the verdict\n * lands before any usage is folded, and a foreign or too-new file leaves the\n * aggregate untouched by construction (no parse-then-fold buffering needed).\n */\nasync function ingestFile(\n\tagg: Aggregate,\n\tfold: FoldState,\n\tfile: string,\n\tsinceMs?: number,\n): Promise<\"ok\" | \"foreign\" | \"version-too-new\"> {\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tconst state = createFileState();\n\tlet first = true;\n\ttry {\n\t\tfor await (const line of rl) {\n\t\t\tif (!line) continue;\n\t\t\tagg.lines++;\n\t\t\tlet entry: unknown;\n\t\t\ttry {\n\t\t\t\tentry = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\tagg.parseErrors++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t\tconst header = asObj(entry);\n\t\t\t\tconst version = header ? asNum(header.version) : 0;\n\t\t\t\tif (!header || asStr(header.type) !== \"session\" || version < 1) {\n\t\t\t\t\tagg.lines--;\n\t\t\t\t\treturn \"foreign\";\n\t\t\t\t}\n\t\t\t\tif (version > MAX_SESSION_VERSION) {\n\t\t\t\t\tagg.lines--;\n\t\t\t\t\treturn \"version-too-new\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tingestEntry(agg, entry, state, fold, sinceMs);\n\t\t}\n\t} finally {\n\t\trl.close();\n\t}\n\treturn \"ok\";\n}\n","// The pi coding agent behind the harness seam (#66 decision 6) - wayfinder\n// ticket #126 (map #121). The payload discriminator is the catalog slug,\n// `pi-mono` (the repo is earendil-works/pi-mono; the binary is `pi`).\n\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isSessionFile, scan, sessionRoots } from \"./scan.js\";\n\nexport const PI_HARNESS_NAME = \"pi-mono\";\n\n/**\n * pi's vendor-assigned tool surface - seven names, published in the vendor's\n * own docs (usage.md §tools). Same fail-closed mechanism as the other\n * adapters: a literal set, never a pattern. Everything outside it comes from\n * a user extension and publishes only as a per-category count. pi has no MCP,\n * no subagents and no skill tool by explicit vendor design, so those\n * categories stay absent rather than zero (#40).\n */\nexport const PI_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n]);\n\nexport const piAdapter: HarnessAdapter = {\n\tname: PI_HARNESS_NAME,\n\tbuiltinTools: PI_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? sessionRoots(),\n\t\t\tisSessionFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","// Local transcript analysis -> the measured-layer wire payload.\n//\n// Wayfinder ticket #37 (map #29), reshaped around the adapter seam by #67\n// (map #60). Everything here runs on the user's machine; only the payloads\n// returned by `buildPayload` are ever candidates to leave it, and only after\n// the approve gate the send channel owns (ticket #41).\n//\n// Typical use:\n//\n// const now = Date.now();\n// const sinceMs = windowStartMs(now, DEFAULT_WINDOW_DAYS);\n// const { config } = await loadSyncConfig({ baseUrl });\n// for (const adapter of await detectedAdapters(sinceMs)) {\n// const { aggregate, stats } = await adapter.scan({ sinceMs });\n// const built = buildPayload({\n// aggregate, stats, syncConfig: config, now,\n// windowDays: DEFAULT_WINDOW_DAYS,\n// harnessName: adapter.name,\n// builtinTools: adapter.builtinTools,\n// projectWorkspaceId: getProjectWorkspaceId,\n// });\n// }\n\nimport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nimport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nimport { GROK_HARNESS_NAME, grokAdapter } from \"./grok/adapter.js\";\nimport { OPENCODE_HARNESS_NAME, opencodeAdapter } from \"./opencode/adapter.js\";\nimport { PI_HARNESS_NAME, piAdapter } from \"./pi/adapter.js\";\nimport { DEFAULT_WINDOW_DAYS, windowStartMs } from \"./shared/window.js\";\nimport type { HarnessAdapter } from \"./types.js\";\n\nexport {\n\tapiEquivalentCost,\n\tbaseModelId,\n\tCACHE_READ_MULTIPLIER,\n\tCACHE_WRITE_1H_MULTIPLIER,\n\tCACHE_WRITE_5M_MULTIPLIER,\n\ttype CacheMultipliers,\n\tcacheMultipliersFor,\n\tGOOGLE_PRICING_TABLE_VERSION,\n\tisLocalModel,\n\tisPricedModel,\n\tLOCAL_PRICING_TABLE_VERSION,\n\tmodelKeyFor,\n\tnormalizeModel,\n\tOPENAI_PRICING_TABLE_VERSION,\n\tPRICING_TABLE_VERSION,\n\tPROVIDER_SEPARATOR,\n\ttype PricePeriod,\n\tpriceAt,\n\tSONNET_5_INTRO_ENDS_MS,\n\tsplitModelKey,\n\ttype TokenCounts,\n\tvendorModelId,\n} from \"@aistack/pricing\";\nexport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nexport {\n\ttype Aggregate as ClaudeAggregate,\n\tcreateAggregate,\n\ttype IngestContext,\n\tingestRecord,\n} from \"./claude/analyzer.js\";\nexport { type ScanOptions, scan, transcriptRoots } from \"./claude/scan.js\";\nexport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nexport { GROK_HARNESS_NAME, grokAdapter } from \"./grok/adapter.js\";\nexport {\n\tOPENCODE_BUILTIN_TOOLS,\n\tOPENCODE_HARNESS_NAME,\n\topencodeAdapter,\n} from \"./opencode/adapter.js\";\nexport {\n\tPI_BUILTIN_TOOLS,\n\tPI_HARNESS_NAME,\n\tpiAdapter,\n} from \"./pi/adapter.js\";\nexport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"./shared/aggregate.js\";\nexport {\n\ttype Atom,\n\ttype AutoSyncPermission,\n\tBUILTIN_TOOLS,\n\tBUNDLED_SYNC_CONFIG,\n\ttype CuratedAllowlist,\n\tEMPTY_OPT_INS,\n\ttype FilteredAtoms,\n\ttype FilterSets,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype OptInNames,\n\tpluginGroup,\n\ttype SyncConfig,\n\ttype SyncConfigSource,\n} from \"./shared/allowlist.js\";\nexport { BUNDLED_CURATED_ALLOWLIST } from \"./shared/bundled-allowlist.js\";\nexport {\n\ttype BuildPayloadInput,\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\ttype MeasuredPayload,\n\tmergeKeptPrivate,\n\ttype PayloadAtom,\n\ttype PayloadInventory,\n\ttype PayloadModel,\n\tSCHEMA_VERSION,\n\ttype SyncBody,\n\tsanitizeModelId,\n} from \"./shared/payload.js\";\nexport { hasRecentFile } from \"./shared/recency.js\";\nexport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"./shared/window.js\";\nexport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"./types.js\";\n\n/**\n * Every harness this build can read, in the order their payloads publish.\n * Registration order is also display order at the gate, so Claude Code - the\n * documented default - stays first.\n */\nexport const HARNESS_ADAPTERS: readonly HarnessAdapter[] = [\n\tclaudeAdapter,\n\tcodexAdapter,\n\tgrokAdapter,\n\topencodeAdapter,\n\tpiAdapter,\n];\n\n/** Display name for a harness discriminator. One name per harness, defined here. */\nexport function harnessLabel(name: string): string {\n\tif (name === CLAUDE_HARNESS_NAME) return \"Claude Code\";\n\tif (name === CODEX_HARNESS_NAME) return \"Codex\";\n\tif (name === GROK_HARNESS_NAME) return \"Grok Build\";\n\t// opencode spells itself lowercase, so its discriminator IS the label -\n\t// but it is an explicit row, so a rename cannot leak a raw slug.\n\tif (name === OPENCODE_HARNESS_NAME) return \"opencode\";\n\t// The vendor renamed pi-mono to Pi (2026-08). The discriminator stays\n\t// \"pi-mono\" - it is the wire id - only the display label changed.\n\tif (name === PI_HARNESS_NAME) return \"Pi\";\n\treturn name;\n}\n\n/** The detected harnesses as one readable phrase. */\nexport function harnessListLabel(adapters: readonly HarnessAdapter[]): string {\n\treturn adapters.map((a) => harnessLabel(a.name)).join(\" or \");\n}\n\n/**\n * The window every detection uses when the caller has no scan in hand. The\n * scan passes its own window start instead, which is the same number.\n */\nexport function detectionSinceMs(now: number = Date.now()): number {\n\treturn windowStartMs(now, DEFAULT_WINDOW_DAYS);\n}\n\n/**\n * The adapters that wrote a transcript inside the window - the machine's LIVE\n * harnesses (#101). A stale one is skipped everywhere this is read: it does not\n * scan, it does not publish, it earns no upsell and it gets no hook.\n */\nexport async function detectedAdapters(\n\tsinceMs: number = detectionSinceMs(),\n): Promise<HarnessAdapter[]> {\n\tconst out: HarnessAdapter[] = [];\n\tfor (const adapter of HARNESS_ADAPTERS) {\n\t\tif (await adapter.detect({ sinceMs })) out.push(adapter);\n\t}\n\treturn out;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { stackGet } from \"../api.js\";\nimport { getToken } from \"../config.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\nexport async function createCommand() {\n\tintro(\"create\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst s = p.spinner();\n\ts.start(\"Fetching stack...\");\n\n\tlet stack: Awaited<ReturnType<typeof stackGet>>;\n\ttry {\n\t\tstack = await stackGet(token);\n\t\tif (!stack) {\n\t\t\ts.stop(\"Not found\");\n\t\t\tp.log.error(\"No stack found. Create a stack on aistack.to first.\");\n\t\t\toutroError(\"not found\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ts.stop(bold(stack.name));\n\t} catch (err) {\n\t\ts.stop(\"Failed to fetch stack\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst localFiles: FileToWrite[] = [];\n\n\tfor (const item of stack.resources) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tlocalFiles.push({ path: file.path ?? file.name, content: file.content });\n\t\t}\n\t}\n\n\t// Linked resources have no files to write - surface them so they aren't\n\t// silently dropped on download (GitHub repos + package refs like MCP servers).\n\tconst linked = stack.resources.filter(\n\t\t(item) => (item.upstream || item.pkg) && !item.files?.length,\n\t);\n\tif (linked.length > 0) {\n\t\tsection(\"linked\", linked.length);\n\t\tlines([dim(\"view only\")]);\n\t\tlines(\n\t\t\tlinked.map((item) =>\n\t\t\t\tdim(\n\t\t\t\t\titem.upstream?.repoUrl ??\n\t\t\t\t\t\t(item.pkg ? `${item.pkg.registry}:${item.pkg.id}` : \"\"),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n\n\tif (localFiles.length === 0) {\n\t\tp.log.warn(\"No local files to write.\");\n\t\toutroSkipped(\"nothing to create\");\n\t\treturn;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst toWrite: FileToWrite[] = [];\n\tconst skipped: { path: string; differs: boolean }[] = [];\n\n\tfor (const f of localFiles) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconst existing = readFileSync(fullPath, \"utf-8\");\n\t\t\tskipped.push({ path: f.path, differs: existing !== f.content });\n\t\t} else {\n\t\t\ttoWrite.push(f);\n\t\t}\n\t}\n\n\tsection(\"local files\", localFiles.length);\n\tlines(toWrite.map((f) => lime(`+ ${f.path}`)));\n\tlines(\n\t\tskipped.map((f) =>\n\t\t\tf.differs\n\t\t\t\t? `${yellow(`= ${f.path}`)} ${dim(\"(differs)\")}`\n\t\t\t\t: dim(`= ${f.path} (identical)`),\n\t\t),\n\t);\n\n\tif (toWrite.length === 0) {\n\t\tdivider();\n\t\tp.log.info(\"All local files already exist.\");\n\t\toutroSkipped(\"nothing to write\");\n\t\treturn;\n\t}\n\n\tdivider();\n\n\tconst confirm = await p.confirm({\n\t\tmessage: `Write ${lime(String(toWrite.length))} new files? ${dim(`(${skipped.length} skipped)`)}`,\n\t});\n\n\tif (p.isCancel(confirm) || !confirm) {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tfor (const f of toWrite) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tconst dir = dirname(fullPath);\n\t\tmkdirSync(dir, { recursive: true });\n\t\twriteFileSync(fullPath, f.content);\n\t}\n\n\tp.log.success(\n\t\t`${lime(String(toWrite.length))} written, ${dim(String(skipped.length) + \" skipped\")}`,\n\t);\n\toutro(lime(\"done\"));\n}\n\ninterface FileToWrite {\n\tpath: string;\n\tcontent: string;\n}\n","import { hostname } from \"node:os\";\nimport * as p from \"@clack/prompts\";\nimport open from \"open\";\nimport { authPoll, authStart } from \"../api.js\";\nimport { saveToken } from \"../config.js\";\nimport { isDisplaySafeName } from \"../harness/shared/aggregate.js\";\nimport { dim, intro, lime, limeBold, outro, outroError } from \"../theme.js\";\n\n/**\n * What to call this machine on the account's linked-machines list (#49).\n *\n * The hostname is only a proposal - the approval page shows it in an editable\n * field before anything is stored. Trimmed to the server's 64-character bound so\n * a long hostname is dropped by us rather than silently by the server, and\n * `.local` is stripped because mDNS suffixes carry no information for a reader.\n */\nexport function proposedMachineName(\n\tread: () => string = hostname,\n): string | undefined {\n\ttry {\n\t\tconst name = read()\n\t\t\t.trim()\n\t\t\t.replace(/\\.local$/i, \"\");\n\t\tif (!name || name.length > 64) return undefined;\n\t\treturn name;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * The device-auth flow itself, without intro/outro framing or process.exit -\n * so `sync` can run it inline on an unlinked machine (#74) and `login` stays\n * the standalone command. Logs its own progress and errors; returns whether a\n * token was saved.\n */\ntype LoginOptions = {\n\tlabel?: string;\n\t/** Existing bearer replaced atomically when this approval completes. */\n\treplaceToken?: string;\n\t/** A sync cannot finish login until the browser chooses a stack. */\n\tdestinationRequired?: boolean;\n};\n\nexport function requestedMachineLabel(label: string): string {\n\tconst trimmed = label.trim();\n\tif (!isDisplaySafeName(trimmed)) {\n\t\tthrow new Error(\n\t\t\t\"Machine label must be 64 characters or fewer and contain only printable characters.\",\n\t\t);\n\t}\n\treturn trimmed;\n}\n\nexport async function performLogin(\n\toptions: LoginOptions = {},\n): Promise<boolean> {\n\tconst s = p.spinner();\n\ts.start(\"Starting authentication...\");\n\n\tlet session: Awaited<ReturnType<typeof authStart>>;\n\ttry {\n\t\tconst requestedLabel =\n\t\t\toptions.label === undefined\n\t\t\t\t? undefined\n\t\t\t\t: requestedMachineLabel(options.label);\n\t\tsession = await authStart(\n\t\t\trequestedLabel ?? proposedMachineName(),\n\t\t\trequestedLabel !== undefined,\n\t\t\t{\n\t\t\t\t...(options.replaceToken ? { replaceToken: options.replaceToken } : {}),\n\t\t\t\t...(options.destinationRequired ? { destinationRequired: true } : {}),\n\t\t\t},\n\t\t);\n\t\ts.stop(\"Session created\");\n\t} catch (err) {\n\t\ts.stop(\"Failed to start authentication\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\treturn false;\n\t}\n\n\tp.log.info(`${dim(\"CODE\")} ${limeBold(session.userCode)}`);\n\tp.log.info(`${dim(\"OPEN\")} ${dim(session.authUrl)}`);\n\n\ttry {\n\t\tawait open(session.authUrl);\n\t} catch {\n\t\tp.log.warn(\n\t\t\t\"Could not open browser automatically. Please visit the URL above.\",\n\t\t);\n\t}\n\n\ts.start(\"Waiting for approval...\");\n\n\tconst maxAttempts = 36;\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 5000));\n\n\t\ttry {\n\t\t\tconst result = await authPoll(session.secretId);\n\n\t\t\tif (result.status === \"approved\" && result.token) {\n\t\t\t\ts.stop(lime(\"Authenticated\"));\n\t\t\t\tsaveToken(result.token, result.userId);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (result.status === \"expired\") {\n\t\t\t\ts.stop(\"Session expired\");\n\t\t\t\tp.log.error(\"Authentication session expired. Please try again.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\ts.stop(\"Error polling\");\n\t\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\ts.stop(\"Timed out\");\n\tp.log.error(\"Authentication timed out after 3 minutes. Please try again.\");\n\treturn false;\n}\n\nexport async function loginCommand(options: LoginOptions = {}) {\n\tintro(\"login\");\n\n\tif (!(await performLogin(options))) {\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tp.log.success(\n\t\t`Token saved. Run ${limeBold(\"npx @use-aistack/cli sync\")} to publish your usage.`,\n\t);\n\toutro(lime(\"done\"));\n}\n","// The documented default sync surface (#56, built by #55/#57).\n//\n// The MCP-free channel: a human types `aistack sync` in their own terminal,\n// so a real TTY exists and the gate can be a @clack/prompts select. Same\n// staged-bytes property as the MCP server (#41): the summary and the confirm\n// derive from the exact serialized `bodyJson`, and that string goes on the\n// wire byte-identical. One gate policy, two renderings.\n//\n// Fail-closed: ctrl-C, ESC, EOF, and a missing TTY all resolve to \"nothing\n// was sent\" before any network call.\n\nimport * as p from \"@clack/prompts\";\nimport { BASE_URL, syncPublish } from \"../api.js\";\nimport {\n\tCODEX_TRUST_INSTRUCTION,\n\tcodexAutoSyncHookInstalled,\n\tcodexHookTrusted,\n} from \"../autosync/codexHook.js\";\nimport {\n\tdisableAutoSync,\n\tenableAutoSync,\n\tsettleAutoSync,\n} from \"../autosync/optin.js\";\nimport { runAutoSync } from \"../autosync/run.js\";\nimport { DEFAULT_FREQUENCY_HOURS, getSettings, getToken } from \"../config.js\";\nimport { loadSyncConfig } from \"../harness/shared/allowlist.js\";\nimport { stageSync } from \"../sync/stage.js\";\nimport { fmtReceivedAt } from \"../sync/summary.js\";\nimport {\n\tbold,\n\tdim,\n\tintro,\n\tlime,\n\toutro,\n\toutroCancel,\n\toutroError,\n\tyellow,\n} from \"../theme.js\";\nimport { offerConnectUpsell } from \"./connect.js\";\nimport { performLogin } from \"./login.js\";\n\nexport interface SyncOptions {\n\t/** `--auto` → true, `--auto on` → \"on\", `--auto off` → \"off\". */\n\tauto?: boolean | string;\n\t/** `--every <hours>`, applied with `--auto on`. */\n\tevery?: string;\n}\n\nexport async function syncCommand(options: SyncOptions = {}): Promise<void> {\n\t// The silent path (#62): no TTY, no prompts, no upsells. Publishes only\n\t// under the standing opt-in and always exits 0 - the hook command's `||`\n\t// offline fallback must never fire on a mere sync failure.\n\tif (options.auto === true) {\n\t\tawait runAutoSync({ baseUrl: BASE_URL });\n\t\treturn;\n\t}\n\n\tif (options.auto === \"on\" || options.auto === \"off\") {\n\t\tintro(\"sync\");\n\t\tconst result =\n\t\t\toptions.auto === \"on\"\n\t\t\t\t? await enableAutoSync(\n\t\t\t\t\t\toptions.every\n\t\t\t\t\t\t\t? Number.parseInt(options.every, 10) || DEFAULT_FREQUENCY_HOURS\n\t\t\t\t\t\t\t: DEFAULT_FREQUENCY_HOURS,\n\t\t\t\t\t)\n\t\t\t\t: await disableAutoSync();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t\toutro(\"done\");\n\t\t} else {\n\t\t\toutroError(result.message);\n\t\t\tprocess.exitCode = 1;\n\t\t}\n\t\treturn;\n\t}\n\tif (options.auto !== undefined) {\n\t\tintro(\"sync\");\n\t\toutroError(`unknown --auto value \"${options.auto}\" (use on or off)`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tintro(\"sync\");\n\n\t// The interactive surface is where a silent failure becomes visible (#62):\n\t// report the last auto-sync outcome, whatever it was.\n\tconst lastAuto = getSettings().autoSyncState?.lastResult;\n\tif (lastAuto !== undefined) {\n\t\tp.log.message(dim(`auto-sync: ${lastAuto}`));\n\t}\n\n\t// The Codex hook does not run until the user trusts it via /hooks (#65 §6).\n\t// Repeat the one-time instruction while the hook is installed but the trust\n\t// hash is verifiably absent; an unreadable config stays silent.\n\tif (codexAutoSyncHookInstalled() && codexHookTrusted() === false) {\n\t\tp.log.warn(CODEX_TRUST_INSTRUCTION);\n\t}\n\n\t// The whole premise of this channel is a human at a terminal. A pipe or a\n\t// model-launched Bash call has no TTY, and a gate that cannot ask must not\n\t// send (#31) - refuse before scanning anything.\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\n\t\toutroError(\"sync needs an interactive terminal. Nothing was sent.\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// An unlinked machine used to hard-block with \"run login first\" (#74). The\n\t// TTY gate above guarantees a human is present, so the device-auth browser\n\t// hop fits here - `sync` is the whole onboarding command.\n\tlet token = getToken();\n\tif (token === null) {\n\t\tp.log.message(\"This machine needs a destination stack. Linking it now.\");\n\t\tif (!(await performLogin({ destinationRequired: true }))) {\n\t\t\toutroError(\"login failed. Nothing was sent.\");\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\ttoken = getToken();\n\t}\n\tif (token === null) {\n\t\toutroError(\"login completed without a saved credential. Nothing was sent.\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Resolve the destination before walking local history. A valid credential\n\t// can outlive its stack choice, especially when login happened before the\n\t// first stack was created. Relinking rotates that credential and returns to\n\t// this same sync, so the user never has to discover a second command.\n\tlet loaded = await loadSyncConfig({ baseUrl: BASE_URL, token });\n\tif (loaded.source === \"bundled\") {\n\t\toutroError(\n\t\t\t\"Could not fetch your settings from aistack, so the destination stack is unknown. Check the network and sync again.\",\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tif (loaded.config.stack === null) {\n\t\tp.log.message(\n\t\t\t\"This machine is not linked to a destination stack. Opening aistack so you can choose one.\",\n\t\t);\n\t\tif (\n\t\t\t!(await performLogin({\n\t\t\t\tdestinationRequired: true,\n\t\t\t\treplaceToken: token,\n\t\t\t}))\n\t\t) {\n\t\t\toutroError(\"linking failed. Nothing was sent.\");\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\ttoken = getToken();\n\t\tif (token === null) {\n\t\t\toutroError(\n\t\t\t\t\"linking completed without a saved credential. Nothing was sent.\",\n\t\t\t);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tloaded = await loadSyncConfig({ baseUrl: BASE_URL, token });\n\t\tif (loaded.source === \"bundled\" || loaded.config.stack === null) {\n\t\t\toutroError(\n\t\t\t\t\"The destination stack could not be confirmed. Nothing was sent.\",\n\t\t\t);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tp.log.success(`Linked this machine to ${loaded.config.stack.name}`);\n\t}\n\tconst destinationToken = token;\n\tconst destinationConfig = loaded;\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning local agent transcripts\");\n\tlet staged: Awaited<ReturnType<typeof stageSync>>;\n\ttry {\n\t\tstaged = await stageSync({\n\t\t\tbaseUrl: BASE_URL,\n\t\t\tgetTokenImpl: () => destinationToken,\n\t\t\tloadConfigImpl: async () => destinationConfig,\n\t\t\tonProgress: (message) => s.message(message),\n\t\t});\n\t} catch (e) {\n\t\ts.stop(\"Scan failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\ts.stop(\"Scan complete\");\n\n\t// Beat one - the same full summary the MCP preview returns, verbatim,\n\t// printed behind the clack bar so it reads as one flow. The text is the\n\t// bytes' description and stays plain; the color is added here, by line\n\t// shape, so the MCP preview and a pipe get the same characters.\n\tp.log.message(staged.summary.split(\"\\n\").map(styleSummaryLine).join(\"\\n\"));\n\n\tif (staged.blockedReason !== null) {\n\t\toutroError(staged.blockedReason);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Beat two - the same short dialog text, as a select. The enum mirrors the\n\t// elicitation's {publish, cancel}; publish is the initial value.\n\tconst decision = await p.select({\n\t\tmessage: staged.dialog.split(\"\\n\").join(dim(\" · \")),\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"publish\",\n\t\t\t\tlabel: \"Publish\",\n\t\t\t\thint: \"no sensitive data is shared\",\n\t\t\t},\n\t\t\t{ value: \"cancel\", label: \"Cancel\", hint: \"nothing leaves this machine\" },\n\t\t],\n\t\tinitialValue: \"publish\",\n\t});\n\n\tif (p.isCancel(decision) || decision !== \"publish\") {\n\t\toutroCancel(\"nothing was sent\");\n\t\treturn;\n\t}\n\n\ts.start(\"Publishing\");\n\ttry {\n\t\tconst res = await syncPublish(staged.token as string, staged.bodyJson);\n\t\tstaged.acknowledgePublish?.();\n\t\ts.stop(\"Published\");\n\t\t// The last thing read is the result, not a receipt (#130): the stamp is\n\t\t// human-form, and the link gets its own line under a sentence that names\n\t\t// the proof. The path stays in the terminal - no browser is opened.\n\t\tconst lines = [\n\t\t\t`Snapshot received ${fmtReceivedAt(res.receivedAt)}`,\n\t\t\t\"\",\n\t\t\t\"Your stack now shows what actually ran:\",\n\t\t\tlime(res.url),\n\t\t];\n\t\tif (res.keptPrivate.refused && staged.body.keptPrivate !== undefined) {\n\t\t\tlines.push(\n\t\t\t\t\"Note: the server refused the kept-private names because its review switch is off. They stayed on this machine.\",\n\t\t\t);\n\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\tlines.push(\n\t\t\t\t`${res.keptPrivate.stored} private review name${res.keptPrivate.stored === 1 ? \"\" : \"s\"} stored at ${res.url}/changes`,\n\t\t\t);\n\t\t}\n\t\tif (res.keptPrivate.machineStored > 0) {\n\t\t\tlines.push(\n\t\t\t\t\"This machine's private label was stored for the same review.\",\n\t\t\t);\n\t\t}\n\t\tp.log.message(lines.join(\"\\n\"));\n\t\t// EVERY interactive sync settles auto-sync against the stack's own\n\t\t// answer (#103): it reconciles the missing triggers when the switch is\n\t\t// on, keeps quiet when it is off, and asks only when nobody has decided.\n\t\t// This is what completes a web-first enable, and what gives a harness\n\t\t// adopted months later its trigger.\n\t\t//\n\t\t// At most one ask per sync (#62): the auto-sync opt-in is the primary\n\t\t// ask; the connect upsell yields and waits for a later sync.\n\t\tconst asked = await settleAutoSync(staged.config.autoSync);\n\t\tif (!asked) await offerConnectUpsell();\n\t\toutro(\"done\");\n\t} catch (e) {\n\t\ts.stop(\"Publish failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t}\n}\n\n/**\n * Colors one summary line the way `collect` colors its output: a caps section\n * header in bold with a dim count, the rule dim, the label column dim, dollars\n * lime, and a skipped-files row yellow. Every other line passes through.\n */\nexport function styleSummaryLine(line: string): string {\n\tif (line.startsWith(\"─\")) return dim(line);\n\tconst section = /^([A-Z][A-Z0-9 .-]+?)( \\d+)?$/.exec(line);\n\tif (section) return `${bold(section[1] ?? \"\")}${dim(section[2] ?? \"\")}`;\n\tconst labelled = /^([a-z-]+)( +)(.*)$/.exec(line);\n\tif (labelled) {\n\t\tconst [, label = \"\", gap = \"\", rest = \"\"] = labelled;\n\t\tconst body =\n\t\t\tlabel === \"skipped\"\n\t\t\t\t? yellow(rest)\n\t\t\t\t: rest.replace(/≈\\$[\\d,]+/g, (m) => lime(m));\n\t\treturn `${dim(label)}${gap}${body}`;\n\t}\n\tconst sub = /^( {2}[a-z]+ +)(.*)$/.exec(line);\n\tif (sub) return `${lime(sub[1] ?? \"\")}${dim(sub[2] ?? \"\")}`;\n\tif (/^ {10}\\S/.test(line)) return dim(line);\n\treturn line;\n}\n","// The Codex half of the background trigger (#66 decision 4, built in #67):\n// a `SessionStart` hook in ~/.codex/hooks.json, matcher `startup` only.\n//\n// Two ways this differs from the Claude hook (hook.ts):\n//\n// 1. THE COMMAND SELF-DETACHES. Codex parses `async` but does not honor it -\n// the runner awaits the hook with a timeout and kill_on_drop (#65 §6).\n// So the command backgrounds the real work under `setsid nohup … &` and\n// exits 0 immediately; kill_on_drop kills only the already-exited shell.\n// 2. THE TRUST GATE. Codex pins each hook command's sha256 as a\n// `trusted_hash` in config.toml. An untrusted or CHANGED command silently\n// does not run, and only the user can trust it, via /hooks inside Codex.\n// That is why the command text uses `@latest` - the text (and therefore\n// the hash) stays stable across CLI updates - and why install prints the\n// one-time trust instruction.\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { parse } from \"smol-toml\";\n\nimport type { HookResult } from \"./hook.js\";\n\nfunction codexHome(): string {\n\treturn process.env.CODEX_HOME || join(homedir(), \".codex\");\n}\n\nexport function codexHooksFile(): string {\n\treturn join(codexHome(), \"hooks.json\");\n}\n\n// `codexPresent()` lived here and keyed on $CODEX_HOME existing. #101 replaced\n// it with `codexAdapter.detect()`: a directory proves an install, and an\n// install is not a user. A fresh Codex with no sessions yet gets its hook from\n// the interactive sync that reconciles hooks (#103), one session later.\n\nexport function codexConfigFile(): string {\n\treturn join(codexHome(), \"config.toml\");\n}\n\n/**\n * EXACT quoting, settled here (#66 left it to this ticket): the outer layer is\n * a JSON string in hooks.json; Codex runs it through a shell, and the single\n * `sh -c '…'` wrapper makes the detach group unambiguous regardless of how\n * that outer shell tokenizes. No `||` fallback like the Claude command - the\n * fallback semantics live INSIDE the detached shell so the hook process itself\n * still exits instantly.\n *\n * DO NOT REFORMAT THIS STRING. Its sha256 is the trust hash; any byte change\n * un-trusts the hook on every machine until each user re-runs /hooks.\n */\nexport const CODEX_HOOK_COMMAND =\n\t\"sh -c 'setsid nohup sh -c \\\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\\\" >/dev/null 2>&1 &'\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\ttimeout?: number;\n\tasync?: boolean;\n\tstatusMessage?: string;\n\tadditionalContextLimit?: number;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface CodexHooksJson {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nfunction readHooksJson(\n\tfile: string,\n): { settings: CodexHooksJson } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as CodexHooksJson };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse - it is the user's Codex\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON - fix it, then retry` };\n\t}\n}\n\n/** The instruction install prints; the interactive sync repeats it while untrusted. */\nexport const CODEX_TRUST_INSTRUCTION =\n\t\"Codex hook written - open Codex and run /hooks once to trust it, or it will not run.\";\n\n/**\n * Add the SessionStart auto-sync hook, matcher `startup` only (resume/clear/\n * compact would multiply runs; the freshness gate would drop them anyway).\n * Idempotent: an existing aistack entry is replaced, not duplicated.\n */\nexport function installCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\tmatcher: \"startup\",\n\t\thooks: [{ type: \"command\", command: CODEX_HOOK_COMMAND, timeout: 30 }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: CODEX_TRUST_INSTRUCTION };\n}\n\n/**\n * Remove only our hook. Other hooks and events stay. A missing file or an\n * absent hook is success - the goal state already holds.\n */\nexport function removeCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tif (!existsSync(file))\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the Codex auto-sync hook present? */\nexport function codexAutoSyncHookInstalled(\n\tfile: string = codexHooksFile(),\n): boolean {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n\n/**\n * Codex hashes the normalized full hook definition, then stores that hash under\n * the hook's source/index key in `[hooks.state]`. Match both pieces: another\n * hook's trusted hash says nothing about ours, and a command-only hash does not\n * match current Codex. `null` means the files could not be read or parsed.\n */\nexport function codexHookTrusted(\n\tconfigFile: string = codexConfigFile(),\n\thooksFile: string = codexHooksFile(),\n): boolean | null {\n\ttry {\n\t\tconst config = parse(readFileSync(configFile, \"utf-8\")) as {\n\t\t\thooks?: { state?: Record<string, { trusted_hash?: unknown }> };\n\t\t};\n\t\tconst read = readHooksJson(hooksFile);\n\t\tif (\"error\" in read) return null;\n\t\tconst sessionStart = read.settings.hooks?.SessionStart;\n\t\tif (!Array.isArray(sessionStart)) return false;\n\n\t\tconst matches: boolean[] = [];\n\t\tfor (const [groupIndex, group] of sessionStart.entries()) {\n\t\t\tfor (const [handlerIndex, handler] of (group.hooks ?? []).entries()) {\n\t\t\t\tif (!isOurs(handler) || typeof handler.command !== \"string\") continue;\n\t\t\t\tconst normalizedHandler: Record<string, unknown> = {\n\t\t\t\t\ttype: \"command\",\n\t\t\t\t\tcommand: handler.command,\n\t\t\t\t\ttimeout:\n\t\t\t\t\t\ttypeof handler.timeout === \"number\"\n\t\t\t\t\t\t\t? Math.max(1, handler.timeout)\n\t\t\t\t\t\t\t: 600,\n\t\t\t\t\tasync: handler.async === true,\n\t\t\t\t};\n\t\t\t\tif (typeof handler.statusMessage === \"string\") {\n\t\t\t\t\tnormalizedHandler.statusMessage = handler.statusMessage;\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\ttypeof handler.additionalContextLimit === \"number\" &&\n\t\t\t\t\thandler.additionalContextLimit !== 2500\n\t\t\t\t) {\n\t\t\t\t\tnormalizedHandler.additionalContextLimit =\n\t\t\t\t\t\thandler.additionalContextLimit;\n\t\t\t\t}\n\n\t\t\t\tconst identity: Record<string, unknown> = {\n\t\t\t\t\tevent_name: \"session_start\",\n\t\t\t\t\thooks: [normalizedHandler],\n\t\t\t\t};\n\t\t\t\tif (typeof group.matcher === \"string\") identity.matcher = group.matcher;\n\t\t\t\tconst currentHash = `sha256:${createHash(\"sha256\")\n\t\t\t\t\t.update(JSON.stringify(canonicalJson(identity)))\n\t\t\t\t\t.digest(\"hex\")}`;\n\t\t\t\tconst key = `${hooksFile}:session_start:${groupIndex}:${handlerIndex}`;\n\t\t\t\tmatches.push(config.hooks?.state?.[key]?.trusted_hash === currentHash);\n\t\t\t}\n\t\t}\n\t\treturn matches.length > 0 && matches.every(Boolean);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\ntype JsonValue = null | boolean | number | string | JsonValue[] | JsonObject;\ntype JsonObject = { [key: string]: JsonValue };\n\nfunction canonicalJson(value: unknown): JsonValue {\n\tif (Array.isArray(value)) return value.map(canonicalJson);\n\tif (value && typeof value === \"object\") {\n\t\tconst sorted: JsonObject = {};\n\t\tfor (const [key, child] of Object.entries(value).sort(([a], [b]) =>\n\t\t\ta.localeCompare(b),\n\t\t)) {\n\t\t\tsorted[key] = canonicalJson(child);\n\t\t}\n\t\treturn sorted;\n\t}\n\tif (\n\t\tvalue === null ||\n\t\ttypeof value === \"boolean\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"string\"\n\t) {\n\t\treturn value;\n\t}\n\tthrow new TypeError(\"hook identity is not JSON-serializable\");\n}\n","// The auto-sync opt-in (#62, map #60), narrowed to active harnesses by #101,\n// and moved onto the stack by #103.\n//\n// WHERE THE PERMISSION LIVES: on the stack, in Convex (#100 decision 2, #102).\n// The local flag and the SessionStart hooks are what this machine holds, and\n// neither of them grants anything - `sync --auto` asks the stack before it\n// publishes. So enable grants on the stack first, revoke revokes here first,\n// and `reconcileAutoSync` brings a machine in line with an answer the owner\n// gave somewhere else.\n//\n// This ask is the PRIMARY post-sync ask: it runs first, and the connect-claude\n// upsell yields to a later sync (at most one ask per sync). \"Maybe later\" and\n// ctrl-C leave no decision, so the question returns on the next manual sync.\n// Only \"Never ask again\" persists a local refusal. A stack that has already\n// decided is never asked at all: `settleAutoSync` reconciles it instead.\n\nimport * as p from \"@clack/prompts\";\nimport { setAutoSync } from \"../api.js\";\nimport {\n\tDEFAULT_FREQUENCY_HOURS,\n\tgetSettings,\n\tgetToken,\n\tnormalizeFrequencyHours,\n\tsaveSettings,\n} from \"../config.js\";\nimport { CLAUDE_HARNESS_NAME } from \"../harness/claude/adapter.js\";\nimport { CODEX_HARNESS_NAME } from \"../harness/codex/adapter.js\";\nimport { GROK_HARNESS_NAME } from \"../harness/grok/adapter.js\";\nimport {\n\ttype AutoSyncPermission,\n\tDEFAULT_WINDOW_DAYS,\n\tdetectedAdapters,\n\tharnessLabel,\n\tharnessListLabel,\n} from \"../harness/index.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport { dim, limeBold } from \"../theme.js\";\nimport {\n\tcodexAutoSyncHookInstalled,\n\tcodexHookTrusted,\n\tinstallCodexAutoSyncHook,\n\tremoveCodexAutoSyncHook,\n} from \"./codexHook.js\";\nimport {\n\tgrokAutoSyncHookInstalled,\n\tinstallGrokAutoSyncHook,\n\tremoveGrokAutoSyncHook,\n} from \"./grokHook.js\";\nimport {\n\tautoSyncHookInstalled,\n\ttype HookResult,\n\tinstallAutoSyncHook,\n\tremoveAutoSyncHook,\n} from \"./hook.js\";\n\nexport interface EnableDeps {\n\tsettingsFile?: string;\n\tinstallHook?: () => HookResult;\n\tremoveHook?: () => HookResult;\n\tinstallCodexHook?: () => HookResult;\n\tremoveCodexHook?: () => HookResult;\n\tinstallGrokHook?: () => HookResult;\n\tremoveGrokHook?: () => HookResult;\n\t/** Is the Claude Code trigger already on this machine? */\n\thookInstalledImpl?: () => boolean;\n\t/** Is the Codex trigger already on this machine? */\n\tcodexHookInstalledImpl?: () => boolean;\n\t/** Is the installed Codex trigger's exact definition trusted? */\n\tcodexHookTrustedImpl?: () => boolean | null;\n\tgrokHookInstalledImpl?: () => boolean;\n\t/** Override the detected harness set. Tests only. */\n\tdetectedImpl?: () => Promise<HarnessAdapter[]>;\n\tgetTokenImpl?: () => string | null;\n\tsetAutoSyncImpl?: typeof setAutoSync;\n}\n\n/** What an unlinked machine is told when it tries to grant the permission. */\nexport const NOT_LINKED =\n\t\"This machine is not linked to an aistack account, and the auto-sync permission lives on your stack. Run `npx @use-aistack/cli sync` first.\";\n\n/** The message when the machine has no active harness to trigger anything. */\nexport const NOTHING_TO_TRIGGER = `No supported session on this machine in the last ${DEFAULT_WINDOW_DAYS} days, so nothing would trigger an auto-sync. Nothing was changed.`;\n\n/**\n * Turn auto-sync on: grant the permission on the STACK, then write the\n * SessionStart hooks - one per DETECTED harness (#101). A hook for a harness\n * whose last session predates the window is a trigger that will never fire, and\n * its install is the step that made a dead Claude Code install look alive.\n *\n * THE STACK IS ASKED FIRST (#103). The permission is the thing that lets a\n * publish happen, and the hooks are dumb local triggers for it - so a refusal\n * from aistack.to leaves the machine exactly as it was, with no hook running an\n * npx at every session start for a permission nobody granted.\n *\n * When a hook write then fails, the local flag is NOT persisted. The stack\n * shows on-but-never-fired, which is true, and the next interactive sync\n * reconciles the missing trigger.\n */\nexport async function enableAutoSync(\n\tfrequencyHours: number = DEFAULT_FREQUENCY_HOURS,\n\tdeps: EnableDeps = {},\n): Promise<HookResult> {\n\tfrequencyHours = normalizeFrequencyHours(frequencyHours);\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) {\n\t\treturn { ok: false, message: NOTHING_TO_TRIGGER };\n\t}\n\tconst names = new Set(detected.map((a) => a.name));\n\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tif (token === null) return { ok: false, message: NOT_LINKED };\n\ttry {\n\t\tawait (deps.setAutoSyncImpl ?? setAutoSync)(token, {\n\t\t\tenabled: true,\n\t\t\tfrequencyHours,\n\t\t});\n\t} catch (e) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync was not turned on: ${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\tif (names.has(CLAUDE_HARNESS_NAME)) {\n\t\tconst result = (deps.installHook ?? installAutoSyncHook)();\n\t\tif (!result.ok) return result;\n\t}\n\n\tlet trustLine: string | null = null;\n\tif (names.has(CODEX_HARNESS_NAME)) {\n\t\tconst codexResult = (deps.installCodexHook ?? installCodexAutoSyncHook)();\n\t\tif (!codexResult.ok) return codexResult;\n\t\t// The one-time /hooks trust step (#65 §6) - repeated by the next\n\t\t// interactive sync while the hook stays untrusted.\n\t\tif ((deps.codexHookTrustedImpl ?? codexHookTrusted)() !== true) {\n\t\t\ttrustLine = codexResult.message;\n\t\t}\n\t}\n\tif (names.has(GROK_HARNESS_NAME)) {\n\t\tconst grokResult = (deps.installGrokHook ?? installGrokAutoSyncHook)();\n\t\tif (!grokResult.ok) return grokResult;\n\t}\n\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: { enabled: true, frequencyHours },\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\treturn {\n\t\tok: true,\n\t\tmessage: [\n\t\t\t`Auto-sync is on. It runs about every ${frequencyHours}h when a ${harnessListLabel(detected)} session starts. Turn it off any time: npx @use-aistack/cli sync --auto off`,\n\t\t\t...(trustLine ? [trustLine] : []),\n\t\t].join(\"\\n\"),\n\t};\n}\n\n/**\n * Revoke: flip the local flag, remove the hooks, then take the permission off\n * the stack. The flag flips even when a hook file cannot be edited, because\n * `sync --auto` gates on it - a stale hook without the flag publishes nothing.\n *\n * THE LOCAL HALF RUNS FIRST, the mirror image of enable (#103). A revoke must\n * never be blocked by an unreachable network: this machine stops publishing the\n * moment the flag flips, whatever aistack.to says next. The server half is\n * still reported when it fails, because it is the half that reaches every OTHER\n * machine, and the owner can also use the switch on their stack page.\n *\n * Removal is unconditional, unlike install: a revoke must reach the hook of a\n * harness that has since gone quiet, and removing an absent hook is success.\n */\nexport async function disableAutoSync(\n\tdeps: EnableDeps = {},\n): Promise<HookResult> {\n\tconst settings = getSettings(deps.settingsFile);\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: {\n\t\t\t\tenabled: false,\n\t\t\t\tfrequencyHours: normalizeFrequencyHours(\n\t\t\t\t\tsettings.autoSync?.frequencyHours,\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\tconst result = (deps.removeHook ?? removeAutoSyncHook)();\n\tconst codexResult = (deps.removeCodexHook ?? removeCodexAutoSyncHook)();\n\tconst grokResult = (deps.removeGrokHook ?? removeGrokAutoSyncHook)();\n\tconst failures = [result, codexResult, grokResult]\n\t\t.filter((r) => !r.ok)\n\t\t.map((r) => r.message);\n\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tif (token !== null) {\n\t\ttry {\n\t\t\tawait (deps.setAutoSyncImpl ?? setAutoSync)(token, { enabled: false });\n\t\t} catch (e) {\n\t\t\tfailures.push(\n\t\t\t\t`aistack.to was not told (${e instanceof Error ? e.message : String(e)}); your other machines keep the permission`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is off on this machine (nothing will publish), but: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\treturn {\n\t\tok: true,\n\t\tmessage: \"Auto-sync is off. The hooks were removed.\",\n\t};\n}\n\n/**\n * Bring the machine in line with the permission the stack holds (#103).\n *\n * This is not an ask and never prompts. The owner already answered, on the web\n * switch or on another machine, and this is the machine catching up with them:\n *\n * - flag ON - install a trigger for every DETECTED harness that lacks one,\n * and mirror the flag locally so `sync --auto` passes its own\n * gate. That is what makes \"flip the web switch, run one sync\"\n * the whole enable story, and it is also what gives a harness\n * adopted months later its trigger.\n * - flag OFF - disable locally first and remove every owned trigger. A\n * failed removal is retried on the next interactive sync, and\n * the local gate keeps a leftover trigger from publishing.\n * - ABSENT - touch nothing. Nobody has decided, and the post-sync ask still\n * owns that case.\n *\n * Returns the one line to print, or `null` when there was nothing to do.\n */\nexport async function reconcileAutoSync(\n\tpermission: AutoSyncPermission | null,\n\tdeps: EnableDeps = {},\n): Promise<HookResult | null> {\n\tif (permission === null) return null;\n\tif (permission.enabled !== true) {\n\t\tconst settings = getSettings(deps.settingsFile);\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncAnswered: true,\n\t\t\t\tautoSync: {\n\t\t\t\t\tenabled: false,\n\t\t\t\t\tfrequencyHours: normalizeFrequencyHours(\n\t\t\t\t\t\tpermission.frequencyHours ?? settings.autoSync?.frequencyHours,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t\tdeps.settingsFile,\n\t\t);\n\t\tconst results = [\n\t\t\t(deps.removeHook ?? removeAutoSyncHook)(),\n\t\t\t(deps.removeCodexHook ?? removeCodexAutoSyncHook)(),\n\t\t\t(deps.removeGrokHook ?? removeGrokAutoSyncHook)(),\n\t\t];\n\t\tconst failures = results.filter((result) => !result.ok);\n\t\tif (failures.length > 0) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: `Auto-sync is off on this machine, but a trigger could not be removed: ${failures.map((result) => result.message).join(\"; \")}. The next interactive sync will retry.`,\n\t\t\t};\n\t\t}\n\t\tconst changed =\n\t\t\tsettings.autoSync?.enabled !== false ||\n\t\t\tresults.some((result) => !result.message.toLowerCase().startsWith(\"no \"));\n\t\tif (!changed) return null;\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmessage: \"Auto-sync is off on this machine. Removed its local triggers.\",\n\t\t};\n\t}\n\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) return null;\n\tconst names = new Set(detected.map((a) => a.name));\n\n\tconst installed: string[] = [];\n\tconst failures: string[] = [];\n\tconst install = (\n\t\tharnessName: string,\n\t\tisInstalled: () => boolean,\n\t\twrite: () => HookResult,\n\t) => {\n\t\tif (!names.has(harnessName) || isInstalled()) return;\n\t\tconst result = write();\n\t\tif (result.ok) installed.push(harnessLabel(harnessName));\n\t\telse failures.push(result.message);\n\t};\n\n\tinstall(\n\t\tCLAUDE_HARNESS_NAME,\n\t\tdeps.hookInstalledImpl ?? autoSyncHookInstalled,\n\t\tdeps.installHook ?? installAutoSyncHook,\n\t);\n\tinstall(\n\t\tCODEX_HARNESS_NAME,\n\t\tdeps.codexHookInstalledImpl ?? codexAutoSyncHookInstalled,\n\t\tdeps.installCodexHook ?? installCodexAutoSyncHook,\n\t);\n\tinstall(\n\t\tGROK_HARNESS_NAME,\n\t\tdeps.grokHookInstalledImpl ?? grokAutoSyncHookInstalled,\n\t\tdeps.installGrokHook ?? installGrokAutoSyncHook,\n\t);\n\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is on for this stack, but a trigger could not be written: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\n\tconst frequencyHours = permission.frequencyHours ?? DEFAULT_FREQUENCY_HOURS;\n\tconst local = getSettings(deps.settingsFile).autoSync;\n\tconst mirrored =\n\t\tlocal?.enabled === true && local.frequencyHours === frequencyHours;\n\tif (!mirrored) {\n\t\tsaveSettings(\n\t\t\t{ autoSyncAnswered: true, autoSync: { enabled: true, frequencyHours } },\n\t\t\tdeps.settingsFile,\n\t\t);\n\t}\n\n\t// Quiet when nothing changed. The steady state is already reported by the\n\t// `auto-sync: <last result>` line the interactive sync prints.\n\tif (installed.length === 0 && mirrored) return null;\n\treturn {\n\t\tok: true,\n\t\tmessage:\n\t\t\tinstalled.length > 0\n\t\t\t\t? `Auto-sync is on for this stack. Installed the ${installed.join(\" and \")} trigger on this machine; it runs about every ${frequencyHours}h when a session starts.`\n\t\t\t\t: `Auto-sync is on for this stack. It runs about every ${frequencyHours}h when a ${harnessListLabel(detected)} session starts.`,\n\t};\n}\n\n/**\n * Everything an interactive sync does about auto-sync, after it publishes.\n *\n * One step with three inputs, because the stack's answer is what decides which\n * of them applies (#103): a stack that has decided is reconciled and never\n * asked again, and only a stack nobody has decided for reaches the ask. The ask\n * asked once and persisted is #62's rule, and the switch now outranks it -\n * re-asking an owner who already answered on the web is asking them twice.\n *\n * Returns true when it ASKED, so the caller knows to hold the connect upsell\n * back to a later sync (at most one ask per sync, #62).\n */\nexport async function settleAutoSync(\n\tpermission: AutoSyncPermission | null,\n\tdeps: EnableDeps = {},\n): Promise<boolean> {\n\tif (permission !== null) {\n\t\tconst result = await reconcileAutoSync(permission, deps);\n\t\tif (result === null) return false;\n\t\tif (result.ok) p.log.success(result.message);\n\t\telse p.log.warn(result.message);\n\t\treturn false;\n\t}\n\treturn offerAutoSyncOptIn(deps);\n}\n\n/**\n * The post-sync ask. Returns true when it asked (so the caller skips the\n * connect upsell this sync), false when it had nothing to ask.\n *\n * The hint names the harnesses this machine actually runs (#101): a Codex-only\n * user is told \"when a Codex session starts\", not a Claude Code sentence that\n * describes nothing they do.\n */\nexport async function offerAutoSyncOptIn(\n\tdeps: EnableDeps = {},\n): Promise<boolean> {\n\tif (getSettings(deps.settingsFile).autoSyncNeverAskAgain === true)\n\t\treturn false;\n\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) return false;\n\n\tconst answer = await p.select({\n\t\tmessage: \"Keep this stack fresh automatically every 6 hours?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"enable\",\n\t\t\t\tlabel: \"Enable\",\n\t\t\t\thint: `a silent sync at most every 6 hours when a ${harnessListLabel(detected)} session starts`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"Maybe later\",\n\t\t\t\thint: \"ask again after your next manual sync\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"never\",\n\t\t\t\tlabel: \"Never ask again\",\n\t\t\t\thint: \"you can still enable it with sync --auto on\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"enable\",\n\t});\n\n\tif (p.isCancel(answer)) return true;\n\n\tif (answer === \"enable\") {\n\t\t// The set is reused, not re-detected: the user answered the hint they saw.\n\t\tconst result = await enableAutoSync(DEFAULT_FREQUENCY_HOURS, {\n\t\t\t...deps,\n\t\t\tdetectedImpl: async () => detected,\n\t\t});\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (answer === \"never\") {\n\t\tsaveSettings({ autoSyncNeverAskAgain: true }, deps.settingsFile);\n\t}\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli sync --auto on\")} ${dim(\n\t\t\t\"(and --auto off to revoke)\",\n\t\t)}`,\n\t);\n\treturn true;\n}\n","import {\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HookResult } from \"./hook.js\";\n\nexport const GROK_HOOK_FILE = join(\n\tprocess.env.GROK_HOME || join(homedir(), \".grok\"),\n\t\"hooks\",\n\t\"aistack.json\",\n);\n\nconst SYNC_COMMAND =\n\t\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\";\n\nexport function grokHookCommand(os: NodeJS.Platform = platform()): string {\n\tif (os === \"win32\") {\n\t\treturn `Start-Process -WindowStyle Hidden -FilePath \"cmd.exe\" -ArgumentList '/d /s /c \"set AISTACK_HOOK_SOURCE=grok&& (${SYNC_COMMAND}) >NUL 2>&1\"'`;\n\t}\n\tif (os === \"darwin\") {\n\t\treturn `nohup sh -c 'export AISTACK_HOOK_SOURCE=grok; ${SYNC_COMMAND}' </dev/null >/dev/null 2>&1 &`;\n\t}\n\treturn `setsid nohup sh -c 'export AISTACK_HOOK_SOURCE=grok; ${SYNC_COMMAND}' >/dev/null 2>&1 &`;\n}\n\ninterface HookEntry {\n\ttype?: unknown;\n\tcommand?: unknown;\n\ttimeout?: unknown;\n}\n\ninterface GrokHookFile {\n\thooks?: Record<string, Array<{ hooks?: HookEntry[] }>>;\n\t[key: string]: unknown;\n}\n\nfunction readHookFile(\n\tfile: string,\n): { value: GrokHookFile } | { error: string } {\n\tif (!existsSync(file)) return { value: {} };\n\ttry {\n\t\tconst value: unknown = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\tconst candidate = value as GrokHookFile;\n\t\t\tif (\n\t\t\t\tcandidate.hooks !== undefined &&\n\t\t\t\t(!candidate.hooks ||\n\t\t\t\t\ttypeof candidate.hooks !== \"object\" ||\n\t\t\t\t\tArray.isArray(candidate.hooks) ||\n\t\t\t\t\tObject.values(candidate.hooks).some(\n\t\t\t\t\t\t(groups) => !Array.isArray(groups),\n\t\t\t\t\t))\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `${file} has a malformed hooks object. Fix it, then retry.`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { value: candidate };\n\t\t}\n\t} catch {\n\t\treturn { error: `${file} is not valid JSON. Fix it, then retry.` };\n\t}\n\treturn { error: `${file} does not hold a JSON object` };\n}\n\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nexport function installGrokAutoSyncHook(\n\tfile: string = GROK_HOOK_FILE,\n\tos: NodeJS.Platform = platform(),\n): HookResult {\n\tconst read = readHookFile(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst foreignKeys = Object.keys(read.value).filter((key) => key !== \"hooks\");\n\tconst foreignEvents = Object.keys(read.value.hooks ?? {}).filter(\n\t\t(key) => key !== \"SessionStart\",\n\t);\n\tconst sessionStart = read.value.hooks?.SessionStart ?? [];\n\tconst foreignHandlers = sessionStart.flatMap((group) =>\n\t\t(group.hooks ?? []).filter((entry) => !isOurs(entry)),\n\t);\n\tif (\n\t\tforeignKeys.length > 0 ||\n\t\tforeignEvents.length > 0 ||\n\t\tforeignHandlers.length > 0\n\t) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${file} contains hooks not owned by AI Stack. Move them to another Grok hook file, then retry.`,\n\t\t};\n\t}\n\tconst value: GrokHookFile = {\n\t\thooks: {\n\t\t\tSessionStart: [\n\t\t\t\t{\n\t\t\t\t\thooks: [\n\t\t\t\t\t\t{ type: \"command\", command: grokHookCommand(os), timeout: 5 },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t};\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(value, null, 2)}\\n`);\n\treturn {\n\t\tok: true,\n\t\tmessage: `Grok Build SessionStart hook written to ${file}. Start a new Grok session or reload hooks before expecting it to run.`,\n\t};\n}\n\nexport function removeGrokAutoSyncHook(\n\tfile: string = GROK_HOOK_FILE,\n): HookResult {\n\tif (!existsSync(file))\n\t\treturn { ok: true, message: \"no Grok Build hook to remove\" };\n\tconst read = readHookFile(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst entries = read.value.hooks?.SessionStart ?? [];\n\tconst onlyOurs =\n\t\tObject.keys(read.value).every((key) => key === \"hooks\") &&\n\t\tObject.keys(read.value.hooks ?? {}).every(\n\t\t\t(key) => key === \"SessionStart\",\n\t\t) &&\n\t\tentries.every((group) =>\n\t\t\t(group.hooks ?? []).every((entry) => isOurs(entry)),\n\t\t);\n\tif (!onlyOurs) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${file} contains hooks not owned by AI Stack and was not removed.`,\n\t\t};\n\t}\n\tunlinkSync(file);\n\treturn { ok: true, message: `Grok Build hook removed from ${file}` };\n}\n\nexport function grokAutoSyncHookInstalled(\n\tfile: string = GROK_HOOK_FILE,\n): boolean {\n\tconst read = readHookFile(file);\n\tif (\"error\" in read) return false;\n\treturn (read.value.hooks?.SessionStart ?? []).some((group) =>\n\t\t(group.hooks ?? []).some((entry) => isOurs(entry)),\n\t);\n}\n","// The background trigger (#62, map #60): a `SessionStart` hook in\n// ~/.claude/settings.json, `async: true`.\n//\n// SessionStart, not SessionEnd - teardown is not guaranteed (crash, SIGKILL,\n// closed terminal), and at start-of-session the previous sessions are fully on\n// disk. The command runs `@latest` through npx, so unattended machines update\n// by construction. The `||` fallback covers the offline case: when the network\n// resolve of `@latest` fails, the second npx runs the cached copy.\n// `sync --auto` always exits 0, so the fallback never fires on a sync failure.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport const CLAUDE_SETTINGS_FILE = join(homedir(), \".claude\", \"settings.json\");\n\nexport const AUTO_SYNC_HOOK_COMMAND =\n\t\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\tasync?: boolean;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface ClaudeSettings {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nexport interface HookResult {\n\tok: boolean;\n\tmessage: string;\n}\n\nfunction readClaudeSettings(\n\tfile: string,\n): { settings: ClaudeSettings } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as ClaudeSettings };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse - it is the user's Claude Code\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON - fix it, then retry` };\n\t}\n}\n\n/**\n * Add the SessionStart auto-sync hook. Idempotent: an existing aistack\n * auto-sync entry (any version of the command) is replaced, not duplicated.\n * All other hooks are preserved byte-for-byte in structure.\n */\nexport function installAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\thooks: [{ type: \"command\", command: AUTO_SYNC_HOOK_COMMAND, async: true }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `SessionStart hook written to ${file}` };\n}\n\n/**\n * Remove only our hook. Other SessionStart hooks and other events stay. A\n * missing file or an absent hook is success - the goal state already holds.\n */\nexport function removeAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tif (!existsSync(file)) return { ok: true, message: \"no hook to remove\" };\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the auto-sync hook present? Used by status reporting. */\nexport function autoSyncHookInstalled(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): boolean {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n","// `sync --auto` - the silent background run (#62, map #60).\n//\n// The tenets from map #29 hold: passive analysis, never passive publish. TWO\n// gates let this path publish, and either one closes it:\n//\n// 1. The machine's own flag (`autoSync.enabled` in\n// ~/.config/aistack/settings.json). Local, free, and checked first.\n// 2. The STACK's permission, read from `/api/sync-config` (#102, #103). The\n// owner holds this one, from any machine and from the web switch, so a\n// revoke reaches a machine whose hooks are still live.\n//\n// No prompts, no upsells, no email, no dialogs. This path also never installs\n// or removes a hook - the interactive sync owns that (`reconcileAutoSync`).\n// The escalation ladder is: one log line per run → the next interactive sync\n// reports the last result → after 3 consecutive failures, one visible\n// systemMessage line.\n//\n// This function never sets a nonzero exit code. The hook command falls back to\n// the npx cache on `||`, and a nonzero exit from a mere sync failure would\n// fire that fallback and run the whole sync twice.\n\nimport {\n\tappendFileSync,\n\tcloseSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { syncPublish } from \"../api.js\";\nimport {\n\ttype AutoSyncState,\n\tgetSettings,\n\tgetToken,\n\tnormalizeFrequencyHours,\n\tsaveSettings,\n} from \"../config.js\";\nimport { loadSyncConfig } from \"../harness/shared/allowlist.js\";\nimport { stageSync } from \"../sync/stage.js\";\n\nexport const SYNC_LOG_FILE = join(homedir(), \".config\", \"aistack\", \"sync.log\");\n\n/** The log stays small: newest 200 lines, older lines fall off. */\nexport const SYNC_LOG_MAX_LINES = 200;\n\n/** The one-line fix, named in the escalation message and nowhere vaguer. */\nconst FIX_COMMAND = \"npx @use-aistack/cli sync\";\n\nexport type AutoSyncDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tsettingsFile?: string;\n\tlogFile?: string;\n\tstageImpl?: typeof stageSync;\n\tpublishImpl?: typeof syncPublish;\n\tgetTokenImpl?: () => string | null;\n\tloadConfigImpl?: typeof loadSyncConfig;\n\t/** Where the systemMessage JSON goes. Defaults to stdout. */\n\temit?: (line: string) => void;\n\t/** Grok hooks keep stdout empty, including failure escalation. */\n\tsuppressOutput?: boolean;\n\treservationFile?: string;\n};\n\n/** What the next interactive sync says when the stack has taken the permission away. */\nexport const REVOKED_RESULT = \"off - auto-sync is switched off for this stack\";\n\nexport function appendLogLine(file: string, line: string): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\tappendFileSync(file, `${line}\\n`);\n\tconst lines = readFileSync(file, \"utf-8\").split(\"\\n\").filter(Boolean);\n\tif (lines.length > SYNC_LOG_MAX_LINES) {\n\t\twriteFileSync(file, `${lines.slice(-SYNC_LOG_MAX_LINES).join(\"\\n\")}\\n`);\n\t}\n}\n\nfunction reserveAttempt(file: string, now: number, windowMs: number): boolean {\n\tmkdirSync(dirname(file), { recursive: true });\n\tfor (;;) {\n\t\ttry {\n\t\t\tconst fd = openSync(file, \"wx\");\n\t\t\twriteFileSync(fd, String(now));\n\t\t\tcloseSync(fd);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\t\tconst held = Number(readFileSync(file, \"utf-8\"));\n\t\t\tif (Number.isFinite(held) && now - held < windowMs) return false;\n\t\t\ttry {\n\t\t\t\tunlinkSync(file);\n\t\t\t} catch (unlinkError) {\n\t\t\t\tif ((unlinkError as NodeJS.ErrnoException).code !== \"ENOENT\")\n\t\t\t\t\tthrow unlinkError;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function runAutoSync(deps: AutoSyncDeps): Promise<void> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst settingsFile = deps.settingsFile;\n\tconst logFile = deps.logFile ?? SYNC_LOG_FILE;\n\tconst emit =\n\t\tdeps.emit ?? ((line: string) => process.stdout.write(`${line}\\n`));\n\tconst stamp = new Date(now).toISOString();\n\n\tconst settings = getSettings(settingsFile);\n\tconst config = settings.autoSync;\n\n\t// The hard gate. A hook left behind after a revoke publishes nothing.\n\tif (config?.enabled !== true) {\n\t\tappendLogLine(logFile, `${stamp} skipped - auto-sync is not enabled`);\n\t\treturn;\n\t}\n\n\t// The freshness gate keys on the last ATTEMPT, not the last success. A\n\t// broken setup then retries once per frequency window, not once per\n\t// session start, and still reaches the 3-failure escalation.\n\tconst frequencyHours = normalizeFrequencyHours(config.frequencyHours);\n\tconst windowMs = frequencyHours * 3_600_000;\n\tconst state: AutoSyncState = settings.autoSyncState ?? {};\n\tconst lastRunAt = state.lastRunAt ?? 0;\n\tif (now - lastRunAt < windowMs) return;\n\tconst reservationFile =\n\t\tdeps.reservationFile ??\n\t\t`${settingsFile ?? join(homedir(), \".config\", \"aistack\", \"settings.json\")}.auto-sync-attempt`;\n\tif (!reserveAttempt(reservationFile, now, windowMs)) return;\n\tsaveSettings({ autoSyncState: { ...state, lastRunAt: now } }, settingsFile);\n\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\n\tlet failure: string | null = null;\n\tlet url: string | undefined;\n\tlet revoked = false;\n\ttry {\n\t\t// The permission the STACK holds (#102/#103), read BEFORE the scan. The\n\t\t// run needs the network to publish anyway, so asking first costs nothing\n\t\t// and spares a revoked machine a full walk of its own history.\n\t\tconst loaded = await loadConfig({\n\t\t\tbaseUrl: deps.baseUrl,\n\t\t\t...(token ? { token } : {}),\n\t\t});\n\t\tif (loaded.config.autoSync?.enabled === false) {\n\t\t\t// EXPLICIT OFF ONLY. An absent flag still publishes, because that\n\t\t\t// publish is what seeds the stack from this machine (#102).\n\t\t\trevoked = true;\n\t\t} else {\n\t\t\tconst serverFrequency = loaded.config.autoSync?.frequencyHours;\n\t\t\tif (\n\t\t\t\tloaded.config.autoSync?.enabled === true &&\n\t\t\t\tserverFrequency !== undefined &&\n\t\t\t\tnormalizeFrequencyHours(serverFrequency) !== frequencyHours\n\t\t\t) {\n\t\t\t\tsaveSettings(\n\t\t\t\t\t{\n\t\t\t\t\t\tautoSync: {\n\t\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t\t\tfrequencyHours: normalizeFrequencyHours(serverFrequency),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tsettingsFile,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst staged = await stage({\n\t\t\t\tbaseUrl: deps.baseUrl,\n\t\t\t\tnow: () => now,\n\t\t\t\ttrigger: \"auto\",\n\t\t\t\t// The same body the gate above read, so the permission and the\n\t\t\t\t// destination cannot come from two different fetches.\n\t\t\t\tloadConfigImpl: async () => loaded,\n\t\t\t\tgetTokenImpl: () => token,\n\t\t\t});\n\t\t\tif (staged.blockedReason !== null) {\n\t\t\t\tfailure = staged.blockedReason;\n\t\t\t} else {\n\t\t\t\tconst res = await publish(staged.token as string, staged.bodyJson);\n\t\t\t\turl = res.url;\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tfailure = e instanceof Error ? e.message : String(e);\n\t}\n\n\t// A revoke is not a failure: the streak, the warning and the last success\n\t// all stay as they were. Only the attempt is stamped, so the machine asks\n\t// once per frequency window instead of once per session start.\n\tif (revoked) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: { ...state, lastRunAt: now, lastResult: REVOKED_RESULT },\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} skipped - ${REVOKED_RESULT}`);\n\t\treturn;\n\t}\n\n\tif (failure === null) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: {\n\t\t\t\t\tlastRunAt: now,\n\t\t\t\t\tlastSuccessAt: now,\n\t\t\t\t\tlastResult: `ok - published at ${stamp}`,\n\t\t\t\t\tconsecutiveFailures: 0,\n\t\t\t\t\tfailureWarned: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} ok - published${url ? ` ${url}` : \"\"}`);\n\t\treturn;\n\t}\n\n\tconst consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;\n\tconst shouldWarn = consecutiveFailures >= 3 && state.failureWarned !== true;\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncState: {\n\t\t\t\t...state,\n\t\t\t\tlastRunAt: now,\n\t\t\t\tlastResult: `failed at ${stamp} - ${failure}`,\n\t\t\t\tconsecutiveFailures,\n\t\t\t\tfailureWarned: state.failureWarned === true || shouldWarn,\n\t\t\t},\n\t\t},\n\t\tsettingsFile,\n\t);\n\tappendLogLine(\n\t\tlogFile,\n\t\t`${stamp} fail (${consecutiveFailures} in a row) - ${failure}`,\n\t);\n\n\t// One visible line, once per failure streak. SessionStart hook JSON:\n\t// Claude Code shows `systemMessage` to the user when the async hook lands.\n\tif (\n\t\tshouldWarn &&\n\t\tdeps.suppressOutput !== true &&\n\t\tprocess.env.AISTACK_HOOK_SOURCE !== \"grok\"\n\t) {\n\t\temit(\n\t\t\tJSON.stringify({\n\t\t\t\tsystemMessage: `aistack auto-sync failed ${consecutiveFailures} times in a row (${failure}). Run \\`${FIX_COMMAND}\\` in a terminal to fix it, or \\`${FIX_COMMAND} --auto off\\` to stop these runs.`,\n\t\t\t}),\n\t\t);\n\t}\n}\n","// Stage one send: scan every ACTIVE harness → build → derive the gate's text\n// from the exact bytes. Active, not installed: a harness with nothing in the\n// window is not scanned and does not publish, so a dead Claude Code install no\n// longer lands a stale snapshot next to a live Codex one (#101).\n//\n// Wayfinder ticket #41 (map #29), widened to the adapter seam by #67 (map\n// #60). The staged `bodyJson` string IS what a publish transmits - the summary\n// and the dialog are derived from it and from nothing else, so the user can\n// never approve a sentence about different bytes (#35's binding constraint).\n// The publish tool takes only the stage id; it can name WHICH staged send to\n// release, never what is in it.\n//\n// One stage covers ALL detected harnesses (#66 decision 4): the payloads ride\n// in one request so the server can land them atomically, and the kept-private\n// union is one list because consent is per name, not per harness.\n\nimport { createHash } from \"node:crypto\";\nimport {\n\tBUNDLED_PRICE_TABLE_ID,\n\tlayeredPricer,\n\ttype PriceTable,\n\tsetActivePricer,\n} from \"@aistack/pricing\";\nimport {\n\tMEASURED_DAYS_V1,\n\ttype MeasuredDay,\n\ttype UsageHarnessDay,\n} from \"@aistack/workflow-rules\";\nimport { fetchDayManifest, fetchPriceTable } from \"../api.js\";\nimport {\n\tgetProjectWorkspaceId,\n\tgetSettings,\n\tgetToken,\n\ttype Settings,\n} from \"../config.js\";\nimport { detectedAdapters } from \"../harness/index.js\";\nimport {\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"../harness/shared/allowlist.js\";\nimport {\n\tapplyDayConsent,\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\tmergeKeptPrivate,\n\ttype SyncBody,\n\ttype SyncTrigger,\n} from \"../harness/shared/payload.js\";\nimport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../harness/shared/window.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport {\n\tbuildMeasuredDays,\n\tbuildUsageDays,\n\tmergeUsageDays,\n} from \"../usage/days.js\";\nimport {\n\ttype DayManifest,\n\ttype DaySelection,\n\tMAX_DAY_WINDOW,\n\tselectDaysToPublish,\n} from \"../usage/diff.js\";\nimport { CLI_VERSION } from \"../version.js\";\nimport {\n\textractLocalWorkflow,\n\textractLocalWorkflowAsync,\n\ttype GitWorkflowRunner,\n\ttype LocalHarnessWorkflow,\n\tmachineUtcOffsetMinutes,\n\ttype WorkflowExtraction,\n} from \"../workflow/index.js\";\nimport {\n\tgrokCacheScope,\n\tloadGrokDateHints,\n\tmapToHints,\n\tsaveGrokDateHints,\n} from \"./grokDateCache.js\";\nimport { buildGateDialog, buildGateSummary } from \"./summary.js\";\n\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\nexport type StagedSend = {\n\t/** Content-derived: the sha256 prefix of `bodyJson`. Same bytes, same id. */\n\tid: string;\n\t/** The exact request body a publish sends, already serialized. */\n\tbodyJson: string;\n\tbody: SyncBody;\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tsummary: string;\n\tdialog: string;\n\tconfig: SyncConfig;\n\ttoken: string | null;\n\tstagedAt: number;\n\t/**\n\t * `null` when this stage may not publish, with `blockedReason` saying why.\n\t * A gate that cannot name its destination must not send (#33 decision 7),\n\t * so no token and no resolved stack both block here, before any dialog.\n\t */\n\tblockedReason: string | null;\n\t/**\n\t * How the day rows were chosen (#307): the counts the gate prints and the\n\t * mode, `diff` against a manifest or `full` when there was none.\n\t */\n\tdays?: DaySelection;\n\t/** Which price table priced this stage (#336). Absent only in fixtures. */\n\tprices?: PriceTableUsed;\n\t/** Commit local Grok date hints only after the server accepted these bytes. */\n\tacknowledgePublish?: () => void;\n};\n\n/**\n * The table the adapters priced against. `served` is the server's\n * `modelPrices` table layered over the bundled one; `bundled` means the fetch\n * failed or the server has no such route, and every figure came from the\n * constants shipped with this CLI version.\n */\nexport type PriceTableUsed = {\n\tid: string;\n\torigin: \"served\" | \"bundled\";\n};\n\nexport type StageDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tgetTokenImpl?: () => string | null;\n\tgetProjectWorkspaceIdImpl?: (directory: string) => string;\n\tloadConfigImpl?: (opts: {\n\t\tbaseUrl: string;\n\t\ttoken?: string;\n\t}) => Promise<LoadedSyncConfig>;\n\t/** Override the adapter set. Tests only. */\n\tadaptersImpl?: (sinceMs: number) => Promise<HarnessAdapter[]>;\n\t/** Override the Git reader the workflow extraction shells out to. Tests only. */\n\tgitRunnerImpl?: GitWorkflowRunner;\n\t/**\n\t * Override the manifest fetch (#307). `null` means the server has none.\n\t * A throw is caught and reads the same: the whole window goes.\n\t */\n\tfetchManifestImpl?: (\n\t\tbaseUrl: string,\n\t\ttoken: string,\n\t) => Promise<DayManifest | null>;\n\t/**\n\t * Override the price table fetch (#336). `null` means the server has none.\n\t * A throw is caught and reads the same: the bundled table prices the stage.\n\t */\n\tfetchPricesImpl?: (baseUrl: string) => Promise<PriceTable | null>;\n\tgetSettingsImpl?: () => Settings;\n\twindowDays?: number;\n\t/**\n\t * How this sync fired (#103). Defaults to `manual`, because every caller but\n\t * the background run has a human at the keyboard.\n\t */\n\ttrigger?: SyncTrigger;\n\t/** Human-facing phase updates for the interactive terminal. */\n\tonProgress?: (message: string) => void;\n};\n\nexport function stageId(bodyJson: string): string {\n\treturn createHash(\"sha256\").update(bodyJson).digest(\"hex\").slice(0, 12);\n}\n\nexport async function stageSync(deps: StageDeps): Promise<StagedSend> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst adapters = deps.adaptersImpl ?? detectedAdapters;\n\tconst windowDays = deps.windowDays ?? DEFAULT_WINDOW_DAYS;\n\tconst projectWorkspaceId =\n\t\tdeps.getProjectWorkspaceIdImpl ?? getProjectWorkspaceId;\n\tconst fetchManifest = deps.fetchManifestImpl ?? fetchDayManifest;\n\tconst fetchPrices = deps.fetchPricesImpl ?? fetchPriceTable;\n\tconst progress = deps.onProgress ?? (() => {});\n\n\t// The price table comes from the server BEFORE any adapter prices a\n\t// response (#336): the adapters call the module-level pricing functions,\n\t// which read whichever pricer is active. Served rows win per key; the\n\t// bundled constants fill what the server does not hold. Unreachable reads\n\t// as bundled, and the gate says which one it was.\n\tlet prices: PriceTableUsed = {\n\t\tid: BUNDLED_PRICE_TABLE_ID,\n\t\torigin: \"bundled\",\n\t};\n\tprogress(\"Checking prices and stack settings\");\n\ttry {\n\t\tconst table = await fetchPrices(deps.baseUrl);\n\t\tif (table) {\n\t\t\tsetActivePricer(layeredPricer(table));\n\t\t\tprices = { id: table.id, origin: \"served\" };\n\t\t} else {\n\t\t\tsetActivePricer(null);\n\t\t}\n\t} catch {\n\t\tsetActivePricer(null);\n\t}\n\n\tconst { config, source } = await loadConfig({\n\t\tbaseUrl: deps.baseUrl,\n\t\t...(token ? { token } : {}),\n\t});\n\n\t// The server's day manifest (#307, ADR-0010): which dates it holds and with\n\t// what fingerprint. Missing (an old server) or failing (network) reads as\n\t// \"send the whole window\"; a publish that repeats a held date is correct,\n\t// only wasteful. The manifest also names the retention, which bounds how\n\t// far back the day scan reaches.\n\tlet manifest: DayManifest | null = null;\n\tif (token) {\n\t\ttry {\n\t\t\tmanifest = await fetchManifest(deps.baseUrl, token);\n\t\t} catch {\n\t\t\tmanifest = null;\n\t\t}\n\t}\n\tconst retentionDays = Math.max(\n\t\t1,\n\t\tMath.min(manifest?.retentionDays ?? MAX_DAY_WINDOW, MAX_DAY_WINDOW),\n\t);\n\n\tconst built: BuiltPayload[] = [];\n\tconst scanStats: Record<string, ScanStats> = {};\n\t// Collected per harness, extracted ONCE below (#213): local Git history is a\n\t// property of the machine, not of whichever harness opened the repository,\n\t// and the metric rows are computed across every synced harness at once.\n\tconst workflowScans: LocalHarnessWorkflow[] = [];\n\tconst usageScans: Map<string, UsageHarnessDay>[] = [];\n\t// One window start for detection AND for the snapshot scan (#101), so a\n\t// harness that counts as detected is exactly a harness with something in\n\t// the window.\n\tconst sinceMs = windowStartMs(now, windowDays);\n\t// The day scan reaches the whole retention (#307): the snapshot stays a\n\t// 30-day block until its readers retire, while the day rows cover every\n\t// date the server would keep. Two scans over the same files; the second is\n\t// the one the days and the workflow blocks come from.\n\tconst daysSinceMs = windowStartMs(now, retentionDays);\n\tconst active = await adapters(sinceMs);\n\tconst historical = await adapters(daysSinceMs);\n\tlet dayScansComplete = true;\n\tlet grokCurrentDates: Map<string, Set<string>> | null = null;\n\tfor (const adapter of active) {\n\t\tprogress(`Scanning recent ${adapter.name} usage`);\n\t\tconst { aggregate, stats } = await adapter.scan({\n\t\t\tsinceMs,\n\t\t\tonProgress: (files) =>\n\t\t\t\tprogress(`Scanning recent ${adapter.name} usage · ${files} files`),\n\t\t});\n\t\tscanStats[adapter.name] = stats;\n\t\tbuilt.push(\n\t\t\tbuildPayload({\n\t\t\t\taggregate,\n\t\t\t\tstats,\n\t\t\t\tsyncConfig: config,\n\t\t\t\tnow,\n\t\t\t\twindowDays,\n\t\t\t\tharnessName: adapter.name,\n\t\t\t\tbuiltinTools: adapter.builtinTools,\n\t\t\t\tprojectWorkspaceId,\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const adapter of historical) {\n\t\tprogress(`Reading historical ${adapter.name} days`);\n\t\tconst { aggregate, workflow, workflowLocal, scanComplete, sessionDates } =\n\t\t\tawait adapter.scan({\n\t\t\t\tsinceMs: daysSinceMs,\n\t\t\t\tonProgress: (files) =>\n\t\t\t\t\tprogress(`Reading historical ${adapter.name} days · ${files} files`),\n\t\t\t});\n\t\tif (scanComplete === false) dayScansComplete = false;\n\t\tif (adapter.name === \"grok-build\")\n\t\t\tgrokCurrentDates = sessionDates ?? new Map();\n\t\tworkflowScans.push({ aggregate: workflow, local: workflowLocal });\n\t\tusageScans.push(\n\t\t\tbuildUsageDays({\n\t\t\t\tharness: adapter.name,\n\t\t\t\taggregate,\n\t\t\t\tpublishCost: config.publishCost,\n\t\t\t\tprojectWorkspaceId,\n\t\t\t}),\n\t\t);\n\t}\n\n\t// The opt-in the machine currently holds, read at stage time so the gate's\n\t// bytes are the bytes sent (#78). Absent from the settings file means this\n\t// machine has never answered, which the backend reads as \"never told us\".\n\tconst settings = (deps.getSettingsImpl ?? getSettings)();\n\n\t// Git runs here and not inside an adapter's scan: the reducers hand back the\n\t// working directories their sessions touched, and reading one repository once\n\t// for all of them is both cheaper and the only way the commit counts stay\n\t// right when two harnesses shared a checkout.\n\t//\n\t// The extraction is skipped entirely when the owner has the switch off. It\n\t// shells out to `git` per repository, and running that work to throw it away\n\t// would be the one visible cost of a preference that is supposed to be free.\n\tlet workflow: WorkflowExtraction | undefined;\n\tif (workflowScans.length > 0 && config.publishWorkflow) {\n\t\tprogress(\"Reading Git history\");\n\t\tworkflow = deps.gitRunnerImpl\n\t\t\t? extractLocalWorkflow({\n\t\t\t\t\tharnesses: workflowScans,\n\t\t\t\t\tfromMs: daysSinceMs,\n\t\t\t\t\ttoMs: now,\n\t\t\t\t\trun: deps.gitRunnerImpl,\n\t\t\t\t})\n\t\t\t: await extractLocalWorkflowAsync({\n\t\t\t\t\tharnesses: workflowScans,\n\t\t\t\t\tfromMs: daysSinceMs,\n\t\t\t\t\ttoMs: now,\n\t\t\t\t});\n\t}\n\n\t// The day rows (#307): usage and workflow joined by date, consent applied\n\t// BEFORE the fingerprint so the hash is over the bytes that go, then diffed\n\t// against the manifest. Today always resends.\n\tconst correctionDates = new Set<string>();\n\tlet acknowledgePublish: (() => void) | undefined;\n\tif (grokCurrentDates && token && config.stack) {\n\t\tconst scope = grokCacheScope(deps.baseUrl, config.stack.slug, token);\n\t\tconst floor = utcDate(daysSinceMs);\n\t\tconst previous = loadGrokDateHints(scope);\n\t\tconst current = mapToHints(grokCurrentDates, floor);\n\t\tfor (const dates of Object.values(previous))\n\t\t\tfor (const date of dates) correctionDates.add(date);\n\t\tfor (const dates of Object.values(current))\n\t\t\tfor (const date of dates) correctionDates.add(date);\n\t\tif (Object.keys(previous).some((id) => !(id in current)))\n\t\t\tdayScansComplete = false;\n\t\tacknowledgePublish = () => saveGrokDateHints(scope, current);\n\t}\n\tconst localDays: MeasuredDay[] = applyDayConsent(\n\t\tbuildMeasuredDays({\n\t\t\tusage: mergeUsageDays(usageScans),\n\t\t\t...(workflow ? { workflow: workflow.days } : {}),\n\t\t\tfrom: utcDate(daysSinceMs),\n\t\t\tto: utcDate(now),\n\t\t\tincludeDates: correctionDates,\n\t\t}),\n\t\tconfig,\n\t);\n\tconst days = selectDaysToPublish({\n\t\tlocal: dayScansComplete ? localDays : [],\n\t\tmanifest,\n\t\ttodayUtc: utcDate(now),\n\t});\n\n\tconst body = buildSyncBody(\n\t\tbuilt,\n\t\tconfig,\n\t\tsettings.autoSync,\n\t\tdeps.trigger,\n\t\thistorical.length > 0 && dayScansComplete\n\t\t\t? {\n\t\t\t\t\taggregateVersion: MEASURED_DAYS_V1,\n\t\t\t\t\tutcOffsetMinutes:\n\t\t\t\t\t\tworkflow?.utcOffsetMinutes ?? machineUtcOffsetMinutes(),\n\t\t\t\t\tdays: days.send,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tCLI_VERSION,\n\t);\n\tprogress(\"Preparing review\");\n\tconst bodyJson = JSON.stringify(body);\n\tconst keptPrivate = mergeKeptPrivate(built.map((b) => b.keptPrivate));\n\n\tconst ctx = {\n\t\tbody,\n\t\tkeptPrivate,\n\t\tconfig,\n\t\tsource,\n\t\tbaseUrl: deps.baseUrl,\n\t\tscanStats,\n\t\tdays,\n\t\tprices,\n\t\t// The real terminal, so the inventory rows break where this window ends\n\t\t// (#217). A pipe reports nothing and the preview falls back to 80.\n\t\twidth: process.stdout.columns,\n\t};\n\n\tlet blockedReason: string | null = null;\n\tif (historical.length === 0) {\n\t\tblockedReason = `No supported harness transcript from the last ${retentionDays} days to read.`;\n\t} else if (token === null) {\n\t\tblockedReason =\n\t\t\t\"This machine is not linked. Run `npx @use-aistack/cli login` first.\";\n\t} else if (config.stack === null) {\n\t\tblockedReason =\n\t\t\tsource === \"bundled\"\n\t\t\t\t? \"Could not fetch your settings from aistack, so the destination stack is unknown. Publish needs it. Check the network and preview again.\"\n\t\t\t\t: \"This machine has no destination stack. Run `npx @use-aistack/cli sync` in an interactive terminal to choose one.\";\n\t}\n\n\treturn {\n\t\tid: stageId(bodyJson),\n\t\tbodyJson,\n\t\tbody,\n\t\tkeptPrivate,\n\t\tsummary: buildGateSummary(ctx),\n\t\tdialog: buildGateDialog(ctx),\n\t\tconfig,\n\t\ttoken,\n\t\tstagedAt: now,\n\t\tblockedReason,\n\t\tdays,\n\t\tprices,\n\t\t...(acknowledgePublish ? { acknowledgePublish } : {}),\n\t};\n}\n","// The per-day usage wire, built from the adapters' per-day seam (#307, map\n// #302, ADR-0010). One `UsageHarnessDay` per (harness, UTC date), holding only\n// combinable atoms: session counts, hashed project keys, per-model token sums,\n// exact dollars priced at each response's own timestamp. No share and no mean\n// leaves here; the server folds a window with `foldUsageDays`.\n//\n// The seam is `Aggregate.usageDays` and `Aggregate.sessionStarts` in\n// ../harness/shared/aggregate.ts, filled by the SAME response stream that\n// fills the window totals, so a fold over these days equals the snapshot's\n// figures up to rounding. Everything here is pure.\n\nimport { baseModelId, pricingTableFor } from \"@aistack/pricing\";\nimport type {\n\tMeasuredDay,\n\tUsageDay,\n\tUsageHarnessDay,\n\tUsageModelDay,\n\tUsageTokens,\n\tWorkflowDay,\n} from \"@aistack/workflow-rules\";\nimport {\n\ttype Aggregate,\n\tcountsTotal,\n\tutcDateOf,\n} from \"../harness/shared/aggregate.js\";\nimport { sanitizeModelId } from \"../harness/shared/payload.js\";\n\n/** Dollars keep six places: exact enough for a per-day sum, stable across runs. */\nconst round6 = (n: number): number => Math.round(n * 1_000_000) / 1_000_000;\n\nexport type BuildUsageDaysInput = {\n\t/** The payload discriminator, e.g. `\"claude-code\"`. */\n\tharness: string;\n\taggregate: Aggregate;\n\t/** THE CONSENT GATE for dollars: off leaves `usd` and `pricingTable` out of the bytes. */\n\tpublishCost: boolean;\n\t/** Resolve one local project directory to its persistent opaque id. */\n\tprojectWorkspaceId: (directory: string) => string;\n};\n\ntype ModelAcc = {\n\ttokens: UsageTokens & {\n\t\tcacheWriteTtl: { fiveMinute: number; oneHour: number; unsplit: number };\n\t};\n\tcostUSD: number;\n\tunpricedTokens: number;\n\ttable: string | null;\n};\n\n/**\n * One harness's usage, one row per UTC date it touched. A date with sessions\n * but no response, or a response but no session start, still gets a row: the\n * fold counts a day active when a session started on it.\n */\nexport function buildUsageDays(\n\tinput: BuildUsageDaysInput,\n): Map<string, UsageHarnessDay> {\n\tconst { aggregate: agg, harness, publishCost, projectWorkspaceId } = input;\n\n\tconst sessionsByDay = new Map<string, number>();\n\tfor (const startMs of agg.sessionStarts.values()) {\n\t\tconst date = utcDateOf(startMs);\n\t\tsessionsByDay.set(date, (sessionsByDay.get(date) ?? 0) + 1);\n\t}\n\n\tconst dates = [\n\t\t...new Set([...agg.usageDays.keys(), ...sessionsByDay.keys()]),\n\t].sort();\n\n\tconst out = new Map<string, UsageHarnessDay>();\n\tfor (const date of dates) {\n\t\tconst acc = agg.usageDays.get(date);\n\t\t// The fast-mode key (`#fast`) is ours, not the vendor's: merge rows onto\n\t\t// the base id the way the snapshot's `groupModels` does. Dollars stay\n\t\t// exact because they were priced per response at the fast rate.\n\t\tconst groups = new Map<string, ModelAcc>();\n\t\tlet unpriced = 0;\n\t\tfor (const [modelKey, m] of acc?.models ?? []) {\n\t\t\tconst id = sanitizeModelId(baseModelId(modelKey));\n\t\t\tlet g = groups.get(id);\n\t\t\tif (!g) {\n\t\t\t\tg = {\n\t\t\t\t\ttokens: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWriteTtl: { fiveMinute: 0, oneHour: 0, unsplit: 0 },\n\t\t\t\t\t},\n\t\t\t\t\tcostUSD: 0,\n\t\t\t\t\tunpricedTokens: 0,\n\t\t\t\t\ttable: null,\n\t\t\t\t};\n\t\t\t\tgroups.set(id, g);\n\t\t\t}\n\t\t\tg.table ??= pricingTableFor(modelKey);\n\t\t\tg.tokens.input += m.counts.input;\n\t\t\tg.tokens.output += m.counts.output;\n\t\t\tg.tokens.cacheRead += m.counts.cacheRead;\n\t\t\tg.tokens.cacheWrite +=\n\t\t\t\tm.counts.cacheWrite5m +\n\t\t\t\tm.counts.cacheWrite1h +\n\t\t\t\tm.counts.cacheWriteUnsplit;\n\t\t\tg.tokens.cacheWriteTtl.fiveMinute += m.counts.cacheWrite5m;\n\t\t\tg.tokens.cacheWriteTtl.oneHour += m.counts.cacheWrite1h;\n\t\t\tg.tokens.cacheWriteTtl.unsplit += m.counts.cacheWriteUnsplit;\n\t\t\tg.costUSD += m.costUSD;\n\t\t\tg.unpricedTokens += m.unpricedTokens;\n\t\t\tunpriced += m.unpricedTokens;\n\t\t}\n\n\t\tconst models: UsageModelDay[] = [...groups.entries()]\n\t\t\t.map(([model, g]) => {\n\t\t\t\tconst { cacheWriteTtl, ...plain } = g.tokens;\n\t\t\t\tconst tokens: UsageTokens =\n\t\t\t\t\tg.tokens.cacheWrite > 0 ? { ...plain, cacheWriteTtl } : plain;\n\t\t\t\tconst row: UsageModelDay = { model, tokens };\n\t\t\t\t// Absent, not zero (#33 decision 11): a model with an unpriced\n\t\t\t\t// response that day carries no dollars, and its tokens sit in\n\t\t\t\t// `excludedTokens.unpriced`. Dollars and their citation travel\n\t\t\t\t// together (#136).\n\t\t\t\tif (\n\t\t\t\t\tpublishCost &&\n\t\t\t\t\tg.unpricedTokens === 0 &&\n\t\t\t\t\tg.table !== null &&\n\t\t\t\t\tcountsTotalOf(tokens) > 0\n\t\t\t\t) {\n\t\t\t\t\trow.usd = round6(g.costUSD);\n\t\t\t\t\trow.pricingTable = g.table;\n\t\t\t\t}\n\t\t\t\treturn row;\n\t\t\t})\n\t\t\t.filter((row) => countsTotalOf(row.tokens) > 0)\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tcountsTotalOf(b.tokens) - countsTotalOf(a.tokens) ||\n\t\t\t\t\ta.model.localeCompare(b.model),\n\t\t\t);\n\n\t\tout.set(date, {\n\t\t\tharness,\n\t\t\tsessions: sessionsByDay.get(date) ?? 0,\n\t\t\tprojectKeys: [\n\t\t\t\t...new Set([...(acc?.projectDirs ?? [])].map(projectWorkspaceId)),\n\t\t\t].sort(),\n\t\t\tmodels,\n\t\t\tsubagentTokens: acc?.subagentTokens ?? 0,\n\t\t\texcludedTokens: { unpriced, synthetic: acc?.syntheticTokens ?? 0 },\n\t\t});\n\t}\n\treturn out;\n}\n\nconst countsTotalOf = (t: UsageTokens): number =>\n\tt.input + t.output + t.cacheWrite + t.cacheRead;\n\n/** Join several harnesses' day maps into one `UsageDay` per date. */\nexport function mergeUsageDays(\n\tperHarness: readonly Map<string, UsageHarnessDay>[],\n): Map<string, UsageDay> {\n\tconst out = new Map<string, UsageDay>();\n\tconst dates = [\n\t\t...new Set(perHarness.flatMap((days) => [...days.keys()])),\n\t].sort();\n\tfor (const date of dates) {\n\t\tconst harnesses: UsageHarnessDay[] = [];\n\t\tfor (const days of perHarness) {\n\t\t\tconst day = days.get(date);\n\t\t\tif (day) harnesses.push(day);\n\t\t}\n\t\tout.set(date, { harnesses });\n\t}\n\treturn out;\n}\n\n/**\n * Join usage days and workflow days by date into the rows the wire carries,\n * limited to `[from, to]` inclusive (`YYYY-MM-DD`). A date outside the window\n * is a clock-skewed or restored transcript and never becomes a row.\n */\nexport function buildMeasuredDays(input: {\n\tusage: Map<string, UsageDay>;\n\tworkflow?: readonly WorkflowDay[];\n\tfrom: string;\n\tto: string;\n\tincludeDates?: ReadonlySet<string>;\n}): MeasuredDay[] {\n\tconst workflowByDate = new Map<string, WorkflowDay>();\n\tfor (const day of input.workflow ?? []) workflowByDate.set(day.date, day);\n\tconst dates = [\n\t\t...new Set([\n\t\t\t...input.usage.keys(),\n\t\t\t...workflowByDate.keys(),\n\t\t\t...(input.includeDates ?? []),\n\t\t]),\n\t]\n\t\t.filter(\n\t\t\t(d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d) && d >= input.from && d <= input.to,\n\t\t)\n\t\t.sort();\n\treturn dates.map((date) => {\n\t\tconst usage = input.usage.get(date);\n\t\tconst workflow = workflowByDate.get(date);\n\t\treturn {\n\t\t\tdate,\n\t\t\t...(usage ? { usage } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t};\n\t});\n}\n\n/** `input + output + cacheWrite + cacheRead` over a `TokenCounts`, for tests. */\nexport { countsTotal };\n","// Diff-only sync (#307, ADR-0010): which local days go on the wire.\n//\n// The server says what it holds in a day manifest, and the CLI sends only the\n// dates it lacks or holds differently. Pure: the manifest fetch lives in\n// ../api.ts and the decision to fall back lives in ../sync/stage.ts.\n\nimport {\n\tdayFingerprint,\n\tMEASURED_DAYS_V1,\n\ttype MeasuredDay,\n} from \"@aistack/workflow-rules\";\n\n/** The server's answer to `GET /api/cli/sync-manifest`. */\nexport type DayManifest = {\n\tretentionDays: number;\n\taggregateVersion: string;\n\tdays: { date: string; fingerprint: string }[];\n};\n\n/** The most days the CLI ever sends: the page's read cap, and the scan's reach. */\nexport const MAX_DAY_WINDOW = 400;\n\nexport type DaySkipReason = \"unchanged\" | \"expired\";\n\nexport type DaySelection = {\n\t/** Missing from the manifest, changed since, or today. */\n\tsend: MeasuredDay[];\n\t/** Dates the server already holds with the same fingerprint. */\n\tunchanged: number;\n\tskipped: { date: string; reason: DaySkipReason }[];\n\t/** How the selection was made, for the gate's one line about it. */\n\tmode: \"diff\" | \"full\";\n};\n\nconst DAY_MS = 86_400_000;\n\n/** The oldest date still inside a retention of `days` ending on `today`. */\nexport function retentionFloor(todayUtc: string, days: number): string {\n\tconst span = Math.max(1, Math.min(days, MAX_DAY_WINDOW));\n\tconst todayMs = Date.parse(`${todayUtc}T00:00:00.000Z`);\n\treturn new Date(todayMs - (span - 1) * DAY_MS).toISOString().slice(0, 10);\n}\n\n/**\n * Pick the days to publish.\n *\n * - No manifest (old server, network failure): every local day inside the\n * default retention goes, and `mode` says `full`.\n * - A manifest on another aggregate version: its fingerprints mean nothing to\n * this CLI, so every day goes, inside the retention it names.\n * - Otherwise a day goes when its date is missing from the manifest, when its\n * fingerprint differs, or when it is today (still running, always resends).\n * - A date older than the retention is dropped: the server would expire it\n * on arrival.\n */\nexport function selectDaysToPublish(input: {\n\tlocal: readonly MeasuredDay[];\n\tmanifest: DayManifest | null;\n\ttodayUtc: string;\n}): DaySelection {\n\tconst { local, manifest, todayUtc } = input;\n\tconst retention = manifest?.retentionDays ?? MAX_DAY_WINDOW;\n\tconst floor = retentionFloor(todayUtc, retention);\n\tconst comparable =\n\t\tmanifest !== null && manifest.aggregateVersion === MEASURED_DAYS_V1;\n\tconst held = new Map<string, string>();\n\tif (comparable) {\n\t\tfor (const day of manifest.days) held.set(day.date, day.fingerprint);\n\t}\n\n\tconst send: MeasuredDay[] = [];\n\tconst skipped: DaySelection[\"skipped\"] = [];\n\tfor (const day of [...local].sort((a, b) => a.date.localeCompare(b.date))) {\n\t\tif (day.date < floor) {\n\t\t\tskipped.push({ date: day.date, reason: \"expired\" });\n\t\t\tcontinue;\n\t\t}\n\t\tif (comparable && day.date !== todayUtc) {\n\t\t\tconst fingerprint = held.get(day.date);\n\t\t\tif (fingerprint !== undefined && fingerprint === dayFingerprint(day)) {\n\t\t\t\tskipped.push({ date: day.date, reason: \"unchanged\" });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tsend.push(day);\n\t}\n\treturn {\n\t\tsend,\n\t\tunchanged: skipped.filter((s) => s.reason === \"unchanged\").length,\n\t\tskipped,\n\t\tmode: comparable ? \"diff\" : \"full\",\n\t};\n}\n","import { execFile, execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type { GitDay } from \"@aistack/workflow-rules\";\n\n/**\n * Both rules changed together in #278: a path a machine owns (a dependency\n * tree, build output, a lockfile) no longer reaches either of them, so both the\n * test-file count and the file-type mix can differ from what v1 published for\n * the same repository.\n */\nexport const TEST_FILE_RULE_VERSION = \"test-files/v2\";\nexport const FILE_TYPE_RULE_VERSION = \"file-types/v2\";\n/**\n * Which commits count at all (#279). A merge commit and a commit whose every\n * path is machine-owned leave the reading. Without this id a reading synced\n * before the rule and one synced after are indistinguishable on the wire while\n * disagreeing about which commits exist.\n */\nexport const COMMIT_SET_RULE_VERSION = \"commit-set/v1\";\n\nexport type GitWorkflowRunner = (\n\tcwd: string,\n\targs: readonly string[],\n) => string | null;\n\nexport type AsyncGitWorkflowRunner = (\n\tcwd: string,\n\targs: readonly string[],\n) => Promise<string | null>;\n\n/** One UTC day of Git history, with the day it belongs to. */\nexport type GitDayRow = GitDay & { date: string };\n\n/**\n * Git history for the touched repositories, one row per UTC day that holds a\n * counted commit (#285). A commit belongs to the day of its author time.\n */\nexport type GitWorkflowResult = {\n\tdays: GitDayRow[];\n};\n\nexport type ExtractGitWorkflowOptions = {\n\t/** Local working directories touched by sessions inside the sync window. */\n\tworkingDirectories: Iterable<string>;\n\tfromMs: number;\n\ttoMs: number;\n\t/**\n\t * This machine's offset from UTC, in minutes east. Cells ship in UTC, and the\n\t * late-night count reads those same cells through this offset, so the count\n\t * and the grid always agree. Every commit uses the one offset, not its own.\n\t */\n\tutcOffsetMinutes: number;\n\trun?: GitWorkflowRunner;\n};\n\nconst defaultRunner: GitWorkflowRunner = (cwd, args) => {\n\ttry {\n\t\treturn execFileSync(\"git\", [...args], {\n\t\t\tcwd,\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t\tmaxBuffer: 64 * 1024 * 1024,\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nconst defaultAsyncRunner: AsyncGitWorkflowRunner = (cwd, args) =>\n\tnew Promise((resolve) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[...args],\n\t\t\t{\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t\tmaxBuffer: 64 * 1024 * 1024,\n\t\t\t},\n\t\t\t(error, stdout) => resolve(error ? null : stdout),\n\t\t);\n\t});\n\n/** A day with no counted commit, carrying the rule ids a fold needs. */\nexport const emptyGitDay = (): GitDay => ({\n\ttestFileRuleVersion: TEST_FILE_RULE_VERSION,\n\tfileTypeRuleVersion: FILE_TYPE_RULE_VERSION,\n\tcommitSetRuleVersion: COMMIT_SET_RULE_VERSION,\n\tcommits: 0,\n\tlateNightCommits: 0,\n\tadditions: 0,\n\tremovals: 0,\n\tchangedLinesPerCommit: [],\n\ttestFileCommits: 0,\n\tchangedLinesByExtension: [],\n\twithheldExtensionLines: 0,\n\tweekdayHourCells: [],\n});\n\ntype MutableGitDay = Omit<\n\tGitDay,\n\t\"changedLinesPerCommit\" | \"changedLinesByExtension\" | \"weekdayHourCells\"\n> & {\n\tchangedLinesPerCommit: number[];\n\textensionLines: Map<string, number>;\n\tcells: Map<string, number>;\n};\n\n/**\n * The names this rule is willing to print. A path with no extension is absent\n * on purpose: `Dockerfile`, `LICENSE` and `.gitignore` are not coding\n * languages, and ranking them as one made the leading language of a TypeScript\n * repository read as `(none)`.\n */\nconst APPROVED_EXTENSIONS: ReadonlySet<string> = new Set([\n\t\".c\",\n\t\".cc\",\n\t\".cjs\",\n\t\".cpp\",\n\t\".cs\",\n\t\".css\",\n\t\".cts\",\n\t\".dart\",\n\t\".ex\",\n\t\".exs\",\n\t\".go\",\n\t\".h\",\n\t\".hpp\",\n\t\".html\",\n\t\".java\",\n\t\".js\",\n\t\".jsx\",\n\t\".json\",\n\t\".kt\",\n\t\".kts\",\n\t\".lua\",\n\t\".md\",\n\t\".mjs\",\n\t\".mts\",\n\t\".php\",\n\t\".py\",\n\t\".r\",\n\t\".rb\",\n\t\".rs\",\n\t\".scala\",\n\t\".scss\",\n\t\".sh\",\n\t\".sql\",\n\t\".svelte\",\n\t\".swift\",\n\t\".toml\",\n\t\".ts\",\n\t\".tsx\",\n\t\".vue\",\n\t\".xml\",\n\t\".yaml\",\n\t\".yml\",\n\t\".zig\",\n]);\n\n/**\n * Directory names a machine owns rather than a person. A dependency tree, a\n * build output directory, or a directory of captured tool output can carry\n * millions of changed lines that nobody wrote, and one accidental commit of one\n * of them is enough to bury every authored line in the reading.\n */\nconst UNAUTHORED_SEGMENTS: ReadonlySet<string> = new Set([\n\t\".bundle\",\n\t\".cache\",\n\t\".cargo\",\n\t\".gradle\",\n\t\".next\",\n\t\".nuxt\",\n\t\".pnpm\",\n\t\".pnpm-store\",\n\t\".svelte-kit\",\n\t\".turbo\",\n\t\".venv\",\n\t\"_generated\",\n\t\"bower_components\",\n\t\"build\",\n\t\"coverage\",\n\t\"dist\",\n\t\"generated\",\n\t\"node_modules\",\n\t\"out\",\n\t\"pods\",\n\t\"site-packages\",\n\t\"target\",\n\t\"third_party\",\n\t\"vendor\",\n\t\"venv\",\n\t\"__pycache__\",\n]);\n\n/** Dependency lockfiles. A resolver writes these, and their extension lies. */\nconst UNAUTHORED_BASENAMES: ReadonlySet<string> = new Set([\n\t\"bun.lock\",\n\t\"bun.lockb\",\n\t\"cargo.lock\",\n\t\"composer.lock\",\n\t\"flake.lock\",\n\t\"gemfile.lock\",\n\t\"go.sum\",\n\t\"mix.lock\",\n\t\"npm-shrinkwrap.json\",\n\t\"package-lock.json\",\n\t\"packages.lock.json\",\n\t\"pipfile.lock\",\n\t\"pnpm-lock.yaml\",\n\t\"podfile.lock\",\n\t\"poetry.lock\",\n\t\"pubspec.lock\",\n\t\"uv.lock\",\n\t\"yarn.lock\",\n]);\n\n/**\n * True when the path is machine-written rather than authored. Those lines leave\n * the reading entirely: they are not withheld, because withholding keeps a line\n * in the denominator, and a line nobody wrote does not belong in either half.\n */\nfunction isUnauthoredPath(file: string): boolean {\n\tconst parts = file.replaceAll(\"\\\\\", \"/\").toLowerCase().split(\"/\");\n\tif (parts.some((part) => UNAUTHORED_SEGMENTS.has(part))) return true;\n\treturn UNAUTHORED_BASENAMES.has(parts.at(-1) ?? \"\");\n}\n\nconst COMMIT_MARKER = \"aistack-commit\";\n\nfunction parseNumstat(\n\tfield: string,\n): { additions: number; removals: number; file: string } | null {\n\tconst normalized = field.replace(/^\\n+(?=(?:\\d+|-)\\t)/, \"\");\n\tconst firstTab = normalized.indexOf(\"\\t\");\n\tconst secondTab = normalized.indexOf(\"\\t\", firstTab + 1);\n\tif (firstTab <= 0 || secondTab <= firstTab) return null;\n\tconst additionsRaw = normalized.slice(0, firstTab);\n\tconst removalsRaw = normalized.slice(firstTab + 1, secondTab);\n\tif (!/^(?:\\d+|-)$/.test(additionsRaw)) return null;\n\tif (!/^(?:\\d+|-)$/.test(removalsRaw)) return null;\n\treturn {\n\t\tadditions: additionsRaw === \"-\" ? 0 : Number(additionsRaw),\n\t\tremovals: removalsRaw === \"-\" ? 0 : Number(removalsRaw),\n\t\tfile: normalized.slice(secondTab + 1),\n\t};\n}\n\nfunction isTestFile(file: string): boolean {\n\tconst normalized = file.replaceAll(\"\\\\\", \"/\").toLowerCase();\n\tconst parts = normalized.split(\"/\");\n\tif (parts.some((part) => [\"test\", \"tests\", \"__tests__\"].includes(part))) {\n\t\treturn true;\n\t}\n\tconst basename = parts.at(-1) ?? \"\";\n\treturn /(?:^|[._-])(test|spec)(?:[._-]|$)/.test(basename);\n}\n\nfunction utcCell(authoredMs: number): { weekdayUtc: number; hourUtc: number } {\n\tconst at = new Date(authoredMs);\n\treturn { weekdayUtc: at.getUTCDay(), hourUtc: at.getUTCHours() };\n}\n\n/** The hour on the machine's clock for a UTC cell. */\nfunction localHour(hourUtc: number, utcOffsetMinutes: number): number {\n\treturn (\n\t\t((((hourUtc * 60 + utcOffsetMinutes) % (24 * 60)) + 24 * 60) % (24 * 60)) /\n\t\t60\n\t);\n}\n\nfunction isLateNight(hour: number): boolean {\n\treturn hour >= 23 || hour < 3;\n}\n\n/**\n * Reduce Git history for the repositories touched by windowed harness sessions.\n * Repository roots and paths exist only during this call and never enter the result.\n */\nexport function extractGitWorkflow(\n\toptions: ExtractGitWorkflowOptions,\n): GitWorkflowResult {\n\tconst run = options.run ?? defaultRunner;\n\tconst roots = new Set<string>();\n\tfor (const directory of options.workingDirectories) {\n\t\tconst root = run(directory, [\"rev-parse\", \"--show-toplevel\"])?.trim();\n\t\tif (root) roots.add(root);\n\t}\n\tconst histories: string[] = [];\n\tfor (const root of roots) {\n\t\tconst history = run(root, gitLogArgs());\n\t\tif (history) histories.push(history);\n\t}\n\treturn reduceGitHistories(histories, options);\n}\n\n/** The non-blocking production path. Tests can keep using the synchronous seam. */\nexport async function extractGitWorkflowAsync(\n\toptions: Omit<ExtractGitWorkflowOptions, \"run\"> & {\n\t\trun?: AsyncGitWorkflowRunner;\n\t},\n): Promise<GitWorkflowResult> {\n\tconst run = options.run ?? defaultAsyncRunner;\n\tconst roots = new Set<string>();\n\tfor (const directory of options.workingDirectories) {\n\t\tconst root = (\n\t\t\tawait run(directory, [\"rev-parse\", \"--show-toplevel\"])\n\t\t)?.trim();\n\t\tif (root) roots.add(root);\n\t}\n\tconst histories = await Promise.all(\n\t\t[...roots].map((root) => run(root, gitLogArgs())),\n\t);\n\treturn reduceGitHistories(\n\t\thistories.filter((history): history is string => history !== null),\n\t\toptions,\n\t);\n}\n\nfunction gitLogArgs(): readonly string[] {\n\treturn [\n\t\t\"log\",\n\t\t\"--all\",\n\t\t\"--no-merges\",\n\t\t`--format=%x00${COMMIT_MARKER}%x00%H%x00%aI%x00`,\n\t\t\"--numstat\",\n\t\t\"-z\",\n\t];\n}\n\nfunction reduceGitHistories(\n\thistories: Iterable<string>,\n\toptions: Pick<\n\t\tExtractGitWorkflowOptions,\n\t\t\"fromMs\" | \"toMs\" | \"utcOffsetMinutes\"\n\t>,\n): GitWorkflowResult {\n\tconst days = new Map<string, MutableGitDay>();\n\tconst dayOf = (date: string): MutableGitDay => {\n\t\tlet day = days.get(date);\n\t\tif (!day) {\n\t\t\tconst {\n\t\t\t\tchangedLinesByExtension: _extensions,\n\t\t\t\tweekdayHourCells: _cells,\n\t\t\t\t...rest\n\t\t\t} = emptyGitDay();\n\t\t\tday = {\n\t\t\t\t...rest,\n\t\t\t\tchangedLinesPerCommit: [],\n\t\t\t\textensionLines: new Map(),\n\t\t\t\tcells: new Map(),\n\t\t\t};\n\t\t\tdays.set(date, day);\n\t\t}\n\t\treturn day;\n\t};\n\tconst seenCommits = new Set<string>();\n\tfor (const history of histories) {\n\t\ttype CurrentCommit = {\n\t\t\tincluded: boolean;\n\t\t\tdate: string;\n\t\t\tcell: { weekdayUtc: number; hourUtc: number };\n\t\t\t/** True once one path a person could have written appears. */\n\t\t\tauthored: boolean;\n\t\t\tadditions: number;\n\t\t\tremovals: number;\n\t\t\tchangedLines: number;\n\t\t\ttouchesTest: boolean;\n\t\t\twithheldLines: number;\n\t\t\textensionLines: Map<string, number>;\n\t\t};\n\t\tlet current: CurrentCommit | undefined;\n\t\t// A commit counts only once its records are read: one with no authored\n\t\t// path leaves the reading entirely, rather than surviving as a commit\n\t\t// that changed nothing (commit-set/v1).\n\t\tconst finishCommit = (): void => {\n\t\t\tif (!current?.included || !current.authored) return;\n\t\t\tconst day = dayOf(current.date);\n\t\t\tday.commits++;\n\t\t\tday.additions += current.additions;\n\t\t\tday.removals += current.removals;\n\t\t\tday.changedLinesPerCommit.push(current.changedLines);\n\t\t\tif (current.touchesTest) day.testFileCommits++;\n\t\t\tconst { weekdayUtc, hourUtc } = current.cell;\n\t\t\tif (isLateNight(localHour(hourUtc, options.utcOffsetMinutes))) {\n\t\t\t\tday.lateNightCommits++;\n\t\t\t}\n\t\t\tconst cellKey = `${weekdayUtc}:${hourUtc}`;\n\t\t\tday.cells.set(cellKey, (day.cells.get(cellKey) ?? 0) + 1);\n\t\t\tday.withheldExtensionLines += current.withheldLines;\n\t\t\tfor (const [extension, lines] of current.extensionLines) {\n\t\t\t\tday.extensionLines.set(\n\t\t\t\t\textension,\n\t\t\t\t\t(day.extensionLines.get(extension) ?? 0) + lines,\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\tconst fields = history.split(\"\\u0000\");\n\t\tfor (let fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {\n\t\t\tconst field = fields[fieldIndex] ?? \"\";\n\t\t\tif (field.replace(/^\\n+/, \"\") === COMMIT_MARKER) {\n\t\t\t\tfinishCommit();\n\t\t\t\tconst hash = fields[++fieldIndex] ?? \"\";\n\t\t\t\tconst authoredAt = fields[++fieldIndex] ?? \"\";\n\t\t\t\tconst authoredMs = Date.parse(authoredAt);\n\t\t\t\tconst included =\n\t\t\t\t\tNumber.isFinite(authoredMs) &&\n\t\t\t\t\tauthoredMs >= options.fromMs &&\n\t\t\t\t\tauthoredMs <= options.toMs &&\n\t\t\t\t\t!seenCommits.has(hash);\n\t\t\t\tcurrent = {\n\t\t\t\t\tincluded,\n\t\t\t\t\tdate: included ? new Date(authoredMs).toISOString().slice(0, 10) : \"\",\n\t\t\t\t\tcell: utcCell(included ? authoredMs : 0),\n\t\t\t\t\tauthored: false,\n\t\t\t\t\tadditions: 0,\n\t\t\t\t\tremovals: 0,\n\t\t\t\t\tchangedLines: 0,\n\t\t\t\t\ttouchesTest: false,\n\t\t\t\t\twithheldLines: 0,\n\t\t\t\t\textensionLines: new Map(),\n\t\t\t\t};\n\t\t\t\tif (included) seenCommits.add(hash);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst stat = parseNumstat(field);\n\t\t\tif (!stat) continue;\n\t\t\tlet file = stat.file;\n\t\t\tif (file.length === 0) {\n\t\t\t\tfieldIndex += 2;\n\t\t\t\tfile = fields[fieldIndex] ?? fields[fieldIndex - 1] ?? \"\";\n\t\t\t}\n\t\t\tif (!current?.included) continue;\n\t\t\tif (isUnauthoredPath(file)) continue;\n\t\t\tcurrent.authored = true;\n\t\t\tconst fileChangedLines = stat.additions + stat.removals;\n\t\t\tcurrent.additions += stat.additions;\n\t\t\tcurrent.removals += stat.removals;\n\t\t\tcurrent.changedLines += fileChangedLines;\n\t\t\tif (isTestFile(file)) current.touchesTest = true;\n\t\t\tif (fileChangedLines <= 0) continue;\n\t\t\t// An empty extension is not in the approved set, so it withholds.\n\t\t\tconst extension = path.extname(file).toLowerCase();\n\t\t\tif (APPROVED_EXTENSIONS.has(extension)) {\n\t\t\t\tcurrent.extensionLines.set(\n\t\t\t\t\textension,\n\t\t\t\t\t(current.extensionLines.get(extension) ?? 0) + fileChangedLines,\n\t\t\t\t);\n\t\t\t} else current.withheldLines += fileChangedLines;\n\t\t}\n\t\tfinishCommit();\n\t}\n\n\treturn {\n\t\tdays: [...days]\n\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t.map(([date, day]) => {\n\t\t\t\tconst { extensionLines, cells, ...rest } = day;\n\t\t\t\treturn {\n\t\t\t\t\tdate,\n\t\t\t\t\t...rest,\n\t\t\t\t\tchangedLinesByExtension: [...extensionLines]\n\t\t\t\t\t\t.map(([extension, changedLines]) => ({ extension, changedLines }))\n\t\t\t\t\t\t.sort((a, b) => a.extension.localeCompare(b.extension)),\n\t\t\t\t\tweekdayHourCells: [...cells]\n\t\t\t\t\t\t.map(([key, commits]) => {\n\t\t\t\t\t\t\tconst [weekdayUtc, hourUtc] = key.split(\":\").map(Number);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tweekdayUtc: weekdayUtc ?? 0,\n\t\t\t\t\t\t\t\thourUtc: hourUtc ?? 0,\n\t\t\t\t\t\t\t\tcommits,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t(a, b) => a.weekdayUtc - b.weekdayUtc || a.hourUtc - b.hourUtc,\n\t\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}),\n\t};\n}\n","import {\n\ttype GitDay,\n\ttype HarnessDay,\n\tWORKFLOW_AGGREGATES_V3,\n\ttype WorkflowDay,\n} from \"@aistack/workflow-rules\";\nimport {\n\temptyGitDay,\n\textractGitWorkflow,\n\textractGitWorkflowAsync,\n\ttype GitWorkflowResult,\n\ttype GitWorkflowRunner,\n} from \"./git.js\";\nimport type {\n\tHarnessWorkflowAggregate,\n\tWorkflowLocalSources,\n} from \"./reducer.js\";\n\nexport type LocalHarnessWorkflow = {\n\taggregate: HarnessWorkflowAggregate;\n\tlocal: WorkflowLocalSources;\n};\n\n/**\n * The workflow section as extracted on the machine (#285): one row per UTC\n * day, each holding only combinable atoms. The server folds a window out of\n * these and computes every row there; nothing here computes a share, a median\n * or a rank.\n */\nexport type WorkflowExtraction = {\n\taggregateVersion: typeof WORKFLOW_AGGREGATES_V3;\n\t/**\n\t * This machine's offset from UTC, in minutes east (#218). Session hours ship\n\t * in UTC, and the page renders them in the owner's local time. The machine is\n\t * the only end of the wire that knows which clock the owner reads.\n\t */\n\tutcOffsetMinutes: number;\n\tdays: WorkflowDay[];\n};\n\nexport type ExtractLocalWorkflowOptions = {\n\tharnesses: readonly LocalHarnessWorkflow[];\n\tfromMs: number;\n\ttoMs: number;\n\trun?: GitWorkflowRunner;\n\t/** Tests only: pin the machine clock so a fixture does not move with the runner's zone. */\n\tutcOffsetMinutes?: number;\n};\n\n/** Read only repositories touched by windowed sessions, then return safe daily rows. */\nexport function extractLocalWorkflow(\n\toptions: ExtractLocalWorkflowOptions,\n): WorkflowExtraction {\n\tconst utcOffsetMinutes =\n\t\toptions.utcOffsetMinutes ?? machineUtcOffsetMinutes();\n\tconst git = extractGitWorkflow({\n\t\tworkingDirectories: options.harnesses.flatMap(({ local }) => [\n\t\t\t...local.projectWorkspaces,\n\t\t]),\n\t\tfromMs: options.fromMs,\n\t\ttoMs: options.toMs,\n\t\tutcOffsetMinutes,\n\t\t...(options.run ? { run: options.run } : {}),\n\t});\n\treturn buildWorkflowExtraction(options.harnesses, git, utcOffsetMinutes);\n}\n\n/** Production extraction with Git subprocesses that do not block terminal UI. */\nexport async function extractLocalWorkflowAsync(\n\toptions: Omit<ExtractLocalWorkflowOptions, \"run\">,\n): Promise<WorkflowExtraction> {\n\tconst utcOffsetMinutes =\n\t\toptions.utcOffsetMinutes ?? machineUtcOffsetMinutes();\n\tconst git = await extractGitWorkflowAsync({\n\t\tworkingDirectories: options.harnesses.flatMap(({ local }) => [\n\t\t\t...local.projectWorkspaces,\n\t\t]),\n\t\tfromMs: options.fromMs,\n\t\ttoMs: options.toMs,\n\t\tutcOffsetMinutes,\n\t});\n\treturn buildWorkflowExtraction(options.harnesses, git, utcOffsetMinutes);\n}\n\n/** Minutes EAST of UTC, the sign convention the wire and the page both read. */\nexport function machineUtcOffsetMinutes(now: Date = new Date()): number {\n\treturn -now.getTimezoneOffset();\n}\n\n/**\n * Join the harness days and the Git days by date.\n *\n * A harness that failed its gate over the window ships every day WITHOUT its\n * phase block: the gate is a window judgment (see `HarnessWorkflowAggregate`),\n * and a day that shipped phase atoms anyway could be folded into a playbook\n * the gate refused. The parallel-project count is the union of workspaces\n * across harnesses on that day, counted here because one workspace opened by\n * two harnesses is one project.\n *\n * Local session keys, project paths, event arguments and timestamps do not\n * enter the returned value.\n */\nexport function buildWorkflowExtraction(\n\tharnessWorkflows: readonly LocalHarnessWorkflow[],\n\tgit: GitWorkflowResult,\n\tutcOffsetMinutes: number = machineUtcOffsetMinutes(),\n): WorkflowExtraction {\n\tconst harnessDays = new Map<string, HarnessDay[]>();\n\tconst projectDays = new Map<string, Set<string>>();\n\tfor (const { aggregate, local } of harnessWorkflows) {\n\t\tfor (const { date, ...day } of aggregate.days) {\n\t\t\tconst rows = harnessDays.get(date) ?? [];\n\t\t\tconst { phase, ...safe } = day;\n\t\t\trows.push(\n\t\t\t\taggregate.gate.publishable && phase ? { ...safe, phase } : safe,\n\t\t\t);\n\t\t\tharnessDays.set(date, rows);\n\t\t}\n\t\tfor (const [date, workspaces] of local.activeProjectDays) {\n\t\t\tconst projects = projectDays.get(date) ?? new Set<string>();\n\t\t\tfor (const project of workspaces) projects.add(project);\n\t\t\tprojectDays.set(date, projects);\n\t\t}\n\t}\n\tconst gitDays = new Map<string, GitDay>();\n\tfor (const { date, ...day } of git.days) gitDays.set(date, day);\n\n\tconst dates = [\n\t\t...new Set([\n\t\t\t...harnessDays.keys(),\n\t\t\t...gitDays.keys(),\n\t\t\t...projectDays.keys(),\n\t\t]),\n\t].sort();\n\n\treturn {\n\t\taggregateVersion: WORKFLOW_AGGREGATES_V3,\n\t\tutcOffsetMinutes,\n\t\tdays: dates.map((date) => {\n\t\t\tconst projects = projectDays.get(date)?.size;\n\t\t\treturn {\n\t\t\t\tdate,\n\t\t\t\tharnesses: harnessDays.get(date) ?? [],\n\t\t\t\tgit: gitDays.get(date) ?? emptyGitDay(),\n\t\t\t\t...(projects === undefined ? {} : { parallelProjects: projects }),\n\t\t\t};\n\t\t}),\n\t};\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nexport type GrokDateHints = Record<string, string[]>;\ntype Cache = Record<string, GrokDateHints>;\nconst defaultFile = path.join(\n\thomedir(),\n\t\".config\",\n\t\"aistack\",\n\t\"grok-session-dates.json\",\n);\n\nexport function grokCacheScope(\n\tbaseUrl: string,\n\tstack: string,\n\ttoken: string,\n): string {\n\treturn createHash(\"sha256\")\n\t\t.update(`${baseUrl}\\0${stack}\\0${token}`)\n\t\t.digest(\"hex\");\n}\nfunction read(file: string): Cache {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(file, \"utf8\"));\n\t\treturn value && typeof value === \"object\" ? (value as Cache) : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\nexport function loadGrokDateHints(\n\tscope: string,\n\tfile = defaultFile,\n): GrokDateHints {\n\treturn read(file)[scope] ?? {};\n}\nexport function saveGrokDateHints(\n\tscope: string,\n\thints: GrokDateHints,\n\tfile = defaultFile,\n): void {\n\tconst cache = read(file);\n\tcache[scope] = hints;\n\tmkdirSync(path.dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(cache, null, 2));\n}\nexport function mapToHints(\n\tvalue: Map<string, Set<string>>,\n\tfloor: string,\n): GrokDateHints {\n\treturn Object.fromEntries(\n\t\t[...value].map(([id, dates]) => [\n\t\t\tid,\n\t\t\t[...dates].filter((d) => d >= floor).sort(),\n\t\t]),\n\t);\n}\n","// The approve gate's two beats, as text.\n//\n// Wayfinder ticket #41 (map #29), shape fixed by the spike #35 and the copy\n// locked in #48. Beat one is the FULL summary, printed as ordinary scrollable\n// transcript output. Beat two is the SHORT elicitation message - it must stay\n// short, or `Accept` falls below the fold and the gate times out (#35, 1H).\n//\n// Everything here derives from the exact bytes that will be sent (`body`),\n// plus the local-only kept-private list that deliberately never enters them.\n// Nothing in this file is accepted as a caller-supplied argument beside the\n// payload - the spike promoted that from a caution to a demonstrated property.\n\nimport {\n\tfoldWorkflowDays,\n\ttype MeasuredDay,\n\tWORKFLOW_AGGREGATES_V3,\n} from \"@aistack/workflow-rules\";\nimport { HARNESS_ADAPTERS, harnessLabel } from \"../harness/index.js\";\nimport type {\n\tKeptPrivateAtom,\n\tNameCategory,\n\tSyncConfig,\n\tSyncConfigSource,\n} from \"../harness/shared/allowlist.js\";\nimport { NAME_CATEGORIES } from \"../harness/shared/allowlist.js\";\nimport type {\n\tMeasuredPayload,\n\tPayloadMeasuredDays,\n\tSyncBody,\n} from \"../harness/shared/payload.js\";\nimport type { ScanStats } from \"../harness/shared/window.js\";\nimport type { DaySelection } from \"../usage/diff.js\";\n\nexport type GateContext = {\n\t/** The exact request body a publish would send. */\n\tbody: SyncBody;\n\t/** The local-only review list - never inside any payload (#44). */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Web origin for the URLs the gate prints, e.g. https://aistack.to */\n\tbaseUrl: string;\n\t/**\n\t * Terminal width the preview wraps to. Absent means 80, which is what a\n\t * pipe or a test gets: the gate must render the same way everywhere except\n\t * for where the lines break.\n\t */\n\twidth?: number;\n\t/**\n\t * Per-harness scan stats, keyed by harness name - the LOCAL-ONLY detail\n\t * behind the payload's bare coverage counts (#75): unreadable file names,\n\t * error classes, foreign-file originators. Like `keptPrivate`, it rides\n\t * beside the body and never inside it.\n\t */\n\tscanStats?: Record<string, ScanStats>;\n\t/**\n\t * Which price table priced the dollars in the body (#336). Printed beside\n\t * them, with the sources each figure cites, so a reader can tell a served\n\t * rate from a bundled one.\n\t */\n\tprices?: { id: string; origin: \"served\" | \"bundled\" };\n\t/**\n\t * How the day rows in `body.measuredDays` were chosen (#307). The rows in\n\t * the bytes are the ones going; this says how many the server already held\n\t * unchanged, which the bytes cannot say.\n\t */\n\tdays?: DaySelection;\n};\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\n/** `4.27B`, `40.7M`, `216k`, `950` - three significant digits, like #40. */\nexport function fmtTokens(n: number): string {\n\tconst sig = (v: number): string => {\n\t\tconst s = v.toPrecision(3);\n\t\treturn s.includes(\".\") ? s.replace(/\\.?0+$/, \"\") : s;\n\t};\n\tif (n >= 1e9) return `${sig(n / 1e9)}B`;\n\tif (n >= 1e6) return `${sig(n / 1e6)}M`;\n\tif (n >= 1e3) return `${sig(n / 1e3)}k`;\n\treturn String(n);\n}\n\n/** `≈$5,840` - whole dollars; the ≈ and \"at API prices\" wording are #37's. */\nexport function fmtUSD(n: number): string {\n\treturn `≈$${Math.round(n).toLocaleString(\"en-US\")}`;\n}\n\nconst fmtPct = (share: number): string => `${(share * 100).toFixed(1)}%`;\n\n/**\n * `2026-08-10 21:03 UTC` - the publish receipt's stamp (#130). Milliseconds\n * and the ISO `T`/`Z` machine form dropped: the last thing a person reads\n * should be the result, not a receipt.\n */\nexport function fmtReceivedAt(ms: number): string {\n\treturn `${new Date(ms).toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n}\n\n/**\n * The dollar figure the gate names, or `null` when none may render.\n *\n * Mirrors the public display's rule (#46): a dollar figure never renders\n * without its pricing table. Summing only the models that carry the field\n * matches what actually goes up - an unpriceable model publishes tokens, not\n * dollars.\n */\nexport function totalUSD(payload: MeasuredPayload): number | null {\n\tlet sum = 0;\n\tlet any = false;\n\tfor (const m of payload.models) {\n\t\tif (m.apiEquivalentUSD === undefined) continue;\n\t\t// The citation may sit on the model (#136) or, in the old single-vendor\n\t\t// shape, on the payload. A figure neither cites stays unrendered.\n\t\tif (m.pricingTable === undefined && payload.pricingTable === null) continue;\n\t\tsum += m.apiEquivalentUSD;\n\t\tany = true;\n\t}\n\treturn any ? sum : null;\n}\n\n/** The normalized processed-token split, when model rows cover the headline. */\nexport function tokenBreakdown(\n\tpayload: MeasuredPayload,\n): { fresh: number; cached: number } | null {\n\tlet fresh = 0;\n\tlet cached = 0;\n\tfor (const model of payload.models) {\n\t\tfresh += model.tokens.input + model.tokens.output + model.tokens.cacheWrite;\n\t\tcached += model.tokens.cacheRead;\n\t}\n\treturn fresh + cached === payload.activity.totalTokens\n\t\t? { fresh, cached }\n\t\t: null;\n}\n\n/** DISTINCT kept-private names, from the send bytes (`inventory.withheld`). */\nexport function withheldCount(payload: MeasuredPayload): number {\n\tconst w = payload.inventory.withheld;\n\treturn (\n\t\tw.builtinTools + w.mcpServers + w.skills + w.subagents + w.slashCommands\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Beat two - the elicitation message. Copy locked in #48; keep it SHORT.\n// ---------------------------------------------------------------------------\n\nexport function buildGateDialog(ctx: GateContext): string {\n\tconst { payloads, keptPrivate } = ctx.body;\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tconst lines = [\"Publish to aistack?\"];\n\tif (n > 0) {\n\t\tlines.push(\n\t\t\tkeptPrivate === undefined\n\t\t\t\t? `${n} name${n === 1 ? \"\" : \"s\"} stay${n === 1 ? \"s\" : \"\"} on this machine`\n\t\t\t\t: `${n} private review name${n === 1 ? \"\" : \"s\"} will be stored`,\n\t\t);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Beat one - the full summary, transcript output.\n// ---------------------------------------------------------------------------\n\nconst CATEGORY_LABEL: Record<NameCategory, string> = {\n\tbuiltinTools: \"actions\",\n\tmcpServers: \"mcp\",\n\tskills: \"skills\",\n\tsubagents: \"agents\",\n\tslashCommands: \"commands\",\n};\n\n// ---------------------------------------------------------------------------\n// Wrapping.\n//\n// EVERY PUBLISHED NAME STAYS ON SCREEN. This is the consent surface, so a name\n// that goes up is a name the person reads first - the inventory rows are never\n// truncated to a count the way the kept-private list is, because that list is\n// the opposite case: those names do NOT leave the machine.\n//\n// What changed in #217 is only where the lines break. An unwrapped inventory\n// row ran to several hundred characters and the terminal broke it mid-name,\n// which reads as noise rather than as a list someone can check.\n// ---------------------------------------------------------------------------\n\n/** The label column every harness line shares: `window 30 days · ...`. */\nconst LABEL_WIDTH = 10;\nconst DEFAULT_WIDTH = 80;\n\n/** Wrap to the caller's terminal, clamped to a width a list stays readable at. */\nexport function wrapWidth(width: number | undefined): number {\n\treturn Math.min(110, Math.max(60, width ?? DEFAULT_WIDTH));\n}\n\n/**\n * One labelled row, wrapped with a hanging indent under its own label.\n *\n * Breaks on spaces only, and the callers join names with \", \", so a name is\n * never split across two lines.\n */\nexport function wrapRow(\n\thead: string,\n\tcontinuation: string,\n\ttext: string,\n\twidth: number,\n): string[] {\n\tconst limit = Math.max(24, width - continuation.length);\n\tconst lines: string[] = [];\n\tlet line = \"\";\n\tfor (const word of text.split(\" \")) {\n\t\tif (line === \"\") {\n\t\t\tline = word;\n\t\t\tcontinue;\n\t\t}\n\t\tif (`${line} ${word}`.length > limit) {\n\t\t\tlines.push(line);\n\t\t\tline = word;\n\t\t} else {\n\t\t\tline = `${line} ${word}`;\n\t\t}\n\t}\n\tif (line !== \"\") lines.push(line);\n\treturn lines.map((l, i) => (i === 0 ? head : continuation) + l);\n}\n\n/** Wrap separator-delimited entries without splitting one model from its share. */\nexport function wrapEntries(\n\thead: string,\n\tcontinuation: string,\n\tentries: readonly string[],\n\twidth: number,\n): string[] {\n\tconst limit = Math.max(24, width - continuation.length);\n\tconst lines: string[] = [];\n\tlet line = \"\";\n\tfor (const entry of entries) {\n\t\tconst next = line ? `${line} · ${entry}` : entry;\n\t\tif (line && next.length > limit) {\n\t\t\tlines.push(line);\n\t\t\tline = entry;\n\t\t} else {\n\t\t\tline = next;\n\t\t}\n\t}\n\tif (line) lines.push(line);\n\treturn lines.map(\n\t\t(line, index) => `${index === 0 ? head : continuation}${line}`,\n\t);\n}\n\n/** Kept-private rows for the gate: one row per group, then singles (#48). */\nexport function keptPrivateRows(\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>,\n): Array<{ label: string; names: number }> {\n\tconst groups = new Map<string, number>();\n\tconst singles: string[] = [];\n\tfor (const category of NAME_CATEGORIES) {\n\t\tfor (const atom of keptPrivate[category]) {\n\t\t\tif (atom.group === null) singles.push(atom.name);\n\t\t\telse groups.set(atom.group, (groups.get(atom.group) ?? 0) + 1);\n\t\t}\n\t}\n\tconst rows = [...groups].map(([label, names]) => ({ label, names }));\n\tfor (const name of singles) rows.push({ label: name, names: 1 });\n\trows.sort((a, b) => b.names - a.names || a.label.localeCompare(b.label));\n\treturn rows;\n}\n\n/**\n * How many kept-private rows the gate names before it counts the rest.\n *\n * Three, not six (#217). These names do NOT leave the machine, which is what\n * makes truncating them safe here and unsafe for the published inventory.\n */\nconst KEPT_PRIVATE_ROWS_SHOWN = 3;\n\n// The harness display names live with the harness names themselves (#101), so\n// one harness has one label everywhere. Re-exported: this module is where the\n// gate's renderers reach for it.\nexport { harnessLabel };\n\n/** How many unreadable files get named before the list truncates. */\nconst UNREADABLE_FILES_SHOWN = 5;\n\n/**\n * The local-only lines behind the bare coverage counts (#75). Everything here\n * stays on this machine: relative paths, error classes, and originator names\n * never enter the payload.\n */\nexport function scanNoteLines(stats: ScanStats, label: string): string[] {\n\tconst out: string[] = [];\n\tconst shown = stats.unreadableFiles.slice(0, UNREADABLE_FILES_SHOWN);\n\tfor (const f of shown) {\n\t\tout.push(` ${f.path} (${f.reason})`);\n\t}\n\tif (stats.unreadableFiles.length > shown.length) {\n\t\tout.push(\n\t\t\t` ...${stats.unreadableFiles.length - shown.length} more`,\n\t\t);\n\t}\n\tif (stats.filesZstdUnsupported > 0) {\n\t\tout.push(\n\t\t\t` ${stats.filesZstdUnsupported} compressed rollout${stats.filesZstdUnsupported === 1 ? \"\" : \"s\"} need Node 22.15 or newer`,\n\t\t);\n\t}\n\tif (stats.filesForeign > 0) {\n\t\tconst origins = [...stats.foreignOriginators]\n\t\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t\t.map(([name, n]) => (n > 1 ? `${name} ×${n}` : name))\n\t\t\t.join(\", \");\n\t\tout.push(\n\t\t\t`skipped ${stats.filesForeign} file${stats.filesForeign === 1 ? \"\" : \"s\"} not written by ${label} (originators: ${origins})`,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * One harness's payload block: window, activity, cost, models, inventory.\n *\n * ONE ALIGNED BLOCK, NO EMPTY HEADINGS (#217). Every row hangs off the same\n * label column, and a section with nothing in it is not announced: a bare\n * `models` heading over nothing said only that the code has a models section.\n * A harness that publishes no names says THAT, in one line, because silence\n * there would read as a harness that was never scanned.\n */\nfunction payloadBlock(\n\tpayload: MeasuredPayload,\n\twidth: number,\n\townWindow: boolean,\n\tstats?: ScanStats,\n): string[] {\n\tconst out: string[] = [];\n\t// The header is unconditional (#130): the `searched` line above names four\n\t// harnesses, so an unlabeled block would be unreadable even when only one\n\t// harness was found. It also CARRIES the activity and the cost, which each\n\t// held a line of their own until #217 - three lines saying one harness's\n\t// totals, repeated per harness, was most of a preview nobody read.\n\tconst label = `${harnessLabel(payload.harness.name)}${payload.harness.version ? ` ${payload.harness.version}` : \"\"}`;\n\tconst days = payload.activity.activeDayDates.length;\n\tconst usd = totalUSD(payload);\n\tconst breakdown = tokenBreakdown(payload);\n\tconst totals = [\n\t\t`${payload.activity.sessions} session${payload.activity.sessions === 1 ? \"\" : \"s\"}`,\n\t\t`${days} active day${days === 1 ? \"\" : \"s\"}`,\n\t\t`${fmtTokens(payload.activity.totalTokens)} tokens processed`,\n\t\t...(breakdown\n\t\t\t? [\n\t\t\t\t\t`${fmtTokens(breakdown.fresh)} fresh`,\n\t\t\t\t\t`${fmtTokens(breakdown.cached)} cached`,\n\t\t\t\t]\n\t\t\t: []),\n\t];\n\t// Wrapped, because the merged header is the longest line in the block and a\n\t// narrow terminal would otherwise break it mid-figure.\n\t// The header is a SECTION, the way `collect` prints one: the harness name in\n\t// caps and its session count. The totals hang under it as the `usage` row,\n\t// with the cost at the end of that row. `at API prices` stays: it is the\n\t// qualifier that makes the figure a lower bound rather than a bill (#93).\n\tout.push(`${label.toUpperCase()} ${payload.activity.sessions}`);\n\n\t// A harness that measured nothing says so in its usage row and stops - not\n\t// even a cost, because there is nothing to price. It still gets its header,\n\t// because a scanned harness reading as an absent one is the mistake #130\n\t// fixed.\n\tif (payload.activity.totalTokens === 0) {\n\t\tout.push(`usage ${totals.slice(1).join(\" · \")}`);\n\t\treturn out;\n\t}\n\tout.push(\n\t\t...wrapRow(\n\t\t\t\"usage \",\n\t\t\t\" \".repeat(LABEL_WIDTH),\n\t\t\t`${totals.slice(1).join(\" · \")} · ${usd === null ? \"cost not published\" : `${fmtUSD(usd)} at API prices`}`,\n\t\t\twidth,\n\t\t),\n\t);\n\n\t// Only when this harness read a different window from the rest.\n\tif (ownWindow) {\n\t\tout.push(\n\t\t\t`window ${payload.window.days} days · ${payload.window.from} → ${payload.window.to}`,\n\t\t);\n\t}\n\n\t// Coverage is silent when clean; a degraded scan is named as a floor (#40).\n\tconst cov = payload.coverage;\n\tif (cov.filesUnreadable > 0 || cov.linesFailed > 0) {\n\t\tout.push(\n\t\t\t`coverage ${cov.filesUnreadable} files unreadable · ${cov.linesFailed} lines failed · this reading is a floor`,\n\t\t);\n\t}\n\t// Local-only detail behind those counts (#75): file names, error classes,\n\t// and the foreign-file line. Printed, never sent.\n\tif (stats) {\n\t\tout.push(...scanNoteLines(stats, harnessLabel(payload.harness.name)));\n\t}\n\n\t// The models are one row, wrapped: `id share ≈$` per model, joined with\n\t// dots. A harness that reports no model prints nothing.\n\t//\n\t// A MODEL UNDER ONE PERCENT ROLLS UP. Four entries where two carry 99.9% of\n\t// the tokens is a row that hides its own headline. The rolled figure keeps\n\t// its dollars only when every model in it published one, the same rule a\n\t// single entry follows: a sum missing a term would understate without\n\t// saying so.\n\tconst shown = payload.models.filter((m) => m.tokenShare >= MODEL_ROLLUP);\n\tconst rolled = payload.models.filter((m) => m.tokenShare < MODEL_ROLLUP);\n\tconst entry = (name: string, share: number, dollars: number | undefined) =>\n\t\t`${name} ${fmtPct(share)}${usd !== null && dollars !== undefined ? ` ${fmtUSD(dollars)}` : \"\"}`;\n\tconst entries = shown.map((m) =>\n\t\tentry(m.id, m.tokenShare, m.apiEquivalentUSD),\n\t);\n\tif (rolled.length > 0) {\n\t\tconst priced = rolled.every((m) => m.apiEquivalentUSD !== undefined);\n\t\tentries.push(\n\t\t\tentry(\n\t\t\t\t`+${rolled.length} more`,\n\t\t\t\trolled.reduce((a, m) => a + m.tokenShare, 0),\n\t\t\t\tpriced\n\t\t\t\t\t? rolled.reduce((a, m) => a + (m.apiEquivalentUSD ?? 0), 0)\n\t\t\t\t\t: undefined,\n\t\t\t),\n\t\t);\n\t}\n\tif (entries.length > 0) {\n\t\tout.push(\n\t\t\t...wrapEntries(\"models \", \" \".repeat(LABEL_WIDTH), entries, width),\n\t\t);\n\t}\n\n\t// The inventory. The counts line is the glance, the rows underneath are the\n\t// consent: every name that publishes is printed.\n\tconst filled = NAME_CATEGORIES.filter(\n\t\t(category) => payload.inventory[category].length > 0,\n\t);\n\tif (filled.length === 0) {\n\t\tout.push(\n\t\t\t`${\"sends\".padEnd(LABEL_WIDTH)}no inventory names from this harness`,\n\t\t);\n\t\treturn out;\n\t}\n\tout.push(\n\t\t`${\"sends\".padEnd(LABEL_WIDTH)}${filled\n\t\t\t.map(\n\t\t\t\t(category) =>\n\t\t\t\t\t`${payload.inventory[category].length} ${CATEGORY_LABEL[category]}`,\n\t\t\t)\n\t\t\t.join(\" · \")}`,\n\t);\n\t// The names indent under their own category label, so a wrapped row and the\n\t// row above it start in the same column. `commands` is the longest label and\n\t// still needs a gap after it, which is why the width is its length plus one.\n\tconst subLabel = Math.max(\n\t\t...filled.map((category) => CATEGORY_LABEL[category].length),\n\t);\n\tconst subIndent = \" \".repeat(2 + subLabel + 1);\n\tfor (const category of filled) {\n\t\tconst names = payload.inventory[category].map((a) => a.name).join(\", \");\n\t\tout.push(\n\t\t\t...wrapRow(\n\t\t\t\t` ${CATEGORY_LABEL[category].padEnd(subLabel)} `,\n\t\t\t\tsubIndent,\n\t\t\t\tnames,\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n\treturn out;\n}\n\n/** The rule between sections, the width `collect` draws it at. */\nconst DIVIDER = \"─\".repeat(40);\n\n/** Token share below which a model joins the rolled-up row (#217). */\nconst MODEL_ROLLUP = 0.01;\n\nconst PHASE_ORDER = [\"scout\", \"build\", \"verify\", \"handoff\", \"unknown\"] as const;\n\n/**\n * The workflow section, as the gate describes it (#213).\n *\n * Everything here is read out of `body.workflow` - the exact bytes a publish\n * sends - for the reason the whole file exists: the person approves a sentence\n * about the bytes, not a sentence about what the code meant to send.\n *\n * The last line names the switch, the way the kept-private block does. A\n * default-on opt-out has to be visible before the first upload, or it is not an\n * opt-out.\n */\nfunction workflowBlock(\n\tworkflowDays: NonNullable<MeasuredDay[\"workflow\"]>[],\n\tutcOffsetMinutes: number,\n\thost: string,\n): string[] {\n\tconst out: string[] = [];\n\tconst folded = foldWorkflowDays(workflowDays, {\n\t\taggregateVersion: WORKFLOW_AGGREGATES_V3,\n\t\tutcOffsetMinutes,\n\t});\n\tconst harnesses = folded?.harnesses ?? [];\n\tconst withPlaybook = harnesses.filter((h) => h.phase);\n\tconst sessions = harnesses.reduce((a, h) => a + h.sessions, 0);\n\tconst ruleVersions = [\n\t\t...new Set(withPlaybook.map((h) => h.phase?.ruleVersion ?? \"\")),\n\t].filter(Boolean);\n\n\tout.push(\n\t\t`workflow ${harnesses.length} harness${harnesses.length === 1 ? \"\" : \"es\"} · ${sessions} sessions · ${WORKFLOW_AGGREGATES_V3}`,\n\t);\n\tconst first = folded?.dates[0];\n\tconst last = folded?.dates.at(-1);\n\tout.push(\n\t\t` ${workflowDays.length} day${workflowDays.length === 1 ? \"\" : \"s\"}${first && last ? ` · ${first} to ${last}` : \"\"}`,\n\t);\n\n\tconst seconds = PHASE_ORDER.map((phase) =>\n\t\twithPlaybook.reduce((a, h) => a + (h.phase?.phaseSec[phase] ?? 0), 0),\n\t);\n\tconst total = seconds.reduce((a, b) => a + b, 0);\n\tif (total > 0) {\n\t\tconst mix = PHASE_ORDER.map(\n\t\t\t(phase, i) => `${phase} ${fmtPct((seconds[i] ?? 0) / total)}`,\n\t\t).join(\" · \");\n\t\tout.push(` ${mix} · ${ruleVersions.join(\", \")}`);\n\t}\n\n\tconst git = folded?.git;\n\tout.push(\n\t\t`git ${git?.commits ?? 0} commits · ${fmtTokens((git?.additions ?? 0) + (git?.removals ?? 0))} lines changed`,\n\t);\n\t// The kept-private block points at a control the owner can click, because\n\t// #48 shipped one. This line NAMES the switch and stops there: the owner\n\t// control is #215's, and directions to a control that does not exist yet\n\t// would be the one false sentence in a preview built to be exact. Extend\n\t// this line with the location when #215 lands it.\n\tout.push(` (Publish workflow is on for ${host})`);\n\treturn out;\n}\n\n/**\n * The day counts (#307): \"31 days to publish, 369 unchanged\" against a\n * manifest, \"400 days to publish\" on a fresh machine or an old server.\n */\nexport function daysLine(\n\tmeasuredDays: PayloadMeasuredDays,\n\tselection?: DaySelection,\n): string {\n\tconst n = measuredDays.days.length;\n\tconst head = `${n} day${n === 1 ? \"\" : \"s\"} to publish`;\n\tconst unchanged = selection?.unchanged ?? 0;\n\treturn unchanged > 0 ? `${head}, ${unchanged} unchanged` : head;\n}\n\nfunction daysBlock(\n\tmeasuredDays: PayloadMeasuredDays,\n\tselection?: DaySelection,\n): string[] {\n\tconst out = [`days ${daysLine(measuredDays, selection)}`];\n\tconst first = measuredDays.days[0]?.date;\n\tconst last = measuredDays.days.at(-1)?.date;\n\tconst usageDays = measuredDays.days.filter((d) => d.usage).length;\n\tif (first && last) {\n\t\tout.push(\n\t\t\t` ${first} to ${last} · ${usageDays} with usage · ${measuredDays.aggregateVersion}`,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport function buildGateSummary(ctx: GateContext): string {\n\tconst { body, keptPrivate, config, source, baseUrl } = ctx;\n\tconst { payloads } = body;\n\tconst host = baseUrl.replace(/^https?:\\/\\//, \"\");\n\tconst out: string[] = [];\n\n\tif (config.stack === null) {\n\t\tout.push(\"to (no linked stack; publish is unavailable)\");\n\t} else {\n\t\tout.push(\n\t\t\t`to ${config.stack.name} · ${host}/stacks/${config.stack.slug}`,\n\t\t);\n\t}\n\n\t// What the CLI LOOKED FOR, in search order - a claim about the CLI, never\n\t// about the person's behavior, so it stays inside #40 (#130). Without it, a\n\t// harness the scan misses reads identically to a harness never installed.\n\t// The client version rides here because it travels (#213) and because one\n\t// fact about the CLI does not earn a line of its own.\n\tout.push(\n\t\t`searched ${HARNESS_ADAPTERS.map((a) => harnessLabel(a.name).toLowerCase()).join(\", \")}`,\n\t);\n\n\t// THE WINDOW IS THE SYNC'S, NOT EACH HARNESS'S (#217). Every payload carries\n\t// the same one, so printing it per harness said the same sentence three\n\t// times. A harness that somehow read a different window keeps its own line\n\t// inside its block rather than being silently folded into this one.\n\tconst windows = new Set(\n\t\tpayloads.map(\n\t\t\t(p) => `${p.window.days} days · ${p.window.from} → ${p.window.to}`,\n\t\t),\n\t);\n\tif (windows.size === 1) {\n\t\tout.push(\n\t\t\t`window ${[...windows][0]}${body.cliVersion ? ` · aistack ${body.cliVersion}` : \"\"}`,\n\t\t);\n\t}\n\n\t// THE TABLE BEHIND THE DOLLARS (#336). Printed only when a figure is on its\n\t// way up: a cost-off stage has nothing the id would cite. The sources are\n\t// the per-model citations the payload carries, so what the reader sees is\n\t// what the server will see.\n\tconst cited = [\n\t\t...new Set(\n\t\t\tpayloads.flatMap((p) =>\n\t\t\t\tp.models.flatMap((m) => (m.pricingTable ? [m.pricingTable] : [])),\n\t\t\t),\n\t\t),\n\t];\n\tif (ctx.prices && cited.length > 0) {\n\t\tconst origin =\n\t\t\tctx.prices.origin === \"served\"\n\t\t\t\t? `${ctx.prices.id} from ${host}`\n\t\t\t\t: `${ctx.prices.id} (bundled; the server table was unavailable)`;\n\t\tout.push(`prices ${origin} · cites ${cited.join(\", \")}`);\n\t}\n\n\t// One block per detected harness, each under its own header.\n\tconst width = wrapWidth(ctx.width);\n\tout.push(\n\t\t...wrapRow(\n\t\t\t\"privacy \",\n\t\t\t\" \".repeat(LABEL_WIDTH),\n\t\t\t\"raw conversation text, paths, repo names, and command arguments stay local\",\n\t\t\twidth,\n\t\t),\n\t);\n\tfor (const payload of payloads) {\n\t\tconst stats = ctx.scanStats?.[payload.harness.name];\n\t\tout.push(\"\", DIVIDER, \"\");\n\t\tout.push(...payloadBlock(payload, width, windows.size > 1, stats));\n\t}\n\n\t// Everything that is not a harness: the day rows, the workflow, git, and the\n\t// kept-private count, under one section.\n\tout.push(\"\", DIVIDER, \"\", \"ALSO PUBLISHING\");\n\n\t// The day rows (#307): how many go and how many the server already holds.\n\t// The counts are the sync's one plain sentence about diff-only publishing.\n\tif (body.measuredDays) {\n\t\tout.push(...daysBlock(body.measuredDays, ctx.days));\n\t}\n\n\t// In the bytes, so it is in the preview (#78's rule, applied to #213). Off\n\t// prints as plainly as `cost not published` does, and for the same reason: a\n\t// section the owner declined is a fact about this send, not an absence.\n\tconst workflowDays = (body.measuredDays?.days ?? []).flatMap((d) =>\n\t\td.workflow ? [d.workflow] : [],\n\t);\n\tif (!config.publishWorkflow || !body.measuredDays) {\n\t\tout.push(\"workflow not published\");\n\t} else if (workflowDays.length === 0) {\n\t\tout.push(\"workflow on, no changed day to publish\");\n\t} else {\n\t\tout.push(\n\t\t\t...workflowBlock(workflowDays, body.measuredDays.utcOffsetMinutes, host),\n\t\t);\n\t}\n\n\t// Kept private is ONE ROW: the count, the first few names, and the rest as\n\t// a count. Truncating is safe here and unsafe for the inventory above: these\n\t// names do NOT leave the machine (#217). The switch is still named before\n\t// the first upload (#48), on the row under it.\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tif (n > 0) {\n\t\tconst rows = keptPrivateRows(keptPrivate);\n\t\tconst shown = rows.slice(0, KEPT_PRIVATE_ROWS_SHOWN);\n\t\tconst examples = shown\n\t\t\t.map((r) => (r.names > 1 ? `${r.label} ×${r.names}` : r.label))\n\t\t\t.join(\", \");\n\t\tconst more =\n\t\t\trows.length > shown.length\n\t\t\t\t? `, ...${rows.length - shown.length} more`\n\t\t\t\t: \"\";\n\t\tout.push(\n\t\t\t`private ${n} review name${n === 1 ? \"\" : \"s\"} · ${examples}${more}`,\n\t\t);\n\t\tif (body.keptPrivate !== undefined && config.stack !== null) {\n\t\t\tout.push(\n\t\t\t\t` stored privately for your review at ${host}/stacks/${config.stack.slug}/changes`,\n\t\t\t);\n\t\t\tout.push(\n\t\t\t\t\" (turn off: Review kept-private names, on your stack)\",\n\t\t\t);\n\t\t} else {\n\t\t\tout.push(\" they stay on this machine\");\n\t\t}\n\t}\n\n\t// Named at the gate because it is in the bytes (#78). It is not measurement\n\t// and not a name, but the rule is that the preview describes what goes, so a\n\t// field nobody can see in the preview does not get to ride along.\n\tif (body.autoSync !== undefined) {\n\t\tout.push(\n\t\t\t`auto-sync ${body.autoSync.enabled ? `on, about every ${body.autoSync.frequencyHours}h` : \"off\"}`,\n\t\t);\n\t}\n\n\tif (source === \"bundled\") {\n\t\tout.push(\"\");\n\t\tout.push(\n\t\t\t\"! could not fetch your settings from aistack - using the bundled list.\",\n\t\t);\n\t\tout.push(\n\t\t\t\" This publishes less: no cost, no ticked names, nothing staged for review.\",\n\t\t);\n\t}\n\n\treturn out.join(\"\\n\");\n}\n","// The floor is set by `node:sqlite`, which the opencode adapter needs.\n// `node:sqlite` landed in 22.5.0 but stayed behind `--experimental-sqlite`\n// until 22.13.0 (and, on the 23 line, until 23.4.0): on the flagged versions\n// the import throws and opencode silently drops out of detection while the\n// file-based adapters keep working. So the gate refuses every flagged\n// version, not just the ones below 22.5.\nexport const MINIMUM_NODE_VERSION = \"22.13.0\";\n\nfunction versionParts(version: string): [number, number, number] | null {\n\tconst match = /^(?:v)?(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version);\n\tif (!match) return null;\n\treturn [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\nfunction atLeast(actual: [number, number, number], floor: string): boolean {\n\tconst minimum = versionParts(floor);\n\tif (!minimum) return false;\n\tfor (let i = 0; i < actual.length; i++) {\n\t\tif (actual[i] !== minimum[i])\n\t\t\treturn (actual[i] as number) > (minimum[i] as number);\n\t}\n\treturn true;\n}\n\nexport function supportsNodeVersion(version: string): boolean {\n\tconst actual = versionParts(version);\n\tif (!actual) return false;\n\tif (!atLeast(actual, MINIMUM_NODE_VERSION)) return false;\n\t// 23.0 to 23.3 still flag node:sqlite even though they sort above 22.13.\n\tif (actual[0] === 23) return atLeast(actual, \"23.4.0\");\n\treturn true;\n}\n\nexport function unsupportedNodeMessage(version: string): string {\n\treturn `aistack requires Node.js ${MINIMUM_NODE_VERSION} or newer (23.x needs 23.4.0). You are running ${version}. Upgrade Node.js so sync can read OpenCode's SQLite usage database.`;\n}\n","// The local stdio MCP server - the send channel picked by the spike #35.\n//\n// Wayfinder ticket #41 (map #29). Two tools, two beats:\n//\n// sync_preview - scans locally, stages the exact send bytes, returns the\n// full summary as ordinary transcript output (beat one).\n// sync_publish - takes the stage id, raises a SHORT `elicitation/create`\n// with an ENUM field (beat two), and sends only on\n// `decision: \"publish\"`.\n//\n// Why elicitation and not `requiresUserInteraction`: the spike showed the\n// permission dialog can be silenced forever with one click and writes a grant\n// broader than the sentence shown, while an elicitation is raised INSIDE the\n// call - there is no string a model can spell to route around it, and no\n// \"don't ask again\" exists for it. The enum widget is the working one; the\n// boolean widget is dead in 2.1.220 and must never ship.\n//\n// Fail-closed, by construction: ESC, a timeout, a headless auto-cancel, an\n// error reply, or a client that never declared the elicitation capability all\n// resolve to \"nothing was sent\". The model's arguments count for nothing -\n// the only path to a send runs through the user's own keystrokes.\n//\n// Hand-rolled JSON-RPC over stdio, zero dependencies, structured so tests can\n// drive `handle()` directly and capture every outbound frame.\n\nimport { type SyncPublishResult, syncPublish } from \"../api.js\";\nimport { type StageDeps, type StagedSend, stageSync } from \"./stage.js\";\nimport { fmtReceivedAt } from \"./summary.js\";\n\nconst SERVER_NAME = \"aistack\";\nconst SERVER_VERSION = \"0.3.0\";\n\n/** How long a staged preview stays publishable. Stale bytes must re-preview. */\nexport const STAGE_TTL_MS = 10 * 60 * 1000;\n\n/**\n * How long the gate waits for the human. Deliberately WELL past the harness's\n * own 120 s tool timeout (#35, 1H measured 92 s for a one-line answer): the\n * server must never be the first to give up. On expiry it resolves as cancel.\n */\nexport const ELICIT_TIMEOUT_MS = 10 * 60 * 1000;\n\nconst PREVIEW_TOOL = {\n\tname: \"sync_preview\",\n\tdescription:\n\t\t\"Scan local agent transcripts (Claude Code, Codex) and stage a measured-usage snapshot for aistack. \" +\n\t\t\"Returns the full preview of exactly what would publish. \" +\n\t\t\"Show the returned text to the user VERBATIM - it is the review surface. Nothing is sent.\",\n\tinputSchema: { type: \"object\", properties: {} },\n\tannotations: {\n\t\ttitle: \"aistack - preview sync (sends nothing)\",\n\t\treadOnlyHint: true,\n\t\topenWorldHint: true,\n\t},\n};\n\nconst PUBLISH_TOOL = {\n\tname: \"sync_publish\",\n\tdescription:\n\t\t\"Publish the staged aistack snapshot named by preview_id. \" +\n\t\t\"Asks the user for confirmation during the call; only their explicit choice sends anything. \" +\n\t\t\"Call sync_preview first and show its output.\",\n\tinputSchema: {\n\t\ttype: \"object\",\n\t\tproperties: {\n\t\t\tpreview_id: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescription: \"The `preview id` line from sync_preview's output.\",\n\t\t\t},\n\t\t},\n\t\trequired: [\"preview_id\"],\n\t},\n\tannotations: {\n\t\ttitle: \"aistack - publish measured usage (asks the user first)\",\n\t\tdestructiveHint: false,\n\t\topenWorldHint: true,\n\t},\n};\n\ntype JsonRpcMessage = {\n\tjsonrpc?: string;\n\tid?: string | number;\n\tmethod?: string;\n\tparams?: Record<string, unknown> | undefined;\n\tresult?: unknown;\n\terror?: unknown;\n};\n\nexport type SyncServerDeps = {\n\tbaseUrl: string;\n\tstageImpl?: (deps: StageDeps) => Promise<StagedSend>;\n\tpublishImpl?: (token: string, bodyJson: string) => Promise<SyncPublishResult>;\n\tnow?: () => number;\n\telicitTimeoutMs?: number;\n\t/** Diagnostics only. NEVER stdout - that would corrupt the protocol. */\n\tlog?: (line: string) => void;\n};\n\nexport type SyncServer = {\n\thandle: (msg: JsonRpcMessage) => void;\n\t/** Test seam: the staged send, if any. */\n\tstaged: () => StagedSend | null;\n};\n\nconst textResult = (text: string, isError = false) => ({\n\tcontent: [{ type: \"text\", text }],\n\t...(isError ? { isError: true } : {}),\n});\n\nexport function createSyncServer(\n\tdeps: SyncServerDeps,\n\tsend: (msg: JsonRpcMessage) => void,\n): SyncServer {\n\tconst now = deps.now ?? Date.now;\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst log = deps.log ?? (() => {});\n\tconst elicitTimeoutMs = deps.elicitTimeoutMs ?? ELICIT_TIMEOUT_MS;\n\n\tlet clientSupportsElicitation = false;\n\tlet staged: StagedSend | null = null;\n\tlet nextRequestId = 1;\n\tconst pending = new Map<string, (reply: JsonRpcMessage | null) => void>();\n\n\tconst ok = (id: string | number | undefined, result: unknown) =>\n\t\tsend({ jsonrpc: \"2.0\", id, result });\n\tconst err = (\n\t\tid: string | number | undefined,\n\t\tcode: number,\n\t\tmessage: string,\n\t) => send({ jsonrpc: \"2.0\", id, error: { code, message } });\n\n\t/** Ask the client something; `null` reply means the gate timed out. */\n\tconst request = (\n\t\tmethod: string,\n\t\tparams: Record<string, unknown>,\n\t\tonReply: (reply: JsonRpcMessage | null) => void,\n\t) => {\n\t\tconst id = `aistack-${nextRequestId++}`;\n\t\tpending.set(id, onReply);\n\t\tsend({ jsonrpc: \"2.0\", id, method, params });\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (pending.delete(id)) onReply(null);\n\t\t}, elicitTimeoutMs);\n\t\t(timer as { unref?: () => void }).unref?.();\n\t};\n\n\tconst runPreview = async (id: string | number | undefined) => {\n\t\ttry {\n\t\t\tstaged = await stage({ baseUrl: deps.baseUrl, now });\n\t\t} catch (e) {\n\t\t\tstaged = null;\n\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\treturn ok(id, textResult(`Preview failed: ${message}`, true));\n\t\t}\n\t\tconst lines = [staged.summary, \"\"];\n\t\tif (staged.blockedReason === null) {\n\t\t\tlines.push(`preview id: ${staged.id}`);\n\t\t\tlines.push(\n\t\t\t\t\"To publish, call sync_publish with this preview id. The user confirms in a dialog during that call.\",\n\t\t\t);\n\t\t} else {\n\t\t\tlines.push(`publish unavailable: ${staged.blockedReason}`);\n\t\t}\n\t\treturn ok(id, textResult(lines.join(\"\\n\")));\n\t};\n\n\tconst runPublish = (\n\t\tid: string | number | undefined,\n\t\targs: Record<string, unknown> | undefined,\n\t) => {\n\t\t// Every refusal below is fail-closed: no dialog was shown, nothing sent.\n\t\tif (!clientSupportsElicitation) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: this Claude Code version did not declare the elicitation capability, \" +\n\t\t\t\t\t\t\"so the approve dialog cannot be shown. The gate never degrades silently - \" +\n\t\t\t\t\t\t\"update Claude Code and try again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tconst previewId = args?.preview_id;\n\t\tif (staged === null) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: nothing is staged. Run sync_preview first and show its output to the user.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (typeof previewId !== \"string\" || previewId !== staged.id) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: preview_id does not match the staged preview. Run sync_preview again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (staged.blockedReason !== null) {\n\t\t\treturn ok(id, textResult(`Not published: ${staged.blockedReason}`, true));\n\t\t}\n\t\tif (now() - staged.stagedAt > STAGE_TTL_MS) {\n\t\t\tstaged = null;\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: the staged preview is older than 10 minutes. Run sync_preview again so the user reviews current bytes.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tconst approvedStage = staged;\n\t\tlog(`elicitation raised for stage ${approvedStage.id}`);\n\t\trequest(\n\t\t\t\"elicitation/create\",\n\t\t\t{\n\t\t\t\tmessage: approvedStage.dialog,\n\t\t\t\trequestedSchema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tdecision: {\n\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t// The enum widget is the one that works (#35, 1G). Never a boolean.\n\t\t\t\t\t\t\tenum: [\"publish\", \"cancel\"],\n\t\t\t\t\t\t\tdescription: \"Publish the snapshot described above?\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"decision\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t(reply) => {\n\t\t\t\tconst result = reply?.result as\n\t\t\t\t\t| { action?: string; content?: { decision?: string } }\n\t\t\t\t\t| undefined;\n\t\t\t\tconst approved =\n\t\t\t\t\tresult?.action === \"accept\" &&\n\t\t\t\t\tresult?.content?.decision === \"publish\";\n\t\t\t\tif (!approved) {\n\t\t\t\t\tconst outcome =\n\t\t\t\t\t\treply === null ? \"timed out\" : (result?.action ?? \"error\");\n\t\t\t\t\tlog(`elicitation resolved without consent: ${outcome}`);\n\t\t\t\t\treturn ok(\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttextResult(\n\t\t\t\t\t\t\t`Not published: the confirmation was not accepted (${outcome}). Nothing left this machine.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlog(`consent received, sending stage ${approvedStage.id}`);\n\t\t\t\tpublish(approvedStage.token as string, approvedStage.bodyJson).then(\n\t\t\t\t\t(res) => {\n\t\t\t\t\t\tapprovedStage.acknowledgePublish?.();\n\t\t\t\t\t\tif (staged?.id === approvedStage.id) staged = null;\n\t\t\t\t\t\t// Same ending as the terminal channel (#130): the result last,\n\t\t\t\t\t\t// the stamp in human form.\n\t\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\t`Published. Snapshot received ${fmtReceivedAt(res.receivedAt)}.`,\n\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\"Your stack now shows what actually ran:\",\n\t\t\t\t\t\t\tres.url,\n\t\t\t\t\t\t];\n\t\t\t\t\t\tconst kp = approvedStage.body.keptPrivate;\n\t\t\t\t\t\tif (res.keptPrivate.refused && kp !== undefined) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\"Note: the server refused the kept-private names because its review switch is off. They stayed on this machine.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t`${res.keptPrivate.stored} private review name${res.keptPrivate.stored === 1 ? \"\" : \"s\"} stored at ${res.url}/changes`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (res.keptPrivate.machineStored > 0) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\"This machine's private label was stored for the same review.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tok(id, textResult(lines.join(\"\\n\")));\n\t\t\t\t\t},\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\t\t\t\tok(\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttextResult(`Publish failed after consent: ${message}`, true),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t};\n\n\tconst handle = (msg: JsonRpcMessage) => {\n\t\tconst { id, method, params } = msg;\n\n\t\t// A reply to something we asked, not a new request.\n\t\tif (method === undefined && id !== undefined && pending.has(String(id))) {\n\t\t\tconst onReply = pending.get(String(id));\n\t\t\tpending.delete(String(id));\n\t\t\tonReply?.(msg);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (method) {\n\t\t\tcase \"initialize\": {\n\t\t\t\tconst capabilities =\n\t\t\t\t\t(params?.capabilities as Record<string, unknown> | undefined) ?? {};\n\t\t\t\tclientSupportsElicitation = \"elicitation\" in capabilities;\n\t\t\t\tlog(\n\t\t\t\t\t`initialize: elicitation ${clientSupportsElicitation ? \"declared\" : \"ABSENT\"}`,\n\t\t\t\t);\n\t\t\t\treturn ok(id, {\n\t\t\t\t\tprotocolVersion:\n\t\t\t\t\t\t(params?.protocolVersion as string | undefined) ?? \"2025-06-18\",\n\t\t\t\t\tcapabilities: { tools: { listChanged: false } },\n\t\t\t\t\tserverInfo: { name: SERVER_NAME, version: SERVER_VERSION },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"ping\":\n\t\t\t\treturn ok(id, {});\n\n\t\t\tcase \"tools/list\":\n\t\t\t\treturn ok(id, { tools: [PREVIEW_TOOL, PUBLISH_TOOL] });\n\n\t\t\tcase \"tools/call\": {\n\t\t\t\tconst name = params?.name;\n\t\t\t\tconst args = params?.arguments as Record<string, unknown> | undefined;\n\t\t\t\tif (name === \"sync_preview\") return void runPreview(id);\n\t\t\t\tif (name === \"sync_publish\") return runPublish(id, args);\n\t\t\t\treturn err(id, -32602, `Unknown tool: ${String(name)}`);\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tif (method?.startsWith(\"notifications/\")) return;\n\t\t\t\tif (method !== undefined)\n\t\t\t\t\treturn err(id, -32601, `Method not found: ${method}`);\n\t\t}\n\t};\n\n\treturn { handle, staged: () => staged };\n}\n\n/** Wire the server to real stdio. Never returns; the harness owns the process. */\nexport function runStdioSyncServer(deps: SyncServerDeps): void {\n\tconst server = createSyncServer(deps, (msg) => {\n\t\tprocess.stdout.write(`${JSON.stringify(msg)}\\n`);\n\t});\n\tlet buffer = \"\";\n\tprocess.stdin.setEncoding(\"utf8\");\n\tprocess.stdin.on(\"data\", (chunk: string) => {\n\t\tbuffer += chunk;\n\t\tlet nl = buffer.indexOf(\"\\n\");\n\t\twhile (nl !== -1) {\n\t\t\tconst line = buffer.slice(0, nl).trim();\n\t\t\tbuffer = buffer.slice(nl + 1);\n\t\t\tif (line) {\n\t\t\t\ttry {\n\t\t\t\t\tserver.handle(JSON.parse(line));\n\t\t\t\t} catch (e) {\n\t\t\t\t\tdeps.log?.(`parse error: ${String(e)}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnl = buffer.indexOf(\"\\n\");\n\t\t}\n\t});\n}\n"],"mappings":"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","names":["p","p","p","path","lines","p","key","dirname","dirname","dirname","key","path","existsSync","readFileSync","homedir","join","path","existsSync","readFileSync","homedir","join","path","parse","readJson","existsSync","readFileSync","homedir","join","path","key","p","readJson","existsSync","readFileSync","join","homedir","existsSync","readdirSync","readFileSync","homedir","join","stat","intro","outro","intro","outro","key","existsSync","homedir","dirname","join","p","counts","p","key","counts","counts","p","key","bump","key","utcDateOf","active","createAggregate","counts","modelKeyFor","readdir","stat","homedir","path","path","homedir","basename","readdir","stat","p","createAggregate","createAggregate","counts","readFileSync","readdir","realpath","stat","homedir","path","parseToml","path","homedir","basename","readdir","scan","exists","realpath","stat","p","ingestFile","readFile","readFileSync","parseToml","createAggregate","scan","createAggregate","bump","key","ingestEvent","createReadStream","readdir","stat","homedir","path","readline","parseToml","path","homedir","stat","lines","readline","createReadStream","readdir","parseToml","scan","read","ingestEvent","counts","createAggregate","scan","createAggregate","counts","noteConfiguredMcpServers","readFileSync","readdir","stat","homedir","path","path","homedir","basename","readdir","errorClass","readError","scan","open","readConfiguredMcpServers","readFileSync","noteConfiguredMcpServers","exists","p","stat","createAggregate","scan","createAggregate","createFileState","noteActivity","counts","readCounts","key","createReadStream","readdir","realpath","stat","homedir","path","readline","path","homedir","sessionRoots","basename","readdir","scan","exists","realpath","stat","ingestFile","p","readline","createReadStream","createFileState","sessionRoots","createAggregate","scan","harnessLabel","join","homedir","dirname","existsSync","intro","outro","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join","p","intro","join","existsSync","readFileSync","confirm","dirname","mkdirSync","writeFileSync","outro","p","read","intro","outro","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","codexHome","read","canonicalJson","key","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","platform","dirname","join","isOurs","read","key","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","isOurs","read","failures","harnessLabel","mkdirSync","readFileSync","unlinkSync","writeFileSync","homedir","dirname","join","createHash","execFileSync","path","basename","lines","stat","key","createHash","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","path","p","lines","line","harnessLabel","p","utcDate","createHash","active","workflow","join","homedir","mkdirSync","dirname","lines","readFileSync","writeFileSync","unlinkSync","failure","intro","outro","lines","section","log","request","lines"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../../pricing/src/table.ts","../../pricing/src/index.ts","../src/version.ts","../src/api.ts","../src/commands/collect.ts","../src/classifier.ts","../src/stableKey.ts","../src/config.ts","../src/git.ts","../src/github-repo.ts","../src/hooks.ts","../src/mcp.ts","../src/plugins.ts","../src/scanner.ts","../src/theme.ts","../src/commands/connect.ts","../src/harness/shared/aggregate.ts","../src/harness/shared/bundled-allowlist.ts","../src/harness/shared/allowlist.ts","../src/harness/shared/recency.ts","../../workflow-rules/src/daily.ts","../../workflow-rules/src/reading.ts","../../workflow-rules/src/componentRules.ts","../../workflow-rules/src/metricRules.ts","../../workflow-rules/src/types.ts","../../workflow-rules/src/phaseRules.ts","../../workflow-rules/src/usage.ts","../../workflow-rules/src/workflowRows.ts","../src/harness/shared/window.ts","../src/harness/shared/payload.ts","../src/workflow/reducer.ts","../src/harness/claude/analyzer.ts","../src/harness/claude/scan.ts","../src/harness/claude/adapter.ts","../src/harness/codex/analyzer.ts","../src/harness/codex/scan.ts","../src/harness/codex/adapter.ts","../src/harness/cursor/cache.ts","../src/harness/cursor/account.ts","../src/harness/cursor/evidence.ts","../src/harness/cursor/local.ts","../src/harness/cursor/scan.ts","../src/harness/cursor/workflow.ts","../src/harness/cursor/adapter.ts","../src/harness/grok/analyzer.ts","../src/harness/grok/scan.ts","../src/harness/grok/adapter.ts","../src/harness/opencode/analyzer.ts","../src/harness/opencode/scan.ts","../src/harness/opencode/adapter.ts","../src/harness/pi/analyzer.ts","../src/harness/pi/scan.ts","../src/harness/pi/adapter.ts","../src/harness/index.ts","../src/commands/create.ts","../src/commands/login.ts","../src/commands/sync.ts","../src/autosync/codexHook.ts","../src/autosync/optin.ts","../src/autosync/cursorHook.ts","../src/autosync/hook.ts","../src/autosync/grokHook.ts","../src/autosync/run.ts","../src/sync/stage.ts","../src/usage/days.ts","../src/usage/diff.ts","../src/workflow/git.ts","../src/workflow/extract.ts","../src/sync/grokDateCache.ts","../src/sync/summary.ts","../src/node-version.ts","../src/sync/server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { BASE_URL } from \"./api.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { connectCommand } from \"./commands/connect.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { supportsNodeVersion, unsupportedNodeMessage } from \"./node-version.js\";\nimport { runStdioSyncServer } from \"./sync/server.js\";\nimport { CLI_VERSION } from \"./version.js\";\n\nconst program = new Command();\n\nprogram\n\t.name(\"aistack\")\n\t.description(\"Measure and share your AI stack from your terminal\")\n\t.version(CLI_VERSION);\n\nprogram\n\t.command(\"login\")\n\t.description(\"Authenticate with AI Stack\")\n\t.option(\"--label <label>\", \"Set the machine label\")\n\t.action((options) => loginCommand(options));\n\nprogram\n\t.command(\"collect\")\n\t.description(\"Scan and upload AI config files from your project\")\n\t.option(\"--no-global\", \"Exclude global config files (~/.claude, etc.)\")\n\t.action((options) => collectCommand({ global: options.global ?? true }));\n\nprogram\n\t.command(\"create\")\n\t.description(\"Download and write your stack's AI config files\")\n\t.action(createCommand);\n\nprogram\n\t.command(\"mcp\")\n\t.description(\n\t\t\"Run the aistack MCP server on stdio (sync preview + gated publish)\",\n\t)\n\t.action(() => {\n\t\t// stdout belongs to the protocol. Diagnostics go to stderr only.\n\t\trunStdioSyncServer({\n\t\t\tbaseUrl: BASE_URL,\n\t\t\tlog: (line) => process.stderr.write(`[aistack-mcp] ${line}\\n`),\n\t\t});\n\t});\n\n// The documented default sync surface (#56): terminal-first, TTY gate.\nprogram\n\t.command(\"sync\")\n\t.description(\"Scan, preview, and publish measured usage (rolling 30 days)\")\n\t.option(\n\t\t\"--auto [state]\",\n\t\t\"silent background sync; 'on' asks your stack for the permission and installs harness hooks, 'off' revokes both\",\n\t)\n\t.option(\n\t\t\"--every <hours>\",\n\t\t\"with --auto on: hours between auto-syncs (default 6)\",\n\t)\n\t.action((options) => syncCommand(options));\n\nprogram\n\t.command(\"connect\")\n\t.description(\"Install the in-session sync surface (MCP server + Skill)\")\n\t.argument(\"<harness>\", 'the harness to connect (\"claude\")')\n\t.action(connectCommand);\n\nif (!supportsNodeVersion(process.versions.node)) {\n\tprocess.stderr.write(`${unsupportedNodeMessage(process.versions.node)}\\n`);\n\tprocess.exitCode = 1;\n} else {\n\tprogram.parse();\n}\n","// The price table as data: dated periods keyed by model id (ADR-0012, #336).\n//\n// One shape serves three places. The Convex `modelPrices` table holds one row\n// per period. The `/api/prices` endpoint serves the same rows to the CLI. And\n// the constants in `index.ts` render themselves into the same rows, so the\n// bundled fallback and the served table go through one lookup.\n//\n// A period runs from its `from` until the next period's `from` for the same\n// (model, provider). There is no `to`. Cache tiers are ABSOLUTE rates per\n// period, never a vendor-level multiplier: models.dev reports them that way,\n// and a multiplier is wrong for at least one Google model.\n//\n// Every period names its `source`. That string is what a surface prints beside\n// a dollar figure, so the citation travels with the rate that produced it.\n\n/** The separator between a provider id and a model id in a pricing key. */\nexport const PROVIDER_SEPARATOR = \":\";\n\n/**\n * The citation for a model that runs on the user's own machine. It is not a\n * vendor list. It is the statement that no per-token charge exists.\n */\nexport const LOCAL_PRICING_TABLE_VERSION = \"local-no-charge\";\n\n/** One dated period, as stored, served and bundled. USD per million tokens. */\nexport type PriceRow = {\n\t/** The vendor's bare API id (the catalog slug). May carry `#fast`. */\n\tmodelSlug: string;\n\t/** Unset is the vendor's own rate. Set names a gateway with its own rate. */\n\tprovider?: string;\n\t/** Inclusive start, epoch ms. `0` means since the model existed. */\n\tfrom: number;\n\tinput: number;\n\toutput: number;\n\tcacheRead?: number;\n\tcacheWrite5m?: number;\n\tcacheWrite1h?: number;\n\t/** The table or dataset that priced this period. Printed beside every dollar. */\n\tsource: string;\n\t/**\n\t * Who sets the vendor rate. Wire-only: a served row carries the catalog's\n\t * provider so a `google:` key can reach a Google model's bare rate. Not a\n\t * column of `modelPrices`.\n\t */\n\tvendor?: Vendor;\n};\n\n/** A whole table, with the id the CLI prints when it says which table it used. */\nexport type PriceTable = {\n\tid: string;\n\trows: PriceRow[];\n};\n\n/**\n * Who sets the rate. A provider only reaches a vendor's rows when it IS that\n * vendor; a gateway re-serving the same model is a different price.\n */\nexport type Vendor = \"anthropic\" | \"openai\" | \"google\" | \"xai\" | \"local\";\n\n/** USD per million tokens, valid over `[from, to)`, with its citation. */\nexport type PricePeriod = {\n\t/** Inclusive lower bound, epoch ms. `null` = since the model existed. */\n\tfrom: number | null;\n\t/** Exclusive upper bound, epoch ms. `null` = still in effect. */\n\tto: number | null;\n\tinput: number;\n\toutput: number;\n\tcacheRead: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\tsource: string;\n};\n\n/**\n * Providers that ARE the vendor, so their rows price at that vendor's list.\n *\n * Deliberately absent, and each absence is a decision from ticket #122's\n * measurements: `opencode` (the opencode-zen gateway), `github-copilot`\n * (re-serves other vendors' slugs at its own terms), `openrouter`, `azure`,\n * `bedrock` and `vercel-ai-gateway` (resellers). A provider joins this map\n * when a harness is measured emitting it, never speculatively.\n */\nexport const PROVIDER_VENDOR: Record<string, Vendor> = {\n\tanthropic: \"anthropic\",\n\topenai: \"openai\",\n\tgoogle: \"google\",\n\txai: \"xai\",\n};\n\n/**\n * Explicit pricing equivalents that do not collapse catalog identity.\n *\n * Grok Build records `grok-4.6-build` for usage while identifying the selected\n * model as `grok-4.6`. The Build id remains the measured and catalog id. It may\n * borrow the base model's cited rate only when no exact rate exists.\n */\nexport const PRICING_ALIASES: Readonly<Record<string, string>> = {\n\t\"grok-4.6-build\": \"grok-4.6\",\n};\n\n/**\n * Providers that run the model on this machine. No API call, no per-token\n * charge. Ids as opencode and pi-mono spell them.\n */\nexport const LOCAL_PROVIDERS = new Set([\n\t\"ollama\",\n\t\"lmstudio\",\n\t\"llama.cpp\",\n\t\"llamacpp\",\n\t\"local\",\n]);\n\nconst FREE_PERIOD: PricePeriod = {\n\tfrom: null,\n\tto: null,\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite5m: 0,\n\tcacheWrite1h: 0,\n\tsource: LOCAL_PRICING_TABLE_VERSION,\n};\n\n/**\n * Split a pricing key into its provider and its model part.\n *\n * A key with no separator has no provider, which is what the single-vendor\n * adapters produce. The split is on the FIRST separator only, so an id that\n * carries its own colon (`ollama:llama3.2:3b`) keeps it.\n */\nexport function splitModelKey(modelKey: string): {\n\tprovider: string | null;\n\tmodel: string;\n} {\n\tconst at = modelKey.indexOf(PROVIDER_SEPARATOR);\n\tif (at === -1) return { provider: null, model: modelKey };\n\treturn {\n\t\tprovider: modelKey.slice(0, at),\n\t\tmodel: modelKey.slice(at + PROVIDER_SEPARATOR.length),\n\t};\n}\n\n/**\n * The alias rules of ADR-0012 decision 6, applied before any lookup: strip the\n * `provider:` prefix, the `#fast` suffix and a trailing `-YYYYMMDD` date. The\n * three parts come back separately so a caller can keep the ones it needs.\n */\nexport function parseMeasuredId(id: string): {\n\tprovider: string | null;\n\tslug: string;\n\tfast: boolean;\n} {\n\tconst { provider, model } = splitModelKey(id);\n\tconst [base, suffix] = model.split(\"#\");\n\treturn {\n\t\tprovider,\n\t\tslug: base.replace(/-\\d{8}$/, \"\"),\n\t\tfast: suffix === \"fast\",\n\t};\n}\n\nconst key = (slug: string, provider: string | null | undefined) =>\n\t`${provider ?? \"\"}\u0000${slug}`;\n\n/**\n * A table indexed for lookup: every (model, provider) with its periods in\n * order, each closed by the next one's start.\n */\nexport class PriceIndex {\n\tprivate readonly periods = new Map<string, PricePeriod[]>();\n\tprivate readonly vendors = new Map<string, Vendor>();\n\treadonly id: string;\n\n\tconstructor(table: PriceTable) {\n\t\tthis.id = table.id;\n\t\tconst groups = new Map<string, PriceRow[]>();\n\t\tfor (const row of table.rows) {\n\t\t\tconst k = key(row.modelSlug, row.provider);\n\t\t\tconst g = groups.get(k) ?? [];\n\t\t\tg.push(row);\n\t\t\tgroups.set(k, g);\n\t\t\tif (row.vendor && row.provider === undefined) {\n\t\t\t\tthis.vendors.set(row.modelSlug, row.vendor);\n\t\t\t}\n\t\t}\n\t\tfor (const [k, rows] of groups) {\n\t\t\trows.sort((a, b) => a.from - b.from);\n\t\t\tthis.periods.set(\n\t\t\t\tk,\n\t\t\t\trows.map((r, i) => ({\n\t\t\t\t\tfrom: r.from === 0 ? null : r.from,\n\t\t\t\t\tto: i + 1 < rows.length ? rows[i + 1].from : null,\n\t\t\t\t\tinput: r.input,\n\t\t\t\t\toutput: r.output,\n\t\t\t\t\tcacheRead: r.cacheRead ?? 0,\n\t\t\t\t\tcacheWrite5m: r.cacheWrite5m ?? 0,\n\t\t\t\t\tcacheWrite1h: r.cacheWrite1h ?? 0,\n\t\t\t\t\tsource: r.source,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\t}\n\n\t/** The vendor a bare row belongs to, when the table says. */\n\tvendorOf(slug: string): Vendor | null {\n\t\treturn this.vendors.get(slug) ?? null;\n\t}\n\n\t/** True when the table holds any row at all for this (model, provider). */\n\thas(slug: string, provider: string | null): boolean {\n\t\treturn this.periods.has(key(slug, provider));\n\t}\n\n\t/** The periods for exactly this (model, provider). */\n\trowsFor(slug: string, provider: string | null): PricePeriod[] {\n\t\treturn this.periods.get(key(slug, provider)) ?? [];\n\t}\n\n\tget size(): number {\n\t\treturn this.periods.size;\n\t}\n}\n\n/**\n * Several tables layered: the first one that holds a key answers for it. The\n * CLI layers the served table over the bundled one; the backend layers\n * `modelPrices` over the same bundled one. The provider rule is stated here,\n * once, for both.\n */\nexport class Pricer {\n\tconstructor(\n\t\tprivate readonly layers: readonly PriceIndex[],\n\t\tprivate readonly vendorHint: (slug: string) => Vendor | null = () => null,\n\t) {}\n\n\t/** The ids of the layers, in lookup order. */\n\tget tableIds(): string[] {\n\t\treturn this.layers.map((l) => l.id);\n\t}\n\n\tprivate vendorOf(slug: string): Vendor | null {\n\t\tfor (const layer of this.layers) {\n\t\t\tconst v = layer.vendorOf(slug);\n\t\t\tif (v) return v;\n\t\t}\n\t\treturn this.vendorHint(slug);\n\t}\n\n\tprivate firstLayerWith(slug: string, provider: string | null) {\n\t\treturn this.layers.find((l) => l.has(slug, provider)) ?? null;\n\t}\n\n\tprivate lookupSlug(slug: string, provider: string | null): string | null {\n\t\tif (this.firstLayerWith(slug, provider)) return slug;\n\t\tconst alias = PRICING_ALIASES[slug];\n\t\treturn alias && this.firstLayerWith(alias, provider) ? alias : null;\n\t}\n\n\t/**\n\t * Every period that applies to a pricing key, or an empty list when none\n\t * can be cited.\n\t *\n\t * A bare key is the vendor's own rate. A local provider is free. A provider\n\t * with rows of its own uses them. A provider that IS the vendor reaches the\n\t * vendor's bare rows. Anything else (a gateway, an unknown provider) holds\n\t * no rate.\n\t */\n\tperiodsFor(modelKey: string): PricePeriod[] {\n\t\tconst { provider, model } = splitModelKey(modelKey);\n\t\tif (provider === null) {\n\t\t\tconst slug = this.lookupSlug(model, null);\n\t\t\treturn slug\n\t\t\t\t? (this.firstLayerWith(slug, null)?.rowsFor(slug, null) ?? [])\n\t\t\t\t: [];\n\t\t}\n\t\tif (LOCAL_PROVIDERS.has(provider)) return [FREE_PERIOD];\n\t\tconst ownSlug = this.lookupSlug(model, provider);\n\t\tif (ownSlug) {\n\t\t\treturn (\n\t\t\t\tthis.firstLayerWith(ownSlug, provider)?.rowsFor(ownSlug, provider) ?? []\n\t\t\t);\n\t\t}\n\t\tconst vendor = PROVIDER_VENDOR[provider];\n\t\tconst vendorSlug = this.lookupSlug(model, null);\n\t\tif (!vendor || !vendorSlug || this.vendorOf(vendorSlug) !== vendor)\n\t\t\treturn [];\n\t\treturn (\n\t\t\tthis.firstLayerWith(vendorSlug, null)?.rowsFor(vendorSlug, null) ?? []\n\t\t);\n\t}\n\n\tisLocal(modelKey: string): boolean {\n\t\tconst { provider } = splitModelKey(modelKey);\n\t\treturn provider !== null && LOCAL_PROVIDERS.has(provider);\n\t}\n\n\tisPriced(modelKey: string): boolean {\n\t\treturn this.periodsFor(modelKey).length > 0;\n\t}\n\n\t/**\n\t * The rate in effect at `atMs`, or `null` when the model is unknown or the\n\t * timestamp predates every period. A `null` timestamp also yields `null`:\n\t * inventing a price for an undated record would attribute the wrong rate.\n\t */\n\tpriceAt(modelKey: string, atMs: number | null): PricePeriod | null {\n\t\tif (atMs === null) return null;\n\t\tfor (const p of this.periodsFor(modelKey)) {\n\t\t\tif (\n\t\t\t\t(p.from === null || atMs >= p.from) &&\n\t\t\t\t(p.to === null || atMs < p.to)\n\t\t\t) {\n\t\t\t\treturn p;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Every rate that applies anywhere inside `[fromMs, toMs]`. */\n\tperiodsInWindow(\n\t\tmodelKey: string,\n\t\tfromMs: number,\n\t\ttoMs: number,\n\t): PricePeriod[] {\n\t\treturn this.periodsFor(modelKey).filter(\n\t\t\t(p) =>\n\t\t\t\t(p.from === null || p.from <= toMs) && (p.to === null || p.to > fromMs),\n\t\t);\n\t}\n\n\t/**\n\t * The citation for this key: the source of the period in effect at `atMs`,\n\t * or of the latest period when no time is given. `null` when unpriced.\n\t */\n\ttableFor(modelKey: string, atMs?: number): string | null {\n\t\tconst periods = this.periodsFor(modelKey);\n\t\tif (periods.length === 0) return null;\n\t\tif (atMs !== undefined) return this.priceAt(modelKey, atMs)?.source ?? null;\n\t\treturn periods[periods.length - 1].source;\n\t}\n}\n\n/** A stable id for a served table: the row count and a hash of the rows. */\nexport function priceTableId(rows: readonly PriceRow[]): string {\n\tconst text = rows\n\t\t.map(\n\t\t\t(r) =>\n\t\t\t\t`${r.modelSlug}|${r.provider ?? \"\"}|${r.from}|${r.input}|${r.output}|${r.cacheRead ?? \"\"}|${r.cacheWrite5m ?? \"\"}|${r.cacheWrite1h ?? \"\"}|${r.source}`,\n\t\t)\n\t\t.sort()\n\t\t.join(\"\\n\");\n\t// FNV-1a, 32-bit. Enough to tell two tables apart in a log line.\n\tlet h = 0x811c9dc5;\n\tfor (let i = 0; i < text.length; i++) {\n\t\th ^= text.charCodeAt(i);\n\t\th = Math.imul(h, 0x01000193) >>> 0;\n\t}\n\treturn `modelPrices/${rows.length}-${h.toString(16).padStart(8, \"0\")}`;\n}\n\n/**\n * Narrow untrusted JSON into a table. A row that fails the shape is dropped\n * rather than failing the whole fetch; a table with no rows is `null` so the\n * caller falls back to the bundled one.\n */\nexport function parsePriceTable(body: unknown): PriceTable | null {\n\tif (typeof body !== \"object\" || body === null) return null;\n\tconst b = body as { id?: unknown; rows?: unknown };\n\tif (typeof b.id !== \"string\" || !Array.isArray(b.rows)) return null;\n\tconst num = (v: unknown): v is number =>\n\t\ttypeof v === \"number\" && Number.isFinite(v) && v >= 0;\n\tconst opt = (v: unknown): number | undefined => (num(v) ? v : undefined);\n\tconst rows: PriceRow[] = [];\n\tfor (const raw of b.rows) {\n\t\tconst r = raw as Record<string, unknown>;\n\t\tif (\n\t\t\ttypeof r?.modelSlug !== \"string\" ||\n\t\t\tr.modelSlug.length === 0 ||\n\t\t\t!num(r.from) ||\n\t\t\t!num(r.input) ||\n\t\t\t!num(r.output) ||\n\t\t\ttypeof r.source !== \"string\"\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst vendor =\n\t\t\tr.vendor === \"anthropic\" ||\n\t\t\tr.vendor === \"openai\" ||\n\t\t\tr.vendor === \"google\" ||\n\t\t\tr.vendor === \"xai\" ||\n\t\t\tr.vendor === \"local\"\n\t\t\t\t? r.vendor\n\t\t\t\t: undefined;\n\t\trows.push({\n\t\t\tmodelSlug: r.modelSlug,\n\t\t\t...(typeof r.provider === \"string\" && r.provider.length > 0\n\t\t\t\t? { provider: r.provider }\n\t\t\t\t: {}),\n\t\t\tfrom: r.from,\n\t\t\tinput: r.input,\n\t\t\toutput: r.output,\n\t\t\t...(opt(r.cacheRead) !== undefined\n\t\t\t\t? { cacheRead: r.cacheRead as number }\n\t\t\t\t: {}),\n\t\t\t...(opt(r.cacheWrite5m) !== undefined\n\t\t\t\t? { cacheWrite5m: r.cacheWrite5m as number }\n\t\t\t\t: {}),\n\t\t\t...(opt(r.cacheWrite1h) !== undefined\n\t\t\t\t? { cacheWrite1h: r.cacheWrite1h as number }\n\t\t\t\t: {}),\n\t\t\tsource: r.source,\n\t\t\t...(vendor ? { vendor } : {}),\n\t\t});\n\t}\n\tif (rows.length === 0) return null;\n\treturn { id: b.id, rows };\n}\n","// Time-aware pinned price table for API-equivalent cost.\n//\n// Wayfinder ticket #37 (map #29), decision 8 of the wire-format grilling #33.\n// Moved out of the CLI by ticket #93 (map #76).\n//\n// WHY THIS IS A PACKAGE AND NOT A CLI FILE\n// Two programs price the same tokens. The CLI prices each response at ingest,\n// where the per-response timestamp still exists. The backend re-prices a\n// published snapshot at READ time, to fill the gaps a stale CLI table left\n// behind - one day of table drift published a stack at $14,764 when the same\n// tokens are worth at least $167,331 (#93). Two copies of a price table drift\n// against each other by construction, so there is one copy and both import it.\n//\n// WHY THIS IS A LIST OF PERIODS AND NOT A FLAT MAP\n// A published \"API-equivalent cost\" covers a rolling 30-day window, and a\n// window can straddle a repricing. On 2026-09-05 the window covers Aug 6 →\n// Sep 5, but `claude-sonnet-5`'s introductory rate ends Aug 31 - so 25 days\n// price at $2/$10 and 5 days at $3/$15. A flat table misprices one side or the\n// other for a month after every repricing, which breaks the honesty tenet the\n// measured layer is built on.\n//\n// So each model's price is a list of effective-from ranges, and every response\n// is priced at the rate in effect at ITS OWN timestamp. Cost therefore has to\n// accumulate at ingest (see analyzer.ts) - summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change.\n//\n// Sources: Anthropic public list prices as of 2026-07-25 (cache multipliers\n// from https://platform.claude.com/docs/en/build-with-claude/prompt-caching:\n// 5m cache write = 1.25x input, 1h cache write = 2x input, read = 0.1x input)\n// and OpenAI public list prices as of 2026-08-02\n// (https://developers.openai.com/api/docs/pricing - cached input is 10% of\n// input, the same multiplier `cacheRead` already uses; Codex reports no cache\n// writes, so the write multipliers never fire for OpenAI rows).\n// Google public list prices as of 2026-08-09\n// (https://ai.google.dev/gemini-api/docs/pricing) were added by ticket #123.\n//\n// A read-time estimate cites the table of the model it priced, which is why the\n// table id sits on the rate rather than beside it.\n//\n// WHY A KEY CAN CARRY A PROVIDER\n// Claude Code and Codex each speak to one vendor, so a bare model id names a\n// rate without ambiguity. opencode and pi-mono route many providers, and two of\n// them RE-SERVE another vendor's models under that vendor's own slug: the\n// opencode-zen gateway and github-copilot both emit `gemini-3-pro-preview`, at\n// prices no list page states (ticket #122). Pricing a re-served model at the\n// vendor's list rate would invent a figure and could overstate, which the\n// lower-bound tenet forbids.\n//\n// So a multi-provider adapter keys its rows `provider:model`, and only a\n// provider this table maps to a vendor reaches that vendor's rates. Everything\n// else - gateways, unknown providers - holds no rate and lands in\n// `unpricedTokens`, which is the honest outcome and needs no new machinery.\n//\n// The separator is `:` and not `/` on purpose: `sanitizeModelId` in the CLI\n// payload rewrites `/` to `-`, so a slash-keyed id would reach the backend as a\n// different string than the one this table holds, and the read-time re-pricer\n// would miss it.\n//\n// A LOCAL MODEL IS FREE, WHICH IS NOT THE SAME FACT AS UNPRICED\n// `ollama:qwen3-coder` costs nothing per token. `openrouter:qwen3-coder` costs\n// something this table cannot name. Both used to look identical - no row, no\n// rate, tokens excluded from coverage - so a local run read as a hole in the\n// table. Local providers now hold a real zero rate, cited as\n// `LOCAL_PRICING_TABLE_VERSION`. Their tokens count as covered and add $0.\n//\n// WHERE THE RATES LIVE NOW (#336, ADR-0012)\n// The constants below are the BUNDLED FALLBACK. The live table is the Convex\n// `modelPrices` table, served to the CLI at `/api/prices` and layered over\n// these constants by `layeredPricer`. `table.ts` holds the row shape and the\n// lookup both sides share; this file holds the constants and the module-level\n// functions the adapters call, which read whichever pricer is active.\n\nexport const PRICING_TABLE_VERSION = \"anthropic-list-2026-07-25\";\nexport const OPENAI_PRICING_TABLE_VERSION = \"openai-list-2026-08-02\";\nexport const GOOGLE_PRICING_TABLE_VERSION = \"google-list-2026-08-09\";\nexport const XAI_PRICING_TABLE_VERSION = \"models.dev@2026-08-29\";\n/**\n * The id the CLI prints when it priced against the bundled constants rather\n * than a table the server served (#336). Bump it when a constant changes.\n */\nexport const BUNDLED_PRICE_TABLE_ID = \"bundled-2026-09-10\";\n\nexport {\n\tLOCAL_PRICING_TABLE_VERSION,\n\tPRICING_ALIASES,\n\tPROVIDER_SEPARATOR,\n\tPriceIndex,\n\ttype PricePeriod,\n\ttype PriceRow,\n\tPricer,\n\ttype PriceTable,\n\tparseMeasuredId,\n\tparsePriceTable,\n\tpriceTableId,\n\tsplitModelKey,\n\ttype Vendor,\n} from \"./table.js\";\n\nimport {\n\tPROVIDER_SEPARATOR,\n\tPriceIndex,\n\ttype PricePeriod,\n\ttype PriceRow,\n\tPricer,\n\ttype PriceTable,\n\tsplitModelKey,\n\ttype Vendor,\n} from \"./table.js\";\n\nexport const CACHE_WRITE_5M_MULTIPLIER = 1.25;\nexport const CACHE_WRITE_1H_MULTIPLIER = 2.0;\nexport const CACHE_READ_MULTIPLIER = 0.1;\n\n/**\n * End of the `claude-sonnet-5` introductory rate. Anthropic documents it as \"in\n * effect through 2026-08-31\", so the post-intro period opens at the following\n * UTC midnight.\n *\n * The boundary is approximated in UTC because the announcement names a date,\n * not a timezone. A response written within a few hours of the boundary can\n * therefore be priced on the wrong side of it - worth a handful of cents on a\n * single day, and the alternative (guessing US/Pacific) is no more defensible.\n */\nexport const SONNET_5_INTRO_ENDS_MS = Date.UTC(2026, 8, 1); // 2026-09-01T00:00:00Z\n\n/**\n * How a vendor charges for cache traffic, as multipliers on its input rate.\n *\n * Only the bundled constants are written this way. A served period carries\n * absolute cache rates (ADR-0012 decision 4), and `cacheMultipliersFor` derives\n * the multipliers back from them for callers that still want the ratio.\n */\nexport type CacheMultipliers = {\n\twrite5m: number;\n\twrite1h: number;\n\tread: number;\n};\n\nconst DEFAULT_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: CACHE_WRITE_5M_MULTIPLIER,\n\twrite1h: CACHE_WRITE_1H_MULTIPLIER,\n\tread: CACHE_READ_MULTIPLIER,\n};\n\n/**\n * Cached input is 10% of input; a write is charged as plain input. Google\n * bills cache storage by the hour instead, which this table cannot see, so a\n * Google figure stays below the true one rather than above it.\n */\nconst GOOGLE_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: 1.0,\n\twrite1h: 1.0,\n\tread: 0.1,\n};\n\nconst XAI_CACHE_MULTIPLIERS: CacheMultipliers = {\n\twrite5m: 0,\n\twrite1h: 0,\n\tread: 0.25,\n};\n\n/** A bundled rate, before it is rendered into dated rows. */\ntype BundledPeriod = {\n\tfrom: number | null;\n\tinput: number;\n\toutput: number;\n};\n\ntype PriceEntry = {\n\tvendor: Vendor;\n\t/** The citation printed next to any dollar figure these rates produce. */\n\ttable: string;\n\tperiods: BundledPeriod[];\n\tcache: CacheMultipliers;\n};\n\nconst anthropic = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"anthropic\",\n\ttable: PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: DEFAULT_CACHE_MULTIPLIERS,\n});\nconst openai = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"openai\",\n\ttable: OPENAI_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: DEFAULT_CACHE_MULTIPLIERS,\n});\nconst google = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"google\",\n\ttable: GOOGLE_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: GOOGLE_CACHE_MULTIPLIERS,\n});\nconst xai = (periods: BundledPeriod[]): PriceEntry => ({\n\tvendor: \"xai\",\n\ttable: XAI_PRICING_TABLE_VERSION,\n\tperiods,\n\tcache: XAI_CACHE_MULTIPLIERS,\n});\nconst flat = (input: number, output: number): BundledPeriod[] => [\n\t{ from: null, input, output },\n];\n\n/**\n * The priced lanes (ADR-0012 decision 7): measured ids that carry a rate but\n * name no model a person can choose. They live here and never in the catalog\n * or in `modelPrices`, so the seed migration and the served table skip them.\n */\nexport const PRICED_LANES: ReadonlySet<string> = new Set([\"codex-auto-review\"]);\n\n/**\n * Only rates we can actually cite are encoded. Inventing historical periods to\n * make the table look complete would fabricate cost for old records, so every\n * model with one known rate gets one open-ended period.\n *\n * Where models.dev and a list page disagreed on 2026-08-29, models.dev won\n * (ADR-0012 decision 12): `gpt-5.6-sol` and `gemini-3.6-flash` below.\n */\nconst PRICES: Record<string, PriceEntry> = {\n\t\"claude-fable-5\": anthropic(flat(10, 50)),\n\t\"claude-mythos-5\": anthropic(flat(10, 50)),\n\t\"claude-opus-5\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-8\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-7\": anthropic(flat(5, 25)),\n\t\"claude-opus-4-6\": anthropic(flat(5, 25)),\n\t\"claude-sonnet-5\": anthropic([\n\t\t{ from: null, input: 2, output: 10 },\n\t\t{ from: SONNET_5_INTRO_ENDS_MS, input: 3, output: 15 },\n\t]),\n\t\"claude-sonnet-4-6\": anthropic(flat(3, 15)),\n\t\"claude-haiku-4-5\": anthropic(flat(1, 5)),\n\t// Fast mode (research preview) - Claude API only, Opus 5 / Opus 4.8 only.\n\t// Opus 4.7 fast mode was removed, so there is deliberately no 4-7 entry.\n\t\"claude-opus-5#fast\": anthropic(flat(10, 50)),\n\t\"claude-opus-4-8#fast\": anthropic(flat(10, 50)),\n\t// OpenAI (Codex) - standard-context tier (<272K; observed context window is\n\t// 258,400).\n\t\"gpt-5.5\": openai(flat(5, 30)),\n\t\"gpt-5.4\": openai(flat(2.5, 15)),\n\t\"gpt-5.4-mini\": openai(flat(0.75, 4.5)),\n\t\"gpt-5.3-codex\": openai(flat(1.75, 14)),\n\t// The gpt-5.6 family launched 2026-07-29; Terra and Luna were repriced on\n\t// 2026-07-30 (-20% / -80%). The one-day launch rates are not on the list\n\t// page and are NOT encoded - a July-29 Terra/Luna record underprices for\n\t// one day rather than carrying a rate we cannot cite (#72).\n\t// Sol: models.dev reports $4 / $20 on 2026-08-29; the earlier $5 / $30 is\n\t// not dated, so the lower rate prices the whole period (lower bound).\n\t\"gpt-5.6-sol\": openai(flat(4, 20)),\n\t\"gpt-5.6-terra\": openai(flat(2, 12)),\n\t\"gpt-5.6-luna\": openai(flat(0.2, 1.2)),\n\t// NOT on OpenAI's list page - an internal Codex routing label with no\n\t// official price (openai/codex#20981). Rate is the aggregator consensus\n\t// ($2.50 / $15.00), scoped in explicitly by ticket #72 because it carries\n\t// real token volume in Codex rollouts. A priced lane, see PRICED_LANES.\n\t\"codex-auto-review\": openai(flat(2.5, 15)),\n\t// Google (opencode, pi-mono) - Standard tier. Where a model is\n\t// context-tiered, the <=200K rate is encoded, exactly as the OpenAI rows\n\t// encode the standard-context tier: the payload carries no per-response\n\t// context length, so the cheaper side keeps the figure a lower bound.\n\t\"gemini-3.1-pro-preview\": google(flat(2, 12)),\n\t// models.dev reports $0.75 / $3.75 on 2026-08-29 (was $1.5 / $7.5).\n\t\"gemini-3.6-flash\": google(flat(0.75, 3.75)),\n\t\"gemini-3.5-flash\": google(flat(1.5, 9)),\n\t\"gemini-3-flash-preview\": google(flat(0.5, 3)),\n\t\"gemini-2.5-pro\": google(flat(1.25, 10)),\n\t\"gemini-2.5-flash\": google(flat(0.3, 2.5)),\n\t// RETIRED from Google's list page by 2026-08-09, and still the largest\n\t// single block of Google tokens measured in #122. Encoded at its launch\n\t// rate: real volume, a rate we can name. Announcement rate, <=200K tier.\n\t\"gemini-3-pro-preview\": google(flat(2, 12)),\n\t// A real Anthropic model with no row until #123. Measured in #122 as\n\t// `claude-opus-4-5-20251101`, which the dated-suffix rule strips to this key.\n\t\"claude-opus-4-5\": anthropic(flat(5, 25)),\n\t// models.dev's xAI row mirrored into the live table on 2026-08-29. Grok\n\t// Build's explicit `grok-4.6-build` pricing alias reaches this base rate.\n\t\"grok-4.6\": xai(flat(2, 6)),\n};\n\n/**\n * The constants above as dated rows. This is what the seed migration writes\n * into `modelPrices` and what the CLI prices against when the server's table\n * is out of reach. Cache tiers are rendered absolute here, once.\n */\nexport function bundledPriceTable(): PriceTable {\n\tconst rows: PriceRow[] = [];\n\tfor (const [modelSlug, entry] of Object.entries(PRICES)) {\n\t\tfor (const p of entry.periods) {\n\t\t\trows.push({\n\t\t\t\tmodelSlug,\n\t\t\t\tfrom: p.from ?? 0,\n\t\t\t\tinput: p.input,\n\t\t\t\toutput: p.output,\n\t\t\t\tcacheRead: p.input * entry.cache.read,\n\t\t\t\tcacheWrite5m: p.input * entry.cache.write5m,\n\t\t\t\tcacheWrite1h: p.input * entry.cache.write1h,\n\t\t\t\tsource: entry.table,\n\t\t\t\tvendor: entry.vendor,\n\t\t\t});\n\t\t}\n\t}\n\treturn { id: BUNDLED_PRICE_TABLE_ID, rows };\n}\n\nconst BUNDLED_INDEX = new PriceIndex(bundledPriceTable());\nconst BUNDLED_PRICER = new Pricer([BUNDLED_INDEX]);\n\n/** The pricer over the bundled constants alone. */\nexport function bundledPricer(): Pricer {\n\treturn BUNDLED_PRICER;\n}\n\n/**\n * A pricer that answers from `table` first and from the bundled constants for\n * every key the table lacks. The CLI installs the served table this way; the\n * backend builds the same shape over `modelPrices`.\n */\nexport function layeredPricer(\n\ttable: PriceTable,\n\tvendorHint?: (slug: string) => Vendor | null,\n): Pricer {\n\treturn new Pricer([new PriceIndex(table), BUNDLED_INDEX], vendorHint);\n}\n\n/**\n * The pricer the module-level functions below consult. The CLI's sync sets it\n * to the served table before scanning (#336) and every adapter prices through\n * it without knowing. Defaults to the bundled constants.\n */\nlet active: Pricer = BUNDLED_PRICER;\n\nexport function setActivePricer(pricer: Pricer | null): void {\n\tactive = pricer ?? BUNDLED_PRICER;\n}\n\n/** The ids of the tables the active pricer consults, served first. */\nexport function activePriceTableIds(): string[] {\n\treturn active.tableIds;\n}\n\n/**\n * Build the pricing key a multi-provider harness reports under. Pass the\n * harness's own provider id verbatim; this table decides what it means.\n */\nexport function modelKeyFor(provider: string, model: string): string {\n\treturn `${provider}${PROVIDER_SEPARATOR}${model}`;\n}\n\nexport type TokenCounts = {\n\tinput: number;\n\toutput: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\t/** `cache_creation_input_tokens` not covered by the TTL breakdown; priced at the 5m rate. */\n\tcacheWriteUnsplit: number;\n\tcacheRead: number;\n};\n\n/**\n * Normalize an observed `message.model` into a pricing key. Handles the\n * dated-suffix variants (`claude-haiku-4-5-20251001`). The `#fast` suffix is\n * appended by the caller from `usage.speed`. A `provider:` prefix passes\n * through untouched, so a caller can normalize a composed key.\n */\nexport function normalizeModel(model: string): string {\n\tconst { provider, model: bare } = splitModelKey(model);\n\tconst [base, suffix] = bare.split(\"#\");\n\tconst stripped = base.replace(/-\\d{8}$/, \"\");\n\tconst normalized = suffix ? `${stripped}#${suffix}` : stripped;\n\treturn provider === null ? normalized : modelKeyFor(provider, normalized);\n}\n\n/**\n * Drop the analyzer's synthetic `#fast` suffix, leaving the id the payload\n * publishes.\n *\n * A `provider:` prefix SURVIVES this, and that is deliberate: the provider is\n * what tells `google:gemini-3-pro-preview` from\n * `github-copilot:gemini-3-pro-preview`, and the backend re-pricer needs that\n * difference at read time. Use `vendorModelId` where a human or the models\n * catalog needs the plain vendor id.\n */\nexport function baseModelId(modelKey: string): string {\n\treturn modelKey.split(\"#\")[0];\n}\n\n/**\n * The vendor-assigned id alone - no provider prefix, no `#fast`. This is the id\n * to show a reader and to match against the models catalog.\n */\nexport function vendorModelId(modelKey: string): string {\n\treturn splitModelKey(baseModelId(modelKey)).model;\n}\n\n/**\n * How this model's vendor charges for cache traffic, as ratios of the latest\n * period's input rate. Falls back to the Anthropic-shaped constants for a\n * model with no rate, so a caller that prices an unknown model still gets a\n * defined shape rather than a crash.\n */\nexport function cacheMultipliersFor(modelKey: string): CacheMultipliers {\n\tconst periods = active.periodsFor(modelKey);\n\tconst p = periods[periods.length - 1];\n\tif (!p) return DEFAULT_CACHE_MULTIPLIERS;\n\tif (p.input === 0) return { write5m: 0, write1h: 0, read: 0 };\n\t// Absolute rates back to ratios; rounded so 0.075 / 0.75 reads 0.1.\n\tconst ratio = (rate: number) => Math.round((rate / p.input) * 1e6) / 1e6;\n\treturn {\n\t\twrite5m: ratio(p.cacheWrite5m),\n\t\twrite1h: ratio(p.cacheWrite1h),\n\t\tread: ratio(p.cacheRead),\n\t};\n}\n\n/**\n * True when this key names a model that runs locally and therefore costs\n * nothing per token. A caller printing dollars uses this to say \"free\", never\n * \"unknown\".\n */\nexport function isLocalModel(modelKey: string): boolean {\n\treturn active.isLocal(modelKey);\n}\n\n/**\n * The rate in effect for `modelKey` at `atMs`, or `null` when the model is\n * unknown or the timestamp predates every period we can cite.\n *\n * A `null` timestamp also yields `null`: a record with no parseable timestamp\n * cannot be priced time-awarely, and inventing a price for it (say, today's)\n * would silently attribute the wrong rate. Its tokens surface as unpriced.\n */\nexport function priceAt(\n\tmodelKey: string,\n\tatMs: number | null,\n): PricePeriod | null {\n\treturn active.priceAt(modelKey, atMs);\n}\n\n/** True when we hold at least one citable rate for this model, at any time. */\nexport function isPricedModel(modelKey: string): boolean {\n\treturn active.isPriced(modelKey);\n}\n\n/**\n * The table id that cites this model's rates, or `null` when it has none.\n *\n * The citation belongs to the rate, not to the harness that reported it: a\n * read-time estimate is cited by the table it was drawn from, and one stack can\n * carry Anthropic and OpenAI rows at once.\n */\nexport function pricingTableFor(\n\tmodelKey: string,\n\tatMs?: number,\n): string | null {\n\treturn active.tableFor(modelKey, atMs);\n}\n\n/**\n * Every rate that applies to `modelKey` anywhere inside `[fromMs, toMs]`.\n *\n * This is the read-time counterpart of `priceAt`. A published snapshot has no\n * per-response timestamps left, so a re-pricer can only ask \"which rates could\n * this window have paid\", and then choose.\n */\nexport function pricePeriodsInWindow(\n\tmodelKey: string,\n\tfromMs: number,\n\ttoMs: number,\n): PricePeriod[] {\n\treturn active.periodsInWindow(modelKey, fromMs, toMs);\n}\n\n/** The dollars one period charges for these tokens. */\nexport function costAtPeriod(p: PricePeriod, t: TokenCounts): number {\n\tconst M = 1_000_000;\n\treturn (\n\t\t(t.input * p.input +\n\t\t\tt.output * p.output +\n\t\t\t(t.cacheWrite5m + t.cacheWriteUnsplit) * p.cacheWrite5m +\n\t\t\tt.cacheWrite1h * p.cacheWrite1h +\n\t\t\tt.cacheRead * p.cacheRead) /\n\t\tM\n\t);\n}\n\n/**\n * Cost of one response's tokens at the rate in effect at its own timestamp.\n * Returns `null` when no rate applies - the caller must surface that as\n * unpriced tokens rather than zeroing it.\n */\nexport function apiEquivalentCost(\n\tmodelKey: string,\n\tt: TokenCounts,\n\tatMs: number | null,\n): number | null {\n\tconst p = active.priceAt(modelKey, atMs);\n\tif (!p) return null;\n\treturn costAtPeriod(p, t);\n}\n","// The one place the CLI's version string lives.\n//\n// `tsup` replaces `__AISTACK_CLI_VERSION__` at build time with the version in\n// `package.json`, so a release cannot ship a stale number. The fallback covers\n// running from source (tests, `tsx src/index.ts`), where no define happens.\ndeclare const __AISTACK_CLI_VERSION__: string | undefined;\n\nexport const CLI_VERSION: string =\n\ttypeof __AISTACK_CLI_VERSION__ === \"string\"\n\t\t? __AISTACK_CLI_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { type PriceTable, parsePriceTable } from \"@aistack/pricing\";\nimport { CLI_VERSION } from \"./version.js\";\n\nexport const BASE_URL = process.env.AISTACK_URL || \"https://aistack.to\";\n\nasync function request(\n\tpath: string,\n\toptions: RequestInit = {},\n): Promise<Response> {\n\treturn fetch(`${BASE_URL}${path}`, {\n\t\t...options,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...options.headers,\n\t\t},\n\t});\n}\n\nfunction authHeaders(token: string): HeadersInit {\n\treturn { Authorization: `Bearer ${token}` };\n}\n\n/**\n * Turn the two statuses #52 introduced into sentences.\n *\n * A bare `429` tells the user nothing they can act on, and a bare `403` reads\n * like a bug rather than a machine that is no longer allowed to do this. Every\n * other status keeps its number, because the number is all we know about it.\n */\nfunction failure(what: string, res: Response): Error {\n\tif (res.status === 429) {\n\t\tconst retry = res.headers.get(\"Retry-After\");\n\t\treturn new Error(\n\t\t\tretry\n\t\t\t\t? `${what}: too many requests. Try again in ${retry} seconds.`\n\t\t\t\t: `${what}: too many requests. Try again in a minute.`,\n\t\t);\n\t}\n\tif (res.status === 403) {\n\t\treturn new Error(\n\t\t\t`${what}: this machine is not allowed to do that. Run \\`aistack login\\` again to re-link it.`,\n\t\t);\n\t}\n\treturn new Error(`${what}: ${res.status}`);\n}\n\n/**\n * Open a device-code session.\n *\n * An automatic `machineName` is a proposal that stays editable. A label the\n * user supplied as a command parameter sets `machineNameReadOnly`, so the\n * confirmation page shows the chosen value without allowing another edit.\n */\nexport async function authStart(\n\tmachineName?: string,\n\tmachineNameReadOnly = false,\n\toptions: { replaceToken?: string; destinationRequired?: boolean } = {},\n): Promise<{\n\tsecretId: string;\n\tuserCode: string;\n\tauthUrl: string;\n}> {\n\tconst res = await request(\"/api/cli/auth/start\", {\n\t\tmethod: \"POST\",\n\t\t...(options.replaceToken\n\t\t\t? { headers: authHeaders(options.replaceToken) }\n\t\t\t: {}),\n\t\t// `cliVersion` rides along so `cli_login_completed` can report which\n\t\t// version linked the machine (#78). The server carries it on the pending\n\t\t// session and reads it at the token exchange.\n\t\tbody: JSON.stringify({\n\t\t\t...(machineName ? { machineName } : {}),\n\t\t\t...(machineNameReadOnly ? { machineNameReadOnly: true } : {}),\n\t\t\tcliVersion: CLI_VERSION,\n\t\t\t...(options.destinationRequired ? { destinationRequired: true } : {}),\n\t\t}),\n\t});\n\tif (!res.ok) throw failure(\"Auth start failed\", res);\n\treturn res.json();\n}\n\nexport async function authPoll(\n\tsecretId: string,\n): Promise<{ status: string; token?: string; userId?: string }> {\n\tconst res = await request(\n\t\t`/api/cli/auth/poll?secretId=${encodeURIComponent(secretId)}`,\n\t);\n\tif (!res.ok) throw failure(\"Auth poll failed\", res);\n\treturn res.json();\n}\n\nexport async function stackCollect(\n\ttoken: string,\n\tdata: { resources: Resource[] },\n): Promise<{ slug: string; shortId: string; url: string }> {\n\tconst res = await request(\"/api/cli/stacks/collect\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(data),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Collect failed\"));\n\t}\n\treturn res.json();\n}\n\n/** A terminal reader's budget for one error, not a log file's. */\nconst MAX_DETAIL_LINES = 4;\nconst MAX_DETAIL_LINE = 160;\nconst MAX_DETAIL = 480;\n\n/**\n * The readable part of a server error.\n *\n * A CONVEX VALIDATION ERROR CARRIES THE WHOLE OBJECT IT REFUSED. The reason and\n * the path come first and are the entire message a user can act on; after them\n * come `Object:` and `Validator:`, each holding a full dump. A real failed sync\n * printed several screens of session rows and buried the one line that said\n * what to fix. Keep the head, cut the rest, and SAY that it was cut - a message\n * silently missing its end is worse than a short one.\n */\nfunction readableDetail(detail: string): string {\n\tconst lines = detail.trim().split(\"\\n\");\n\tconst kept: string[] = [];\n\tlet cut = lines.length > MAX_DETAIL_LINES;\n\tfor (const line of lines.slice(0, MAX_DETAIL_LINES)) {\n\t\t// A dump line is one enormous line, so the cap lands mid-object. Drop it\n\t\t// entirely rather than print 160 characters of someone's session rows.\n\t\tif (line.length > MAX_DETAIL_LINE) {\n\t\t\tcut = true;\n\t\t\tcontinue;\n\t\t}\n\t\tkept.push(line);\n\t}\n\tlet text = kept.join(\"\\n\").trim();\n\tif (text.length > MAX_DETAIL) {\n\t\ttext = text.slice(0, MAX_DETAIL).trimEnd();\n\t\tcut = true;\n\t}\n\tif (!text) text = lines[0]?.slice(0, MAX_DETAIL_LINE).trimEnd() ?? \"\";\n\treturn cut ? `${text}\\n(detail truncated)` : text;\n}\n\nasync function formatHttpError(res: Response, label: string): Promise<string> {\n\tconst prefix = `${label}: ${res.status} ${res.statusText || \"\"}`.trim();\n\tconst text = await res.text().catch(() => \"\");\n\tif (!text) return prefix;\n\ttry {\n\t\tconst body = JSON.parse(text) as { error?: string; message?: string };\n\t\tconst detail = body.error || body.message;\n\t\tif (detail) return `${prefix} - ${readableDetail(detail)}`;\n\t} catch {}\n\tconst snippet = readableDetail(text);\n\treturn snippet ? `${prefix} - ${snippet}` : prefix;\n}\n\nexport type SyncPublishResult = {\n\treceivedAt: number;\n\tstackSlug: string;\n\turl: string;\n\tkeptPrivate: { stored: number; machineStored: number; refused: boolean };\n};\n\n/**\n * Publish one approved snapshot.\n *\n * Takes the staged body as an ALREADY-SERIALIZED string: the bytes the user\n * approved at the gate are the bytes on the wire, with no re-serialization\n * step between them (#35's binding constraint, #41).\n */\nexport async function syncPublish(\n\ttoken: string,\n\tbodyJson: string,\n): Promise<SyncPublishResult> {\n\tconst res = await request(\"/api/cli/sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: bodyJson,\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Sync failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Sync failed\"));\n\t}\n\treturn res.json();\n}\n\n/**\n * The day manifest (#307, ADR-0010): what the server holds for this machine,\n * date by date, each with its fingerprint, plus the retention in days.\n *\n * `null` means the server has no such route (an old backend) and the caller\n * publishes its whole window. 401 throws the same sentence a publish would,\n * so the fix is the same command either way.\n */\nexport async function fetchDayManifest(\n\tbaseUrl: string,\n\ttoken: string,\n): Promise<{\n\tretentionDays: number;\n\taggregateVersion: string;\n\tdays: { date: string; fingerprint: string }[];\n} | null> {\n\tconst res = await fetch(`${baseUrl}/api/cli/sync-manifest`, {\n\t\theaders: { \"Content-Type\": \"application/json\", ...authHeaders(token) },\n\t});\n\tif (res.status === 404) return null;\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Manifest fetch failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Manifest fetch failed\"));\n\t}\n\tconst body = (await res.json()) as {\n\t\tretentionDays?: unknown;\n\t\taggregateVersion?: unknown;\n\t\tdays?: unknown;\n\t};\n\tconst retentionDays =\n\t\ttypeof body.retentionDays === \"number\" && body.retentionDays > 0\n\t\t\t? body.retentionDays\n\t\t\t: 400;\n\tconst aggregateVersion =\n\t\ttypeof body.aggregateVersion === \"string\" ? body.aggregateVersion : \"\";\n\tconst days = Array.isArray(body.days)\n\t\t? body.days.flatMap((d: unknown) => {\n\t\t\t\tconst row = d as { date?: unknown; fingerprint?: unknown };\n\t\t\t\treturn typeof row?.date === \"string\" &&\n\t\t\t\t\ttypeof row?.fingerprint === \"string\"\n\t\t\t\t\t? [{ date: row.date, fingerprint: row.fingerprint }]\n\t\t\t\t\t: [];\n\t\t\t})\n\t\t: [];\n\treturn { retentionDays, aggregateVersion, days };\n}\n\n/**\n * The server's price table (#336): the `modelPrices` rows the CLI layers over\n * its bundled constants before pricing at ingest. Public, no bearer.\n *\n * `null` means the server has no such route (an old backend) or served a table\n * with no usable rows; the caller prices from the bundled table and says so.\n * A network failure throws and the caller treats it the same way.\n */\nexport async function fetchPriceTable(\n\tbaseUrl: string,\n): Promise<PriceTable | null> {\n\tconst res = await fetch(`${baseUrl}/api/prices`, {\n\t\theaders: { Accept: \"application/json\" },\n\t});\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw failure(\"Price table fetch failed\", res);\n\treturn parsePriceTable(await res.json());\n}\n\nexport type AutoSyncSetResult = {\n\tautoSync: { enabled: boolean; frequencyHours: number };\n\tlastAutoSyncAt: number | null;\n};\n\n/**\n * Set the auto-sync permission on the stack this machine is linked to (#103).\n *\n * The destination is the stack bound to the BEARER, exactly like a publish -\n * the body says what the permission is, never whose it is. The frequency goes\n * out only when the flag goes on: off keeps no schedule, and sending a number\n * with it would overwrite the interval the owner picked for the next enable.\n */\nexport async function setAutoSync(\n\ttoken: string,\n\tflag: { enabled: boolean; frequencyHours?: number },\n): Promise<AutoSyncSetResult> {\n\tconst res = await request(\"/api/cli/auto-sync\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(\n\t\t\tflag.enabled && flag.frequencyHours !== undefined\n\t\t\t\t? { enabled: true, frequencyHours: flag.frequencyHours }\n\t\t\t\t: { enabled: flag.enabled },\n\t\t),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 403 || res.status === 429)\n\t\tthrow failure(\"Auto-sync update failed\", res);\n\tif (!res.ok) {\n\t\tthrow new Error(await formatHttpError(res, \"Auto-sync update failed\"));\n\t}\n\treturn res.json();\n}\n\nexport async function stackGet(token: string): Promise<StackData | null> {\n\tconst res = await request(\"/api/cli/stacks\", {\n\t\theaders: authHeaders(token),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\n\t\t\t\"Authentication expired. Run `npx @use-aistack/cli login` again.\",\n\t\t);\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw failure(\"Stack fetch failed\", res);\n\treturn res.json();\n}\n\n// Types used across the CLI\nexport interface ResourceFile {\n\tname: string;\n\tcontent: string;\n\tpath?: string;\n\ttags?: string[];\n}\n\nexport interface Resource {\n\ttype: string;\n\tname: string;\n\tdescription?: string;\n\tgroup: string;\n\tstableKey: string;\n\tfiles?: ResourceFile[];\n\tupstream?: {\n\t\trepoUrl: string;\n\t\tpath?: string;\n\t\tlicense?: string;\n\t\tstars?: number;\n\t\tlastCommitSha?: string;\n\t\tlastSyncAt?: number;\n\t};\n\tpkg?: {\n\t\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\t\tid: string;\n\t\tversion?: string;\n\t\ttransport?: \"stdio\" | \"http\" | \"sse\";\n\t};\n}\n\nexport interface StackData {\n\tname: string;\n\tslug: string;\n\tshortId: string;\n\tresources: Resource[];\n}\n","import * as p from \"@clack/prompts\";\nimport { type Resource, stackCollect, stackGet } from \"../api.js\";\nimport { classify } from \"../classifier.js\";\nimport { getExcludedPaths, getToken, saveExcludedPaths } from \"../config.js\";\nimport { buildRepoLinkResource, detectRepoUrl } from \"../git.js\";\nimport { repoNameFromCanonical } from \"../github-repo.js\";\nimport { detectHooks } from \"../hooks.js\";\nimport { detectMcpServers } from \"../mcp.js\";\nimport { detectInstalledPlugins } from \"../plugins.js\";\nimport { type ScannedFile, scanGlobal, scanLocal } from \"../scanner.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tred,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\n// Sentinel selection key for the detected repo link. NUL-prefixed so it can\n// never collide with a real ScannedFile.relativePath, letting links ride the\n// existing excluded[] selection/persistence model with no config.ts changes.\nconst REPO_LINK_KEY = \"\\0repo-link\";\n\n/**\n * A non-file resource surfaced during collect - the repo link, an installed\n * plugin, etc. Toggleable and persisted exactly like a scanned file, keyed by\n * its sentinel. Everything attaches to the single stack (global) server-side.\n */\ninterface DetectedLink {\n\tkey: string;\n\tresource: Resource;\n\tlabel: string;\n}\n\nexport async function collectCommand(options: { global: boolean }) {\n\tintro(\"collect\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst savedExcluded = getExcludedPaths(cwd);\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning...\");\n\n\tconst localFiles = scanLocal(cwd);\n\tconst globalFiles = options.global ? scanGlobal() : [];\n\ts.stop(\"Scan complete\");\n\n\tif (localFiles.length === 0 && globalFiles.length === 0) {\n\t\tp.log.warn(\"No AI configuration files found.\");\n\t\toutroSkipped(\"nothing to collect\");\n\t\treturn;\n\t}\n\n\t// Apply saved exclusions\n\tconst allFiles = [...localFiles, ...globalFiles];\n\tlet selectedFiles = allFiles.filter(\n\t\t(f) => !savedExcluded.includes(f.relativePath),\n\t);\n\tlet excluded = allFiles.filter((f) => savedExcluded.includes(f.relativePath));\n\n\t// Show file counts\n\tp.log.info(\n\t\t`${lime(String(selectedFiles.length))} included${excluded.length > 0 ? ` · ${dim(String(excluded.length) + \" excluded\")}` : \"\"}`,\n\t);\n\n\t// Detect non-file links: the repo this lives in, installed Claude Code\n\t// plugins, MCP servers, hooks. Each is toggleable and included by default\n\t// unless previously deselected. All graceful no-ops.\n\tconst detectedLinks: DetectedLink[] = [];\n\tconst repoUrl = detectRepoUrl(cwd);\n\tif (repoUrl) {\n\t\tdetectedLinks.push({\n\t\t\tkey: REPO_LINK_KEY,\n\t\t\tresource: buildRepoLinkResource(repoUrl),\n\t\t\tlabel: `repo · ${repoNameFromCanonical(repoUrl)}`,\n\t\t});\n\t}\n\tfor (const resource of detectInstalledPlugins()) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0plugin:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `plugin · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectMcpServers(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0mcp:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `mcp · ${resource.name}`,\n\t\t});\n\t}\n\tfor (const resource of detectHooks(cwd)) {\n\t\tdetectedLinks.push({\n\t\t\tkey: `\\0hook:${resource.stableKey}`,\n\t\t\tresource,\n\t\t\tlabel: `hook · ${resource.name}`,\n\t\t});\n\t}\n\n\tconst includedLinks = new Set(\n\t\tdetectedLinks\n\t\t\t.filter((l) => !savedExcluded.includes(l.key))\n\t\t\t.map((l) => l.key),\n\t);\n\tconst withLinks = (base: Resource[]): Resource[] => [\n\t\t...base,\n\t\t...detectedLinks\n\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t.map((l) => l.resource),\n\t];\n\n\t// Classify selected files\n\tlet allResources = withLinks(classify(selectedFiles));\n\n\t// Fetch the existing stack and diff against its resources.\n\tlet existingStack: Awaited<ReturnType<typeof stackGet>> = null;\n\ttry {\n\t\texistingStack = await stackGet(token);\n\t} catch (err) {\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\t// Show file list or diff\n\tif (existingStack) {\n\t\tconst diff = diffResources(allResources, existingStack.resources);\n\t\tconst changeCount = diff.added + diff.changed + diff.removed;\n\n\t\tif (changeCount === 0) {\n\t\t\tp.log.info(\"No changes since last collect.\");\n\t\t\toutroSkipped(\"nothing to upload\");\n\t\t\treturn;\n\t\t}\n\n\t\tdivider();\n\t\tsection(\"changes\");\n\t\tlines(\n\t\t\tdiff.details.map((f) => {\n\t\t\t\tif (f.status === \"added\") return lime(`+ ${f.name}`);\n\t\t\t\tif (f.status === \"changed\") return yellow(`~ ${f.name}`);\n\t\t\t\treturn red(`- ${f.name}`);\n\t\t\t}),\n\t\t);\n\t\tif (diff.unchanged > 0) {\n\t\t\tlines([dim(`${diff.unchanged} unchanged`)]);\n\t\t}\n\t\tdivider();\n\t} else {\n\t\tconst local = selectedFiles.filter((f) => f.source === \"local\");\n\t\tconst global = selectedFiles.filter((f) => f.source === \"global\");\n\n\t\tif (local.length > 0) {\n\t\t\tp.log.step(`${bold(\"LOCAL\")} ${dim(String(local.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(local)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tif (global.length > 0) {\n\t\t\tp.log.step(`${bold(\"GLOBAL\")} ${dim(String(global.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(global)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tconst shownLinks = detectedLinks.filter((l) => includedLinks.has(l.key));\n\t\tif (shownLinks.length > 0) {\n\t\t\tp.log.step(`${bold(\"LINKS\")} ${dim(String(shownLinks.length))}`);\n\t\t\tdivider();\n\t\t\tlines(shownLinks.map((l) => dim(` ${l.label}`)));\n\t\t\tdivider();\n\t\t}\n\t}\n\n\t// Action: upload, customize, or cancel\n\tconst action = await p.select({\n\t\tmessage: existingStack\n\t\t\t? \"Upload changes?\"\n\t\t\t: `Upload ${bold(String(selectedFiles.length))} files to your stack?`,\n\t\toptions: [\n\t\t\t{ value: \"upload\", label: \"Upload\" },\n\t\t\t{ value: \"customize\", label: \"Select files\" },\n\t\t\t{ value: \"cancel\", label: \"Cancel\" },\n\t\t],\n\t});\n\n\tif (p.isCancel(action) || action === \"cancel\") {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tif (action === \"customize\") {\n\t\tconst linkOptions = detectedLinks.map((l) => ({\n\t\t\tvalue: l.key,\n\t\t\tlabel: l.label,\n\t\t\thint: \"link\",\n\t\t}));\n\t\tconst selected = await p.multiselect({\n\t\t\tmessage: \"Select files to include:\",\n\t\t\toptions: [\n\t\t\t\t...linkOptions,\n\t\t\t\t...allFiles.map((f) => ({\n\t\t\t\t\tvalue: f.relativePath,\n\t\t\t\t\tlabel: f.relativePath,\n\t\t\t\t\thint: `${f.type}${f.source === \"global\" ? \" · global\" : \"\"}`,\n\t\t\t\t})),\n\t\t\t],\n\t\t\tinitialValues: [\n\t\t\t\t...detectedLinks\n\t\t\t\t\t.filter((l) => includedLinks.has(l.key))\n\t\t\t\t\t.map((l) => l.key),\n\t\t\t\t...selectedFiles.map((f) => f.relativePath),\n\t\t\t],\n\t\t});\n\n\t\tif (p.isCancel(selected)) {\n\t\t\toutroCancel();\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tconst selectedSet = new Set(selected as string[]);\n\t\tselectedFiles = allFiles.filter((f) => selectedSet.has(f.relativePath));\n\t\texcluded = allFiles.filter((f) => !selectedSet.has(f.relativePath));\n\t\tincludedLinks.clear();\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (selectedSet.has(l.key)) includedLinks.add(l.key);\n\t\t}\n\t\tallResources = withLinks(classify(selectedFiles));\n\n\t\tif (selectedFiles.length === 0 && includedLinks.size === 0) {\n\t\t\tp.log.warn(\"No files selected.\");\n\t\t\toutroSkipped(\"nothing to collect\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\ts.start(\"Uploading...\");\n\ttry {\n\t\tconst result = await stackCollect(token, { resources: allResources });\n\t\ts.stop(lime(\"Uploaded\"));\n\t\tconst excludedKeys = excluded.map((f) => f.relativePath);\n\t\tfor (const l of detectedLinks) {\n\t\t\tif (!includedLinks.has(l.key)) excludedKeys.push(l.key);\n\t\t}\n\t\tsaveExcludedPaths(cwd, excludedKeys);\n\t\tp.log.success(dim(result.url));\n\t\toutro(lime(\"done\"));\n\t} catch (err) {\n\t\ts.stop(\"Upload failed\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"upload failed\");\n\t\tprocess.exit(1);\n\t}\n}\n\nconst TYPE_ORDER = [\n\t\"config\",\n\t\"prompt\",\n\t\"rule\",\n\t\"command\",\n\t\"skill\",\n\t\"subagent\",\n\t\"mcp\",\n\t\"hook\",\n\t\"custom\",\n];\n\nfunction groupByType(files: ScannedFile[]): Map<string, ScannedFile[]> {\n\tconst map = new Map<string, ScannedFile[]>();\n\tfor (const f of files) {\n\t\tconst existing = map.get(f.type) ?? [];\n\t\texisting.push(f);\n\t\tmap.set(f.type, existing);\n\t}\n\tconst sorted = new Map<string, ScannedFile[]>();\n\tfor (const type of TYPE_ORDER) {\n\t\tconst group = map.get(type);\n\t\tif (group) sorted.set(type, group);\n\t}\n\tfor (const [type, group] of map) {\n\t\tif (!sorted.has(type)) sorted.set(type, group);\n\t}\n\treturn sorted;\n}\n\ninterface DiffResult {\n\tadded: number;\n\tchanged: number;\n\tremoved: number;\n\tunchanged: number;\n\tdetails: Array<{ name: string; status: \"added\" | \"changed\" | \"removed\" }>;\n}\n\nexport function diffResources(\n\tcurrent: Resource[],\n\texisting: Resource[],\n): DiffResult {\n\tconst existingMap = new Map<string, string>();\n\tfor (const item of existing) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\texistingMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst currentMap = new Map<string, string>();\n\tfor (const item of current) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tcurrentMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst details: DiffResult[\"details\"] = [];\n\tlet added = 0;\n\tlet changed = 0;\n\tlet unchanged = 0;\n\n\tfor (const [key, content] of currentMap) {\n\t\tconst prev = existingMap.get(key);\n\t\tif (prev === undefined) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: key, status: \"added\" });\n\t\t} else if (prev !== content) {\n\t\t\tchanged++;\n\t\t\tdetails.push({ name: key, status: \"changed\" });\n\t\t} else {\n\t\t\tunchanged++;\n\t\t}\n\t}\n\n\tlet removed = 0;\n\tfor (const key of existingMap.keys()) {\n\t\tif (!currentMap.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: key, status: \"removed\" });\n\t\t}\n\t}\n\n\t// Linked resources (GitHub repos AND package refs like MCP servers) carry no\n\t// files, so the file maps above can't see them. Diff them by stableKey -\n\t// unique for both `linked:<repo>:<path>` and `linked:pkg:<registry>:<id>` -\n\t// otherwise a link-only change is invisible and collect wrongly reports\n\t// \"nothing to upload\".\n\tconst linkLabel = (item: Resource): string => {\n\t\tif (item.upstream)\n\t\t\treturn `link: ${repoNameFromCanonical(item.upstream.repoUrl)}`;\n\t\tif (item.pkg) return `link: ${item.pkg.id}`;\n\t\treturn `link: ${item.name}`;\n\t};\n\tconst linkMap = (items: Resource[]): Map<string, Resource> => {\n\t\tconst map = new Map<string, Resource>();\n\t\tfor (const item of items) {\n\t\t\tif ((item.upstream || item.pkg) && !item.files?.length) {\n\t\t\t\tmap.set(item.stableKey, item);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t};\n\tconst existingLinks = linkMap(existing);\n\tconst currentLinks = linkMap(current);\n\tfor (const [key, item] of currentLinks) {\n\t\tif (!existingLinks.has(key)) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"added\" });\n\t\t}\n\t}\n\tfor (const [key, item] of existingLinks) {\n\t\tif (!currentLinks.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: linkLabel(item), status: \"removed\" });\n\t\t}\n\t}\n\n\treturn { added, changed, removed, unchanged, details };\n}\n","import { basename, dirname } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport type { ScannedFile } from \"./scanner.js\";\nimport { computeStableKey } from \"./stableKey.js\";\n\nexport function classify(files: ScannedFile[]): Resource[] {\n\t// Group by {group, source, type, containing directory}\n\tconst groups = new Map<string, ScannedFile[]>();\n\tconst singletons: ScannedFile[] = [];\n\n\tconst singletonRoots = new Set([\n\t\t\".\",\n\t\t\"~\",\n\t\t\"~/.claude\",\n\t\t\"~/.cursor\",\n\t\t\"~/.continue\",\n\t\t\".claude\",\n\t\t\".cursor\",\n\t\t\".github\",\n\t]);\n\n\tfor (const file of files) {\n\t\tconst dir = dirname(file.relativePath);\n\t\tconst isSingleton = singletonRoots.has(dir);\n\n\t\tif (isSingleton) {\n\t\t\tsingletons.push(file);\n\t\t} else {\n\t\t\tconst key = `${file.group}:${file.source}:${file.type}:${dir}`;\n\t\t\tconst existing = groups.get(key) ?? [];\n\t\t\texisting.push(file);\n\t\t\tgroups.set(key, existing);\n\t\t}\n\t}\n\n\tconst items: Resource[] = [];\n\n\t// Singletons: one Resource per file\n\tfor (const file of singletons) {\n\t\tconst relPath = file.relativePath\n\t\t\t.replace(/^~\\/\\.[^/]+\\//, \"\")\n\t\t\t.replace(/^\\.[^/]+\\//, \"\");\n\t\titems.push({\n\t\t\ttype: file.type,\n\t\t\tname: file.relativePath,\n\t\t\tgroup: file.group,\n\t\t\tstableKey: computeStableKey(file.group, file.type, relPath),\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: basename(file.relativePath),\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\tpath: file.relativePath,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\t// Groups: one Resource per directory group\n\tfor (const [, groupFiles] of groups) {\n\t\tconst first = groupFiles[0];\n\t\tconst dir = dirname(first.relativePath);\n\t\tconst relPath = dir\n\t\t\t.replace(/^~\\/\\.claude\\//, \"\")\n\t\t\t.replace(/^\\.claude\\//, \"\")\n\t\t\t.replace(/^~\\/\\.cursor\\//, \"\")\n\t\t\t.replace(/^\\.cursor\\//, \"\");\n\t\tconst typeLabel =\n\t\t\tfirst.type === \"subagent\" ? \"subagents\" : `${first.type}s`;\n\n\t\titems.push({\n\t\t\ttype: first.type,\n\t\t\tname: dir,\n\t\t\tdescription: `${groupFiles.length} ${typeLabel}`,\n\t\t\tgroup: first.group,\n\t\t\tstableKey: computeStableKey(first.group, first.type, relPath),\n\t\t\tfiles: groupFiles.map((f) => ({\n\t\t\t\tname: basename(f.relativePath),\n\t\t\t\tcontent: f.content,\n\t\t\t\tpath: f.relativePath,\n\t\t\t})),\n\t\t});\n\t}\n\n\treturn items;\n}\n","export function computeStableKey(\n\tgroup: string,\n\ttype: string,\n\trelPath: string,\n): string {\n\treturn `${group}:${type}:${relPath}`;\n}\n","import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { BASE_URL } from \"./api.js\";\n\nconst CONFIG_DIR = join(homedir(), \".config\", \"aistack\");\nconst CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n\ninterface ServerCredentials {\n\ttoken: string;\n\tuserId?: string;\n}\n\n/**\n * Credentials keyed by server URL (#61). Since hash-at-rest (#52) the server\n * stores only a hash, so this file holds the only plaintext copy of each\n * token. The old flat `{token, userId}` form let a localhost login overwrite\n * the prod token, which was unrecoverable. The map keeps one entry per server.\n */\ninterface CredentialsFile {\n\tservers: Record<string, ServerCredentials>;\n}\n\n/** Where a legacy flat token is assumed to come from. */\nconst DEFAULT_SERVER_URL = \"https://aistack.to\";\n\n/**\n * Read the credentials file and lift the legacy flat form into the map.\n *\n * A legacy token carries no record of which server issued it. It is assigned\n * to the default prod URL, not the caller's current server: every real flat\n * token came from prod, and keying it under a localhost caller would put it\n * exactly where the next localhost login overwrites it.\n */\nfunction readCredentials(file: string): {\n\tdata: CredentialsFile;\n\tlegacy: boolean;\n} {\n\tconst empty = { data: { servers: {} }, legacy: false };\n\tif (!existsSync(file)) return empty;\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (!raw || typeof raw !== \"object\") return empty;\n\t\tif (raw.servers && typeof raw.servers === \"object\") {\n\t\t\treturn {\n\t\t\t\tdata: { servers: raw.servers as Record<string, ServerCredentials> },\n\t\t\t\tlegacy: false,\n\t\t\t};\n\t\t}\n\t\tif (typeof raw.token === \"string\" && raw.token) {\n\t\t\treturn {\n\t\t\t\tdata: {\n\t\t\t\t\tservers: {\n\t\t\t\t\t\t[DEFAULT_SERVER_URL]: { token: raw.token, userId: raw.userId },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tlegacy: true,\n\t\t\t};\n\t\t}\n\t\t// A cleared legacy file is `{}` - empty, but safe to rewrite.\n\t\treturn { data: { servers: {} }, legacy: true };\n\t} catch {\n\t\t// Do not rewrite an unreadable file. It may still hold a token that a\n\t\t// human can recover, and this file holds the only plaintext copy.\n\t\treturn empty;\n\t}\n}\n\nfunction writeCredentials(file: string, data: CredentialsFile): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(data, null, 2));\n}\n\nexport function getToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): string | null {\n\tconst { data, legacy } = readCredentials(file);\n\tif (legacy) writeCredentials(file, data);\n\treturn data.servers[serverUrl]?.token ?? null;\n}\n\nexport function saveToken(\n\ttoken: string,\n\tuserId?: string,\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tconst { data } = readCredentials(file);\n\tdata.servers[serverUrl] = { token, userId };\n\twriteCredentials(file, data);\n}\n\n/** Remove only the current server's entry. Other servers keep their tokens. */\nexport function clearToken(\n\tserverUrl: string = BASE_URL,\n\tfile: string = CREDENTIALS_FILE,\n): void {\n\tif (!existsSync(file)) return;\n\tconst { data } = readCredentials(file);\n\tdelete data.servers[serverUrl];\n\twriteCredentials(file, data);\n}\n\nconst SETTINGS_FILE = join(CONFIG_DIR, \"settings.json\");\n\n/**\n * Machine-local switches (#56). A separate file from credentials.json so a\n * login overwrite never resets an answered upsell, and clearing settings never\n * touches the token.\n */\nexport interface AutoSyncConfig {\n\t/** The standing opt-in. `sync --auto` publishes nothing when false. */\n\tenabled: boolean;\n\t/** Minimum hours between auto-sync attempts. Default 6. */\n\tfrequencyHours: number;\n}\n\n/** Bookkeeping the `sync --auto` runs write. Separate from the opt-in. */\nexport interface AutoSyncState {\n\t/** Epoch ms of the last attempt (success or failure). The freshness gate. */\n\tlastRunAt?: number;\n\tlastSuccessAt?: number;\n\t/** One line about the last run, shown on the next interactive sync. */\n\tlastResult?: string;\n\tconsecutiveFailures?: number;\n\t/** The 3-failure systemMessage went out. Reset on success. */\n\tfailureWarned?: boolean;\n}\n\nexport const DEFAULT_FREQUENCY_HOURS = 6;\nexport const MAX_FREQUENCY_HOURS = 24;\n\nexport function normalizeFrequencyHours(value: number | undefined): number {\n\tif (value === undefined || !Number.isFinite(value))\n\t\treturn DEFAULT_FREQUENCY_HOURS;\n\treturn Math.min(MAX_FREQUENCY_HOURS, Math.max(1, Math.round(value)));\n}\n\nexport interface Settings {\n\t/** The post-sync connect-claude upsell was answered (either way). */\n\tconnectClaudeAnswered?: boolean;\n\t/** Legacy binary answer. Kept readable so existing settings still parse. */\n\tautoSyncAnswered?: boolean;\n\t/** The owner explicitly chose not to see the post-sync auto-sync ask again. */\n\tautoSyncNeverAskAgain?: boolean;\n\tautoSync?: AutoSyncConfig;\n\tautoSyncState?: AutoSyncState;\n}\n\nexport function getSettings(file: string = SETTINGS_FILE): Settings {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\treturn raw && typeof raw === \"object\" ? (raw as Settings) : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nexport function saveSettings(\n\tpatch: Partial<Settings>,\n\tfile: string = SETTINGS_FILE,\n): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(\n\t\tfile,\n\t\tJSON.stringify({ ...getSettings(file), ...patch }, null, 2),\n\t);\n}\n\nconst PROJECTS_FILE = join(CONFIG_DIR, \"projects.json\");\nconst PROJECT_WORKSPACE_ID_RE = /^[A-Za-z0-9_-]{22}$/;\n\ninterface ProjectEntry {\n\texcluded?: string[];\n\tworkspaceId?: string;\n}\n\ninterface ProjectsData {\n\t[directory: string]: ProjectEntry;\n}\n\nfunction readProjects(file: string = PROJECTS_FILE): ProjectsData {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\t// Tolerate legacy entries: string values (oldest) and objects that still\n\t\t// carry a `name` field. Only exclusions and project workspace identifiers survive.\n\t\tconst data: ProjectsData = {};\n\t\tfor (const [key, value] of Object.entries(raw)) {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\tdata[key] = {};\n\t\t\t} else if (value && typeof value === \"object\") {\n\t\t\t\tconst excluded = (value as { excluded?: string[] }).excluded;\n\t\t\t\tconst workspaceId = (value as { workspaceId?: unknown }).workspaceId;\n\t\t\t\tdata[key] = {\n\t\t\t\t\t...(Array.isArray(excluded) ? { excluded } : {}),\n\t\t\t\t\t...(typeof workspaceId === \"string\" ? { workspaceId } : {}),\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeProjects(data: ProjectsData, file: string = PROJECTS_FILE): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(data, null, 2));\n}\n\nexport function getProjectWorkspaceId(\n\tdirectory: string,\n\tdeps: { file?: string; createId?: () => string } = {},\n): string {\n\tconst file = deps.file ?? PROJECTS_FILE;\n\tconst data = readProjects(file);\n\tconst held = data[directory]?.workspaceId;\n\tif (held && PROJECT_WORKSPACE_ID_RE.test(held)) return held;\n\tconst workspaceId = (\n\t\tdeps.createId ?? (() => randomBytes(16).toString(\"base64url\"))\n\t)();\n\tdata[directory] = { ...data[directory], workspaceId };\n\twriteProjects(data, file);\n\treturn workspaceId;\n}\n\nexport function getExcludedPaths(\n\tdirectory: string,\n\tfile: string = PROJECTS_FILE,\n): string[] {\n\treturn readProjects(file)[directory]?.excluded ?? [];\n}\n\nexport function saveExcludedPaths(\n\tdirectory: string,\n\texcluded: string[],\n\tfile: string = PROJECTS_FILE,\n): void {\n\tconst data = readProjects(file);\n\tdata[directory] = {\n\t\t...data[directory],\n\t\texcluded: excluded.length > 0 ? excluded : undefined,\n\t};\n\twriteProjects(data, file);\n}\n","import { execFileSync } from \"node:child_process\";\nimport type { Resource } from \"./api.js\";\nimport {\n\tcanonicalizeRepoUrl,\n\tnormalizeUpstreamPath,\n\trepoNameFromCanonical,\n} from \"./github-repo.js\";\n\n/**\n * Returns the raw `origin` remote URL for a working directory, or null when it\n * can't be determined. Injectable so `detectRepoUrl` stays unit-testable\n * without spawning git.\n */\nexport type GitRemoteRunner = (cwd: string) => string | null;\n\nexport const defaultGitRemoteRunner: GitRemoteRunner = (cwd) => {\n\ttry {\n\t\t// argv form (no shell) - git walks up to the repo root itself, and\n\t\t// stderr is swallowed so \"not a git repository\" never leaks into the\n\t\t// CLI's output. git missing / no repo / no origin all throw → null.\n\t\treturn execFileSync(\"git\", [\"-C\", cwd, \"remote\", \"get-url\", \"origin\"], {\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n/**\n * Detect the canonical GitHub repo URL for `cwd`, or null. Non-GitHub origins\n * (GitLab, Bitbucket, …) canonicalize to null, so this is a graceful no-op\n * outside of GitHub repos.\n */\nexport function detectRepoUrl(\n\tcwd: string,\n\trun: GitRemoteRunner = defaultGitRemoteRunner,\n): string | null {\n\tconst raw = run(cwd);\n\tif (!raw) return null;\n\treturn canonicalizeRepoUrl(raw);\n}\n\nexport interface LinkSpec {\n\t/** Canonical GitHub URL (https://github.com/owner/repo). */\n\tcanonical: string;\n\t/** Optional subpath within the repo. */\n\tpath?: string;\n\tname: string;\n\ttype: string;\n\tgroup: string;\n\t/** Optional pinned commit, stored as upstream.lastCommitSha. */\n\tsha?: string;\n}\n\n/**\n * Build a linked-resource payload. Mirrors the web `linkResource` mutation: no\n * files (upstream presence is the storage discriminator) and the exact\n * `linked:${canonical}:${normPath}` stableKey so the web unlink UI - which\n * matches by stableKey - recognizes it. `path`/`lastCommitSha` are omitted when\n * empty so the by_upstream dedup index matches at both write and query.\n */\nexport function buildLinkResource(spec: LinkSpec): Resource {\n\tconst normPath = normalizeUpstreamPath(spec.path);\n\treturn {\n\t\ttype: spec.type,\n\t\tname: spec.name,\n\t\tgroup: spec.group,\n\t\tstableKey: `linked:${spec.canonical}:${normPath}`,\n\t\tupstream: {\n\t\t\trepoUrl: spec.canonical,\n\t\t\t...(normPath ? { path: normPath } : {}),\n\t\t\t...(spec.sha ? { lastCommitSha: spec.sha } : {}),\n\t\t},\n\t};\n}\n\n/** The repo this project lives in: a GitHub link (stack-owned server-side). */\nexport function buildRepoLinkResource(canonical: string): Resource {\n\treturn buildLinkResource({\n\t\tcanonical,\n\t\tname: repoNameFromCanonical(canonical),\n\t\ttype: \"custom\",\n\t\tgroup: \"generic\",\n\t});\n}\n","/**\n * Trimmed copy of `src/lib/github-repo.ts` - the CANONICAL parser, whose\n * `github-repo.test.ts` is the canonical test table. Copied (not imported)\n * because the CLI ships as an independent npm package and its tsconfig\n * (`rootDir: \"src\"` + `declaration: true`) forbids cross-rootDir imports.\n * Keep these functions in sync with the canonical source.\n *\n * Only the pieces the CLI needs are included: `parseRepo`,\n * `canonicalizeRepoUrl`, `repoNameFromCanonical`, and `normalizeUpstreamPath`\n * (a null from `canonicalizeRepoUrl` is the CLI's graceful-skip signal, so\n * `isGithubRepoUrl` is intentionally omitted).\n */\n\nfunction isGithubHost(host: string): boolean {\n\tconst h = host.toLowerCase();\n\treturn h === \"github.com\" || h === \"www.github.com\";\n}\n\nexport function parseRepo(\n\tinput: string,\n): { owner: string; repo: string } | null {\n\tconst trimmed = input.trim();\n\tif (!trimmed) return null;\n\n\t// SCP-like form `git@host:owner/repo`: take the host and the path after the\n\t// colon. Otherwise strip the scheme, then split the leading host off the path.\n\tconst scpMatch = trimmed.match(/^[^@]+@([^:]+):(.+)$/);\n\tlet host: string;\n\tlet withoutHost: string;\n\tif (scpMatch) {\n\t\thost = scpMatch[1];\n\t\twithoutHost = scpMatch[2];\n\t} else {\n\t\tconst withoutScheme = trimmed.replace(/^[a-z]+:\\/\\//i, \"\");\n\t\tconst slash = withoutScheme.indexOf(\"/\");\n\t\tif (slash === -1) return null;\n\t\thost = withoutScheme.slice(0, slash);\n\t\twithoutHost = withoutScheme.slice(slash + 1);\n\t}\n\tif (!isGithubHost(host)) return null;\n\n\t// Drop any query string or anchor, then split into path segments.\n\tconst pathPart = withoutHost.replace(/[?#].*$/, \"\");\n\tconst segments = pathPart.split(\"/\").filter(Boolean);\n\n\tconst owner = segments[0];\n\tconst repo = segments[1]?.replace(/\\.git$/, \"\");\n\tif (!owner || !repo) return null;\n\n\treturn { owner: owner.toLowerCase(), repo: repo.toLowerCase() };\n}\n\nexport function canonicalizeRepoUrl(input: string): string | null {\n\tconst parsed = parseRepo(input);\n\tif (!parsed) return null;\n\treturn `https://github.com/${parsed.owner}/${parsed.repo}`;\n}\n\nexport function repoNameFromCanonical(canonical: string): string {\n\treturn parseRepo(canonical)?.repo ?? \"\";\n}\n\nexport function normalizeUpstreamPath(path: string | undefined): string {\n\tif (!path) return \"\";\n\treturn path.split(\"/\").filter(Boolean).join(\"/\");\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Extract hooks defined inline in Claude Code settings as discrete `hook`\n * resources - one per event (PreToolUse, PostToolUse, …). Without this they\n * only ride inside the collected settings.json config blob and never surface as\n * first-class hooks.\n *\n * These are HOSTED resources (the event's config block is the content), so they\n * participate in the normal file-based diff. They intentionally duplicate data\n * also present in the settings.json resource; the distinct `hooks:` stableKeys\n * mean no collision, and first-class visibility was the deliberate tradeoff.\n */\n\ninterface SettingsFile {\n\thooks?: Record<string, unknown>;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction hooksFrom(\n\tpath: string,\n\tsource: \"local\" | \"global\",\n\tout: Resource[],\n\tseen: Set<string>,\n) {\n\tconst hooks = readJson<SettingsFile>(path)?.hooks;\n\tif (!hooks || typeof hooks !== \"object\") return;\n\tfor (const [event, config] of Object.entries(hooks)) {\n\t\tconst stableKey = `hooks:${source}:${event}`;\n\t\tif (seen.has(stableKey)) continue;\n\t\tseen.add(stableKey);\n\t\tout.push({\n\t\t\ttype: \"hook\",\n\t\t\tname: event,\n\t\t\tgroup: \"claude-code\",\n\t\t\tstableKey,\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: `${event}.json`,\n\t\t\t\t\tcontent: JSON.stringify(config, null, 2),\n\t\t\t\t\tpath: `hooks/${event}.json`,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n}\n\n/**\n * Detect inline hooks from project settings (`.claude/settings.json` +\n * `.claude/settings.local.json`) and global settings (`~/.claude/settings.json`).\n * Project settings win over the `.local` override on the same event.\n */\nexport function detectHooks(cwd: string, home: string = homedir()): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\thooksFrom(join(cwd, \".claude\", \"settings.json\"), \"local\", out, seen);\n\thooksFrom(join(cwd, \".claude\", \"settings.local.json\"), \"local\", out, seen);\n\thooksFrom(join(home, \".claude\", \"settings.json\"), \"global\", out, seen);\n\treturn out;\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { Resource } from \"./api.js\";\n\n/**\n * Detect configured MCP servers and resolve each to a `pkg` reference (its\n * package identity), parsed from the launch `command`/`args` - npm/PyPI/OCI for\n * stdio servers, or a URL for remote (http/sse) servers. `env` is intentionally\n * dropped (it carries secrets), so this is a safer representation than uploading\n * the raw config file.\n */\n\nexport interface McpServerConfig {\n\tcommand?: string;\n\targs?: string[];\n\ttype?: string;\n\ttransport?: string;\n\turl?: string;\n}\n\nexport interface PkgRef {\n\tregistry: \"npm\" | \"pypi\" | \"oci\" | \"url\";\n\tid: string;\n\tversion?: string;\n\ttransport?: \"stdio\" | \"http\" | \"sse\";\n}\n\nfunction commandName(cmd: string): string {\n\treturn cmd.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? cmd;\n}\n\n/** Split an npm/PyPI spec into id + version, handling scoped npm (@scope/n@v). */\nfunction splitVersion(spec: string): { id: string; version?: string } {\n\tconst at = spec.indexOf(\"@\", spec.startsWith(\"@\") ? 1 : 0);\n\tif (at <= 0) return { id: spec };\n\treturn { id: spec.slice(0, at), version: spec.slice(at + 1) || undefined };\n}\n\n/** First arg that isn't a flag (and isn't in `skip`). */\nfunction firstPositional(args: string[], skip = 0): string | undefined {\n\tfor (const a of args.slice(skip)) {\n\t\tif (!a.startsWith(\"-\")) return a;\n\t}\n\treturn undefined;\n}\n\n// docker/podman flags that consume the following token (so it isn't the image).\nconst CONTAINER_VALUE_FLAGS = new Set([\n\t\"-e\",\n\t\"--env\",\n\t\"-v\",\n\t\"--volume\",\n\t\"-p\",\n\t\"--publish\",\n\t\"-w\",\n\t\"--workdir\",\n\t\"--name\",\n\t\"--mount\",\n\t\"--network\",\n\t\"-u\",\n\t\"--user\",\n\t\"-l\",\n\t\"--label\",\n]);\n\nfunction containerImage(args: string[]): string | undefined {\n\tconst runIdx = args.indexOf(\"run\");\n\tconst rest = runIdx >= 0 ? args.slice(runIdx + 1) : args;\n\tfor (let i = 0; i < rest.length; i++) {\n\t\tconst a = rest[i];\n\t\tif (a.startsWith(\"-\")) {\n\t\t\tif (CONTAINER_VALUE_FLAGS.has(a) && !a.includes(\"=\")) i++;\n\t\t\tcontinue;\n\t\t}\n\t\treturn a; // first positional after `run` is the image\n\t}\n\treturn undefined;\n}\n\n/** Split a container image ref into id + tag (ignoring a registry host:port). */\nfunction splitImageTag(image: string): { id: string; version?: string } {\n\tconst colon = image.lastIndexOf(\":\");\n\tif (colon > 0 && !image.slice(colon + 1).includes(\"/\")) {\n\t\treturn { id: image.slice(0, colon), version: image.slice(colon + 1) };\n\t}\n\treturn { id: image };\n}\n\n/** Parse a single MCP server config into a package reference, or null. */\nexport function parseMcpPackage(server: McpServerConfig): PkgRef | null {\n\t// Remote server: a URL endpoint (http/sse).\n\tif (server.url) {\n\t\tlet safeUrl: string;\n\t\ttry {\n\t\t\tconst parsed = new URL(server.url);\n\t\t\tparsed.username = \"\";\n\t\t\tparsed.password = \"\";\n\t\t\tsafeUrl = parsed.toString();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tconst t = (server.type ?? server.transport ?? \"\").toLowerCase();\n\t\treturn {\n\t\t\tregistry: \"url\",\n\t\t\tid: safeUrl,\n\t\t\ttransport: t === \"sse\" ? \"sse\" : \"http\",\n\t\t};\n\t}\n\n\tconst command = server.command ? commandName(server.command) : \"\";\n\tif (!command) return null;\n\tconst args = server.args ?? [];\n\n\t// npm-family runners.\n\tif (command === \"npx\" || command === \"bunx\" || command === \"pnpx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif ((command === \"pnpm\" || command === \"yarn\") && args[0] === \"dlx\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"npm\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\n\t// Python-family runners.\n\tif (command === \"uvx\") {\n\t\tconst spec = firstPositional(args);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"pipx\" && args[0] === \"run\") {\n\t\tconst spec = firstPositional(args, 1);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (command === \"uv\" && args[0] === \"tool\" && args[1] === \"run\") {\n\t\tconst spec = firstPositional(args, 2);\n\t\treturn spec\n\t\t\t? { registry: \"pypi\", ...splitVersion(spec), transport: \"stdio\" }\n\t\t\t: null;\n\t}\n\tif (/^python[0-9.]*$/.test(command)) {\n\t\tconst i = args.indexOf(\"-m\");\n\t\tconst mod = i >= 0 ? args[i + 1] : undefined;\n\t\treturn mod ? { registry: \"pypi\", id: mod, transport: \"stdio\" } : null;\n\t}\n\n\t// Container runners.\n\tif (command === \"docker\" || command === \"podman\") {\n\t\tconst image = containerImage(args);\n\t\tif (!image) return null;\n\t\treturn { registry: \"oci\", ...splitImageTag(image), transport: \"stdio\" };\n\t}\n\n\t// node/deno/bun running a local script, or an unknown command → skip.\n\treturn null;\n}\n\n/** Build a `type:\"mcp\"` linked resource from a parsed package reference. */\nexport function buildMcpResource(\n\tname: string,\n\tgroup: string,\n\tpkg: PkgRef,\n): Resource {\n\treturn {\n\t\ttype: \"mcp\",\n\t\tname,\n\t\tgroup,\n\t\tstableKey: `linked:pkg:${pkg.registry}:${pkg.id}`,\n\t\tpkg,\n\t};\n}\n\nfunction readText(path: string): string | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn readFileSync(path, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readParsed<T>(\n\tpath: string,\n\tparse: (raw: string) => unknown,\n): T | null {\n\tconst raw = readText(path);\n\tif (raw === null) return null;\n\ttry {\n\t\treturn parse(raw) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction readJson<T>(path: string): T | null {\n\treturn readParsed<T>(path, JSON.parse);\n}\nfunction readYaml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseYaml);\n}\nfunction readToml<T>(path: string): T | null {\n\treturn readParsed<T>(path, parseToml);\n}\n\ntype ServerMap = Record<string, McpServerConfig> | undefined;\ninterface McpFile {\n\tmcpServers?: ServerMap;\n\tservers?: ServerMap; // VS Code uses `servers`\n}\ninterface ClaudeJson {\n\tmcpServers?: ServerMap;\n\tprojects?: Record<string, { mcpServers?: ServerMap }>;\n}\n// Continue uses a LIST under mcpServers; Codex (TOML) uses `mcp_servers`.\ninterface ContinueYaml {\n\tmcpServers?: Array<{ name?: string } & McpServerConfig>;\n}\ninterface CodexToml {\n\tmcp_servers?: Record<string, McpServerConfig>;\n}\ninterface GrokToml {\n\tmcp_servers?: Record<string, McpServerConfig & { enabled?: boolean }>;\n\tdisabled_mcp_servers?: string[];\n}\n\n/** Normalize Continue's list form to the common name→config map. */\nfunction continueListToMap(file: ContinueYaml | null): ServerMap {\n\tif (!file?.mcpServers?.length) return undefined;\n\tconst map: Record<string, McpServerConfig> = {};\n\tfile.mcpServers.forEach((s, i) => {\n\t\tmap[s.name ?? `server-${i}`] = s;\n\t});\n\treturn map;\n}\n\n/** Per-OS VS Code globalStorage bases (+ Code-OSS/VSCodium variants). */\nfunction vscodeGlobalStorageBases(home: string): string[] {\n\tconst apps = [\"Code\", \"Code - OSS\", \"VSCodium\"];\n\tlet root: string;\n\tif (platform() === \"darwin\") {\n\t\troot = join(home, \"Library\", \"Application Support\");\n\t} else if (platform() === \"win32\") {\n\t\troot = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n\t} else {\n\t\troot = process.env.XDG_CONFIG_HOME ?? join(home, \".config\");\n\t}\n\treturn apps.map((app) => join(root, app, \"User\", \"globalStorage\"));\n}\n\n/**\n * Read MCP server configs across the known tool locations and return one\n * `type:\"mcp\"` pkg-link resource per server, deduped by package identity.\n * Project-level configs win over global ones on the same identity.\n */\nexport function detectMcpServers(\n\tcwd: string,\n\thome: string = homedir(),\n): Resource[] {\n\tconst out: Resource[] = [];\n\tconst seen = new Set<string>();\n\tconst add = (servers: ServerMap, group: string) => {\n\t\tfor (const [name, cfg] of Object.entries(servers ?? {})) {\n\t\t\tconst pkg = parseMcpPackage(cfg);\n\t\t\tif (!pkg) continue;\n\t\t\tconst resource = buildMcpResource(name, group, pkg);\n\t\t\tif (seen.has(resource.stableKey)) continue;\n\t\t\tseen.add(resource.stableKey);\n\t\t\tout.push(resource);\n\t\t}\n\t};\n\tconst grokHome = process.env.GROK_HOME ?? join(home, \".grok\");\n\tconst grokGlobal = readToml<GrokToml>(join(grokHome, \"config.toml\"));\n\tconst grokProject = readToml<GrokToml>(join(cwd, \".grok\", \"config.toml\"));\n\tconst disabled = new Set([\n\t\t...(grokGlobal?.disabled_mcp_servers ?? []),\n\t\t...(grokProject?.disabled_mcp_servers ?? []),\n\t]);\n\tconst effectiveGrok = {\n\t\t...(grokGlobal?.mcp_servers ?? {}),\n\t\t...(grokProject?.mcp_servers ?? {}),\n\t};\n\tadd(\n\t\tObject.fromEntries(\n\t\t\tObject.entries(effectiveGrok).filter(\n\t\t\t\t([name, config]) => config.enabled !== false && !disabled.has(name),\n\t\t\t),\n\t\t),\n\t\t\"grok-build\",\n\t);\n\n\t// Project configs first (so they win dedup over global).\n\tadd(readJson<McpFile>(join(cwd, \".mcp.json\"))?.mcpServers, \"claude-code\");\n\tadd(readJson<McpFile>(join(cwd, \"mcp.json\"))?.mcpServers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\tadd(readJson<McpFile>(join(cwd, \".vscode\", \"mcp.json\"))?.servers, \"generic\");\n\tadd(\n\t\treadJson<McpFile>(join(cwd, \"claude_desktop_config.json\"))?.mcpServers,\n\t\t\"claude-desktop\",\n\t);\n\n\t// Continue (project): a list per YAML file under .continue/mcpServers/.\n\tfor (const file of listYamlFiles(join(cwd, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Roo (project).\n\tadd(readJson<McpFile>(join(cwd, \".roo\", \"mcp.json\"))?.mcpServers, \"roo\");\n\n\t// --- Global / stack-scoped: user-level tool configs ---\n\tconst claudeJson = readJson<ClaudeJson>(join(home, \".claude.json\"));\n\tadd(claudeJson?.projects?.[cwd]?.mcpServers, \"claude-code\");\n\tadd(claudeJson?.mcpServers, \"claude-code\");\n\tadd(\n\t\treadJson<McpFile>(join(home, \".cursor\", \"mcp.json\"))?.mcpServers,\n\t\t\"cursor\",\n\t);\n\t// Windsurf (global only).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".codeium\", \"windsurf\", \"mcp_config.json\"))\n\t\t\t?.mcpServers,\n\t\t\"windsurf\",\n\t);\n\t// Cline + Roo: VS Code extension globalStorage (OS-specific base).\n\tfor (const base of vscodeGlobalStorageBases(home)) {\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"saoudrizwan.claude-dev\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"cline_mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"cline\",\n\t\t);\n\t\tadd(\n\t\t\treadJson<McpFile>(\n\t\t\t\tjoin(\n\t\t\t\t\tbase,\n\t\t\t\t\t\"rooveterinaryinc.roo-cline\",\n\t\t\t\t\t\"settings\",\n\t\t\t\t\t\"mcp_settings.json\",\n\t\t\t\t),\n\t\t\t)?.mcpServers,\n\t\t\t\"roo\",\n\t\t);\n\t}\n\t// Continue (global).\n\tfor (const file of listYamlFiles(join(home, \".continue\", \"mcpServers\"))) {\n\t\tadd(continueListToMap(readYaml<ContinueYaml>(file)), \"continue\");\n\t}\n\t// Gemini CLI (global).\n\tadd(\n\t\treadJson<McpFile>(join(home, \".gemini\", \"settings.json\"))?.mcpServers,\n\t\t\"gemini\",\n\t);\n\t// Codex CLI (global, TOML).\n\tadd(\n\t\treadToml<CodexToml>(join(home, \".codex\", \"config.toml\"))?.mcp_servers,\n\t\t\"codex\",\n\t);\n\n\treturn out;\n}\n\n/** List `*.yaml`/`*.yml` files in a directory (empty if absent). */\nfunction listYamlFiles(dir: string): string[] {\n\ttry {\n\t\treturn readdirSync(dir)\n\t\t\t.filter((f) => f.endsWith(\".yaml\") || f.endsWith(\".yml\"))\n\t\t\t.map((f) => join(dir, f));\n\t} catch {\n\t\treturn [];\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Resource } from \"./api.js\";\nimport { buildLinkResource } from \"./git.js\";\nimport { canonicalizeRepoUrl } from \"./github-repo.js\";\n\n/**\n * Detect installed Claude Code plugins from the on-disk registry and resolve\n * each to a GitHub link pointing at its TRUE upstream source (not the\n * aggregator marketplace), attached at stack scope (the user's toolchain).\n *\n * Registry shape (~/.claude/plugins/):\n * installed_plugins.json → { plugins: { \"<name>@<marketplace>\": [{ gitCommitSha, version, ... }] } }\n * known_marketplaces.json → { \"<marketplace>\": { source: { repo }, installLocation } }\n * <installLocation>/.claude-plugin/marketplace.json → { plugins: [{ name, source, ... }] }\n */\n\ninterface InstalledEntry {\n\tscope?: string;\n\tversion?: string;\n\tgitCommitSha?: string;\n}\n\nexport interface InstalledPlugins {\n\tplugins?: Record<string, InstalledEntry[]>;\n}\n\nexport interface KnownMarketplace {\n\tsource?: { source?: string; repo?: string; url?: string };\n\tinstallLocation?: string;\n}\n\nexport type KnownMarketplaces = Record<string, KnownMarketplace>;\n\n/** A plugin's source in marketplace.json - polymorphic. */\ntype PluginSource =\n\t| string\n\t| {\n\t\t\tsource?: string;\n\t\t\turl?: string;\n\t\t\tpath?: string;\n\t\t\tref?: string;\n\t\t\tsha?: string;\n\t };\n\ninterface ManifestPlugin {\n\tname: string;\n\tsource?: PluginSource;\n\trepository?: string;\n\thomepage?: string;\n}\n\nexport interface Manifest {\n\tplugins?: ManifestPlugin[];\n}\n\nfunction marketplaceRepoUrl(mp: KnownMarketplace | undefined): string | null {\n\tconst src = mp?.source;\n\tif (!src) return null;\n\tif (src.repo) return `https://github.com/${src.repo}`;\n\treturn src.url ?? null;\n}\n\n/** Resolve a plugin entry's source to a repo URL (+ optional subpath / sha). */\nfunction resolveSource(\n\tentry: ManifestPlugin,\n\tmpRepoUrl: string | null,\n): { url: string; path?: string; sha?: string } | null {\n\tconst src = entry.source;\n\tif (typeof src === \"string\") {\n\t\tif (!mpRepoUrl) return null;\n\t\tconst path = src.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n\t\treturn { url: mpRepoUrl, path: path || undefined };\n\t}\n\tif (src && typeof src === \"object\" && src.url) {\n\t\treturn { url: src.url, path: src.path, sha: src.sha };\n\t}\n\tconst fallback = entry.repository ?? entry.homepage ?? mpRepoUrl;\n\treturn fallback ? { url: fallback } : null;\n}\n\n/** Pure: map the parsed registry + manifests to plugin link resources. */\nexport function resolvePluginLinks(\n\tinstalled: InstalledPlugins,\n\tmarketplaces: KnownMarketplaces,\n\tmanifests: Record<string, Manifest>,\n): Resource[] {\n\tconst out: Resource[] = [];\n\tfor (const [key, entries] of Object.entries(installed.plugins ?? {})) {\n\t\tconst at = key.lastIndexOf(\"@\");\n\t\tif (at <= 0) continue;\n\t\tconst pluginName = key.slice(0, at);\n\t\tconst marketplace = key.slice(at + 1);\n\n\t\tconst mpRepoUrl = marketplaceRepoUrl(marketplaces[marketplace]);\n\t\tconst entry = manifests[marketplace]?.plugins?.find(\n\t\t\t(p) => p.name === pluginName,\n\t\t);\n\t\tif (!entry) continue;\n\n\t\tconst resolved = resolveSource(entry, mpRepoUrl);\n\t\tif (!resolved) continue;\n\n\t\tconst canonical = canonicalizeRepoUrl(resolved.url);\n\t\tif (!canonical) continue; // non-GitHub source → skip (graceful)\n\n\t\tout.push(\n\t\t\tbuildLinkResource({\n\t\t\t\tcanonical,\n\t\t\t\tpath: resolved.path,\n\t\t\t\tname: pluginName,\n\t\t\t\ttype: \"plugin\",\n\t\t\t\tgroup: \"claude-code\",\n\t\t\t\tsha: resolved.sha ?? entries[0]?.gitCommitSha,\n\t\t\t}),\n\t\t);\n\t}\n\treturn out;\n}\n\nfunction readJson<T>(path: string): T | null {\n\ttry {\n\t\tif (!existsSync(path)) return null;\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** IO wrapper: read the registry + manifests, return plugin link resources. */\nexport function detectInstalledPlugins(\n\tpluginsDir: string = join(homedir(), \".claude\", \"plugins\"),\n): Resource[] {\n\tconst installed = readJson<InstalledPlugins>(\n\t\tjoin(pluginsDir, \"installed_plugins.json\"),\n\t);\n\tif (!installed?.plugins) return [];\n\n\tconst marketplaces =\n\t\treadJson<KnownMarketplaces>(join(pluginsDir, \"known_marketplaces.json\")) ??\n\t\t{};\n\n\tconst manifests: Record<string, Manifest> = {};\n\tfor (const key of Object.keys(installed.plugins)) {\n\t\tconst mp = key.slice(key.lastIndexOf(\"@\") + 1);\n\t\tif (!mp || manifests[mp]) continue;\n\t\tconst installLocation =\n\t\t\tmarketplaces[mp]?.installLocation ?? join(pluginsDir, \"marketplaces\", mp);\n\t\tconst manifest = readJson<Manifest>(\n\t\t\tjoin(installLocation, \".claude-plugin\", \"marketplace.json\"),\n\t\t);\n\t\tif (manifest) manifests[mp] = manifest;\n\t}\n\n\treturn resolvePluginLinks(installed, marketplaces, manifests);\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, relative } from \"node:path\";\nimport ignore from \"ignore\";\n\nexport type FileType =\n\t| \"rule\"\n\t| \"mcp\"\n\t| \"skill\"\n\t| \"command\"\n\t| \"prompt\"\n\t| \"hook\"\n\t| \"subagent\"\n\t| \"config\"\n\t| \"custom\";\n\nexport interface ScannedFile {\n\tpath: string;\n\trelativePath: string;\n\tcontent: string;\n\ttype: FileType;\n\tsource: \"local\" | \"global\";\n\tgroup: string;\n}\n\nconst MAX_FILE_SIZE = 100 * 1024; // 100KB\n\ninterface FilePattern {\n\tpath: string;\n\ttype: FileType;\n\tgroup: string;\n}\n\nconst LOCAL_PATTERNS: FilePattern[] = [\n\t// Rules\n\t{ path: \"CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"AGENTS.md\", type: \"rule\", group: \"claude-code\" },\n\t{ path: \"GROK.md\", type: \"rule\", group: \"grok-build\" },\n\t{ path: \"GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t{ path: \".cursorrules\", type: \"rule\", group: \"cursor\" },\n\t{ path: \".windsurfrules\", type: \"rule\", group: \"windsurf\" },\n\t{ path: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ path: \".roorules\", type: \"rule\", group: \"roo\" },\n\t{ path: \".github/copilot-instructions.md\", type: \"rule\", group: \"copilot\" },\n\t// MCP servers are detected separately as pkg-reference links (see mcp.ts) -\n\t// their config files are intentionally NOT collected as content here (which\n\t// would also upload `env` secrets).\n\t// Config\n\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t{\n\t\tpath: \".claude/settings.local.json\",\n\t\ttype: \"config\",\n\t\tgroup: \"claude-code\",\n\t},\n\t// Prompts\n\t{ path: \"system-prompt.md\", type: \"prompt\", group: \"generic\" },\n];\n\nconst LOCAL_DIR_PATTERNS: { dir: string; type: FileType; group: string }[] = [\n\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t{ dir: \".clinerules\", type: \"rule\", group: \"cline\" },\n\t{ dir: \".windsurf/rules\", type: \"rule\", group: \"windsurf\" },\n\t{ dir: \".roo/rules\", type: \"rule\", group: \"roo\" },\n\t{ dir: \".github/instructions\", type: \"rule\", group: \"copilot\" },\n\t{ dir: \".github/prompts\", type: \"prompt\", group: \"copilot\" },\n\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t{ dir: \".claude/skills\", type: \"skill\", group: \"claude-code\" },\n\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t{ dir: \".grok/skills\", type: \"skill\", group: \"grok-build\" },\n\t{ dir: \".grok/commands\", type: \"command\", group: \"grok-build\" },\n\t{ dir: \".grok/agents\", type: \"subagent\", group: \"grok-build\" },\n\t{ dir: \".grok/hooks\", type: \"hook\", group: \"grok-build\" },\n\t{ dir: \".cursor/skills\", type: \"skill\", group: \"cursor\" },\n\t{ dir: \".agents/skills\", type: \"skill\", group: \"generic\" },\n\t{ dir: \".agents/commands\", type: \"command\", group: \"generic\" },\n\t{ dir: \"prompts\", type: \"prompt\", group: \"generic\" },\n\t{ dir: \".ai\", type: \"custom\", group: \"generic\" },\n];\n\nfunction loadGitignore(cwd: string): ReturnType<typeof ignore> {\n\tconst ig = ignore();\n\tconst gitignorePath = join(cwd, \".gitignore\");\n\tif (existsSync(gitignorePath)) {\n\t\tig.add(readFileSync(gitignorePath, \"utf-8\"));\n\t}\n\tig.add([\"node_modules\", \".git\", \"dist\", \"build\", \".next\", \".output\"]);\n\treturn ig;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n\ttry {\n\t\tconst stat = statSync(filePath);\n\t\tif (stat.size > MAX_FILE_SIZE) return null;\n\t\treturn readFileSync(filePath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction walkDir(dir: string, maxDepth = 3, currentDepth = 0): string[] {\n\tif (currentDepth >= maxDepth || !existsSync(dir)) return [];\n\tconst results: string[] = [];\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tif (entry.isFile()) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\tresults.push(...walkDir(fullPath, maxDepth, currentDepth + 1));\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors, etc */\n\t}\n\treturn results;\n}\n\nexport function scanLocal(cwd: string): ScannedFile[] {\n\tconst ig = loadGitignore(cwd);\n\tconst results: ScannedFile[] = [];\n\n\tfor (const pattern of LOCAL_PATTERNS) {\n\t\tconst filePath = join(cwd, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (!ig.ignores(rel)) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: pattern.type,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: pattern.group,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { dir, type, group } of LOCAL_DIR_PATTERNS) {\n\t\tconst dirPath = join(cwd, dir);\n\t\tconst files = walkDir(dirPath);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Scan for skill directories (dirs with SKILL.md, 3 levels deep)\n\ttry {\n\t\tfor (const entry of readdirSync(cwd, { withFileTypes: true }).filter((e) =>\n\t\t\te.isDirectory(),\n\t\t)) {\n\t\t\tif ([\".grok\", \".claude\", \".cursor\", \".agents\"].includes(entry.name))\n\t\t\t\tcontinue;\n\t\t\tif (ig.ignores(`${entry.name}/`)) continue;\n\t\t\tscanSkillDirs(join(cwd, entry.name), cwd, ig, results, 1);\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n\n\treturn results;\n}\n\nfunction scanSkillDirs(\n\tdir: string,\n\tcwd: string,\n\tig: ReturnType<typeof ignore>,\n\tresults: ScannedFile[],\n\tdepth: number,\n) {\n\tif (depth > 3) return;\n\tconst skillMd = join(dir, \"SKILL.md\");\n\tif (existsSync(skillMd)) {\n\t\tconst files = walkDir(dir, 1);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t\tgroup: \"generic\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tconst rel = relative(cwd, join(dir, entry.name));\n\t\t\t\tif (!ig.ignores(`${rel}/`)) {\n\t\t\t\t\tscanSkillDirs(join(dir, entry.name), cwd, ig, results, depth + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n}\n\nexport function scanGlobal(): ScannedFile[] {\n\tconst home = homedir();\n\tconst results: ScannedFile[] = [];\n\n\tconst globalPatterns: FilePattern[] = [\n\t\t{ path: \".claude/CLAUDE.md\", type: \"rule\", group: \"claude-code\" },\n\t\t{ path: \".grok/GROK.md\", type: \"rule\", group: \"grok-build\" },\n\t\t{ path: \".claude/settings.json\", type: \"config\", group: \"claude-code\" },\n\t\t{ path: \".continue/config.json\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".continue/config.yaml\", type: \"config\", group: \"continue\" },\n\t\t{ path: \".aider.conf.yml\", type: \"config\", group: \"aider\" },\n\t\t{ path: \".gemini/GEMINI.md\", type: \"rule\", group: \"gemini\" },\n\t\t{ path: \".gemini/settings.json\", type: \"config\", group: \"gemini\" },\n\t\t{ path: \".codex/config.toml\", type: \"config\", group: \"codex\" },\n\t\t{\n\t\t\tpath: \".codeium/windsurf/memories/global_rules.md\",\n\t\t\ttype: \"rule\",\n\t\t\tgroup: \"windsurf\",\n\t\t},\n\t];\n\n\tfor (const pattern of globalPatterns) {\n\t\tconst filePath = join(home, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tresults.push({\n\t\t\t\tpath: filePath,\n\t\t\t\trelativePath: `~/${pattern.path}`,\n\t\t\t\tcontent,\n\t\t\t\ttype: pattern.type,\n\t\t\t\tsource: \"global\",\n\t\t\t\tgroup: pattern.group,\n\t\t\t});\n\t\t}\n\t}\n\n\tconst globalDirs: { dir: string; type: FileType; group: string }[] = [\n\t\t{ dir: \".claude/commands\", type: \"command\", group: \"claude-code\" },\n\t\t{ dir: \".claude/agents\", type: \"subagent\", group: \"claude-code\" },\n\t\t{ dir: \".claude/hooks\", type: \"hook\", group: \"claude-code\" },\n\t\t{ dir: \".grok/skills\", type: \"skill\", group: \"grok-build\" },\n\t\t{ dir: \".grok/commands\", type: \"command\", group: \"grok-build\" },\n\t\t{ dir: \".grok/agents\", type: \"subagent\", group: \"grok-build\" },\n\t\t{ dir: \".grok/hooks\", type: \"hook\", group: \"grok-build\" },\n\t\t{ dir: \".cursor/rules\", type: \"rule\", group: \"cursor\" },\n\t\t{ dir: \".cursor/skills\", type: \"skill\", group: \"cursor\" },\n\t\t{ dir: \".agents/skills\", type: \"skill\", group: \"generic\" },\n\t\t{ dir: \".agents/commands\", type: \"command\", group: \"generic\" },\n\t];\n\n\tfor (const { dir, type, group } of globalDirs) {\n\t\tconst dirPath = join(home, dir);\n\t\tconst files = walkDir(dirPath, 2);\n\t\tfor (const filePath of files) {\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"global\",\n\t\t\t\t\tgroup,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Global skills: ~/.claude/skills/<name>/SKILL.md (+ supporting files). Not\n\t// covered by globalDirs since each skill is its own dir keyed by SKILL.md.\n\tconst skillsRoot = join(home, \".claude\", \"skills\");\n\ttry {\n\t\tfor (const entry of readdirSync(skillsRoot, { withFileTypes: true })) {\n\t\t\tif (!entry.isDirectory()) continue;\n\t\t\tconst skillDir = join(skillsRoot, entry.name);\n\t\t\tif (!existsSync(join(skillDir, \"SKILL.md\"))) continue;\n\t\t\tfor (const filePath of walkDir(skillDir, 2)) {\n\t\t\t\tconst content = readFileSafe(filePath);\n\t\t\t\tif (content !== null) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tpath: filePath,\n\t\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\t\tsource: \"global\",\n\t\t\t\t\t\tgroup: \"claude-code\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* skills dir absent / permission errors */\n\t}\n\n\treturn results;\n}\n","import * as p from \"@clack/prompts\";\n\nconst esc = (code: string) => `\\x1b[${code}m`;\nconst reset = esc(\"0\");\n\nconst LIME = \"163;230;53\";\nconst BLACK = \"0;0;0\";\nconst YELLOW = \"250;204;21\";\nconst RED = \"248;113;113\";\nconst MUTED = \"120;120;120\";\n\nexport const lime = (s: string) => `${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const limeBold = (s: string) =>\n\t`${esc(\"1\")}${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const bgLime = (s: string) =>\n\t`${esc(`48;2;${LIME}`)}${esc(`38;2;${BLACK}`)}${s}${reset}`;\nexport const yellow = (s: string) => `${esc(`38;2;${YELLOW}`)}${s}${reset}`;\nexport const red = (s: string) => `${esc(`38;2;${RED}`)}${s}${reset}`;\nexport const dim = (s: string) => `${esc(`38;2;${MUTED}`)}${s}${reset}`;\nexport const bold = (s: string) => `${esc(\"1\")}${s}${reset}`;\n\n// ■ logo square in lime + AISTACK in bold on lime bg\nexport const banner = (cmd: string) =>\n\t`${lime(\"■\")} ${bgLime(` AISTACK `)} ${bold(cmd.toUpperCase())}`;\n\n// Compact line with clack-style bar\nconst BAR = `${esc(`38;2;${MUTED}`)}│${reset}`;\n\nexport function lines(items: string[]) {\n\tfor (const item of items) {\n\t\tconsole.log(`${BAR} ${item}`);\n\t}\n}\n\nexport function section(label: string, count?: number) {\n\tconsole.log(`${BAR}`);\n\tconst countStr = count !== undefined ? ` ${dim(String(count))}` : \"\";\n\tconsole.log(`${BAR} ${bold(label.toUpperCase())}${countStr}`);\n}\n\nexport function divider() {\n\tconsole.log(`${BAR} ${dim(\"─\".repeat(40))}`);\n}\n\nexport function intro(cmd: string) {\n\tconsole.log();\n\tp.intro(banner(cmd));\n}\n\nexport function outro(msg: string) {\n\tp.outro(msg);\n\tconsole.log();\n}\n\nexport function outroError(msg: string) {\n\tp.outro(red(msg));\n\tconsole.log();\n}\n\nexport function outroCancel(msg = \"cancelled\") {\n\tp.cancel(dim(msg));\n\tconsole.log();\n}\n\nexport function outroSkipped(msg: string) {\n\tp.outro(dim(msg));\n\tconsole.log();\n}\n","// `aistack connect claude` - the opt-in in-session sync surface (#56, #57).\n//\n// Installs BOTH halves or NEITHER: the user-scoped MCP server registration and\n// the Skill copy travel together, because the Skill drives `sync_preview` /\n// `sync_publish` and has nothing to do without the server (#56 decision 3).\n// The harness argument leaves room for `connect codex` later without a rename.\n\nimport { spawnSync } from \"node:child_process\";\nimport { cpSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport { getSettings, saveSettings } from \"../config.js\";\nimport { claudeAdapter, detectionSinceMs } from \"../harness/index.js\";\nimport {\n\tdim,\n\tintro,\n\tlimeBold,\n\toutro,\n\toutroError,\n\toutroSkipped,\n} from \"../theme.js\";\n\n/** The documented manual install line, printed when we cannot run it. */\nexport const MANUAL_MCP_ADD =\n\t\"claude mcp add --scope user aistack -- npx -y @use-aistack/cli mcp\";\n\nconst MCP_ADD_ARGS = [\n\t\"mcp\",\n\t\"add\",\n\t\"--scope\",\n\t\"user\",\n\t\"aistack\",\n\t\"--\",\n\t\"npx\",\n\t\"-y\",\n\t\"@use-aistack/cli\",\n\t\"mcp\",\n];\n\nconst MCP_REMOVE_ARGS = [\"mcp\", \"remove\", \"--scope\", \"user\", \"aistack\"];\n\nexport const SKILL_DEST = join(homedir(), \".claude\", \"skills\", \"aistack-sync\");\n\nexport interface RunResult {\n\t/** The binary was not found on PATH. */\n\tnotFound: boolean;\n\tstatus: number | null;\n\toutput: string;\n}\n\nexport type Runner = (args: string[]) => RunResult;\n\nfunction runClaude(args: string[]): RunResult {\n\tconst r = spawnSync(\"claude\", args, { encoding: \"utf-8\" });\n\tconst notFound =\n\t\tr.error !== undefined &&\n\t\t(r.error as NodeJS.ErrnoException).code === \"ENOENT\";\n\treturn {\n\t\tnotFound,\n\t\tstatus: r.status,\n\t\toutput: `${r.stdout ?? \"\"}${r.stderr ?? \"\"}`,\n\t};\n}\n\n/** Is the `claude` binary reachable? Cheap check used to skip the upsell. */\nexport function claudeOnPath(run: Runner = runClaude): boolean {\n\treturn !run([\"--version\"]).notFound;\n}\n\n/**\n * The bundled Skill directory, resolved relative to this module. In the\n * published package that is `<pkg>/skills/aistack-sync` next to `dist/`; in\n * dev it is two levels up from `src/commands/`. Walking up covers both.\n */\nexport function findSkillSource(\n\tfromDir: string = dirname(fileURLToPath(import.meta.url)),\n): string | null {\n\tlet dir = fromDir;\n\tfor (let i = 0; i < 4; i++) {\n\t\tconst candidate = join(dir, \"skills\", \"aistack-sync\");\n\t\tif (existsSync(join(candidate, \"SKILL.md\"))) return candidate;\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) break;\n\t\tdir = parent;\n\t}\n\treturn null;\n}\n\nexport interface ConnectOutcome {\n\tok: boolean;\n\tmessage: string;\n}\n\n/**\n * Install the server registration, then the Skill. If the Skill copy fails\n * after a fresh registration, the registration is rolled back - both halves\n * or neither.\n */\nexport function installClaudeConnect(\n\trun: Runner = runClaude,\n\tcopySkill: (src: string, dest: string) => void = (src, dest) =>\n\t\tcpSync(src, dest, { recursive: true }),\n): ConnectOutcome {\n\tconst source = findSkillSource();\n\tif (source === null) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage:\n\t\t\t\t\"this install is missing its bundled Skill (skills/aistack-sync) - nothing was installed\",\n\t\t};\n\t}\n\n\tconst add = run(MCP_ADD_ARGS);\n\tif (add.notFound) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude was not found on PATH - nothing was installed. Manual install:\\n${MANUAL_MCP_ADD}`,\n\t\t};\n\t}\n\tconst alreadyRegistered =\n\t\tadd.status !== 0 && add.output.includes(\"already exists\");\n\tif (add.status !== 0 && !alreadyRegistered) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `claude mcp add failed - nothing was installed.\\n${add.output.trim()}`,\n\t\t};\n\t}\n\n\ttry {\n\t\tcopySkill(source, SKILL_DEST);\n\t} catch (e) {\n\t\t// Both halves or neither: a fresh registration without its Skill is\n\t\t// rolled back. A pre-existing registration is left as it was.\n\t\tif (!alreadyRegistered) run(MCP_REMOVE_ARGS);\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `copying the Skill to ${SKILL_DEST} failed - the MCP registration was ${\n\t\t\t\talreadyRegistered ? \"left as it was\" : \"rolled back\"\n\t\t\t}.\\n${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\treturn {\n\t\tok: true,\n\t\tmessage: `Installed the user-scoped aistack MCP server and Skill. Say ${limeBold('\"sync my stack\"')} in any Claude Code session. Every send still requires your confirmation. Remove it with: claude mcp remove --scope user aistack`,\n\t};\n}\n\nexport async function connectCommand(harness: string): Promise<void> {\n\tintro(\"connect\");\n\n\tif (harness !== \"claude\") {\n\t\toutroError(`unknown harness \"${harness}\" - supported: claude`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tif (!claudeOnPath()) {\n\t\tp.log.warn(\n\t\t\t`claude was not found on PATH. Manual install:\\n${dim(MANUAL_MCP_ADD)}\\nplus copy skills/aistack-sync from this package to ${dim(SKILL_DEST)}`,\n\t\t);\n\t\toutroSkipped(\"nothing was installed\");\n\t\treturn;\n\t}\n\n\tconst result = installClaudeConnect();\n\tif (!result.ok) {\n\t\toutroError(result.message);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tp.log.success(result.message);\n\toutro(\"done\");\n}\n\nexport interface UpsellDeps {\n\t/** Override the Claude activity check. Tests only. */\n\tclaudeActiveImpl?: () => Promise<boolean>;\n\t/** Override the PATH check. Tests only. */\n\tclaudeOnPathImpl?: () => boolean;\n\t/** Override the settings file. Tests only. */\n\tsettingsFile?: string;\n}\n\n/** Has Claude Code written a transcript inside the sync window? */\nexport function claudeRecentlyActive(): Promise<boolean> {\n\treturn claudeAdapter.detect({ sinceMs: detectionSinceMs() });\n}\n\n/**\n * The post-sync upsell (#56 decision 2), asked once per machine. Any explicit\n * answer persists to ~/.config/aistack/settings.json; ctrl-C is not an answer\n * and the question returns on the next sync.\n *\n * Two gates, both silent. The offer needs a Claude Code the user actually runs\n * (#101) - a months-old install asked a Codex-only user to connect a harness\n * they had left behind, which is what opened #100. It also needs `claude` on\n * PATH, because that binary is what installs the MCP server.\n */\nexport async function offerConnectUpsell(deps: UpsellDeps = {}): Promise<void> {\n\tif (getSettings(deps.settingsFile).connectClaudeAnswered === true) return;\n\tif (!(await (deps.claudeActiveImpl ?? claudeRecentlyActive)())) return;\n\tif (!(deps.claudeOnPathImpl ?? claudeOnPath)()) return;\n\n\tconst answer = await p.select({\n\t\tmessage:\n\t\t\t\"Add AI Stack commands to Claude Code? Every send still asks first.\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"No, don't ask again\",\n\t\t\t\thint: \"you can install later with aistack connect claude\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"install\",\n\t\t\t\tlabel: \"Install MCP + Skill\",\n\t\t\t\thint: \"user scope; enables preview and confirmed send commands\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"later\",\n\t});\n\n\tif (p.isCancel(answer)) return;\n\tsaveSettings({ connectClaudeAnswered: true }, deps.settingsFile);\n\n\tif (answer === \"install\") {\n\t\tconst result = installClaudeConnect();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn;\n\t}\n\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli connect claude\")}`,\n\t);\n}\n","// Harness-agnostic aggregate machinery: the fold target every adapter fills,\n// and the finalize step that turns it into display-ready rows.\n//\n// Extracted from the Claude analyzer by ticket #67 (map #60) so the Codex\n// adapter can reuse the same totals, name hygiene, and model rows without\n// inheriting Claude's record-dedup logic. Everything here is pure - no I/O,\n// no console.\n\nimport { isPricedModel, type TokenCounts } from \"@aistack/pricing\";\n\n// ---------------------------------------------------------------------------\n// Narrowing helpers - records are untrusted external JSON\n// ---------------------------------------------------------------------------\n\nexport type Obj = Record<string, unknown>;\n\nexport const asObj = (v: unknown): Obj | null =>\n\ttypeof v === \"object\" && v !== null && !Array.isArray(v) ? (v as Obj) : null;\nexport const asStr = (v: unknown): string | null =>\n\ttypeof v === \"string\" && v.length > 0 ? v : null;\nexport const asNum = (v: unknown): number =>\n\ttypeof v === \"number\" && Number.isFinite(v) ? v : 0;\nexport const asArr = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);\n\n/**\n * Every name that becomes a Map key or leaves this module goes through here.\n *\n * These are user-chosen strings (skill names, MCP servers, subagent types,\n * slash commands, model ids) and a hostile one is a real vector: control\n * characters move a terminal cursor, and an unterminated bidi override (U+202E)\n * reorders the rest of the rendered line - including the count and percentage\n * printed beside the name. Both survive `JSON.stringify`, which escapes C0 but\n * not bidi. See CVE-2021-42574 (\"Trojan Source\").\n *\n * Sanitizing at ingest rather than at print means the guarantee travels with\n * the module: `finalize()`'s output is safe for any consumer, not just the\n * renderer that happens to sit in front of it today.\n */\nconst NAME_UNSAFE_RE =\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point\n\t/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\u2066-\\u2069\\ufeff]/g;\nconst NAME_MAX = 64;\n\nexport function cleanName(s: string): string {\n\tconst stripped = s.replace(NAME_UNSAFE_RE, \"�\").trim();\n\tif (stripped.length === 0) return \"(unnamed)\";\n\treturn stripped.length > NAME_MAX\n\t\t? `${stripped.slice(0, NAME_MAX - 1)}…`\n\t\t: stripped;\n}\n\n/**\n * The same bar as `cleanName`, asked as a question.\n *\n * Used on names arriving from the NETWORK - the per-stack opt-ins the sync\n * config carries (#44). Those are the user's own strings, so the curated list's\n * conventional charset is the wrong bar: parentheses, accents and CJK are all\n * legitimate names someone runs. What is refused is what cannot be rendered\n * safely, which is exactly what `cleanName` strips on the way in.\n */\nexport function isDisplaySafeName(s: string): boolean {\n\tif (s.length === 0 || s.trim().length === 0) return false;\n\tif (s.length > NAME_MAX) return false;\n\t// A `g`-flagged regex carries `lastIndex` across `.test` calls, so this uses\n\t// a fresh non-global copy rather than the shared literal.\n\treturn !new RegExp(NAME_UNSAFE_RE.source).test(s);\n}\n\n/** `asStr` for anything that will be used as a name. */\nexport const asName = (v: unknown): string | null => {\n\tconst s = asStr(v);\n\treturn s === null ? null : cleanName(s);\n};\n\n// ---------------------------------------------------------------------------\n// Aggregate\n// ---------------------------------------------------------------------------\n\nexport type ModelUsage = TokenCounts & {\n\tmessages: number;\n\t/**\n\t * API-equivalent cost accumulated per response at that response's own rate\n\t * (#33 decision 8). Not derivable from the token totals above once a window\n\t * straddles a repricing.\n\t */\n\tcostUSD: number;\n\t/** Tokens whose own timestamp had no citable rate. Surfaced, never zeroed. */\n\tunpricedTokens: number;\n};\n\n/**\n * The fold target. `Seen` is the adapter's own dedup bookkeeping type -\n * Claude keys responses by `message.id`, Codex needs none - kept generic so\n * the shared shape does not import any one harness's record semantics.\n */\nexport type Aggregate<Seen = unknown> = {\n\t// provenance / scan health\n\tfiles: number;\n\tlines: number;\n\tparseErrors: number;\n\trecords: number;\n\tassistantRecords: number;\n\t/** Distinct API responses actually counted. */\n\tdistinctResponses: number;\n\t/** Extra records of a response already counted (same message.id AND requestId). */\n\tcontinuationsFolded: number;\n\t/** Same message.id under a NEW requestId - a genuine replay (e.g. /btw sidechain). */\n\trealReplaysFolded: number;\n\t/** Times a later record superseded an earlier one because it carried a larger total. */\n\tsupersededByLarger: number;\n\t/** Assistant records with no message.id - counted without dedup protection. */\n\tunkeyedResponses: number;\n\tsyntheticRecords: number;\n\tsyntheticTokens: number;\n\ttoolBlocksWithoutId: number;\n\t/** Responses whose first attempt ran on a different model (#33 decision 9). */\n\tfallbackAttempts: number;\n\tuntypedMirrors: number;\n\t/** Records with no parseable timestamp - cannot be priced time-awarely. */\n\tuntimestampedResponses: number;\n\tprojectDirs: Set<string>; // held only to count - names never leave this module\n\tccVersions: Set<string>;\n\tmirroredIterationTypes: Map<string, number>;\n\n\t// tokens\n\tbyModel: Map<string, ModelUsage>;\n\tsidechainTokens: number;\n\tmainTokens: number;\n\n\t// activity\n\tsessions: Set<string>;\n\tactiveDays: Set<string>; // UTC YYYY-MM-DD\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\n\t// tools / skills / mcp / agents\n\ttoolCalls: Map<string, number>;\n\tskillCalls: Map<string, number>;\n\tmcpServerCalls: Map<string, number>;\n\tmcpToolCalls: Map<string, number>;\n\tsubagentCalls: Map<string, number>;\n\tslashCommands: Map<string, number>;\n\ttoolCallDedup: Set<string>;\n\n\t// content-block shape\n\tthinkingBlocks: number;\n\ttextBlocks: number;\n\twebSearchRequests: number;\n\twebFetchRequests: number;\n\n\t// adapter-owned dedup bookkeeping\n\tseen: Map<string, Seen>;\n\n\t// THE PER-DAY SEAM (#307, ADR-0010). The same response stream that fills\n\t// the window totals above also lands in one bucket per UTC date here, so a\n\t// fold over the days equals the window up to rounding. Nothing in these\n\t// maps is a share or a mean: `usage/days.ts` turns them into the wire.\n\tusageDays: Map<string, UsageDayAcc>;\n\t/** Session id -> earliest in-window timestamp. A session belongs to the day it STARTED. */\n\tsessionStarts: Map<string, number>;\n};\n\n/** One model's response sums inside one UTC day. */\nexport type UsageDayModel = {\n\tcounts: TokenCounts;\n\t/** Sum of the responses that had a citable rate at their own timestamp. */\n\tcostUSD: number;\n\t/** Tokens of the responses that had none. Surfaced, never zeroed. */\n\tunpricedTokens: number;\n};\n\nexport type UsageDayAcc = {\n\tmodels: Map<string, UsageDayModel>;\n\t/** Tokens of sidechain (subagent) responses, all models. */\n\tsubagentTokens: number;\n\tsyntheticTokens: number;\n\t/** Local project directories touched this day. Hashed before they leave. */\n\tprojectDirs: Set<string>;\n};\n\nexport const utcDateOf = (ms: number): string =>\n\tnew Date(ms).toISOString().slice(0, 10);\n\nfunction usageDayAcc(agg: Aggregate<never> | Aggregate<unknown>, tsMs: number) {\n\tconst date = utcDateOf(tsMs);\n\tlet day = agg.usageDays.get(date);\n\tif (!day) {\n\t\tday = {\n\t\t\tmodels: new Map(),\n\t\t\tsubagentTokens: 0,\n\t\t\tsyntheticTokens: 0,\n\t\t\tprojectDirs: new Set(),\n\t\t};\n\t\tagg.usageDays.set(date, day);\n\t}\n\treturn day;\n}\n\n/**\n * Land one response's tokens on the day of its own timestamp. `sign` is -1\n * when an adapter retracts a response it counted before (Claude's dedup). An\n * untimestamped response has no day and stays in the window totals only.\n */\nexport function noteUsageResponse(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tresponse: {\n\t\ttsMs: number | null;\n\t\tmodelKey: string;\n\t\tcounts: TokenCounts;\n\t\tcostUSD: number | null;\n\t\tsidechain?: boolean;\n\t},\n\tsign: 1 | -1 = 1,\n): void {\n\tif (response.tsMs === null) return;\n\tconst day = usageDayAcc(agg, response.tsMs);\n\tlet m = day.models.get(response.modelKey);\n\tif (!m) {\n\t\tm = {\n\t\t\tcounts: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheWrite5m: 0,\n\t\t\t\tcacheWrite1h: 0,\n\t\t\t\tcacheWriteUnsplit: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t},\n\t\t\tcostUSD: 0,\n\t\t\tunpricedTokens: 0,\n\t\t};\n\t\tday.models.set(response.modelKey, m);\n\t}\n\tconst c = response.counts;\n\tm.counts.input += sign * c.input;\n\tm.counts.output += sign * c.output;\n\tm.counts.cacheWrite5m += sign * c.cacheWrite5m;\n\tm.counts.cacheWrite1h += sign * c.cacheWrite1h;\n\tm.counts.cacheWriteUnsplit += sign * c.cacheWriteUnsplit;\n\tm.counts.cacheRead += sign * c.cacheRead;\n\tif (response.costUSD === null) m.unpricedTokens += sign * countsTotal(c);\n\telse m.costUSD += sign * response.costUSD;\n\tif (response.sidechain) day.subagentTokens += sign * countsTotal(c);\n}\n\nexport function noteSyntheticTokens(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\ttsMs: number | null,\n\ttokens: number,\n): void {\n\tif (tsMs === null) return;\n\tusageDayAcc(agg, tsMs).syntheticTokens += tokens;\n}\n\n/** Remember the earliest timestamp seen for a session: that is the day it started. */\nexport function noteSessionStart(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tsessionId: string,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null) return;\n\tconst held = agg.sessionStarts.get(sessionId);\n\tif (held === undefined || tsMs < held) agg.sessionStarts.set(sessionId, tsMs);\n}\n\n/** A project belongs to every day it was touched. */\nexport function noteProjectDay(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tdirectory: string,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null) return;\n\tusageDayAcc(agg, tsMs).projectDirs.add(directory);\n}\n\nexport function createAggregate<Seen = unknown>(): Aggregate<Seen> {\n\treturn {\n\t\tfiles: 0,\n\t\tlines: 0,\n\t\tparseErrors: 0,\n\t\trecords: 0,\n\t\tassistantRecords: 0,\n\t\tdistinctResponses: 0,\n\t\tcontinuationsFolded: 0,\n\t\trealReplaysFolded: 0,\n\t\tsupersededByLarger: 0,\n\t\tunkeyedResponses: 0,\n\t\tsyntheticRecords: 0,\n\t\tsyntheticTokens: 0,\n\t\ttoolBlocksWithoutId: 0,\n\t\tfallbackAttempts: 0,\n\t\tuntypedMirrors: 0,\n\t\tuntimestampedResponses: 0,\n\t\tprojectDirs: new Set(),\n\t\tccVersions: new Set(),\n\t\tmirroredIterationTypes: new Map(),\n\t\tbyModel: new Map(),\n\t\tsidechainTokens: 0,\n\t\tmainTokens: 0,\n\t\tsessions: new Set(),\n\t\tactiveDays: new Set(),\n\t\tfirstTs: null,\n\t\tlastTs: null,\n\t\ttoolCalls: new Map(),\n\t\tskillCalls: new Map(),\n\t\tmcpServerCalls: new Map(),\n\t\tmcpToolCalls: new Map(),\n\t\tsubagentCalls: new Map(),\n\t\tslashCommands: new Map(),\n\t\ttoolCallDedup: new Set(),\n\t\tthinkingBlocks: 0,\n\t\ttextBlocks: 0,\n\t\twebSearchRequests: 0,\n\t\twebFetchRequests: 0,\n\t\tseen: new Map(),\n\t\tusageDays: new Map(),\n\t\tsessionStarts: new Map(),\n\t};\n}\n\nexport const bump = (m: Map<string, number>, k: string, n = 1) =>\n\tm.set(k, (m.get(k) ?? 0) + n);\n\nexport function emptyUsage(): ModelUsage {\n\treturn {\n\t\tinput: 0,\n\t\toutput: 0,\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: 0,\n\t\tmessages: 0,\n\t\tcostUSD: 0,\n\t\tunpricedTokens: 0,\n\t};\n}\n\nexport const countsTotal = (t: TokenCounts): number =>\n\tt.input +\n\tt.output +\n\tt.cacheWrite5m +\n\tt.cacheWrite1h +\n\tt.cacheWriteUnsplit +\n\tt.cacheRead;\n\n/**\n * Fold one priced usage delta into the per-model totals. The Claude adapter\n * has its own apply/retract pair (dedup can un-count a response); an adapter\n * whose records are already deltas - Codex - adds through here.\n */\nexport function addModelUsage(\n\tagg: Aggregate<never> | Aggregate<unknown>,\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\tcostUSD: number | null,\n\tmessages = 1,\n\t/**\n\t * Where the response sits in time, for the per-day seam (#307). An adapter\n\t * that passes nothing keeps the window totals right and lands no day.\n\t */\n\tat?: { tsMs: number | null; sidechain?: boolean },\n): void {\n\tif (at) {\n\t\tnoteUsageResponse(agg, {\n\t\t\ttsMs: at.tsMs,\n\t\t\tmodelKey,\n\t\t\tcounts,\n\t\t\tcostUSD,\n\t\t\t...(at.sidechain ? { sidechain: true } : {}),\n\t\t});\n\t}\n\tlet m = agg.byModel.get(modelKey);\n\tif (!m) {\n\t\tm = emptyUsage();\n\t\tagg.byModel.set(modelKey, m);\n\t}\n\tm.messages += messages;\n\tm.input += counts.input;\n\tm.output += counts.output;\n\tm.cacheWrite5m += counts.cacheWrite5m;\n\tm.cacheWrite1h += counts.cacheWrite1h;\n\tm.cacheWriteUnsplit += counts.cacheWriteUnsplit;\n\tm.cacheRead += counts.cacheRead;\n\tif (costUSD === null) m.unpricedTokens += countsTotal(counts);\n\telse m.costUSD += costUSD;\n}\n\n// ---------------------------------------------------------------------------\n// Finalize - the shape the wire payload is derived from\n// ---------------------------------------------------------------------------\n\nexport type ModelRow = {\n\t/** Pricing key: normalized vendor id, plus `#fast` when speed was fast. */\n\tmodelKey: string;\n\ttokens: TokenCounts;\n\ttotalTokens: number;\n\tmessages: number;\n\tshare: number;\n\t/** Accumulated at each response's own rate. `null` when nothing was priced. */\n\tcostUSD: number | null;\n\t/** Tokens inside this row that no rate covered. */\n\tunpricedTokens: number;\n};\n\nexport type Finalized = {\n\tmodels: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n\tcacheHitShare: number;\n\tsidechainShare: number;\n\tactiveDays: number;\n\tfirstTs: number | null;\n\tlastTs: number | null;\n\tsessions: number;\n\tprojects: number;\n\ttools: Array<[string, number]>;\n\tskills: Array<[string, number]>;\n\tmcpServers: Array<[string, number]>;\n\tsubagents: Array<[string, number]>;\n\tslashCommands: Array<[string, number]>;\n\ttotalToolCalls: number;\n\t/** Newest harness version observed, or null when none was recorded. */\n\tharnessVersion: string | null;\n};\n\nfunction buildModelRows(agg: Aggregate): {\n\trows: ModelRow[];\n\ttotalTokens: number;\n\ttotalCostUSD: number;\n\tunpricedModels: string[];\n\tunpricedTokens: number;\n} {\n\tconst rows: ModelRow[] = [];\n\tlet totalTokens = 0;\n\tlet totalCostUSD = 0;\n\tconst unpricedModels: string[] = [];\n\tlet unpricedTokens = 0;\n\n\tfor (const [modelKey, u] of agg.byModel) {\n\t\tconst tokens: TokenCounts = {\n\t\t\tinput: u.input,\n\t\t\toutput: u.output,\n\t\t\tcacheWrite5m: u.cacheWrite5m,\n\t\t\tcacheWrite1h: u.cacheWrite1h,\n\t\t\tcacheWriteUnsplit: u.cacheWriteUnsplit,\n\t\t\tcacheRead: u.cacheRead,\n\t\t};\n\t\tconst sum = countsTotal(tokens);\n\t\ttotalTokens += sum;\n\t\tif (u.unpricedTokens > 0) {\n\t\t\tunpricedModels.push(modelKey);\n\t\t\tunpricedTokens += u.unpricedTokens;\n\t\t}\n\t\ttotalCostUSD += u.costUSD;\n\t\trows.push({\n\t\t\tmodelKey,\n\t\t\ttokens,\n\t\t\ttotalTokens: sum,\n\t\t\tmessages: u.messages,\n\t\t\tshare: 0,\n\t\t\t// A model we hold no rate for at all reports null rather than $0.00,\n\t\t\t// so \"we can't price this\" never reads as \"this was free\".\n\t\t\tcostUSD: isPricedModel(modelKey) ? u.costUSD : null,\n\t\t\tunpricedTokens: u.unpricedTokens,\n\t\t});\n\t}\n\tfor (const r of rows) r.share = totalTokens ? r.totalTokens / totalTokens : 0;\n\trows.sort(\n\t\t(a, b) =>\n\t\t\tb.totalTokens - a.totalTokens || a.modelKey.localeCompare(b.modelKey),\n\t);\n\treturn { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens };\n}\n\nfunction computeCacheHitShare(rows: ModelRow[]): number {\n\tlet cacheRead = 0;\n\tlet inputClass = 0;\n\tfor (const r of rows) {\n\t\tcacheRead += r.tokens.cacheRead;\n\t\tinputClass +=\n\t\t\tr.tokens.input +\n\t\t\tr.tokens.cacheRead +\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t}\n\treturn inputClass ? cacheRead / inputClass : 0;\n}\n\n/**\n * Newest observed harness version, compared numerically per dotted segment\n * so `2.1.9` doesn't sort above `2.1.220`.\n */\nexport function newestVersion(versions: Iterable<string>): string | null {\n\tlet best: string | null = null;\n\tlet bestParts: number[] = [];\n\tfor (const v of versions) {\n\t\tconst parts = v.split(\".\").map((p) => Number.parseInt(p, 10));\n\t\tif (parts.some((n) => !Number.isFinite(n))) continue;\n\t\tif (best === null || compareParts(parts, bestParts) > 0) {\n\t\t\tbest = v;\n\t\t\tbestParts = parts;\n\t\t}\n\t}\n\treturn best;\n}\n\nfunction compareParts(a: number[], b: number[]): number {\n\tconst len = Math.max(a.length, b.length);\n\tfor (let i = 0; i < len; i++) {\n\t\tconst d = (a[i] ?? 0) - (b[i] ?? 0);\n\t\tif (d !== 0) return d;\n\t}\n\treturn 0;\n}\n\nexport function finalize(agg: Aggregate): Finalized {\n\tconst { rows, totalTokens, totalCostUSD, unpricedModels, unpricedTokens } =\n\t\tbuildModelRows(agg);\n\n\tconst byCount = (m: Map<string, number>): Array<[string, number]> =>\n\t\t[...m.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));\n\n\tlet totalToolCalls = 0;\n\tfor (const v of agg.toolCalls.values()) totalToolCalls += v;\n\tfor (const v of agg.mcpToolCalls.values()) totalToolCalls += v;\n\n\tconst sideTotal = agg.sidechainTokens + agg.mainTokens;\n\n\treturn {\n\t\tmodels: rows,\n\t\ttotalTokens,\n\t\ttotalCostUSD,\n\t\tunpricedModels,\n\t\tunpricedTokens,\n\t\tcacheHitShare: computeCacheHitShare(rows),\n\t\tsidechainShare: sideTotal ? agg.sidechainTokens / sideTotal : 0,\n\t\tactiveDays: agg.activeDays.size,\n\t\tfirstTs: agg.firstTs,\n\t\tlastTs: agg.lastTs,\n\t\tsessions: agg.sessions.size,\n\t\tprojects: agg.projectDirs.size,\n\t\ttools: byCount(agg.toolCalls),\n\t\tskills: byCount(agg.skillCalls),\n\t\tmcpServers: byCount(agg.mcpServerCalls),\n\t\tsubagents: byCount(agg.subagentCalls),\n\t\tslashCommands: byCount(agg.slashCommands),\n\t\ttotalToolCalls,\n\t\tharnessVersion: newestVersion(agg.ccVersions),\n\t};\n}\n","// The bundled curated allowlist - the fallback copy for `/api/sync-config`.\n//\n// Wayfinder ticket #37 (map #29), decision 4 of the wire-format grilling #33.\n//\n// WHAT BELONGS HERE, AND WHY IT IS SHORT\n// These four classes of name are user-chosen. A Skill called `acme-q3-pricing`,\n// an MCP server called `internal-billing`, a subagent called `client-migration`\n// - each is a real leak, and none of them is distinguishable from a public name\n// by shape.\n//\n// THE BAR (grilling #42): a name qualifies if the STRING carries no private\n// information no matter who typed it. That is a property of the string, not of\n// the user and not of the artifact.\n//\n// The bar is deliberately NOT \"the name identifies a public artifact, so\n// publishing it reveals nothing the user hasn't already published\". That was the\n// original wording and it is wrong: `stripe` is on this list, and publishing it\n// plainly does reveal something the user never published - that they use Stripe.\n// It cannot be the harm, because revealing what you use is the entire product.\n// The harm is narrower: strings drawn from the user's private vocabulary, which\n// leak a relationship (an employer, a client, a codename) rather than a\n// preference. `stripe` and `filesystem` are safe even for someone who named\n// their own server that by coincidence.\n//\n// Three sources meet that bar:\n// 1. Claude Code's own built-in subagent types and slash commands (vendor-\n// assigned, same class as a built-in tool name).\n// 2. Skills that ship with Claude Code itself.\n// 3. MCP servers with a public, documented, first-party endpoint.\n//\n// WHY THIS LIST DOES NOT NEED TO BE LONG (#42 decision 1)\n// It is no longer the only road to publishing a name. The approve gate offers\n// every kept-private name as an explicit, default-off tick, and the tick set\n// comes back down with the rest of the sync config. This list only exists to\n// spare a user from ticking boxes nobody would think twice about - so it can\n// stay strict, and every user-chosen name goes through the person who knows\n// whether it is a secret.\n//\n// The author's own `alp-river:*` plugin is deliberately NOT seeded, even though\n// it is genuinely published. This list is GLOBAL: seeding it would publish those\n// names for every user who installs the plugin without any of them ticking\n// anything, and an author adding their own names to the default everyone else\n// inherits is what would make the list untrustworthy for every other entry.\n//\n// `/api/sync-config` (ticket #38) serves the AUTHORITATIVE list. This copy only\n// covers the case where that endpoint can't be reached, which for an installed\n// user is permanent if the plugin never auto-updates. Growing the curated list\n// is server-side work; adding entries here only helps the offline case.\n\nimport type { CuratedAllowlist } from \"./allowlist.js\";\n\n/** Claude Code's own subagent types. Vendor-assigned, not user-chosen. */\nconst BUILTIN_SUBAGENTS = [\n\t\"(default)\",\n\t\"claude\",\n\t\"claude-code-guide\",\n\t\"Explore\",\n\t\"fork\",\n\t\"general-purpose\",\n\t\"Plan\",\n\t\"statusline-setup\",\n] as const;\n\n/** Skills bundled with Claude Code. */\nconst BUILTIN_SKILLS = [\n\t\"artifact-capabilities\",\n\t\"artifact-design\",\n\t\"claude-api\",\n\t\"code-review\",\n\t\"codebase-design\",\n\t\"dataviz\",\n\t\"diagnosing-bugs\",\n\t\"domain-modeling\",\n\t\"fewer-permission-prompts\",\n\t\"grilling\",\n\t\"init\",\n\t\"keybindings-help\",\n\t\"loop\",\n\t\"prototype\",\n\t\"research\",\n\t\"review\",\n\t\"run\",\n\t\"schedule\",\n\t\"security-review\",\n\t\"simplify\",\n\t\"tdd\",\n\t\"update-config\",\n] as const;\n\n/** Claude Code's own slash commands. */\nconst BUILTIN_SLASH_COMMANDS = [\n\t\"add-dir\",\n\t\"agents\",\n\t\"bug\",\n\t\"clear\",\n\t\"compact\",\n\t\"config\",\n\t\"context\",\n\t\"cost\",\n\t\"doctor\",\n\t\"effort\",\n\t\"exit\",\n\t\"export\",\n\t\"fast\",\n\t\"help\",\n\t\"hooks\",\n\t\"ide\",\n\t\"init\",\n\t\"login\",\n\t\"logout\",\n\t\"mcp\",\n\t\"memory\",\n\t\"model\",\n\t\"output-style\",\n\t\"permissions\",\n\t\"plugin\",\n\t\"privacy-settings\",\n\t\"release-notes\",\n\t\"resume\",\n\t\"review\",\n\t\"rewind\",\n\t\"security-review\",\n\t\"status\",\n\t\"statusline\",\n\t\"terminal-setup\",\n\t\"todos\",\n\t\"upgrade\",\n\t\"usage\",\n\t\"vim\",\n\t\"workflows\",\n] as const;\n\n/**\n * MCP servers with a public first-party endpoint.\n *\n * Matched against the server segment the analyzer parses out of an\n * `mcp__<server>__<tool>` name, which is the LOCAL alias the user configured -\n * so this only fires when the user kept the conventional name. A renamed server\n * is kept private, which is the correct direction to fail.\n *\n * ONE normalization applies first (#42 decision 5): a server provided by a\n * plugin is observed as `plugin_<plugin>_<server>`, a string Claude Code\n * generates rather than one the user typed. Strip that wrapper before matching,\n * and publish the NORMALIZED name. The safety property is that normalization can\n * only ever emit a string already on this list - a non-matching inner segment\n * emits nothing and the raw name falls through to the gate's review list - so a\n * bug here is bounded by an already-vetted set. If the upstream convention\n * changes, matching reverts to keeping names private: a fail-safe regression.\n */\nconst PUBLIC_MCP_SERVERS = [\n\t\"chrome-devtools\",\n\t\"context7\",\n\t\"deepwiki\",\n\t\"figma\",\n\t\"filesystem\",\n\t\"git\",\n\t\"github\",\n\t\"huggingface\",\n\t\"ide\",\n\t\"linear\",\n\t\"notion\",\n\t\"playwright\",\n\t\"puppeteer\",\n\t\"sentry\",\n\t\"slack\",\n\t\"stripe\",\n] as const;\n\nexport const BUNDLED_CURATED_ALLOWLIST: CuratedAllowlist = {\n\tmcpServers: PUBLIC_MCP_SERVERS,\n\tskills: BUILTIN_SKILLS,\n\tsubagents: BUILTIN_SUBAGENTS,\n\tslashCommands: BUILTIN_SLASH_COMMANDS,\n};\n","// Fail-closed name filtering for the measured layer.\n//\n// Wayfinder ticket #37 (map #29), decisions 2-4 of the wire-format grilling #33.\n//\n// THE INVERSION THIS FILE EXISTS TO PERFORM\n// The prototype's `toolCalls` map was a catch-all: anything that wasn't an\n// `mcp__*` tool, a Skill, or an Agent fell THROUGH into it, and from there into\n// the payload. That is denylist-shaped - a tool name nobody anticipated\n// publishes by default. Here a name publishes only if it matches a known list,\n// and everything else is withheld and published as a per-category count.\n//\n// Two classes of name, two mechanisms:\n// - Built-in Claude Code tool names are VENDOR-assigned and enumerable, so\n// they match a hardcoded literal set (BUILTIN_TOOLS below).\n// - MCP servers / Skills / subagents / slash commands are USER-chosen and can\n// carry a client name, a project codename, or an internal system's name.\n// They match a curated list fetched from aistack, with the bundled copy\n// below as the fallback.\n//\n// Model ids are exempt from all of this - see decision 3 and payload.ts.\n//\n// WHY FETCHED AND NOT ONLY BUNDLED (decision 4)\n// Third-party marketplace plugin auto-update defaults to OFF, and a\n// `plugin.json` whose `version` isn't bumped ships nothing. A bundled-only list\n// is, for an installed user, frozen forever - a Skill that becomes public next\n// month would never publish. The filtering itself still runs client-side:\n// fail-closed only means something if it happens before the send.\n\nimport { isDisplaySafeName } from \"./aggregate.js\";\nimport { BUNDLED_CURATED_ALLOWLIST } from \"./bundled-allowlist.js\";\n\n/**\n * Every built-in tool Claude Code can emit as a `tool_use` block name.\n *\n * Deliberately a literal set and not a pattern: a pattern is a denylist wearing\n * a hat. Grounded in the observed corpus (22 distinct names across 235,961\n * records) plus the documented tool surface, including tools that are deferred\n * or unavailable in most sessions - an unknown-but-real built-in withheld as a\n * count is a small loss; an unknown-and-user-named tool published verbatim is\n * the leak this whole file prevents.\n *\n * `Task` is the pre-rename spelling of `Agent`; the analyzer folds it into\n * `Agent` at ingest, so it is here only to make the set self-documenting.\n */\nexport const BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"Agent\",\n\t\"Artifact\",\n\t\"AskUserQuestion\",\n\t\"Bash\",\n\t\"BashOutput\",\n\t\"CronCreate\",\n\t\"CronDelete\",\n\t\"CronList\",\n\t\"DesignSync\",\n\t\"Edit\",\n\t\"EndConversation\",\n\t\"EnterPlanMode\",\n\t\"EnterWorktree\",\n\t\"ExitPlanMode\",\n\t\"ExitWorktree\",\n\t\"Glob\",\n\t\"Grep\",\n\t\"KillBash\",\n\t\"KillShell\",\n\t\"ListMcpResourcesTool\",\n\t\"LS\",\n\t\"Monitor\",\n\t\"MultiEdit\",\n\t\"NotebookEdit\",\n\t\"NotebookRead\",\n\t\"PushNotification\",\n\t\"Read\",\n\t\"ReadMcpResourceDirTool\",\n\t\"ReadMcpResourceTool\",\n\t\"RemoteTrigger\",\n\t\"ReportFindings\",\n\t\"ScheduleWakeup\",\n\t\"SendMessage\",\n\t\"SendUserFile\",\n\t\"Skill\",\n\t\"SlashCommand\",\n\t\"Task\",\n\t\"TaskCreate\",\n\t\"TaskGet\",\n\t\"TaskList\",\n\t\"TaskOutput\",\n\t\"TaskStop\",\n\t\"TaskUpdate\",\n\t\"TodoWrite\",\n\t\"ToolSearch\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n\t\"Workflow\",\n\t\"Write\",\n]);\n\n/** The four user-chosen atom classes that need the curated list. */\nexport type CuratedAllowlist = {\n\tmcpServers: readonly string[];\n\tskills: readonly string[];\n\tsubagents: readonly string[];\n\tslashCommands: readonly string[];\n};\n\n/** The five inventory classes the payload carries. */\nexport const NAME_CATEGORIES = [\n\t\"builtinTools\",\n\t\"mcpServers\",\n\t\"skills\",\n\t\"subagents\",\n\t\"slashCommands\",\n] as const;\n\nexport type NameCategory = (typeof NAME_CATEGORIES)[number];\n\n/**\n * Names this stack's owner has explicitly ticked for publication (#42\n * decision 1), served per-stack by the authenticated half of `/api/sync-config`.\n *\n * The curated list is a convenience default, not the coverage mechanism: every\n * user-chosen name class is unbounded and unenumerable, so a hand-curated list\n * can only ever be a rounding error against the real population. Coverage comes\n * from here - from the person who knows which of their names are secret.\n *\n * `builtinTools` is included for symmetry even though that class is\n * vendor-assigned: a built-in this version of the client has never heard of is\n * kept private like anything else, and the owner can tick it.\n */\nexport type OptInNames = Record<NameCategory, readonly string[]>;\n\nexport const EMPTY_OPT_INS: OptInNames = {\n\tbuiltinTools: [],\n\tmcpServers: [],\n\tskills: [],\n\tsubagents: [],\n\tslashCommands: [],\n};\n\nexport type SyncConfig = {\n\tallowlist: CuratedAllowlist;\n\t/**\n\t * Stack-level cost preference (decision 11). When false the payload omits\n\t * cost entirely rather than zeroing it - see payload.ts.\n\t */\n\tpublishCost: boolean;\n\t/** Per-stack ticked names, unioned into the allowlist before filtering. */\n\toptIns: OptInNames;\n\t/**\n\t * Whether this stack stages its kept-private names on the web so the owner\n\t * can tick them there (#48). Off means the machine sends the payload alone\n\t * and the names never leave it.\n\t */\n\treviewKeptPrivate: boolean;\n\t/**\n\t * Whether the measured workflow section publishes (#213).\n\t *\n\t * The third bit in this family and the same shape as the two above: a\n\t * stack-level preference, default on, applied here on the machine. Off means\n\t * `buildSyncBody` leaves the section out of the bytes entirely.\n\t */\n\tpublishWorkflow: boolean;\n\t/**\n\t * The stack the bearer token is bound to - where a publish would land.\n\t *\n\t * The approve gate must name its destination BEFORE the send (#33\n\t * decision 7, #41), and beat one points at `/stacks/{slug}/changes` (#48),\n\t * so both ride on the authenticated half of the config fetch. `null` when\n\t * the fetch was anonymous, failed, or the token resolved no stack - and a\n\t * gate that cannot name its destination must not publish.\n\t */\n\tstack: { name: string; slug: string } | null;\n\t/**\n\t * The auto-sync permission the STACK holds (#102, read by #103).\n\t *\n\t * Three states, not two, and the third is the whole point. `null` means no\n\t * owner has ever decided, and that is the one state a machine's local opt-in\n\t * may still seed. `{ enabled: false }` means the owner said no, and\n\t * `sync --auto` publishes nothing on this machine until they say otherwise.\n\t *\n\t * `frequencyHours` is absent when the value could not be read - see\n\t * `readAutoSync`.\n\t */\n\tautoSync: AutoSyncPermission | null;\n};\n\n/** What the stack allows, as the CLI reads it off the wire. */\nexport type AutoSyncPermission = {\n\tenabled: boolean;\n\tfrequencyHours?: number;\n};\n\n/**\n * Used when `/api/sync-config` can't be reached.\n *\n * `publishCost: false` is deliberate. The toggle is a stack-level preference we\n * do not hold locally, and the fail-closed default for a preference we can't\n * read is the one that transmits less. A user whose fetch failed sees cost\n * missing from the gate and can retry; the reverse - publishing cost the stack\n * had opted out of - is not recoverable, because the snapshot is immutable.\n */\nexport const BUNDLED_SYNC_CONFIG: SyncConfig = {\n\tallowlist: BUNDLED_CURATED_ALLOWLIST,\n\tpublishCost: false,\n\t// Empty for the same reason, and it is the load-bearing half of #42\n\t// decision 2: a failed config fetch reverts every ticked name to\n\t// kept-private. Losing the network publishes LESS, never more.\n\toptIns: EMPTY_OPT_INS,\n\t// Same direction again (#48): a machine that cannot read the switch does not\n\t// upload the names it is holding back. The default is ON server-side, so this\n\t// costs the owner one retry and never costs them a name.\n\treviewKeptPrivate: false,\n\t// Fail closed a third time (#213). A machine that could not read the switch\n\t// publishes measurement and no workflow section, which costs the owner one\n\t// retry and can never publish a section they had turned off.\n\tpublishWorkflow: false,\n\t// No fetch, no destination - and the gate refuses to publish without one.\n\tstack: null,\n\t// No fetch, no permission either. This costs nothing on its own: `stack` is\n\t// null in the same breath, so the stage blocks before any publish.\n\tautoSync: null,\n};\n\n// ---------------------------------------------------------------------------\n// Fetch\n// ---------------------------------------------------------------------------\n\nconst SYNC_CONFIG_PATH = \"/api/sync-config\";\nconst FETCH_TIMEOUT_MS = 5_000;\n\nexport type SyncConfigSource = \"fetched\" | \"bundled\";\n\nexport type LoadedSyncConfig = {\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Present when the fetch failed and the bundled copy was used. */\n\terror?: string;\n};\n\n/**\n * A name arriving from the network is no more trusted than one from a\n * transcript. Names are matched by exact equality, so a hostile list can widen\n * what publishes but can never smuggle a wildcard - and the approve gate\n * renders every name that will publish, which is what defuses that residual\n * trust (decision 4). Charset and length are still bounded so a pathological\n * entry can't reach a terminal or a database column.\n */\nconst CURATED_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9 ._:@/-]{0,63}$/;\n\nfunction readNameList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && CURATED_NAME_RE.test(item)) out.push(item);\n\t}\n\treturn out;\n}\n\n/**\n * Opt-ins are read against a LOOSER bar than the curated list.\n *\n * A curated entry is ours and conventional, so the tight charset costs nothing.\n * An opt-in is the user's own name - `(default)`, an accented word, a CJK skill\n * - and dropping it here would silently un-tick a decision they made at the\n * gate. The bar that survives is the one that matters for a string we print and\n * store: no control characters, no bidi overrides, bounded length.\n */\nfunction readOptInList(v: unknown): string[] {\n\tif (!Array.isArray(v)) return [];\n\tconst out: string[] = [];\n\tfor (const item of v) {\n\t\tif (typeof item === \"string\" && isDisplaySafeName(item)) out.push(item);\n\t}\n\treturn out;\n}\n\nfunction readOptIns(v: unknown): OptInNames {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v))\n\t\treturn EMPTY_OPT_INS;\n\tconst obj = v as Record<string, unknown>;\n\treturn {\n\t\tbuiltinTools: readOptInList(obj.builtinTools),\n\t\tmcpServers: readOptInList(obj.mcpServers),\n\t\tskills: readOptInList(obj.skills),\n\t\tsubagents: readOptInList(obj.subagents),\n\t\tslashCommands: readOptInList(obj.slashCommands),\n\t};\n}\n\n/**\n * A slug becomes a URL path segment the gate prints, so it gets the tightest\n * bar of any string here. The name is display text and gets `isDisplaySafeName`.\n */\nconst STACK_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;\n\nfunction readStack(v: unknown): SyncConfig[\"stack\"] {\n\tif (typeof v !== \"object\" || v === null || Array.isArray(v)) return null;\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.name !== \"string\" || !isDisplaySafeName(obj.name)) return null;\n\tif (typeof obj.slug !== \"string\" || !STACK_SLUG_RE.test(obj.slug))\n\t\treturn null;\n\treturn { name: obj.name, slug: obj.slug };\n}\n\n/**\n * Read the stack's auto-sync permission (#103).\n *\n * ABSENT AND OFF ARE DIFFERENT ANSWERS. Absent - the key is missing or null -\n * means no owner has decided, and only that lets a local flag seed the server.\n * A value that is PRESENT but unreadable is not that state: a permission the\n * machine cannot read is a permission it does not hold, so it reads as off.\n * The frequency is left out there rather than guessed, because off keeps no\n * schedule and a made-up number would outlive the garbled value that caused it.\n */\nfunction readAutoSync(v: unknown): AutoSyncPermission | null {\n\tif (v === undefined || v === null) return null;\n\tif (typeof v !== \"object\" || Array.isArray(v)) return { enabled: false };\n\tconst obj = v as Record<string, unknown>;\n\tif (typeof obj.enabled !== \"boolean\") return { enabled: false };\n\tif (\n\t\ttypeof obj.frequencyHours !== \"number\" ||\n\t\t!Number.isFinite(obj.frequencyHours)\n\t)\n\t\treturn { enabled: false };\n\treturn { enabled: obj.enabled, frequencyHours: obj.frequencyHours };\n}\n\nfunction readSyncConfig(raw: unknown): SyncConfig | null {\n\tif (typeof raw !== \"object\" || raw === null || Array.isArray(raw))\n\t\treturn null;\n\tconst obj = raw as Record<string, unknown>;\n\tconst listRaw = obj.allowlist;\n\tif (typeof listRaw !== \"object\" || listRaw === null) return null;\n\tconst list = listRaw as Record<string, unknown>;\n\treturn {\n\t\tallowlist: {\n\t\t\tmcpServers: readNameList(list.mcpServers),\n\t\t\tskills: readNameList(list.skills),\n\t\t\tsubagents: readNameList(list.subagents),\n\t\t\tslashCommands: readNameList(list.slashCommands),\n\t\t},\n\t\t// Anything other than an explicit `true` fails closed.\n\t\tpublishCost: obj.publishCost === true,\n\t\t// Absent means \"no stack resolved\" - an anonymous fetch, or a token bound\n\t\t// to nothing. Both fail closed to publishing no user-chosen names.\n\t\toptIns: readOptIns(obj.optIns),\n\t\t// Anything other than an explicit `true` keeps the names on the machine.\n\t\treviewKeptPrivate: obj.reviewKeptPrivate === true,\n\t\t// And anything other than an explicit `true` keeps the workflow section\n\t\t// off the wire. An old server that has never heard of the field answers\n\t\t// without it, and that reads as off - which is right: it has no place to\n\t\t// put the section either.\n\t\tpublishWorkflow: obj.publishWorkflow === true,\n\t\tstack: readStack(obj.stack),\n\t\tautoSync: readAutoSync(obj.autoSync),\n\t};\n}\n\n/**\n * Fetch the curated allowlist and the cost preference, falling back to the\n * bundled copy on any failure. Never throws - an unreachable aistack must not\n * prevent a local analysis from running, it must only narrow what could publish.\n */\nexport async function loadSyncConfig(opts: {\n\tbaseUrl: string;\n\t/**\n\t * Bearer for the authenticated half: `publishCost`, `publishWorkflow`,\n\t * `optIns`, `reviewKeptPrivate` and the destination stack. Absent, the server answers\n\t * with the anonymous fail-closed body - same allowlist, everything else off.\n\t */\n\ttoken?: string;\n\tfetchImpl?: typeof fetch;\n\ttimeoutMs?: number;\n}): Promise<LoadedSyncConfig> {\n\tconst doFetch = opts.fetchImpl ?? fetch;\n\ttry {\n\t\tconst res = await doFetch(`${opts.baseUrl}${SYNC_CONFIG_PATH}`, {\n\t\t\tsignal: AbortSignal.timeout(opts.timeoutMs ?? FETCH_TIMEOUT_MS),\n\t\t\theaders: {\n\t\t\t\tAccept: \"application/json\",\n\t\t\t\t...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),\n\t\t\t},\n\t\t});\n\t\tif (!res.ok) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: `sync-config returned ${res.status}`,\n\t\t\t};\n\t\t}\n\t\tconst parsed = readSyncConfig(await res.json());\n\t\tif (!parsed) {\n\t\t\treturn {\n\t\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\t\tsource: \"bundled\",\n\t\t\t\terror: \"sync-config response was not the expected shape\",\n\t\t\t};\n\t\t}\n\t\treturn { config: parsed, source: \"fetched\" };\n\t} catch (err) {\n\t\treturn {\n\t\t\tconfig: BUNDLED_SYNC_CONFIG,\n\t\t\tsource: \"bundled\",\n\t\t\terror: err instanceof Error ? err.message : \"sync-config fetch failed\",\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Filtering\n// ---------------------------------------------------------------------------\n\nexport type Atom = { name: string; count: number };\n\n/**\n * One observed name that will NOT publish, as the approve gate needs to render\n * it: the raw string, how often it ran, and the plugin it came from.\n *\n * Local only - this never enters the payload. It exists because the gate offers\n * every kept-private name as an explicit, default-off tick (#42 decision 1), and\n * it cannot offer what the analyzer does not hand back.\n */\nexport type KeptPrivateAtom = {\n\tname: string;\n\tcount: number;\n\t/** Plugin prefix, for the gate's grouped bulk tick. `null` when standalone. */\n\tgroup: string | null;\n};\n\nexport type FilteredAtoms = {\n\t/** Publishable names, ordered by count descending. */\n\tallowed: Atom[];\n\t/** The rest, with everything the gate needs to offer them as ticks. */\n\tkeptPrivate: KeptPrivateAtom[];\n\t/** How many DISTINCT names were kept private. */\n\twithheld: number;\n};\n\n/**\n * A server an MCP plugin provides is observed as `plugin_<plugin>_<server>`.\n *\n * That whole string is GENERATED by Claude Code - the user typed none of it -\n * which is a different class from a hand-edited `.mcp.json` alias. Strip the\n * wrapper before matching (#42 decision 5).\n *\n * The split takes the FIRST underscore-free segment as the plugin name. A plugin\n * whose own name carries an underscore therefore splits wrong, the inner segment\n * matches nothing, and the raw name stays kept private - the same direction\n * every other miss fails in.\n */\nconst PLUGIN_MCP_RE = /^plugin_([^_]+)_(.+)$/;\n\n/** `plugin:artifact` is the convention for a plugin's skills and subagents. */\nconst PLUGIN_PREFIX_RE = /^([^:\\s]+):(.+)$/;\n\n/**\n * The plugin a name came from, for the gate's grouped bulk tick.\n *\n * Grouping is a UI affordance only. What the gate STORES is every name in the\n * group, expanded (#42 decision 3): a stored `alp-river:*` would be a standing\n * grant to names that do not exist yet, and nobody can consent to a name they\n * have not thought of.\n */\nexport function pluginGroup(name: string): string | null {\n\treturn (\n\t\tPLUGIN_MCP_RE.exec(name)?.[1] ?? PLUGIN_PREFIX_RE.exec(name)?.[1] ?? null\n\t);\n}\n\nexport type FilterSets = {\n\t/** Curated list UNION this stack's opt-ins. A match here publishes verbatim. */\n\tpublishable: ReadonlySet<string>;\n\t/**\n\t * The curated list alone - the only target normalization may match.\n\t *\n\t * This is what makes the normalization safe to state in one line:\n\t * normalization can only ever emit a string that is already curated. The\n\t * blast radius of a bug in it is an already-vetted set, by construction.\n\t */\n\tcurated: ReadonlySet<string>;\n};\n\n/**\n * Resolve the name an atom would publish under, or `null` to keep it private.\n *\n * Raw match first, so a name the owner ticked publishes exactly as they saw it\n * at the gate. Only an unmatched name is normalized, and only against the\n * curated list.\n */\nfunction publishedName(name: string, sets: FilterSets): string | null {\n\tif (sets.publishable.has(name)) return name;\n\tconst inner = PLUGIN_MCP_RE.exec(name)?.[2];\n\tif (inner && sets.curated.has(inner)) return inner;\n\treturn null;\n}\n\n/**\n * Split observed atoms into what publishes and what stays on the machine.\n *\n * The withheld figure counts distinct names, not calls: it answers \"how much of\n * my inventory is not shown\", which is the honesty question, without leaking\n * how heavily any single kept-private thing is used.\n *\n * Counts are merged by PUBLISHED name, because normalization can map two\n * observed names onto one - a plugin-provided `chrome-devtools` and a directly\n * configured one both publish as `chrome-devtools`, and two rows with the same\n * name would double-count that server in the rendered inventory.\n */\nexport function filterAtoms(\n\tatoms: readonly Atom[],\n\tsets: FilterSets,\n): FilteredAtoms {\n\tconst merged = new Map<string, number>();\n\tconst keptPrivate: KeptPrivateAtom[] = [];\n\tfor (const atom of atoms) {\n\t\tconst published = publishedName(atom.name, sets);\n\t\tif (published === null) {\n\t\t\tkeptPrivate.push({\n\t\t\t\tname: atom.name,\n\t\t\t\tcount: atom.count,\n\t\t\t\tgroup: pluginGroup(atom.name),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tmerged.set(published, (merged.get(published) ?? 0) + atom.count);\n\t}\n\tconst allowed = [...merged].map(([name, count]) => ({ name, count }));\n\tallowed.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\tkeptPrivate.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n\treturn { allowed, keptPrivate, withheld: keptPrivate.length };\n}\n","// \"Is this harness alive?\" - the detection primitive behind every adapter's\n// `detect()` (#101, decided in #100).\n//\n// A directory that merely exists proves nothing: a Claude Code install from\n// months ago leaves its transcript root behind forever, and detection keyed on\n// that root scanned it, published a dead snapshot next to a live one, and asked\n// its owner to connect a harness they had stopped using.\n//\n// So detection asks the same question the scan asks: did this harness write\n// anything inside the rolling window? It answers with `stat` calls only -\n// nothing is opened, nothing is parsed. A live harness answers on the first\n// recent file it meets, which is usually the first file it meets. A dead\n// harness pays a full walk of stats to say no, which is the cost the old\n// `exists()` check saved and the reason the answer was wrong.\n//\n// Directory mtimes cannot prune this walk. `claude/scan.ts` verified why:\n// appending to a file does not move its parent directory's mtime, so a resumed\n// session writes in-window records into a directory that looks untouched.\n\nimport type { Dirent, Stats } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport type RecencyOptions = {\n\t/** Override the directory reader. Tests only. */\n\treaddirImpl?: (dir: string) => Promise<Dirent[]>;\n\t/** Override the stat call. Tests only. */\n\tstatImpl?: (file: string) => Promise<Stats>;\n};\n\n/**\n * True when any file under `roots` whose basename passes `matches` was modified\n * at or after `sinceMs`. Unreadable directories and files are silence, not an\n * error - the same fail-quiet rule the scanners hold, and for the same reason:\n * the error object carries the absolute path.\n */\nexport async function hasRecentFile(\n\troots: readonly string[],\n\tmatches: (basename: string) => boolean,\n\tsinceMs: number,\n\topts: RecencyOptions = {},\n): Promise<boolean> {\n\tconst readDir =\n\t\topts.readdirImpl ??\n\t\t((dir: string) => readdir(dir, { withFileTypes: true }));\n\tconst statFile = opts.statImpl ?? stat;\n\n\tconst seen = new Set<string>();\n\tconst pending: string[] = [...roots];\n\twhile (pending.length > 0) {\n\t\tconst dir = pending.pop() as string;\n\t\tif (seen.has(dir)) continue;\n\t\tseen.add(dir);\n\n\t\tlet entries: Dirent[];\n\t\ttry {\n\t\t\tentries = await readDir(dir);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const e of entries) {\n\t\t\tconst full = path.join(dir, e.name);\n\t\t\tif (e.isDirectory()) {\n\t\t\t\tpending.push(full);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!e.isFile() || !matches(e.name)) continue;\n\t\t\ttry {\n\t\t\t\tconst st = await statFile(full);\n\t\t\t\tif (st.mtimeMs >= sinceMs) return true;\n\t\t\t} catch {\n\t\t\t\t/* unreadable file - it proves nothing either way */\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n","// The daily unit of the workflow wire, and the fold that turns days into a window.\n//\n// Wayfinder ticket #285 (map #200). Ticket #277 moved the wire from one 30-day\n// section to per-day rows so the page can offer 30-day, 7-day and 24-hour\n// windows and a manual sync still builds a continuous series. This module is\n// the shape of one day and the arithmetic that adds days together.\n//\n// ONLY COMBINABLE ATOMS. A day carries counts, sums, maxes and bucket\n// histograms, never a median, a share or a mean: a share of one day cannot be\n// added to a share of another, and a median of medians is nothing. Every\n// figure the page prints is computed AFTER the fold, over the window's atoms,\n// by the row rules in this package.\n//\n// THE FOLD HAS THE DAY'S SHAPE. `WorkflowDay` and the window are the same type\n// with one exception (`dates`, the days the window holds), so a rule written\n// against a window reads a single day unchanged and the tests can fold a\n// fixture of one day and compare it with itself.\n//\n// A READING IS ONE MACHINE'S, PER DAY (ADR-0009). The fold adds days of ONE\n// machine. Nothing here merges two machines: the Git day carries no commit\n// identity, so two clones of one repository would count a shared commit twice.\n\nimport type { PhaseId } from \"./types.js\";\n\nexport const WORKFLOW_AGGREGATES_V2 = \"workflow-aggregates/v2\";\n\n/**\n * `workflow-aggregates/v3` adds the optional `context` block to a harness day\n * (#358). Every other atom is unchanged, so a v2 day folds beside a v3 day and\n * an old client keeps publishing v2 days the server stores as they are.\n */\nexport const WORKFLOW_AGGREGATES_V3 = \"workflow-aggregates/v3\";\n\n/** The bucket rule both histograms cite. A bump changes what a bucket index means. */\nexport const LOG_BUCKETS_V1 = \"log-buckets/v1\";\n\n/**\n * The half-octave rule the context histograms cite (#358). Token counts span\n * 1k to 1M, and a whole octave quotes a median up to 41% off; a half octave\n * keeps the bucket median within 20% of the value.\n */\nexport const LOG_BUCKETS_V2 = \"log-buckets/v2\";\n\nexport type PhaseTotals = Record<PhaseId, number>;\n\nexport const EMPTY_PHASE_TOTALS: Readonly<PhaseTotals> = Object.freeze({\n\tscout: 0,\n\tbuild: 0,\n\tverify: 0,\n\thandoff: 0,\n\tunknown: 0,\n});\n\n/**\n * One bucket of measured session length, and the session facts that fold with\n * it. The playbook splits its two tracks at the median session, and with no\n * session rows on the wire the split is a median over these buckets.\n *\n * `merged`, `verified`, `mergedVerified` and `openedWithScout` are session\n * COUNTS inside the bucket, so a share over any subset is a ratio of sums.\n */\nexport type SessionLengthBucket = {\n\t/** `logBucket(measured minutes)`. */\n\tbucket: number;\n\tsessions: number;\n\tphaseSec: PhaseTotals;\n\t/** Sessions whose shell ran `gh pr merge`. */\n\tmerged: number;\n\t/** Sessions holding at least one verify run. */\n\tverified: number;\n\t/** Sessions that are both. */\n\tmergedVerified: number;\n\t/** Sessions whose first classified event was scout. */\n\topenedWithScout: number;\n};\n\nexport type HarnessDay = {\n\tharness: string;\n\t/** Sessions that STARTED on this day. A session spanning midnight counts once. */\n\tsessions: number;\n\t/** Start-hour histogram, UTC. The page shifts it into the owner's local time. */\n\tstartHours: readonly { hourUtc: number; sessions: number }[];\n\t/**\n\t * The phase reading. Absent when the harness failed its gate over the sync\n\t * window: the rules left more than 20% of its measured time unclassified.\n\t */\n\tphase?: {\n\t\truleVersion: string;\n\t\tsessions: number;\n\t\tphaseSec: PhaseTotals;\n\t\tphaseEvents: PhaseTotals;\n\t\twaitingSec: number;\n\t\tidleSec: number;\n\t\t/** Sessions holding at least one verify event. */\n\t\tsessionsWithVerify: number;\n\t\t/** Sessions holding at least one handoff event. */\n\t\tsessionsWithHandoff: number;\n\t\tbucketRuleVersion: string;\n\t\tlengths: readonly SessionLengthBucket[];\n\t};\n\trouting?: {\n\t\tmain: readonly { model: string; tokens: number }[];\n\t\tsubagents: readonly { model: string; tokens: number }[];\n\t};\n\tdelegation?: {\n\t\tmainToolCalls: number;\n\t\tsubagentToolCalls: number;\n\t\t/** A max: the widest concurrent fan-out any one parent reached. */\n\t\twidestFanOut: number;\n\t\t/** A max: the most children any one parent had. */\n\t\tmostSubagents: number;\n\t};\n\t/** Event cells. The weekday is the day's own; it rides along so a fold needs no calendar. */\n\tactivity: readonly { weekdayUtc: number; hourUtc: number; events: number }[];\n\t/** Responses per effort level. Absent on a harness that records no effort. */\n\teffort?: readonly { level: EffortLevel; turns: number }[];\n\t/** Absent on a harness that records no thinking tokens. */\n\tthinking?: { thinkingTokens: number; responseTokens: number };\n\t/** Turn duration histogram, `logBucket(seconds)`. Absent when the harness records no duration. */\n\tturnDurations?: {\n\t\tbucketRuleVersion: string;\n\t\tbuckets: readonly { bucket: number; turns: number }[];\n\t};\n\t/** Turns that ended with a question back, over all turns. Absent without a question marker. */\n\tquestions?: { asked: number; turns: number };\n\t/** Absent on a harness without a built-in web search tool. */\n\twebSearches?: number;\n\t/**\n\t * Per-call context (#358): the tokens each API call carried in, bucketed\n\t * under `log-buckets/v2`. Absent on a day from a client that predates the\n\t * block or a harness that logs no per-call usage.\n\t */\n\tcontext?: ContextDay;\n};\n\n/**\n * One day of per-call context for one harness. Combinable atoms only.\n *\n * A CALL is one API response; the context is what the request carried in\n * (fresh input plus cache reads plus cache writes). Main and subagent calls\n * are two histograms because a subagent has its own context window and its\n * own first call. A FIRST CALL is the first call of a main session: its cache\n * read is the harness part (system prompt and tools, cached across sessions)\n * and its cache write plus fresh input is the instructions part (project\n * instructions, memory, skills, agents, the first prompt). The two sums and\n * the count give the fold a mean over first calls, which is the one figure\n * the reading needs from them.\n */\nexport type ContextDay = {\n\tbucketRuleVersion: string;\n\tcalls: {\n\t\tmain: readonly { bucket: number; calls: number }[];\n\t\tsubagents: readonly { bucket: number; calls: number }[];\n\t};\n\t/** First calls of main sessions that started this day, by context bucket. */\n\tfirstCalls: { main: readonly { bucket: number; sessions: number }[] };\n\t/** Sum over first calls of the cross-session cached prefix. */\n\tfirstCallHarnessTokens: number;\n\t/** Sum over first calls of the per-session part. */\n\tfirstCallInstructionsTokens: number;\n\tfirstCallCount: number;\n\t/** A max over every call of the day, main and subagent alike. */\n\tmaxContext: number;\n\t/** Compaction boundaries the harness logged on this day. */\n\tcompactions: number;\n\t/** The context window the harness logged (Codex). Absent when it logs none. */\n\twindow?: number;\n};\n\nexport type EffortLevel = \"low\" | \"medium\" | \"high\" | \"other\";\n\nexport const EFFORT_LEVELS: readonly EffortLevel[] = [\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"other\",\n];\n\n/** Map a harness's own effort string onto the four public levels. */\nexport function effortLevelOf(effort: string): EffortLevel {\n\tswitch (effort.toLowerCase()) {\n\t\tcase \"low\":\n\t\tcase \"minimal\":\n\t\t\treturn \"low\";\n\t\tcase \"medium\":\n\t\t\treturn \"medium\";\n\t\tcase \"high\":\n\t\tcase \"xhigh\":\n\t\tcase \"max\":\n\t\tcase \"ultra\":\n\t\t\treturn \"high\";\n\t\tdefault:\n\t\t\treturn \"other\";\n\t}\n}\n\nexport type GitDay = {\n\ttestFileRuleVersion: string;\n\tfileTypeRuleVersion: string;\n\tcommitSetRuleVersion: string;\n\tcommits: number;\n\t/** Commits whose author hour, on the machine's clock, falls between 23:00 and 03:00. */\n\tlateNightCommits: number;\n\tadditions: number;\n\tremovals: number;\n\t/** One entry per commit, for the log-scale strip. Order carries no meaning. */\n\tchangedLinesPerCommit: readonly number[];\n\ttestFileCommits: number;\n\tchangedLinesByExtension: readonly {\n\t\textension: string;\n\t\tchangedLines: number;\n\t}[];\n\twithheldExtensionLines: number;\n\t/** UTC cells, like the harness activity cells. */\n\tweekdayHourCells: readonly {\n\t\tweekdayUtc: number;\n\t\thourUtc: number;\n\t\tcommits: number;\n\t}[];\n};\n\n/** The three Git sums of one day, dated. What the mirrored bars draw. */\nexport type GitDayTotals = {\n\tdate: string;\n\tadditions: number;\n\tremovals: number;\n\tcommits: number;\n};\n\nexport type WorkflowDay = {\n\t/** The UTC date, `YYYY-MM-DD`. Sessions belong to the day they started. */\n\tdate: string;\n\tharnesses: readonly HarnessDay[];\n\tgit: GitDay;\n\t/**\n\t * Distinct project workspaces with a session that overlapped this day, across\n\t * every harness. Absent when no session touched a workspace.\n\t */\n\tparallelProjects?: number;\n};\n\n/**\n * A window: the fold of one machine's days.\n *\n * Same shape as a day, plus the dates it holds. A window over zero days is\n * `undefined` rather than a row of zeroes, so nothing downstream prints a\n * measurement nobody made.\n */\nexport type WorkflowWindow = Omit<WorkflowDay, \"date\"> & {\n\taggregateVersion: string;\n\tdates: readonly string[];\n\t/** Minutes east of UTC on the publishing machine. */\n\tutcOffsetMinutes?: number;\n\t/** The per-day parallel-project counts, for the median over days. */\n\tparallelProjectDays: readonly number[];\n\t/**\n\t * One Git entry per stored day, sorted by date, for the per-day picture\n\t * (#288). A derived list rather than the raw days: the page reads nothing\n\t * else per day, and the raw days would carry every harness atom with them.\n\t */\n\tgitDays: readonly GitDayTotals[];\n\t/** Days on which at least one harness recorded a web search count. */\n\twebSearchDays: number;\n};\n\n/**\n * The bucket index of a positive quantity on a base-2 log scale.\n *\n * Bucket 0 holds everything under 1, bucket k holds [2^(k-1), 2^k). A session\n * of 3 minutes lands in bucket 2, one of 90 minutes in bucket 7. The scale is\n * `log-buckets/v1`; the unit is the caller's (minutes for session length,\n * seconds for turn duration) and travels in the field name.\n */\nexport function logBucket(value: number): number {\n\tif (!(value >= 1)) return 0;\n\treturn Math.floor(Math.log2(value)) + 1;\n}\n\n/** The lower and upper bound of a bucket, in the caller's unit. */\nexport function bucketRange(bucket: number): { low: number; high: number } {\n\tif (bucket <= 0) return { low: 0, high: 1 };\n\treturn { low: 2 ** (bucket - 1), high: 2 ** bucket };\n}\n\n/**\n * The geometric middle of a bucket, which is where a value drawn from it is\n * quoted. Bucket 0 quotes as 0.5.\n */\nexport function bucketMid(bucket: number): number {\n\tconst { low, high } = bucketRange(bucket);\n\treturn Math.sqrt(Math.max(low, 0.25) * high);\n}\n\n/**\n * The median over a histogram: the bucket holding the middle item, quoted at\n * its geometric middle. `undefined` on an empty histogram.\n *\n * A median over buckets is what the wire allows (#285): the exact median needs\n * every value, and every value is what the wire no longer carries.\n */\nexport function medianBucket(\n\tbuckets: readonly { bucket: number; count: number }[],\n): number | undefined {\n\tconst total = buckets.reduce((sum, row) => sum + row.count, 0);\n\tif (total <= 0) return undefined;\n\tconst sorted = [...buckets].sort((a, b) => a.bucket - b.bucket);\n\tconst middle = (total + 1) / 2;\n\tlet seen = 0;\n\tfor (const row of sorted) {\n\t\tseen += row.count;\n\t\tif (seen >= middle) return row.bucket;\n\t}\n\treturn sorted[sorted.length - 1]?.bucket;\n}\n\n/**\n * The bucket index of a positive quantity on a half-octave log scale,\n * `log-buckets/v2`. Bucket 0 holds everything under 1, bucket k holds\n * [2^((k-1)/2), 2^(k/2)). A context of 50,000 tokens lands in bucket 32, one\n * of 200,000 in bucket 36.\n */\nexport function logBucketV2(value: number): number {\n\tif (!(value >= 1)) return 0;\n\treturn Math.floor(2 * Math.log2(value)) + 1;\n}\n\n/** The lower and upper bound of a `log-buckets/v2` bucket. */\nexport function bucketRangeV2(bucket: number): { low: number; high: number } {\n\tif (bucket <= 0) return { low: 0, high: 1 };\n\treturn { low: 2 ** ((bucket - 1) / 2), high: 2 ** (bucket / 2) };\n}\n\n/** The geometric middle of a `log-buckets/v2` bucket. Bucket 0 quotes as 0.5. */\nexport function bucketMidV2(bucket: number): number {\n\tconst { low, high } = bucketRangeV2(bucket);\n\treturn Math.sqrt(Math.max(low, 0.25) * high);\n}\n\n/**\n * The bucket holding the item at quantile `q` (0..1), counting from the\n * lowest bucket: the item of rank `ceil(q * total)`, at least 1. `undefined`\n * on an empty histogram. `medianBucket` keeps its own middle rule; this one\n * is for the tails (p90).\n */\nexport function quantileBucket(\n\tbuckets: readonly { bucket: number; count: number }[],\n\tq: number,\n): number | undefined {\n\tconst total = buckets.reduce((sum, row) => sum + row.count, 0);\n\tif (total <= 0) return undefined;\n\tconst sorted = [...buckets].sort((a, b) => a.bucket - b.bucket);\n\tconst rank = Math.min(total, Math.max(1, Math.ceil(q * total)));\n\tlet seen = 0;\n\tfor (const row of sorted) {\n\t\tseen += row.count;\n\t\tif (seen >= rank) return row.bucket;\n\t}\n\treturn sorted[sorted.length - 1]?.bucket;\n}\n\n/** The median of a plain list, or `undefined` on an empty one. */\nexport function median(values: readonly number[]): number | undefined {\n\tif (values.length === 0) return undefined;\n\tconst sorted = [...values].sort((a, b) => a - b);\n\tconst mid = Math.floor(sorted.length / 2);\n\tconst midValue = sorted[mid] as number;\n\treturn sorted.length % 2 === 0\n\t\t? ((sorted[mid - 1] as number) + midValue) / 2\n\t\t: midValue;\n}\n\nfunction addPhaseTotals(into: PhaseTotals, from: PhaseTotals): void {\n\tfor (const phase of Object.keys(into) as PhaseId[]) {\n\t\tinto[phase] += from[phase] ?? 0;\n\t}\n}\n\nfunction sumBy<T>(\n\trows: readonly T[],\n\tkey: (row: T) => string,\n\tadd: (into: T, from: T) => void,\n\tclone: (row: T) => T,\n): T[] {\n\tconst merged = new Map<string, T>();\n\tfor (const row of rows) {\n\t\tconst k = key(row);\n\t\tconst held = merged.get(k);\n\t\tif (held) add(held, row);\n\t\telse merged.set(k, clone(row));\n\t}\n\treturn [...merged.values()];\n}\n\nfunction foldCells<T extends { weekdayUtc: number; hourUtc: number }>(\n\trows: readonly T[],\n\tfield: \"events\" | \"commits\",\n): T[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => `${row.weekdayUtc}:${row.hourUtc}`,\n\t\t(into, from) => {\n\t\t\t(into as Record<string, number>)[field] += (\n\t\t\t\tfrom as Record<string, number>\n\t\t\t)[field] as number;\n\t\t},\n\t\t(row) => ({ ...row }),\n\t).sort((a, b) => a.weekdayUtc - b.weekdayUtc || a.hourUtc - b.hourUtc);\n}\n\nfunction foldModels(\n\trows: readonly { model: string; tokens: number }[],\n): { model: string; tokens: number }[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => row.model,\n\t\t(into, from) => {\n\t\t\tinto.tokens += from.tokens;\n\t\t},\n\t\t(row) => ({ ...row }),\n\t).sort((a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model));\n}\n\nfunction foldCountBuckets<K extends string>(\n\trows: readonly ({ bucket: number } & Record<K, number>)[],\n\tfield: K,\n): ({ bucket: number } & Record<K, number>)[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => String(row.bucket),\n\t\t(into, from) => {\n\t\t\t(into as Record<string, number>)[field] += from[field];\n\t\t},\n\t\t(row) => ({ ...row }),\n\t).sort((a, b) => a.bucket - b.bucket);\n}\n\n/**\n * Add context days together. Histograms merge by bucket, sums and counts add,\n * the max is a max, and the window is the LAST day's: the caller hands days\n * in date order, so the last one that logged a window is the latest reading.\n */\nexport function foldContextDays(days: readonly ContextDay[]): ContextDay {\n\tconst versions = [...new Set(days.map((d) => d.bucketRuleVersion))]\n\t\t.sort()\n\t\t.join(\" · \");\n\tconst windows = days\n\t\t.map((d) => d.window)\n\t\t.filter((w): w is number => w !== undefined);\n\tconst window = windows[windows.length - 1];\n\treturn {\n\t\tbucketRuleVersion: versions,\n\t\tcalls: {\n\t\t\tmain: foldCountBuckets(\n\t\t\t\tdays.flatMap((d) => d.calls.main),\n\t\t\t\t\"calls\",\n\t\t\t),\n\t\t\tsubagents: foldCountBuckets(\n\t\t\t\tdays.flatMap((d) => d.calls.subagents),\n\t\t\t\t\"calls\",\n\t\t\t),\n\t\t},\n\t\tfirstCalls: {\n\t\t\tmain: foldCountBuckets(\n\t\t\t\tdays.flatMap((d) => d.firstCalls.main),\n\t\t\t\t\"sessions\",\n\t\t\t),\n\t\t},\n\t\tfirstCallHarnessTokens: days.reduce(\n\t\t\t(sum, d) => sum + d.firstCallHarnessTokens,\n\t\t\t0,\n\t\t),\n\t\tfirstCallInstructionsTokens: days.reduce(\n\t\t\t(sum, d) => sum + d.firstCallInstructionsTokens,\n\t\t\t0,\n\t\t),\n\t\tfirstCallCount: days.reduce((sum, d) => sum + d.firstCallCount, 0),\n\t\tmaxContext: Math.max(0, ...days.map((d) => d.maxContext)),\n\t\tcompactions: days.reduce((sum, d) => sum + d.compactions, 0),\n\t\t...(window === undefined ? {} : { window }),\n\t};\n}\n\nfunction foldLengths(\n\trows: readonly SessionLengthBucket[],\n): SessionLengthBucket[] {\n\treturn sumBy(\n\t\trows,\n\t\t(row) => String(row.bucket),\n\t\t(into, from) => {\n\t\t\tinto.sessions += from.sessions;\n\t\t\taddPhaseTotals(into.phaseSec, from.phaseSec);\n\t\t\tinto.merged += from.merged;\n\t\t\tinto.verified += from.verified;\n\t\t\tinto.mergedVerified += from.mergedVerified;\n\t\t\tinto.openedWithScout += from.openedWithScout;\n\t\t},\n\t\t(row) => ({ ...row, phaseSec: { ...row.phaseSec } }),\n\t).sort((a, b) => a.bucket - b.bucket);\n}\n\n/**\n * Add one harness's days together.\n *\n * An optional block is present on the fold when ANY day carried it. A rule\n * version is the set of versions seen, joined with \" · \" when they differ, so a\n * window that straddles a rule bump says so rather than citing the newer rule\n * for days the older one classified.\n */\nexport function foldHarnessDays(days: readonly HarnessDay[]): HarnessDay {\n\tconst first = days[0];\n\tif (!first) throw new Error(\"foldHarnessDays needs at least one day\");\n\tconst versions = (values: readonly string[]): string =>\n\t\t[...new Set(values)].sort().join(\" · \");\n\n\tconst out: HarnessDay = {\n\t\tharness: first.harness,\n\t\tsessions: days.reduce((sum, day) => sum + day.sessions, 0),\n\t\tstartHours: sumBy(\n\t\t\tdays.flatMap((day) => day.startHours),\n\t\t\t(row) => String(row.hourUtc),\n\t\t\t(into, from) => {\n\t\t\t\tinto.sessions += from.sessions;\n\t\t\t},\n\t\t\t(row) => ({ ...row }),\n\t\t).sort((a, b) => a.hourUtc - b.hourUtc),\n\t\tactivity: foldCells(\n\t\t\tdays.flatMap((day) => day.activity),\n\t\t\t\"events\",\n\t\t),\n\t};\n\n\tconst phases = days.flatMap((day) => (day.phase ? [day.phase] : []));\n\tif (phases.length > 0) {\n\t\tconst phaseSec = { ...EMPTY_PHASE_TOTALS };\n\t\tconst phaseEvents = { ...EMPTY_PHASE_TOTALS };\n\t\tfor (const phase of phases) {\n\t\t\taddPhaseTotals(phaseSec, phase.phaseSec);\n\t\t\taddPhaseTotals(phaseEvents, phase.phaseEvents);\n\t\t}\n\t\tout.phase = {\n\t\t\truleVersion: versions(phases.map((phase) => phase.ruleVersion)),\n\t\t\tsessions: phases.reduce((sum, phase) => sum + phase.sessions, 0),\n\t\t\tphaseSec,\n\t\t\tphaseEvents,\n\t\t\twaitingSec: phases.reduce((sum, phase) => sum + phase.waitingSec, 0),\n\t\t\tidleSec: phases.reduce((sum, phase) => sum + phase.idleSec, 0),\n\t\t\tsessionsWithVerify: phases.reduce(\n\t\t\t\t(sum, phase) => sum + phase.sessionsWithVerify,\n\t\t\t\t0,\n\t\t\t),\n\t\t\tsessionsWithHandoff: phases.reduce(\n\t\t\t\t(sum, phase) => sum + phase.sessionsWithHandoff,\n\t\t\t\t0,\n\t\t\t),\n\t\t\tbucketRuleVersion: versions(\n\t\t\t\tphases.map((phase) => phase.bucketRuleVersion),\n\t\t\t),\n\t\t\tlengths: foldLengths(phases.flatMap((phase) => phase.lengths)),\n\t\t};\n\t}\n\n\tconst routings = days.flatMap((day) => (day.routing ? [day.routing] : []));\n\tif (routings.length > 0) {\n\t\tout.routing = {\n\t\t\tmain: foldModels(routings.flatMap((routing) => routing.main)),\n\t\t\tsubagents: foldModels(routings.flatMap((routing) => routing.subagents)),\n\t\t};\n\t}\n\n\tconst delegations = days.flatMap((day) =>\n\t\tday.delegation ? [day.delegation] : [],\n\t);\n\tif (delegations.length > 0) {\n\t\tout.delegation = {\n\t\t\tmainToolCalls: delegations.reduce((sum, d) => sum + d.mainToolCalls, 0),\n\t\t\tsubagentToolCalls: delegations.reduce(\n\t\t\t\t(sum, d) => sum + d.subagentToolCalls,\n\t\t\t\t0,\n\t\t\t),\n\t\t\twidestFanOut: Math.max(...delegations.map((d) => d.widestFanOut)),\n\t\t\tmostSubagents: Math.max(...delegations.map((d) => d.mostSubagents)),\n\t\t};\n\t}\n\n\tconst efforts = days.flatMap((day) => day.effort ?? []);\n\tif (days.some((day) => day.effort)) {\n\t\tout.effort = sumBy(\n\t\t\tefforts,\n\t\t\t(row) => row.level,\n\t\t\t(into, from) => {\n\t\t\t\tinto.turns += from.turns;\n\t\t\t},\n\t\t\t(row) => ({ ...row }),\n\t\t).sort(\n\t\t\t(a, b) => EFFORT_LEVELS.indexOf(a.level) - EFFORT_LEVELS.indexOf(b.level),\n\t\t);\n\t}\n\n\tconst thinkings = days.flatMap((day) => (day.thinking ? [day.thinking] : []));\n\tif (thinkings.length > 0) {\n\t\tout.thinking = {\n\t\t\tthinkingTokens: thinkings.reduce((sum, t) => sum + t.thinkingTokens, 0),\n\t\t\tresponseTokens: thinkings.reduce((sum, t) => sum + t.responseTokens, 0),\n\t\t};\n\t}\n\n\tconst durations = days.flatMap((day) =>\n\t\tday.turnDurations ? [day.turnDurations] : [],\n\t);\n\tif (durations.length > 0) {\n\t\tout.turnDurations = {\n\t\t\tbucketRuleVersion: versions(durations.map((d) => d.bucketRuleVersion)),\n\t\t\tbuckets: sumBy(\n\t\t\t\tdurations.flatMap((d) => d.buckets),\n\t\t\t\t(row) => String(row.bucket),\n\t\t\t\t(into, from) => {\n\t\t\t\t\tinto.turns += from.turns;\n\t\t\t\t},\n\t\t\t\t(row) => ({ ...row }),\n\t\t\t).sort((a, b) => a.bucket - b.bucket),\n\t\t};\n\t}\n\n\tconst questions = days.flatMap((day) =>\n\t\tday.questions ? [day.questions] : [],\n\t);\n\tif (questions.length > 0) {\n\t\tout.questions = {\n\t\t\tasked: questions.reduce((sum, q) => sum + q.asked, 0),\n\t\t\tturns: questions.reduce((sum, q) => sum + q.turns, 0),\n\t\t};\n\t}\n\n\tif (days.some((day) => day.webSearches !== undefined)) {\n\t\tout.webSearches = days.reduce(\n\t\t\t(sum, day) => sum + (day.webSearches ?? 0),\n\t\t\t0,\n\t\t);\n\t}\n\n\tconst contexts = days.flatMap((day) => (day.context ? [day.context] : []));\n\tif (contexts.length > 0) out.context = foldContextDays(contexts);\n\n\treturn out;\n}\n\n/** Add Git days together. Rule versions join as a set, like the harness fold. */\n/**\n * The most per-commit entries a fold keeps. One entry per commit adds up: a\n * 30-day window on a busy machine passed 17k commits, and Convex refuses an\n * array over 8192 entries. The strip that draws them shows a distribution,\n * so a quantile sample of the sorted values is the same picture.\n */\nexport const MAX_CHANGED_LINES_PER_COMMIT = 4096;\n\n/**\n * At most `max` values, evenly spaced through the SORTED input so every\n * quantile (the median included) survives the cut. Returns the values sorted.\n */\nfunction sampleSorted(values: readonly number[], max: number): number[] {\n\tconst sorted = [...values].sort((a, b) => a - b);\n\tif (sorted.length <= max) return sorted;\n\tconst out: number[] = [];\n\tfor (let i = 0; i < max; i++) {\n\t\tout.push(\n\t\t\tsorted[Math.floor((i * (sorted.length - 1)) / (max - 1))] as number,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport function foldGitDays(days: readonly GitDay[]): GitDay {\n\tconst versions = (values: readonly string[]): string =>\n\t\t[...new Set(values)].sort().join(\" · \");\n\treturn {\n\t\ttestFileRuleVersion: versions(days.map((d) => d.testFileRuleVersion)),\n\t\tfileTypeRuleVersion: versions(days.map((d) => d.fileTypeRuleVersion)),\n\t\tcommitSetRuleVersion: versions(days.map((d) => d.commitSetRuleVersion)),\n\t\tcommits: days.reduce((sum, d) => sum + d.commits, 0),\n\t\tlateNightCommits: days.reduce((sum, d) => sum + d.lateNightCommits, 0),\n\t\tadditions: days.reduce((sum, d) => sum + d.additions, 0),\n\t\tremovals: days.reduce((sum, d) => sum + d.removals, 0),\n\t\tchangedLinesPerCommit: sampleSorted(\n\t\t\tdays.flatMap((d) => [...d.changedLinesPerCommit]),\n\t\t\tMAX_CHANGED_LINES_PER_COMMIT,\n\t\t),\n\t\ttestFileCommits: days.reduce((sum, d) => sum + d.testFileCommits, 0),\n\t\tchangedLinesByExtension: sumBy(\n\t\t\tdays.flatMap((d) => d.changedLinesByExtension),\n\t\t\t(row) => row.extension,\n\t\t\t(into, from) => {\n\t\t\t\tinto.changedLines += from.changedLines;\n\t\t\t},\n\t\t\t(row) => ({ ...row }),\n\t\t).sort((a, b) => a.extension.localeCompare(b.extension)),\n\t\twithheldExtensionLines: days.reduce(\n\t\t\t(sum, d) => sum + d.withheldExtensionLines,\n\t\t\t0,\n\t\t),\n\t\tweekdayHourCells: foldCells(\n\t\t\tdays.flatMap((d) => d.weekdayHourCells),\n\t\t\t\"commits\",\n\t\t),\n\t};\n}\n\nexport type FoldOptions = {\n\taggregateVersion: string;\n\tutcOffsetMinutes?: number;\n};\n\n/**\n * Fold one machine's days into a window. `undefined` when there are no days.\n *\n * Days are keyed by date and the caller has already replaced a re-synced day,\n * so two entries with one date here would be a bug upstream; the fold takes\n * them as they come rather than guessing which is newer.\n */\nexport function foldWorkflowDays(\n\tdays: readonly WorkflowDay[],\n\toptions: FoldOptions,\n): WorkflowWindow | undefined {\n\tif (days.length === 0) return undefined;\n\t// Date order, so a \"latest\" inside the harness fold (the logged context\n\t// window) is the latest day's and not the last row's.\n\tconst dated = [...days].sort((a, b) => a.date.localeCompare(b.date));\n\tconst byHarness = new Map<string, HarnessDay[]>();\n\tfor (const day of dated) {\n\t\tfor (const harness of day.harnesses) {\n\t\t\tconst held = byHarness.get(harness.harness) ?? [];\n\t\t\theld.push(harness);\n\t\t\tbyHarness.set(harness.harness, held);\n\t\t}\n\t}\n\tconst parallelProjectDays = days.flatMap((day) =>\n\t\tday.parallelProjects === undefined ? [] : [day.parallelProjects],\n\t);\n\tconst webSearchDays = days.filter((day) =>\n\t\tday.harnesses.some((harness) => harness.webSearches !== undefined),\n\t).length;\n\treturn {\n\t\taggregateVersion: options.aggregateVersion,\n\t\t...(options.utcOffsetMinutes === undefined\n\t\t\t? {}\n\t\t\t: { utcOffsetMinutes: options.utcOffsetMinutes }),\n\t\tdates: [...new Set(days.map((day) => day.date))].sort(),\n\t\tharnesses: [...byHarness.values()]\n\t\t\t.map(foldHarnessDays)\n\t\t\t.sort((a, b) => a.harness.localeCompare(b.harness)),\n\t\tgit: foldGitDays(days.map((day) => day.git)),\n\t\t...(parallelProjectDays.length === 0\n\t\t\t? {}\n\t\t\t: { parallelProjects: Math.max(...parallelProjectDays) }),\n\t\tparallelProjectDays,\n\t\tgitDays: [...days]\n\t\t\t.sort((a, b) => a.date.localeCompare(b.date))\n\t\t\t.map((day) => ({\n\t\t\t\tdate: day.date,\n\t\t\t\tadditions: day.git.additions,\n\t\t\t\tremovals: day.git.removals,\n\t\t\t\tcommits: day.git.commits,\n\t\t\t})),\n\t\twebSearchDays,\n\t};\n}\n","// One folded workflow window, and the facts derived from it.\n//\n// Wayfinder ticket #218 (map #200), reshaped by #285: the wire is per-day rows\n// now (`daily.ts`), and a reading is the FOLD of one machine's days over a\n// window. Everything below reads the fold; nothing reads a day.\n//\n// A READING IS ONE MACHINE'S, PER DAY (ADR-0009). Every derivation is scoped to\n// a single machine's window, so nothing here has to answer what two machines'\n// figures would mean together.\n\nimport type { HarnessDay, PhaseTotals, WorkflowWindow } from \"./daily.js\";\nimport type { PhaseId } from \"./types.js\";\n\nexport type WorkflowReading = WorkflowWindow;\nexport type WorkflowHarnessReading = HarnessDay;\n\n/**\n * The kit's inputs, which are the only component fact that does NOT live in the\n * workflow wire: skills and MCP servers are inventory, and inventory travels in\n * the measured payload. One entry per harness, already name-filtered on the\n * machine.\n */\nexport type KitReading = readonly {\n\tharness: string;\n\tskills: readonly { name: string; callShare: number }[];\n\tmcpServers: readonly { name: string; callShare: number }[];\n}[];\n\nconst EMPTY_TOTALS: PhaseTotals = {\n\tscout: 0,\n\tbuild: 0,\n\tverify: 0,\n\thandoff: 0,\n\tunknown: 0,\n};\n\n/** Every harness that shipped a phase reading, i.e. passed its own gate. */\nexport function playbookHarnesses(\n\treading: WorkflowReading,\n): WorkflowHarnessReading[] {\n\treturn reading.harnesses.filter((harness) => harness.phase !== undefined);\n}\n\n/** Measured seconds per phase, summed over the harnesses that shipped a phase reading. */\nexport function totalPhaseSec(reading: WorkflowReading): PhaseTotals {\n\tconst totals = { ...EMPTY_TOTALS };\n\tfor (const harness of playbookHarnesses(reading)) {\n\t\tfor (const phase of Object.keys(totals) as PhaseId[]) {\n\t\t\ttotals[phase] += harness.phase?.phaseSec[phase] ?? 0;\n\t\t}\n\t}\n\treturn totals;\n}\n\n/** Share of TOTAL measured time per phase, `unknown` included, summing to 1. */\nexport function phaseShare(reading: WorkflowReading): PhaseTotals | undefined {\n\tconst totals = totalPhaseSec(reading);\n\tconst measured = Object.values(totals).reduce((sum, sec) => sum + sec, 0);\n\tif (measured <= 0) return undefined;\n\tconst shares = { ...totals };\n\tfor (const phase of Object.keys(totals) as PhaseId[]) {\n\t\tshares[phase] = totals[phase] / measured;\n\t}\n\treturn shares;\n}\n\n/**\n * The unknown share of one harness's measured time over the window.\n *\n * Derived here rather than carried: a share cannot fold, so the day ships the\n * seconds and the window computes the ratio.\n */\nexport function unknownShareOf(harness: WorkflowHarnessReading): number {\n\tconst phase = harness.phase;\n\tif (!phase) return 0;\n\tconst measured = Object.values(phase.phaseSec).reduce((a, b) => a + b, 0);\n\treturn measured <= 0 ? 0 : phase.phaseSec.unknown / measured;\n}\n\n/** Sessions across every harness that shipped a phase reading. */\nexport function phaseSessionCount(reading: WorkflowReading): number {\n\treturn playbookHarnesses(reading).reduce(\n\t\t(sum, harness) => sum + (harness.phase?.sessions ?? 0),\n\t\t0,\n\t);\n}\n\n/**\n * Share of sessions holding at least one event of `phase`.\n *\n * The denominator is the PLAYBOOK sessions, not every synced session: a harness\n * held back by the gate ships no phase reading at all, so it can neither raise\n * nor lower this share. The scope line above it counts every session, which is\n * the number a reader doing arithmetic would use, and the two denominators are\n * why the lead names its scope before it prints a share.\n */\nexport function sessionShareWith(\n\treading: WorkflowReading,\n\tphase: \"verify\" | \"handoff\",\n): number | undefined {\n\tconst sessions = phaseSessionCount(reading);\n\tif (sessions === 0) return undefined;\n\tconst key = phase === \"verify\" ? \"sessionsWithVerify\" : \"sessionsWithHandoff\";\n\tconst hits = playbookHarnesses(reading).reduce(\n\t\t(sum, harness) => sum + (harness.phase?.[key] ?? 0),\n\t\t0,\n\t);\n\treturn hits / sessions;\n}\n\n/** The start-hour histogram over every harness, in UTC. */\nexport function startHoursUtc(reading: WorkflowReading): Map<number, number> {\n\tconst counts = new Map<number, number>();\n\tfor (const harness of reading.harnesses) {\n\t\tfor (const cell of harness.startHours) {\n\t\t\tcounts.set(cell.hourUtc, (counts.get(cell.hourUtc) ?? 0) + cell.sessions);\n\t\t}\n\t}\n\treturn counts;\n}\n\n/** A UTC hour on the owner's clock. */\nexport function ownerLocalHour(hourUtc: number, offsetMinutes: number): number {\n\treturn Math.floor(((((hourUtc * 60 + offsetMinutes) / 60) % 24) + 24) % 24);\n}\n\n/**\n * The hour most sessions start, in the OWNER's local time.\n *\n * Undefined without an offset. A reader's own clock would put a stranger's habit\n * at the wrong hour and describe nobody (spec), and UTC would do the same to\n * every owner outside London.\n */\nexport function modalStartHour(reading: WorkflowReading): number | undefined {\n\tconst offsetMinutes = reading.utcOffsetMinutes;\n\tif (offsetMinutes === undefined) return undefined;\n\tconst counts = new Map<number, number>();\n\tfor (const [hourUtc, sessions] of startHoursUtc(reading)) {\n\t\tconst hour = ownerLocalHour(hourUtc, offsetMinutes);\n\t\tcounts.set(hour, (counts.get(hour) ?? 0) + sessions);\n\t}\n\tif (counts.size === 0) return undefined;\n\t// Ties go to the earlier hour, so the same reading always names the same one.\n\treturn [...counts.entries()].sort(\n\t\t(a, b) => b[1] - a[1] || a[0] - b[0],\n\t)[0]?.[0];\n}\n\n/** Distinct phase rule versions in this reading, in the order the page should print them. */\nexport function phaseRuleVersions(reading: WorkflowReading): string[] {\n\treturn [\n\t\t...new Set(\n\t\t\tplaybookHarnesses(reading).flatMap((harness) =>\n\t\t\t\t(harness.phase?.ruleVersion ?? \"\").split(\" · \").filter(Boolean),\n\t\t\t),\n\t\t),\n\t].sort();\n}\n\n/**\n * True when one reading carries aggregates from more than one phase rule set.\n *\n * \"A rule-set bump reclassifies old sessions from local raw records at the next\n * sync. A session whose raw records are gone keeps its old aggregate, and the\n * page shows a mixed-version tag\" (spec). With daily rows the same thing happens\n * to a window that straddles a bump: the older days keep the older rule.\n */\nexport function hasMixedRuleVersions(reading: WorkflowReading): boolean {\n\treturn phaseRuleVersions(reading).length > 1;\n}\n\nexport type LeadFactsInput = {\n\treading: WorkflowReading;\n\t/** Every synced session on this machine, including harnesses held back by the gate. */\n\tsessionCount: number;\n\t/** Every synced harness on this machine, for the same reason. */\n\tharnessCount: number;\n};\n\n/**\n * The five figures `lead-templates/v1` prints, derived from one window.\n *\n * Absent inputs stay absent: the lead drops a sentence it cannot fill, and this\n * function never substitutes a default for a measurement that does not exist.\n */\nexport function buildLeadFacts(input: LeadFactsInput): {\n\tsessionCount: number;\n\tharnessCount: number;\n\tplaybookHarnessCount: number;\n\tphaseShare?: PhaseTotals;\n\tverifySessionShare?: number;\n\thandoffSessionShare?: number;\n\tmodalStartHourOwnerLocal?: number;\n\truleVersion?: string;\n} {\n\tconst { reading, sessionCount, harnessCount } = input;\n\tconst versions = phaseRuleVersions(reading);\n\tconst shares = phaseShare(reading);\n\tconst verify = sessionShareWith(reading, \"verify\");\n\tconst handoff = sessionShareWith(reading, \"handoff\");\n\tconst hour = modalStartHour(reading);\n\treturn {\n\t\tsessionCount,\n\t\tharnessCount,\n\t\tplaybookHarnessCount: playbookHarnesses(reading).length,\n\t\t...(shares ? { phaseShare: shares } : {}),\n\t\t...(verify === undefined ? {} : { verifySessionShare: verify }),\n\t\t...(handoff === undefined ? {} : { handoffSessionShare: handoff }),\n\t\t...(hour === undefined ? {} : { modalStartHourOwnerLocal: hour }),\n\t\t// Mixed versions print as the set. One reading classified by two rule sets\n\t\t// has no single rule id to cite, and citing the newer one would claim the\n\t\t// older sessions were reclassified when they were not.\n\t\t...(versions.length === 0 ? {} : { ruleVersion: versions.join(\" · \") }),\n\t};\n}\n","// The versioned component rule pool: `component-rules/v2`.\n//\n// Wayfinder ticket #218 (map #200) declared v1 so a component could compete\n// for a podium slot beside a pool metric. Ticket #277 took fit off the page,\n// and #285 folded the wire into windows, so v2 is a smaller claim: each\n// component names ONE headline figure over the folded window, with a band the\n// API carries and nothing ranks by. The order on the page is fixed\n// (`workflowRows.ts`).\n//\n// A COMPONENT RULE MEASURES NOTHING NEW. Every value below is arithmetic over\n// atoms the machine already shipped, which keeps the CLI the only source of\n// measured atoms.\n//\n// BAND VALUES ARE DEFAULTS, NOT PROVEN DATA, the caveat `metric-rules/v2`\n// carries.\n\nimport { bucketMid, medianBucket } from \"./daily.js\";\nimport type { MetricUnit } from \"./metricRules.js\";\nimport type { KitReading, WorkflowReading } from \"./reading.js\";\nimport { modalStartHour, playbookHarnesses } from \"./reading.js\";\n\nexport const COMPONENT_RULES_V2 = \"component-rules/v2\";\n\nexport type ComponentInput = {\n\treading: WorkflowReading;\n\t/** Inventory for the same machine. Absent when no payload carried one. */\n\tkit?: KitReading;\n};\n\nexport type ComponentRule = {\n\tid: string;\n\tversion: string;\n\t/** Sentence fragment completing \"<value> <label>\", like a metric rule's. */\n\tlabel: string;\n\tunit: MetricUnit;\n\tband: { low: number; high: number };\n\t/** The measurement, or `undefined` when this reading cannot support the row. */\n\tevaluate: (input: ComponentInput) => number | undefined;\n\t/** Share of the machine's synced harnesses the row counts, 0..1. */\n\tcoverage: (input: ComponentInput) => number;\n};\n\n/** Git history counts every synced harness, whatever the harness itself records (spec). */\nconst gitCoverage = (): number => 1;\n\nfunction harnessShare(\n\tinput: ComponentInput,\n\tcounts: (input: ComponentInput) => number,\n): number {\n\tconst synced = input.reading.harnesses.length;\n\tif (synced === 0) return 0;\n\treturn counts(input) / synced;\n}\n\nfunction topShare(entries: readonly { value: number }[]): number | undefined {\n\tconst total = entries.reduce((sum, entry) => sum + entry.value, 0);\n\tif (total <= 0) return undefined;\n\tconst top = Math.max(...entries.map((entry) => entry.value));\n\treturn top / total;\n}\n\nexport const COMPONENT_RULES: readonly ComponentRule[] = [\n\t{\n\t\tid: \"activity-heatmap\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of events fall in the three busiest hours of the day\",\n\t\tunit: \"share\",\n\t\t// Three of twenty-four hours is an eighth of the clock. A day spread evenly\n\t\t// lands near it; a night owl runs far above it.\n\t\tband: { low: 0.125, high: 0.35 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst byHour = new Map<number, number>();\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tfor (const cell of harness.activity) {\n\t\t\t\t\tbyHour.set(\n\t\t\t\t\t\tcell.hourUtc,\n\t\t\t\t\t\t(byHour.get(cell.hourUtc) ?? 0) + cell.events,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst total = [...byHour.values()].reduce((sum, n) => sum + n, 0);\n\t\t\tif (total <= 0) return undefined;\n\t\t\tconst busiest = [...byHour.values()].sort((a, b) => b - a).slice(0, 3);\n\t\t\treturn busiest.reduce((sum, n) => sum + n, 0) / total;\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.activity.length > 0)\n\t\t\t\t\t\t.length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"start-hours\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"is the most common start hour\",\n\t\tunit: \"hour\",\n\t\t// A band on a clock face means little; the row is never ranked, and the\n\t\t// figure is a position rather than a size. Kept for shape.\n\t\tband: { low: 9, high: 18 },\n\t\tevaluate: ({ reading }) => modalStartHour(reading),\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.startHours.length > 0)\n\t\t\t\t\t\t.length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"phase-playbook\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"median measured session\",\n\t\tunit: \"minutes\",\n\t\tband: { low: 10, high: 60 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst bucket = medianBucket(\n\t\t\t\tplaybookHarnesses(reading).flatMap((harness) =>\n\t\t\t\t\t(harness.phase?.lengths ?? []).map((row) => ({\n\t\t\t\t\t\tbucket: row.bucket,\n\t\t\t\t\t\tcount: row.sessions,\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn bucket === undefined ? undefined : bucketMid(bucket);\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(input, ({ reading }) => playbookHarnesses(reading).length),\n\t},\n\t{\n\t\tid: \"git-ledger\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of changed lines are removals\",\n\t\tunit: \"share\",\n\t\t// Most work adds more than it takes away. A ledger that removes as much as\n\t\t// it adds is the surprising one, and so is one that never removes.\n\t\tband: { low: 0.15, high: 0.35 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst changed = reading.git.additions + reading.git.removals;\n\t\t\treturn changed > 0 ? reading.git.removals / changed : undefined;\n\t\t},\n\t\tcoverage: gitCoverage,\n\t},\n\t{\n\t\tid: \"coding-languages\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of changed lines are one file type\",\n\t\tunit: \"share\",\n\t\tband: { low: 0.35, high: 0.7 },\n\t\tevaluate: ({ reading }) => {\n\t\t\t// The withheld lines are a real bucket, not a rounding loss: they belong\n\t\t\t// in the denominator, or a stack whose top language is unapproved would\n\t\t\t// read as more concentrated than it is.\n\t\t\tconst named = reading.git.changedLinesByExtension.map((row) => ({\n\t\t\t\tvalue: row.changedLines,\n\t\t\t}));\n\t\t\tconst total =\n\t\t\t\tnamed.reduce((sum, row) => sum + row.value, 0) +\n\t\t\t\treading.git.withheldExtensionLines;\n\t\t\tif (total <= 0 || named.length === 0) return undefined;\n\t\t\treturn Math.max(...named.map((row) => row.value)) / total;\n\t\t},\n\t\tcoverage: gitCoverage,\n\t},\n\t{\n\t\tid: \"kit\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of skill and MCP calls go to one artifact\",\n\t\tunit: \"share\",\n\t\tband: { low: 0.15, high: 0.4 },\n\t\tevaluate: ({ kit }) => {\n\t\t\tif (!kit) return undefined;\n\t\t\tconst byName = new Map<string, number>();\n\t\t\tfor (const harness of kit) {\n\t\t\t\tfor (const atom of [...harness.skills, ...harness.mcpServers]) {\n\t\t\t\t\tbyName.set(atom.name, (byName.get(atom.name) ?? 0) + atom.callShare);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn topShare([...byName.values()].map((share) => ({ value: share })));\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ kit }) =>\n\t\t\t\t\t(kit ?? []).filter(\n\t\t\t\t\t\t(harness) =>\n\t\t\t\t\t\t\tharness.skills.length > 0 || harness.mcpServers.length > 0,\n\t\t\t\t\t).length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"model-routing\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of main-loop tokens run on one model\",\n\t\tunit: \"share\",\n\t\tband: { low: 0.4, high: 0.85 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tconst main = reading.harnesses.flatMap((harness) => [\n\t\t\t\t...(harness.routing?.main ?? []),\n\t\t\t]);\n\t\t\tconst byModel = new Map<string, number>();\n\t\t\tfor (const row of main) {\n\t\t\t\tbyModel.set(row.model, (byModel.get(row.model) ?? 0) + row.tokens);\n\t\t\t}\n\t\t\treturn topShare(\n\t\t\t\t[...byModel.values()].map((tokens) => ({ value: tokens })),\n\t\t\t);\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.routing).length,\n\t\t\t),\n\t},\n\t{\n\t\tid: \"delegation\",\n\t\tversion: COMPONENT_RULES_V2,\n\t\tlabel: \"of tool calls run inside a subagent\",\n\t\tunit: \"share\",\n\t\tband: { low: 0, high: 0.3 },\n\t\tevaluate: ({ reading }) => {\n\t\t\tlet main = 0;\n\t\t\tlet subagents = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tmain += harness.delegation?.mainToolCalls ?? 0;\n\t\t\t\tsubagents += harness.delegation?.subagentToolCalls ?? 0;\n\t\t\t}\n\t\t\tconst total = main + subagents;\n\t\t\treturn total > 0 ? subagents / total : undefined;\n\t\t},\n\t\tcoverage: (input) =>\n\t\t\tharnessShare(\n\t\t\t\tinput,\n\t\t\t\t({ reading }) =>\n\t\t\t\t\treading.harnesses.filter((harness) => harness.delegation).length,\n\t\t\t),\n\t},\n];\n\nexport function componentRule(id: string): ComponentRule | undefined {\n\treturn COMPONENT_RULES.find((rule) => rule.id === id);\n}\n","// The versioned metric rule pool: `metric-rules/v2`.\n//\n// Wayfinder ticket #214 (map #200) declared v1 over per-session facts the CLI\n// reduced on the machine. Ticket #285 moved the wire to daily rows of\n// combinable atoms and moved EVERY evaluation to the server, over the folded\n// window (`daily.ts`). That is v2: the same pool, minus the two rows #277\n// dropped (model switches, effort switches), with the effort and turn rows\n// reshaped to what a histogram can say.\n//\n// A rule declares what it measures (`evaluate`), which harnesses can supply\n// the signal (`counts`, for coverage and the coverage tag), and the typical\n// band the value sits against. The band is DATA THE PAGE DOES NOT RANK BY\n// (#277): fit stays in the API as a number, and the order on the page is the\n// fixed editorial one in `workflowRows.ts`.\n//\n// BAND VALUES ARE DEFAULTS, NOT PROVEN DATA. No calibration run has happened,\n// and a rule version bump corrects one once real synced readings are in.\n\nimport type { HarnessDay } from \"./daily.js\";\nimport { bucketMid, median, medianBucket } from \"./daily.js\";\nimport type { WorkflowReading } from \"./reading.js\";\n\nexport const METRIC_RULES_V2 = \"metric-rules/v2\";\n\nexport type MetricUnit = \"share\" | \"count\" | \"minutes\" | \"hour\";\n\n/** The typical range surprise is measured against, in the metric's own unit. */\nexport type Band = { low: number; high: number };\n\nexport type MetricRule = {\n\tid: string;\n\tversion: string;\n\t/** Sentence fragment completing \"<value> <label>\", e.g. \"of commits land between 23:00 and 03:00\". */\n\tlabel: string;\n\tkind: \"exact\" | \"proxy\";\n\tunit: MetricUnit;\n\tband: Band;\n\t/**\n\t * True for a harness whose fold carries this metric's signal, or `\"all\"`\n\t * when the signal comes from Git history, which counts every synced harness\n\t * regardless of what the harness itself records (spec, \"Fit\").\n\t */\n\tcounts: ((harness: HarnessDay) => boolean) | \"all\";\n\tevaluate: (reading: WorkflowReading) => number | undefined;\n};\n\nexport const METRIC_RULES: readonly MetricRule[] = [\n\t{\n\t\tid: \"late-night-commits\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of commits land between 23:00 and 03:00\",\n\t\tkind: \"exact\",\n\t\tunit: \"share\",\n\t\tcounts: \"all\",\n\t\tband: { low: 0, high: 0.15 },\n\t\tevaluate: (reading) => {\n\t\t\tconst git = reading.git;\n\t\t\tif (git.commits === 0) return undefined;\n\t\t\treturn git.lateNightCommits / git.commits;\n\t\t},\n\t},\n\t{\n\t\tid: \"parallel-projects\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"projects run in parallel on a median active day\",\n\t\tkind: \"proxy\",\n\t\tunit: \"count\",\n\t\tcounts: \"all\",\n\t\tband: { low: 1, high: 1.5 },\n\t\tevaluate: (reading) => median(reading.parallelProjectDays),\n\t},\n\t{\n\t\tid: \"thinking-share\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of response tokens are thinking\",\n\t\tkind: \"proxy\",\n\t\tunit: \"share\",\n\t\tcounts: (harness) => harness.thinking !== undefined,\n\t\tband: { low: 0.1, high: 0.3 },\n\t\tevaluate: (reading) => {\n\t\t\tlet thinking = 0;\n\t\t\tlet response = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tthinking += harness.thinking?.thinkingTokens ?? 0;\n\t\t\t\tresponse += harness.thinking?.responseTokens ?? 0;\n\t\t\t}\n\t\t\treturn response > 0 ? thinking / response : undefined;\n\t\t},\n\t},\n\t{\n\t\tid: \"effort-levels\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of turns run at high effort\",\n\t\tkind: \"exact\",\n\t\tunit: \"share\",\n\t\tcounts: (harness) => harness.effort !== undefined,\n\t\tband: { low: 0.2, high: 0.5 },\n\t\tevaluate: (reading) => {\n\t\t\tlet high = 0;\n\t\t\tlet total = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tfor (const row of harness.effort ?? []) {\n\t\t\t\t\ttotal += row.turns;\n\t\t\t\t\tif (row.level === \"high\") high += row.turns;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total > 0 ? high / total : undefined;\n\t\t},\n\t},\n\t{\n\t\tid: \"turn-duration\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"median turn duration\",\n\t\tkind: \"exact\",\n\t\tunit: \"minutes\",\n\t\tcounts: (harness) => harness.turnDurations !== undefined,\n\t\tband: { low: 0.25, high: 2 },\n\t\tevaluate: (reading) => {\n\t\t\tconst bucket = medianBucket(\n\t\t\t\treading.harnesses.flatMap((harness) =>\n\t\t\t\t\t(harness.turnDurations?.buckets ?? []).map((row) => ({\n\t\t\t\t\t\tbucket: row.bucket,\n\t\t\t\t\t\tcount: row.turns,\n\t\t\t\t\t})),\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn bucket === undefined ? undefined : bucketMid(bucket) / 60;\n\t\t},\n\t},\n\t{\n\t\tid: \"question-back-share\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"of turns end with a question back to the human\",\n\t\tkind: \"proxy\",\n\t\tunit: \"share\",\n\t\tcounts: (harness) => harness.questions !== undefined,\n\t\tband: { low: 0, high: 0.15 },\n\t\tevaluate: (reading) => {\n\t\t\tlet asked = 0;\n\t\t\tlet turns = 0;\n\t\t\tfor (const harness of reading.harnesses) {\n\t\t\t\tasked += harness.questions?.asked ?? 0;\n\t\t\t\tturns += harness.questions?.turns ?? 0;\n\t\t\t}\n\t\t\treturn turns > 0 ? asked / turns : undefined;\n\t\t},\n\t},\n\t{\n\t\tid: \"web-searches-per-active-day\",\n\t\tversion: METRIC_RULES_V2,\n\t\tlabel: \"web searches per active day, inside the harness\",\n\t\tkind: \"proxy\",\n\t\tunit: \"count\",\n\t\tcounts: (harness) => harness.webSearches !== undefined,\n\t\tband: { low: 0, high: 4 },\n\t\tevaluate: (reading) => {\n\t\t\tif (reading.webSearchDays === 0) return undefined;\n\t\t\tconst total = reading.harnesses.reduce(\n\t\t\t\t(sum, harness) => sum + (harness.webSearches ?? 0),\n\t\t\t\t0,\n\t\t\t);\n\t\t\treturn total / reading.webSearchDays;\n\t\t},\n\t},\n];\n\nexport function metricRule(id: string): MetricRule | undefined {\n\treturn METRIC_RULES.find((m) => m.id === id);\n}\n","// Shared vocabulary for the measured workflow surface's rule core.\n//\n// Wayfinder ticket #214 (map #200). Terms follow CONTEXT.md's \"Workflow\n// surface\" section. This package holds the DETERMINISTIC rules only - no I/O,\n// no harness-specific file reading (that is ticket #219's local reducers) and\n// no server-side fit ranking or rotation state (that is ticket #218).\n\n/** Matches the four `*_HARNESS_NAME` constants in packages/cli/src/harness/*\\/adapter.ts. */\nexport type HarnessName =\n\t| \"claude-code\"\n\t| \"codex\"\n\t| \"cursor\"\n\t| \"grok-build\"\n\t| \"opencode\"\n\t| \"pi-mono\";\n\nexport const HARNESS_NAMES: readonly HarnessName[] = [\n\t\"claude-code\",\n\t\"codex\",\n\t\"cursor\",\n\t\"grok-build\",\n\t\"opencode\",\n\t\"pi-mono\",\n];\n\n/**\n * Display label for a coverage tag. Mirrors `harnessLabel` in\n * packages/cli/src/harness/index.ts - kept as a small local copy rather than\n * an import so this package stays dependency-free of the CLI (the web app\n * imports it too, see #217).\n */\nexport function harnessLabel(name: HarnessName): string {\n\tswitch (name) {\n\t\tcase \"claude-code\":\n\t\t\treturn \"Claude Code\";\n\t\tcase \"cursor\":\n\t\t\treturn \"Cursor\";\n\t\tcase \"codex\":\n\t\t\treturn \"Codex\";\n\t\tcase \"grok-build\":\n\t\t\treturn \"Grok Build\";\n\t\tcase \"opencode\":\n\t\t\treturn \"opencode\";\n\t\tcase \"pi-mono\":\n\t\t\treturn \"Pi\";\n\t}\n}\n\n/**\n * The public phase set (spec: docs/specs/workflow-surface.md, \"Phases\").\n * Scout is reading and searching before the change, handoff is the exchange\n * at a blocking human gate. `unknown` is a visible fifth bucket, never hidden.\n */\nexport type PhaseId = \"scout\" | \"build\" | \"verify\" | \"handoff\" | \"unknown\";\n\nexport const PHASES: readonly PhaseId[] = [\n\t\"scout\",\n\t\"build\",\n\t\"verify\",\n\t\"handoff\",\n\t\"unknown\",\n];\n\n/**\n * One recorded tool call, already reduced to the three fields a phase rule\n * needs: when it happened, which tool fired, and its sanitized argument (a\n * skill or subagent name, or a shell command string). Producing this tuple\n * from a harness's own transcript format is the harness reducer's job\n * (ticket #219) - this package only classifies it.\n */\nexport type HarnessEvent = readonly [tsMs: number, tool: string, arg: string];\n","// The versioned phase classifier: `phase-rules/v1`.\n//\n// Wayfinder ticket #214 (map #200), the shipping rule set proven in ticket\n// #196 (`prototypes/phase-extraction/extract.mjs`, PR #197) and specced in\n// docs/specs/workflow-surface.md (\"Phases\"). Ported here as the production\n// rule core - the harness reducers (ticket #219) call `classifyEvent` and\n// `deriveSessionPhases` over their own reduced event lists; this file makes\n// no assumption about how those events were read off disk.\n//\n// First match wins. A rule-set bump reclassifies old sessions from local raw\n// records at the next sync (spec); a session whose raw records are gone\n// keeps its old aggregate tagged with the rule id it was computed under.\n\nimport type { HarnessEvent, HarnessName, PhaseId } from \"./types.js\";\nimport { PHASES } from \"./types.js\";\n\nexport const PHASE_RULES_V1 = \"phase-rules/v1\";\n\n/**\n * The gate a harness's playbook must clear to ship (owner decision, #196,\n * 2026-08-22): 20% or less of a harness's measured time left unclassified.\n * Per harness, so one unreadable harness (opencode measured 28%) holds back\n * only its own playbook.\n */\nexport const UNKNOWN_GATE = 0.2;\n\nexport type PhaseClassification = { ruleId: string; phase: PhaseId };\n\n/**\n * Handoff markers are per adapter, not one global list (#196): Claude Code\n * records `AskUserQuestion` and `ExitPlanMode`, Codex records\n * `request_user_input`, opencode records `question`. Pi records no tool\n * calls at all, so it has none to name.\n */\nexport const HANDOFF_MARKERS: Record<HarnessName, readonly string[]> = {\n\t\"claude-code\": [\n\t\t\"AskUserQuestion\",\n\t\t\"ExitPlanMode\",\n\t\t\"mcp__curia__ask_human\",\n\t\t\"mcp__curia__request_review\",\n\t],\n\tcodex: [\"request_user_input\"],\n\tcursor: [\"ask_question\"],\n\t\"grok-build\": [\"ask_user_question\", \"request_user_input\"],\n\topencode: [\"question\"],\n\t\"pi-mono\": [],\n};\n\n/** Every handoff marker across every adapter, for classifying an event whose harness isn't known yet. */\nconst ALL_HANDOFF_MARKERS = new Set(\n\tObject.values(HANDOFF_MARKERS).flat() as string[],\n);\n\n/**\n * Tools that surface a handoff's result rather than opening one - the curia\n * MCP layer sits above every harness, so these spellings don't vary by adapter.\n */\nconst HANDOFF_SURFACE_TOOLS = [\n\t\"mcp__curia__open_pull_request\",\n\t\"mcp__curia__publish_preview\",\n\t\"mcp__curia__report_result\",\n\t\"mcp__curia__notify\",\n];\n\nconst SCOUT_TOOLS = [\n\t\"Read\",\n\t\"Grep\",\n\t\"Glob\",\n\t\"WebFetch\",\n\t\"WebSearch\",\n\t\"ToolSearch\",\n\t// cross-harness spellings of the same read/search tools\n\t\"read\",\n\t\"grep\",\n\t\"glob\",\n\t\"list\",\n\t\"ls\",\n\t\"webfetch\",\n\t\"websearch\",\n\t\"web_search\",\n\t\"tool_search\",\n\t\"codebase_search\",\n\t\"find\",\n\t\"search_tool\",\n\t\"search_files\",\n\t\"list_dir\",\n\t\"read_file\",\n];\n\nconst EDIT_TOOLS = [\n\t\"Edit\",\n\t\"Write\",\n\t\"NotebookEdit\",\n\t// cross-harness spellings\n\t\"edit\",\n\t\"write\",\n\t\"patch\",\n\t\"multiedit\",\n\t\"apply_patch\",\n\t\"write_file\",\n\t\"edit_file\",\n];\n\nconst REVIEW_SKILLS = [\"code-review\", \"security-review\", \"review\"];\nconst SCOUT_AGENTS = [\"Explore\", \"Plan\", \"research\"];\nconst SHELL_TOOLS = [\n\t\"Bash\",\n\t\"bash\",\n\t\"shell\",\n\t\"local_shell\",\n\t\"exec_command\",\n\t\"run_terminal_command\",\n];\nconst SKILL_TOOLS = [\"Skill\", \"skill\"];\nconst AGENT_TOOLS = [\"Agent\", \"Task\", \"task\", \"agent\"];\n\n/**\n * A todo/task tool carries no information about the work itself - the\n * neighbor data decided this (#196): against a base rate of scout 72% / build\n * 23%, `TaskCreate` sits at build 1%, `TaskStop` at 4%, `Monitor` at 7%.\n * Filing the whole family as build added 4 points on the strength of tools\n * that build nothing, so it inherits the phase of the event before it.\n */\nconst BOOKKEEPING_TOOLS = [\n\t\"TaskCreate\",\n\t\"TaskUpdate\",\n\t\"TaskStop\",\n\t\"TaskOutput\",\n\t\"todowrite\",\n\t\"TodoWrite\",\n\t\"Monitor\",\n\t\"SendUserFile\",\n\t\"SendMessage\",\n\t\"ListAgents\",\n];\n\nfunction isShell(tool: string): boolean {\n\treturn SHELL_TOOLS.includes(tool);\n}\n\nconst skillLeaf = (arg: string): string => arg.split(\":\").pop() ?? arg;\n\n// ---------------------------------------------------------------------------\n// Chain-segment command matching (#196, structural defect 1: 85% of recorded\n// shell commands hold a chain or a pipe, so a whole-string prefix match reads\n// only the first command and misses the rest).\n// ---------------------------------------------------------------------------\n\n/** Splits a shell command into its `&&`/`||`/`;`/`|` segments, normalized. */\nexport function chainSegments(arg: string): string[] {\n\treturn arg\n\t\t.split(/(?:&&|\\|\\||;|\\|)/)\n\t\t.map((s) =>\n\t\t\ts\n\t\t\t\t.trim()\n\t\t\t\t.replace(/^(?:\\w+=\\S*\\s+)+/, \"\")\n\t\t\t\t.replace(/^(?:cd\\s+\\S+\\s*)$/, \"\")\n\t\t\t\t// `git -C <path> log` is the same rule as `git log`. 650 real uses\n\t\t\t\t// matched nothing before this normalization (#196).\n\t\t\t\t.replace(/^git\\s+-C\\s+\\S+\\s+/, \"git \"),\n\t\t)\n\t\t.filter(Boolean);\n}\n\nfunction cmdIs(seg: string, heads: readonly string[]): boolean {\n\tfor (const h of heads) if (seg === h || seg.startsWith(`${h} `)) return true;\n\treturn false;\n}\n\n// Head lists measured off the owner's real history, not guessed (#196,\n// structural defect 2: the first draft's guessed heads left verify at 0%\n// across 464 sessions).\nconst TEST_HEADS = [\n\t\"pnpm test\",\n\t\"vitest\",\n\t\"tsc\",\n\t\"biome\",\n\t\"pnpm build\",\n\t\"pnpm lint\",\n\t\"pnpm typecheck\",\n\t\"npm test\",\n\t\"npm run test\",\n\t\"pnpm vitest\",\n\t\"npx vitest\",\n\t\"npx tsc\",\n\t\"npx biome\",\n\t\"pnpm exec\",\n\t\"pytest\",\n\t\"cargo test\",\n\t\"go test\",\n\t\"node --test\",\n\t\"make test\",\n\t\"pnpm check\",\n\t\"npm run build\",\n\t\"npm run lint\",\n];\nconst PUBLISH_HEADS = [\n\t\"git push\",\n\t\"gh pr create\",\n\t\"gh pr merge\",\n\t\"npm publish\",\n\t\"pnpm publish\",\n\t\"gh release\",\n];\nconst CHANGE_HEADS = [\n\t\"git add\",\n\t\"git commit\",\n\t\"mkdir\",\n\t\"cp\",\n\t\"mv\",\n\t\"rm\",\n\t\"touch\",\n\t\"sed\",\n\t\"pnpm add\",\n\t\"npm install\",\n\t\"git checkout\",\n\t\"git restore\",\n\t\"git stash\",\n\t\"git mv\",\n\t\"git rm\",\n\t\"git rebase\",\n\t\"git merge\",\n\t\"git cherry-pick\",\n\t\"git init\",\n\t\"git branch\",\n\t\"git worktree\",\n\t\"pnpm install\",\n\t\"pnpm remove\",\n\t\"npm i\",\n\t\"npm ci\",\n\t\"yarn add\",\n\t\"chmod\",\n\t\"ln\",\n\t\"tee\",\n\t\"echo\",\n\t\"printf\",\n\t\"npx convex\",\n\t\"pnpm convex\",\n\t\"pnpm dlx\",\n\t\"npx create\",\n];\nconst READ_HEADS = [\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"wc\",\n\t\"grep\",\n\t\"rg\",\n\t\"find\",\n\t\"git log\",\n\t\"git show\",\n\t\"git diff\",\n\t\"git status\",\n\t\"gh issue view\",\n\t\"gh issue list\",\n\t\"gh pr view\",\n\t\"gh pr list\",\n\t\"curl\",\n\t\"pwd\",\n\t\"which\",\n\t\"whoami\",\n\t\"echo $\",\n\t\"env\",\n\t\"printenv\",\n\t\"node --version\",\n\t\"node -v\",\n\t\"pnpm --version\",\n\t\"df\",\n\t\"du\",\n\t\"ps\",\n\t\"top\",\n\t\"file\",\n\t\"stat\",\n\t\"tree\",\n\t\"jq\",\n\t\"sort\",\n\t\"uniq\",\n\t\"cut\",\n\t\"awk\",\n\t\"diff\",\n\t\"gh api\",\n\t\"gh run\",\n\t\"gh workflow\",\n\t\"gh search\",\n\t\"gh issue\",\n\t\"gh pr\",\n\t\"git remote\",\n\t\"git fetch\",\n\t\"git ls-files\",\n\t\"git blame\",\n\t\"git describe\",\n\t\"git rev-parse\",\n\t\"sqlite3\",\n\t\"date\",\n\t\"uname\",\n\t\"man\",\n\t\"history\",\n\t\"type\",\n\t\"nl\",\n\t\"strings\",\n\t\"pgrep\",\n\t\"basename\",\n\t\"dirname\",\n\t\"realpath\",\n];\n\n/**\n * Flag-aware rules for dual-use commands (#196, structural defect 3): the\n * head alone files these wrong. 2,374 of 2,733 `sed` calls are `sed -n`, a\n * read filed as a change; only 199 of 7,758 `echo` calls redirect to a file.\n * These run BEFORE the head lists.\n */\nconst DUAL_USE_RULES: ReadonlyArray<{\n\tid: string;\n\tmatch: RegExp;\n\tbuild: RegExp;\n}> = [\n\t{ id: \"sed\", match: /^sed\\b/, build: /^sed\\s+(-[a-zA-Z]*i|--in-place)\\b/ },\n\t{ id: \"echo\", match: /^(echo|printf)\\b/, build: />>?\\s*\\S/ },\n\t{ id: \"cat\", match: /^cat\\b/, build: /^cat\\s*(>>?\\s*\\S|<<)/ },\n];\n\nfunction dualUse(seg: string): PhaseId | null {\n\tfor (const rule of DUAL_USE_RULES) {\n\t\tif (!rule.match.test(seg)) continue;\n\t\treturn rule.build.test(seg) ? \"build\" : \"scout\";\n\t}\n\treturn null;\n}\n\nconst CHAIN_PHASE_RANK: Record<PhaseId, number> = {\n\tverify: 4,\n\thandoff: 3,\n\tbuild: 2,\n\tscout: 1,\n\tunknown: 0,\n};\n\n/**\n * Classifies a full shell command by splitting it into chain segments,\n * classifying each, and letting the strongest phase in the chain win -\n * ordered verify, handoff, build, scout (spec, rule family 2).\n */\nfunction classifyShellChain(arg: string): PhaseId | null {\n\tlet best: PhaseId | null = null;\n\tfor (const seg of chainSegments(arg)) {\n\t\tlet p = dualUse(seg);\n\t\tif (!p) {\n\t\t\tif (cmdIs(seg, TEST_HEADS)) p = \"verify\";\n\t\t\telse if (cmdIs(seg, PUBLISH_HEADS)) p = \"handoff\";\n\t\t\telse if (cmdIs(seg, CHANGE_HEADS)) p = \"build\";\n\t\t\telse if (cmdIs(seg, READ_HEADS)) p = \"scout\";\n\t\t}\n\t\tif (p && (!best || CHAIN_PHASE_RANK[p] > CHAIN_PHASE_RANK[best])) best = p;\n\t}\n\treturn best;\n}\n\n// ---------------------------------------------------------------------------\n// The rule table. First match wins. A rule with `phase: null` derives its\n// phase from `test`'s return value instead of a fixed id.\n// ---------------------------------------------------------------------------\n\ntype Rule = {\n\tid: string;\n\t/** `null` means the phase comes from `test`'s return value. `\"@prev\"` means \"inherit\". */\n\tphase: PhaseId | \"@prev\" | null;\n\ttest: (tool: string, arg: string) => PhaseId | boolean;\n};\n\nconst RULES_V1: readonly Rule[] = [\n\t{\n\t\tid: \"handoff.surface\",\n\t\tphase: \"handoff\",\n\t\ttest: (t) => HANDOFF_SURFACE_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"verify.review-skill\",\n\t\tphase: \"verify\",\n\t\ttest: (t, a) => SKILL_TOOLS.includes(t) && REVIEW_SKILLS.includes(a),\n\t},\n\t// Forge stage markers (#166 round 3): named rules where the harness records\n\t// the skill call, matched on the last path segment so the\n\t// plugin-namespaced spelling `forge:crossfire` counts too.\n\t{\n\t\tid: \"verify.crossfire-skill\",\n\t\tphase: \"verify\",\n\t\ttest: (t, a) => SKILL_TOOLS.includes(t) && skillLeaf(a) === \"crossfire\",\n\t},\n\t{\n\t\tid: \"build.forge-skill\",\n\t\tphase: \"build\",\n\t\ttest: (t, a) => SKILL_TOOLS.includes(t) && skillLeaf(a) === \"forge\",\n\t},\n\t{\n\t\tid: \"build.edit-tool\",\n\t\tphase: \"build\",\n\t\ttest: (t) => EDIT_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"scout.read-tool\",\n\t\tphase: \"scout\",\n\t\ttest: (t) => SCOUT_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"scout.skill-load\",\n\t\tphase: \"scout\",\n\t\ttest: (t) => SKILL_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"scout.scout-agent\",\n\t\tphase: \"scout\",\n\t\ttest: (t, a) => AGENT_TOOLS.includes(t) && SCOUT_AGENTS.includes(a),\n\t},\n\t// Plan bookkeeping inherits the phase of the event before it (#196).\n\t// `phase: null` here means \"look at prevPhase\", signaled via the\n\t// sentinel below rather than a boolean/PhaseId return.\n\t{\n\t\tid: \"inherit.bookkeeping\",\n\t\tphase: \"@prev\",\n\t\ttest: (t) => BOOKKEEPING_TOOLS.includes(t),\n\t},\n\t{\n\t\tid: \"chain-cmd\",\n\t\tphase: null,\n\t\ttest: (t, a) => (isShell(t) ? (classifyShellChain(a) ?? false) : false),\n\t},\n\t{ id: \"unknown.shell\", phase: \"unknown\", test: (t) => isShell(t) },\n\t{\n\t\tid: \"unknown.agent\",\n\t\tphase: \"unknown\",\n\t\ttest: (t) => AGENT_TOOLS.includes(t),\n\t},\n\t{ id: \"unknown.tool\", phase: \"unknown\", test: () => true },\n];\n\nconst RULE_SETS: Record<string, readonly Rule[]> = {\n\t[PHASE_RULES_V1]: RULES_V1,\n};\n\n/**\n * Classifies one event under the named rule set. `prevPhase` resolves plan\n * bookkeeping's inherited phase (`null` before the first non-bookkeeping\n * event of a session, which classifies as `unknown`).\n */\nexport function classifyEvent(\n\ttool: string,\n\targ: string,\n\tprevPhase: PhaseId | null,\n\truleSet: string = PHASE_RULES_V1,\n\tharness?: HarnessName,\n): PhaseClassification {\n\tconst handoffMarkers = harness\n\t\t? new Set(HANDOFF_MARKERS[harness])\n\t\t: ALL_HANDOFF_MARKERS;\n\tif (handoffMarkers.has(tool)) {\n\t\treturn { ruleId: \"handoff.blocking-call\", phase: \"handoff\" };\n\t}\n\tconst rules = RULE_SETS[ruleSet];\n\tif (!rules) throw new Error(`unknown phase rule set: ${ruleSet}`);\n\tfor (const rule of rules) {\n\t\tconst res = rule.test(tool, arg);\n\t\tif (res === false) continue;\n\t\tif (rule.phase === null) {\n\t\t\t// `chain-cmd`: the phase IS the result.\n\t\t\treturn { ruleId: `${rule.id}.${res}`, phase: res as PhaseId };\n\t\t}\n\t\tif (rule.phase === \"@prev\") {\n\t\t\treturn { ruleId: rule.id, phase: prevPhase ?? \"unknown\" };\n\t\t}\n\t\treturn { ruleId: rule.id, phase: rule.phase };\n\t}\n\treturn { ruleId: \"unknown.tool\", phase: \"unknown\" };\n}\n\n// ---------------------------------------------------------------------------\n// Session time attribution: each event owns the gap to the next event,\n// capped at 5 minutes. The tail (after the last event) is capped at 60s.\n// The wait at a blocking handoff call renders as a striped waiting slice.\n// ---------------------------------------------------------------------------\n\nconst CAP_SEC = 300;\nconst TAIL_SEC = 60;\n\n/** Residual families for the unknown bucket, weighed by time before any rule is proposed for them (#196). */\nexport type ResidualFamily =\n\t| \"mcp server\"\n\t| \"harness bookkeeping\"\n\t| \"delegation\"\n\t| \"other harness tool\"\n\t| \"interpreter run\"\n\t| \"remote or container\"\n\t| \"shell construct\"\n\t| \"other shell command\";\n\nconst INTERPRETER_HEADS = [\n\t\"python3\",\n\t\"python\",\n\t\"node\",\n\t\"bun\",\n\t\"deno\",\n\t\"ruby\",\n\t\"perl\",\n\t\"php\",\n\t\"tsx\",\n];\nconst REMOTE_HEADS = [\"ssh\", \"tmux\", \"docker\", \"scp\", \"rsync\", \"kubectl\"];\n\nfunction residualFamily(tool: string, arg: string): ResidualFamily {\n\tif (tool.startsWith(\"mcp__\") || /chrome-devtools/.test(tool))\n\t\treturn \"mcp server\";\n\tif (BOOKKEEPING_TOOLS.includes(tool)) return \"harness bookkeeping\";\n\tif (AGENT_TOOLS.includes(tool)) return \"delegation\";\n\tif (!isShell(tool)) return \"other harness tool\";\n\tconst seg = chainSegments(arg)[0] ?? arg;\n\tconst head = (seg.trim().split(/\\s+/)[0] ?? \"\").split(\"/\").pop() ?? \"\";\n\tif (INTERPRETER_HEADS.includes(head)) return \"interpreter run\";\n\tif (REMOTE_HEADS.includes(head)) return \"remote or container\";\n\tif (/^(for|while|until|if|timeout|sleep|true|bash|sh|zsh)$/.test(head))\n\t\treturn \"shell construct\";\n\treturn \"other shell command\";\n}\n\nexport type SessionPhaseDerivation = {\n\t/** Seconds of measured time per phase, including the visible `unknown` bucket. */\n\tphaseSec: Record<PhaseId, number>;\n\t/** Event counts per phase. */\n\tphaseEvents: Record<PhaseId, number>;\n\t/** Seconds spent waiting at a blocking handoff call (a striped slice, spec). */\n\twaitingSec: number;\n\t/** Seconds spent idle between events for any other reason, above the cap. */\n\tidleSec: number;\n\t/** Rule id -> event count, for auditing which rule fired how often. */\n\truleTally: Record<string, number>;\n\t/** Unknown seconds by residual family, for weighing the next rule proposal. */\n\tresidualSec: Partial<Record<ResidualFamily, number>>;\n\truleSet: string;\n};\n\nfunction emptyPhaseRecord(): Record<PhaseId, number> {\n\treturn { scout: 0, build: 0, verify: 0, handoff: 0, unknown: 0 };\n}\n\n/**\n * Derives one session's phase mix from its raw events. Each event owns the\n * gap to the next event (capped at 5 minutes); the last event owns a fixed\n * 60s tail. A gap following a blocking handoff call counts as waiting rather\n * than idle.\n */\nexport function deriveSessionPhases(\n\tevents: readonly HarnessEvent[],\n\truleSet: string = PHASE_RULES_V1,\n\tharness?: HarnessName,\n): SessionPhaseDerivation {\n\tconst phaseSec = emptyPhaseRecord();\n\tconst phaseEvents = emptyPhaseRecord();\n\tconst ruleTally: Record<string, number> = {};\n\tconst residualSec: Partial<Record<ResidualFamily, number>> = {};\n\tlet waitingSec = 0;\n\tlet idleSec = 0;\n\tlet prevPhase: PhaseId | null = null;\n\n\tfor (let i = 0; i < events.length; i++) {\n\t\tconst event = events[i];\n\t\tif (!event) continue;\n\t\tconst [ts, tool, arg] = event;\n\t\tconst next = events[i + 1];\n\t\tconst gapSec = next ? (next[0] - ts) / 1000 : TAIL_SEC;\n\t\tconst ownSec = Math.min(gapSec, CAP_SEC);\n\n\t\tconst { ruleId, phase } = classifyEvent(\n\t\t\ttool,\n\t\t\targ,\n\t\t\tprevPhase,\n\t\t\truleSet,\n\t\t\tharness,\n\t\t);\n\t\tif (phase !== \"unknown\") prevPhase = phase;\n\t\truleTally[ruleId] = (ruleTally[ruleId] ?? 0) + 1;\n\t\tphaseSec[phase] += ownSec;\n\t\tphaseEvents[phase] += 1;\n\n\t\tif (phase === \"unknown\") {\n\t\t\tconst family = residualFamily(tool, arg);\n\t\t\tresidualSec[family] = (residualSec[family] ?? 0) + ownSec;\n\t\t}\n\n\t\tconst overflow = gapSec - ownSec;\n\t\tif (overflow > 0) {\n\t\t\tconst handoffMarkers = harness\n\t\t\t\t? new Set(HANDOFF_MARKERS[harness])\n\t\t\t\t: ALL_HANDOFF_MARKERS;\n\t\t\tif (handoffMarkers.has(tool)) waitingSec += overflow;\n\t\t\telse idleSec += overflow;\n\t\t}\n\t}\n\n\treturn {\n\t\tphaseSec,\n\t\tphaseEvents,\n\t\twaitingSec,\n\t\tidleSec,\n\t\truleTally,\n\t\tresidualSec,\n\t\truleSet,\n\t};\n}\n\n/** Share of a session's measured (non-unknown) attribution that landed in `unknown`, 0..1. */\nexport function unknownShare(derivation: SessionPhaseDerivation): number {\n\tconst total = PHASES.reduce((sum, p) => sum + derivation.phaseSec[p], 0);\n\treturn total > 0 ? derivation.phaseSec.unknown / total : 0;\n}\n","// The daily unit of the usage wire, and the fold that turns days into a window.\n//\n// Tickets #305, #306 and #315 (ADR-0010, ADR-0011). The snapshot payload the\n// CLI sends today is one 30-day block with its shares already computed. This\n// module is its per-day successor: one `measuredDays` row per (stack, machine,\n// date) holds `{ date, usage?, workflow? }` under ONE version, `measured-days/v1`,\n// with ONE fingerprint over both blocks.\n//\n// ONLY COMBINABLE ATOMS. A usage day carries token sums, session counts, project\n// keys and exact dollars, never a share or a mean. Shares, active days and the\n// dollar total come out of the fold, over the window's atoms. A day that lacks\n// the cache-write split folds its whole `cacheWrite` into `unsplit`.\n//\n// THE FOLD HAS THE DAY'S SHAPE. A window over one day prints the day's own\n// figures, and the tests fold a fixture of one day and compare it with itself.\n//\n// A READING IS ONE MACHINE'S, PER DAY (ADR-0009). Nothing here merges machines.\n\nimport type { WorkflowDay } from \"./daily.js\";\n\nexport const MEASURED_DAYS_V1 = \"measured-days/v1\";\n\nexport type UsageTokens = {\n\tinput: number;\n\toutput: number;\n\tcacheWrite: number;\n\tcacheRead: number;\n\t/**\n\t * The cache-write split by TTL. `unsplit` holds writes from payloads that\n\t * predate the split. Absent when the day recorded no split at all.\n\t */\n\tcacheWriteTtl?: { fiveMinute: number; oneHour: number; unsplit: number };\n};\n\nexport type UsageModelDay = {\n\tmodel: string;\n\ttokens: UsageTokens;\n\t/** Exact dollars the CLI priced at ingest; absent when unpriced or publishCost off. */\n\tusd?: number;\n\tpricingTable?: string;\n};\n\nexport type UsageHarnessDay = {\n\tharness: string;\n\t/** Sessions that STARTED this day. */\n\tsessions: number;\n\t/** Hashed project keys touched this day, sorted unique. */\n\tprojectKeys: readonly string[];\n\tmodels: readonly UsageModelDay[];\n\t/** Tokens spent inside subagent turns, all models. */\n\tsubagentTokens: number;\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\nexport type UsageDay = { harnesses: readonly UsageHarnessDay[] };\n\n/** One stored row. When `workflow` is present, `workflow.date === date`. */\nexport type MeasuredDay = {\n\tdate: string;\n\tusage?: UsageDay;\n\tworkflow?: WorkflowDay;\n};\n\nexport type UsageWindowModel = {\n\tmodel: string;\n\ttokens: UsageTokens;\n\ttotalTokens: number;\n\t/** `totalTokens / window totalTokens`, rounded to 4 places, like the CLI's `tokenShare`. */\n\ttokenShare: number;\n\t/** Exact sum over days that carried usd; `undefined` when no day did. */\n\tusd: number | undefined;\n\t/** Tokens of the days that carried no usd, for the server's per-day fill. */\n\tunpricedTokens: UsageTokens;\n\t/** Dates lacking usd for this model, sorted unique. */\n\tunpricedDates: readonly string[];\n\tpricingTables: readonly string[];\n};\n\nexport type UsageWindowHarness = {\n\tharness: string;\n\tsessions: number;\n\ttotalTokens: number;\n\ttokenShare: number;\n};\n\nexport type UsageWindow = {\n\taggregateVersion: string;\n\tdates: readonly string[];\n\t/** Days with at least one session. */\n\tactiveDays: number;\n\tsessions: number;\n\tprojectKeys: readonly string[];\n\ttokens: UsageTokens;\n\ttotalTokens: number;\n\t/**\n\t * `cacheRead / (input + cacheRead + cacheWrite)`: cache reads over the\n\t * input side, as the CLI's `computeCacheHitShare`. Output is not in the\n\t * denominator. Rounded to 4 places.\n\t */\n\tcacheHitShare: number;\n\t/** `subagentTokens / totalTokens`, rounded to 4 places. */\n\tsubagentShare: number;\n\tmodels: readonly UsageWindowModel[];\n\tharnesses: readonly UsageWindowHarness[];\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\n/** The CLI's `round4`: four decimal places on every share. */\nconst round4 = (n: number): number => Math.round(n * 10_000) / 10_000;\n\nexport function emptyUsageTokens(): UsageTokens {\n\treturn { input: 0, output: 0, cacheWrite: 0, cacheRead: 0 };\n}\n\n/**\n * Mutating add. The TTL split folds when either side carries one; a side\n * without the split adds its whole `cacheWrite` to `unsplit`, so the split's\n * three parts always sum to `cacheWrite` on the result.\n */\nexport function addUsageTokens(into: UsageTokens, from: UsageTokens): void {\n\tif (into.cacheWriteTtl || from.cacheWriteTtl) {\n\t\tconst a = into.cacheWriteTtl ?? {\n\t\t\tfiveMinute: 0,\n\t\t\toneHour: 0,\n\t\t\tunsplit: into.cacheWrite,\n\t\t};\n\t\tconst b = from.cacheWriteTtl ?? {\n\t\t\tfiveMinute: 0,\n\t\t\toneHour: 0,\n\t\t\tunsplit: from.cacheWrite,\n\t\t};\n\t\tinto.cacheWriteTtl = {\n\t\t\tfiveMinute: a.fiveMinute + b.fiveMinute,\n\t\t\toneHour: a.oneHour + b.oneHour,\n\t\t\tunsplit: a.unsplit + b.unsplit,\n\t\t};\n\t}\n\tinto.input += from.input;\n\tinto.output += from.output;\n\tinto.cacheWrite += from.cacheWrite;\n\tinto.cacheRead += from.cacheRead;\n}\n\n/**\n * The processed-token total. The wire normalizes every provider to disjoint\n * buckets, so cache reads are added exactly once. This matches Codex account\n * activity, OpenAI's cache-inclusive `input_tokens + output_tokens`, and the\n * CLI snapshot total.\n */\nexport function totalOfTokens(t: UsageTokens): number {\n\treturn t.input + t.output + t.cacheWrite + t.cacheRead;\n}\n\ntype ModelAcc = {\n\tmodel: string;\n\ttokens: UsageTokens;\n\tusd: number | undefined;\n\tunpricedTokens: UsageTokens;\n\tunpricedDates: Set<string>;\n\tpricingTables: Set<string>;\n};\n\n/**\n * Fold one machine's usage days into a window.\n *\n * `dates` is sorted unique. Models sort by `totalTokens` desc then model id,\n * harnesses likewise, matching the CLI's `groupModels` order. Empty input gives\n * a zeroed window with every share 0.\n */\nexport function foldUsageDays(\n\tdays: readonly { date: string; usage: UsageDay }[],\n): UsageWindow {\n\tconst tokens = emptyUsageTokens();\n\tconst projectKeys = new Set<string>();\n\tconst activeDates = new Set<string>();\n\tconst models = new Map<string, ModelAcc>();\n\tconst harnesses = new Map<\n\t\tstring,\n\t\t{ harness: string; sessions: number; tokens: UsageTokens }\n\t>();\n\tlet sessions = 0;\n\tlet subagentTokens = 0;\n\tconst excludedTokens = { unpriced: 0, synthetic: 0 };\n\n\tfor (const day of days) {\n\t\tfor (const h of day.usage.harnesses) {\n\t\t\tsessions += h.sessions;\n\t\t\tif (h.sessions > 0) activeDates.add(day.date);\n\t\t\tsubagentTokens += h.subagentTokens;\n\t\t\texcludedTokens.unpriced += h.excludedTokens.unpriced;\n\t\t\texcludedTokens.synthetic += h.excludedTokens.synthetic;\n\t\t\tfor (const key of h.projectKeys) projectKeys.add(key);\n\t\t\tconst held = harnesses.get(h.harness) ?? {\n\t\t\t\tharness: h.harness,\n\t\t\t\tsessions: 0,\n\t\t\t\ttokens: emptyUsageTokens(),\n\t\t\t};\n\t\t\theld.sessions += h.sessions;\n\t\t\tharnesses.set(h.harness, held);\n\t\t\tfor (const m of h.models) {\n\t\t\t\taddUsageTokens(tokens, m.tokens);\n\t\t\t\taddUsageTokens(held.tokens, m.tokens);\n\t\t\t\tconst acc = models.get(m.model) ?? {\n\t\t\t\t\tmodel: m.model,\n\t\t\t\t\ttokens: emptyUsageTokens(),\n\t\t\t\t\tusd: undefined,\n\t\t\t\t\tunpricedTokens: emptyUsageTokens(),\n\t\t\t\t\tunpricedDates: new Set<string>(),\n\t\t\t\t\tpricingTables: new Set<string>(),\n\t\t\t\t};\n\t\t\t\taddUsageTokens(acc.tokens, m.tokens);\n\t\t\t\tif (m.usd === undefined) {\n\t\t\t\t\taddUsageTokens(acc.unpricedTokens, m.tokens);\n\t\t\t\t\tacc.unpricedDates.add(day.date);\n\t\t\t\t} else {\n\t\t\t\t\tacc.usd = (acc.usd ?? 0) + m.usd;\n\t\t\t\t}\n\t\t\t\tif (m.pricingTable) acc.pricingTables.add(m.pricingTable);\n\t\t\t\tmodels.set(m.model, acc);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst totalTokens = totalOfTokens(tokens);\n\tconst share = (n: number): number =>\n\t\ttotalTokens ? round4(n / totalTokens) : 0;\n\tconst inputSide = tokens.input + tokens.cacheRead + tokens.cacheWrite;\n\treturn {\n\t\taggregateVersion: MEASURED_DAYS_V1,\n\t\tdates: [...new Set(days.map((day) => day.date))].sort(),\n\t\tactiveDays: activeDates.size,\n\t\tsessions,\n\t\tprojectKeys: [...projectKeys].sort(),\n\t\ttokens,\n\t\ttotalTokens,\n\t\tcacheHitShare: inputSide ? round4(tokens.cacheRead / inputSide) : 0,\n\t\tsubagentShare: totalTokens ? round4(subagentTokens / totalTokens) : 0,\n\t\tmodels: [...models.values()]\n\t\t\t.map((acc) => ({\n\t\t\t\tmodel: acc.model,\n\t\t\t\ttokens: acc.tokens,\n\t\t\t\ttotalTokens: totalOfTokens(acc.tokens),\n\t\t\t\ttokenShare: share(totalOfTokens(acc.tokens)),\n\t\t\t\tusd: acc.usd,\n\t\t\t\tunpricedTokens: acc.unpricedTokens,\n\t\t\t\tunpricedDates: [...acc.unpricedDates].sort(),\n\t\t\t\tpricingTables: [...acc.pricingTables].sort(),\n\t\t\t}))\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tb.totalTokens - a.totalTokens || a.model.localeCompare(b.model),\n\t\t\t),\n\t\tharnesses: [...harnesses.values()]\n\t\t\t.map((h) => ({\n\t\t\t\tharness: h.harness,\n\t\t\t\tsessions: h.sessions,\n\t\t\t\ttotalTokens: totalOfTokens(h.tokens),\n\t\t\t\ttokenShare: share(totalOfTokens(h.tokens)),\n\t\t\t}))\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tb.totalTokens - a.totalTokens || a.harness.localeCompare(b.harness),\n\t\t\t),\n\t\texcludedTokens,\n\t};\n}\n\n/** JSON with object keys sorted at every depth, so key order never changes the hash. */\nfunction canonicalJson(value: unknown): string {\n\tif (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n\tif (value && typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>)\n\t\t\t.filter(([, v]) => v !== undefined)\n\t\t\t.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n\t\treturn `{${entries\n\t\t\t.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`)\n\t\t\t.join(\",\")}}`;\n\t}\n\treturn JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * FNV-1a, 64-bit, over the UTF-8 bytes of `text`. Pure JS with no imports so\n * it runs in the Convex runtime and in Node alike. Returns 16 hex characters.\n */\nfunction fnv1a64(text: string): string {\n\tconst prime = 0x100000001b3n;\n\tconst mask = 0xffffffffffffffffn;\n\tlet hash = 0xcbf29ce484222325n;\n\tconst bytes = new TextEncoder().encode(text);\n\tfor (const byte of bytes) {\n\t\thash ^= BigInt(byte);\n\t\thash = (hash * prime) & mask;\n\t}\n\treturn hash.toString(16).padStart(16, \"0\");\n}\n\n/**\n * The content identity of one stored day: a hex hash over `MEASURED_DAYS_V1`\n * and both blocks, stable across key order. Two days that differ only in key\n * order hash equal; a version bump changes every hash. This is identity for\n * skipping an unchanged re-sync, not a security primitive.\n */\nexport function dayFingerprint(day: MeasuredDay): string {\n\treturn fnv1a64(\n\t\tcanonicalJson({\n\t\t\tversion: MEASURED_DAYS_V1,\n\t\t\tdate: day.date,\n\t\t\tusage: day.usage,\n\t\t\tworkflow: day.workflow,\n\t\t}),\n\t);\n}\n\nexport type RangeId = \"30d\" | \"7d\" | \"24h\";\n\nexport const RANGES: readonly RangeId[] = [\"30d\", \"7d\", \"24h\"];\n\nconst DAY_MS = 86_400_000;\n\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\n/** Midnight UTC of the day holding `ms`. */\nconst dayStart = (ms: number): number => Math.floor(ms / DAY_MS) * DAY_MS;\n\nconst rangeLength = (range: RangeId): number =>\n\trange === \"30d\" ? 30 : range === \"7d\" ? 7 : 1;\n\n/**\n * The inclusive `YYYY-MM-DD` UTC dates a range covers, ending today.\n * 24h is today only; 7d is today-6..today; 30d is today-29..today.\n */\nexport function rangeDates(\n\trange: RangeId,\n\tnowMs: number,\n): { from: string; to: string } {\n\tconst today = dayStart(nowMs);\n\treturn {\n\t\tfrom: utcDate(today - (rangeLength(range) - 1) * DAY_MS),\n\t\tto: utcDate(today),\n\t};\n}\n\n/**\n * The same-length range immediately before `rangeDates`: 24h is yesterday, 7d\n * is the 7 days before, 30d is 60 to 30 days ago.\n */\nexport function previousRangeDates(\n\trange: RangeId,\n\tnowMs: number,\n): { from: string; to: string } {\n\tconst length = rangeLength(range);\n\treturn rangeDates(range, dayStart(nowMs) - length * DAY_MS);\n}\n\n/** Inclusive on both ends. Dates are `YYYY-MM-DD`, so string order is date order. */\nexport function inDateRange(\n\tdate: string,\n\tr: { from: string; to: string },\n): boolean {\n\treturn date >= r.from && date <= r.to;\n}\n\n/**\n * `(current - previous) / previous`. `null` when `previous` is 0 or either\n * side is not finite: a change from nothing has no ratio.\n */\nexport function ratioChange(current: number, previous: number): number | null {\n\tif (!Number.isFinite(current) || !Number.isFinite(previous)) return null;\n\tif (previous === 0) return null;\n\treturn (current - previous) / previous;\n}\n","// The rows of one reading, in the fixed order the page prints them.\n//\n// Wayfinder ticket #218 (map #200) built the rows for a fit ranking. Ticket\n// #277 took fit off the page: on the first prod reading 15 of 16 rows sat under\n// the fit line and every row the owner wanted scored zero, so the section\n// moved to a fixed editorial order with a picture on every row. Ticket #285\n// dropped the ranking state from the server. This file is now the join\n// between the two rule pools and that order, and it is where the CLI, the\n// server and the page agree on which rows exist, what they are called, and\n// which of them are flat.\n//\n// FIT STAYS IN THE API AS A NUMBER NOTHING RANKS BY. `surprise` and `fit` are\n// still computed per row because the band is part of the versioned rule and a\n// reader of the API may want them. No caller sorts by them.\n\nimport type { ComponentInput } from \"./componentRules.js\";\nimport { COMPONENT_RULES } from \"./componentRules.js\";\nimport type { Band, MetricUnit } from \"./metricRules.js\";\nimport { METRIC_RULES } from \"./metricRules.js\";\nimport { type HarnessName, harnessLabel } from \"./types.js\";\n\n/** The podium: the first three rows in the fixed order. */\nexport const HIGHLIGHT_SLOTS = 3;\n\nexport type RowKind = \"metric\" | \"component\";\n\n/** `metric:late-night-commits`, `component:git-ledger`. Stable across rule versions. */\nexport function metricRowId(metricId: string): string {\n\treturn `metric:${metricId}`;\n}\n\nexport function componentRowId(componentId: string): string {\n\treturn `component:${componentId}`;\n}\n\nexport type WorkflowRowOrder = {\n\trowId: string;\n\t/** The plain name the page prints (#284). */\n\tname: string;\n\t/**\n\t * True when the row's head holds its whole picture, so the row never\n\t * expands (#284): no chevron, no body.\n\t */\n\tflat: boolean;\n};\n\n/**\n * The fixed editorial order (#284, decision 2), one entry per row either pool\n * can produce. A row absent from the reading is skipped, and the order of the\n * rest does not change.\n */\nexport const WORKFLOW_ROW_ORDER: readonly WorkflowRowOrder[] = [\n\t{\n\t\trowId: \"component:activity-heatmap\",\n\t\tname: \"When work happens\",\n\t\tflat: false,\n\t},\n\t{ rowId: \"component:start-hours\", name: \"Session start times\", flat: false },\n\t{\n\t\trowId: \"metric:late-night-commits\",\n\t\tname: \"Late-night commits\",\n\t\tflat: true,\n\t},\n\t{ rowId: \"component:phase-playbook\", name: \"Session length\", flat: false },\n\t{ rowId: \"component:git-ledger\", name: \"Lines changed\", flat: false },\n\t{ rowId: \"component:coding-languages\", name: \"Languages\", flat: false },\n\t{ rowId: \"component:kit\", name: \"Skills and MCP\", flat: false },\n\t{ rowId: \"component:model-routing\", name: \"Models used\", flat: false },\n\t{ rowId: \"component:delegation\", name: \"Subagents\", flat: false },\n\t{ rowId: \"metric:effort-levels\", name: \"Effort levels\", flat: false },\n\t{ rowId: \"metric:thinking-share\", name: \"Thinking tokens\", flat: false },\n\t{ rowId: \"metric:turn-duration\", name: \"Turn length\", flat: false },\n\t{ rowId: \"metric:question-back-share\", name: \"Questions asked\", flat: true },\n\t{\n\t\trowId: \"metric:web-searches-per-active-day\",\n\t\tname: \"Web searches\",\n\t\tflat: true,\n\t},\n\t{ rowId: \"metric:parallel-projects\", name: \"Parallel projects\", flat: true },\n];\n\nconst ORDER_INDEX = new Map(\n\tWORKFLOW_ROW_ORDER.map((row, index) => [row.rowId, index]),\n);\n\nexport function rowOrder(rowId: string): WorkflowRowOrder | undefined {\n\treturn WORKFLOW_ROW_ORDER.find((row) => row.rowId === rowId);\n}\n\n/** Every row id either rule pool can produce. */\nexport const KNOWN_ROW_IDS: ReadonlySet<string> = new Set([\n\t...METRIC_RULES.map((rule) => metricRowId(rule.id)),\n\t...COMPONENT_RULES.map((rule) => componentRowId(rule.id)),\n]);\n\n/**\n * One row of the reading.\n *\n * Both pools produce the same shape: a metric row's value is the rule's\n * evaluation over the folded window, and a component row's value is arithmetic\n * over the same window. Both are computed on the server.\n */\nexport type WorkflowRow = {\n\t/** Stable across syncs and rule versions: what a pin or a hide is keyed on. */\n\trowId: string;\n\tkind: RowKind;\n\truleId: string;\n\truleVersion: string;\n\tlabel: string;\n\tname: string;\n\tflat: boolean;\n\tunit: MetricUnit;\n\tvalue: number;\n\tband: Band;\n\t/** Share of this reading's synced harnesses the row counts, 0..1. */\n\tcoverage: number;\n\tcoverageTag?: string;\n\t/** Distance outside the typical band, 0..1. Nothing ranks by it. */\n\tsurprise: number;\n\t/** Coverage times surprise. Nothing ranks by it. */\n\tfit: number;\n};\n\n/**\n * How far outside its typical band a value sits, as 0..1.\n *\n * `d / (d + width)`, so one band width outside reads as 0.5 and the scale never\n * reaches 1. A value inside the band scores 0.\n */\nexport function surpriseOf(value: number, band: Band): number {\n\tconst width = Math.max(band.high - band.low, Number.EPSILON);\n\tconst distance =\n\t\tvalue < band.low\n\t\t\t? band.low - value\n\t\t\t: value > band.high\n\t\t\t\t? value - band.high\n\t\t\t\t: 0;\n\tif (distance === 0) return 0;\n\treturn distance / (distance + width);\n}\n\n/** Fit is coverage times surprise (spec, CONTEXT.md). */\nexport function fitOf(coverage: number, surprise: number): number {\n\treturn coverage * surprise;\n}\n\n/** The coverage tag naming the counted harnesses, or `undefined` when every synced harness counts. */\nexport function coverageTag(\n\tcounted: readonly string[],\n\tsynced: readonly string[],\n): string | undefined {\n\tif (counted.length === 0 || counted.length === synced.length)\n\t\treturn undefined;\n\treturn `counts: ${counted.map((name) => harnessLabel(name as HarnessName)).join(\" · \")}`;\n}\n\n/**\n * Build the row set for one reading, in the fixed order.\n *\n * \"A row ships when its measurement exists. A missing measurement stays absent,\n * so no separate first-ship list exists\" (spec). Both pools follow it: a rule\n * returns undefined for a window that cannot support its row, and the row is\n * skipped rather than printed as a zero.\n */\nexport function buildWorkflowRows(input: ComponentInput): WorkflowRow[] {\n\tconst rows: WorkflowRow[] = [];\n\tconst synced = input.reading.harnesses.map((harness) => harness.harness);\n\n\tfor (const rule of METRIC_RULES) {\n\t\tconst value = rule.evaluate(input.reading);\n\t\tif (value === undefined) continue;\n\t\tconst counted =\n\t\t\trule.counts === \"all\"\n\t\t\t\t? synced\n\t\t\t\t: input.reading.harnesses\n\t\t\t\t\t\t.filter(rule.counts)\n\t\t\t\t\t\t.map((harness) => harness.harness);\n\t\tconst coverage =\n\t\t\trule.counts === \"all\"\n\t\t\t\t? 1\n\t\t\t\t: synced.length === 0\n\t\t\t\t\t? 0\n\t\t\t\t\t: counted.length / synced.length;\n\t\tconst tag =\n\t\t\trule.counts === \"all\" ? undefined : coverageTag(counted, synced);\n\t\trows.push(\n\t\t\tfinishRow({\n\t\t\t\trowId: metricRowId(rule.id),\n\t\t\t\tkind: \"metric\",\n\t\t\t\truleId: rule.id,\n\t\t\t\truleVersion: rule.version,\n\t\t\t\tlabel: rule.label,\n\t\t\t\tunit: rule.unit,\n\t\t\t\tvalue,\n\t\t\t\tband: rule.band,\n\t\t\t\tcoverage,\n\t\t\t\t...(tag === undefined ? {} : { coverageTag: tag }),\n\t\t\t}),\n\t\t);\n\t}\n\n\tfor (const rule of COMPONENT_RULES) {\n\t\tconst value = rule.evaluate(input);\n\t\tif (value === undefined) continue;\n\t\trows.push(\n\t\t\tfinishRow({\n\t\t\t\trowId: componentRowId(rule.id),\n\t\t\t\tkind: \"component\",\n\t\t\t\truleId: rule.id,\n\t\t\t\truleVersion: rule.version,\n\t\t\t\tlabel: rule.label,\n\t\t\t\tunit: rule.unit,\n\t\t\t\tvalue,\n\t\t\t\tband: rule.band,\n\t\t\t\tcoverage: rule.coverage(input),\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn rows.sort(\n\t\t(a, b) =>\n\t\t\t(ORDER_INDEX.get(a.rowId) ?? Number.MAX_SAFE_INTEGER) -\n\t\t\t\t(ORDER_INDEX.get(b.rowId) ?? Number.MAX_SAFE_INTEGER) ||\n\t\t\ta.rowId.localeCompare(b.rowId),\n\t);\n}\n\nfunction finishRow(\n\trow: Omit<WorkflowRow, \"surprise\" | \"fit\" | \"name\" | \"flat\">,\n): WorkflowRow {\n\tconst order = rowOrder(row.rowId);\n\tconst surprise = surpriseOf(row.value, row.band);\n\treturn {\n\t\t...row,\n\t\tname: order?.name ?? row.label,\n\t\tflat: order?.flat ?? false,\n\t\tsurprise,\n\t\tfit: fitOf(row.coverage, surprise),\n\t};\n}\n\nexport type Placement = \"highlight\" | \"normal\";\n\nexport type PlacedRow = WorkflowRow & {\n\tplacement: Placement;\n};\n\n/**\n * Place one reading's rows in the fixed order. The first three rows on the\n * page are the podium. There are no pins and no hides (#303, #321): the owner\n * has no per-row control, so placement is a function of the order alone.\n */\nexport function placeRows(rows: readonly WorkflowRow[]): PlacedRow[] {\n\tconst ordered = [...rows].sort(\n\t\t(a, b) =>\n\t\t\t(ORDER_INDEX.get(a.rowId) ?? Number.MAX_SAFE_INTEGER) -\n\t\t\t(ORDER_INDEX.get(b.rowId) ?? Number.MAX_SAFE_INTEGER),\n\t);\n\treturn ordered.map((row, index) => ({\n\t\t...row,\n\t\tplacement: index < HIGHLIGHT_SLOTS ? \"highlight\" : \"normal\",\n\t}));\n}\n","// The rolling window and the scan-health shape every adapter reports.\n// Shared so the payload builder and each harness scanner agree by import\n// rather than by convention (#67).\n\n/** Rolling window locked by the owner in the #32 prototype resolution. */\nexport const DEFAULT_WINDOW_DAYS = 30;\n\n/**\n * UTC midnight opening a rolling window of `days` calendar days ending on the\n * day containing `now` (inclusive). `days = 30` therefore spans today plus the\n * 29 preceding days.\n *\n * Defined once and shared by the scan filter and the payload's `window.from`, so\n * the reported window and the records actually counted cannot drift apart.\n */\nexport function windowStartMs(now: number, days: number): number {\n\tconst startOfToday = Date.UTC(\n\t\tnew Date(now).getUTCFullYear(),\n\t\tnew Date(now).getUTCMonth(),\n\t\tnew Date(now).getUTCDate(),\n\t);\n\treturn startOfToday - (days - 1) * 86_400_000;\n}\n\nexport type ScanStats = {\n\t/** Files found on disk before any window filter. */\n\tfilesFound: number;\n\t/** Files actually opened and read. */\n\tfilesRead: number;\n\t/** Files skipped because their mtime predates the window. */\n\tfilesSkippedByMtime: number;\n\t/** Files skipped because a resolved path was already scanned (overlapping roots). */\n\tfilesSkippedAsDuplicate: number;\n\t/**\n\t * Files that could not be read (permissions, or pruned mid-scan). Counted\n\t * rather than thrown: an unhandled read error would surface the absolute path\n\t * AND the munged project directory in the crash output, which is exactly what\n\t * this tool promises never to emit.\n\t */\n\tfilesUnreadable: number;\n\t/**\n\t * Files excluded because they fail the genuine-rollout fingerprint (#73):\n\t * another tool wrote them into the harness's log directory, so their usage\n\t * would distort the numbers. Codex sets this; Claude has no known impostors.\n\t */\n\tfilesForeign: number;\n\t/**\n\t * LOCAL-ONLY detail behind the counts above. `buildPayload` copies coverage\n\t * fields one by one, so nothing below can reach the wire.\n\t *\n\t * `session_meta.originator` values seen on foreign files, value → file count.\n\t */\n\tforeignOriginators: Map<string, number>;\n\t/** LOCAL-ONLY: one `{path relative to the scan root, error class}` per unreadable file. */\n\tunreadableFiles: Array<{ path: string; reason: string }>;\n\t/** Subset of `filesUnreadable`: `.zst` rollouts this Node runtime cannot decompress. */\n\tfilesZstdUnsupported: number;\n};\n\nexport function emptyScanStats(): ScanStats {\n\treturn {\n\t\tfilesFound: 0,\n\t\tfilesRead: 0,\n\t\tfilesSkippedByMtime: 0,\n\t\tfilesSkippedAsDuplicate: 0,\n\t\tfilesUnreadable: 0,\n\t\tfilesForeign: 0,\n\t\tforeignOriginators: new Map(),\n\t\tunreadableFiles: [],\n\t\tfilesZstdUnsupported: 0,\n\t};\n}\n","// The wire payload builder - the only thing in this module that decides what\n// leaves the machine.\n//\n// Wayfinder ticket #37 (map #29). Shape fixed by the wire-format grilling #33;\n// nothing here is open design.\n//\n// Two invariants this file is responsible for:\n// 1. FAIL-CLOSED NAMES. Every freeform name is matched against an allowlist\n// before it can reach the payload; unmatched names publish only as\n// per-category counts (#33 decisions 2-4). Model ids are the sole exempt\n// class (decision 3) and are charset/length sanitized instead.\n// 2. COST IS ABSENT, NOT ZEROED. With `publishCost` off, the cost fields are\n// not in the payload at all (#33 decision 11) - there is nothing to\n// \"reveal\" server-side, because nothing was transmitted.\n\nimport { baseModelId, pricingTableFor } from \"@aistack/pricing\";\nimport type { MeasuredDay, WorkflowDay } from \"@aistack/workflow-rules\";\nimport { MEASURED_DAYS_V1 } from \"@aistack/workflow-rules\";\nimport type { WorkflowExtraction } from \"../../workflow/index.js\";\nimport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\ttype ModelRow,\n} from \"./aggregate.js\";\nimport {\n\ttype Atom,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"./allowlist.js\";\nimport { type ScanStats, windowStartMs } from \"./window.js\";\n\nexport const SCHEMA_VERSION = 2;\n\nexport type PayloadModel = {\n\t/** Vendor-assigned id, sanitized. `catalogSlug` is resolved SERVER-side at read time. */\n\tid: string;\n\ttokenShare: number;\n\ttokens: {\n\t\tinput: number;\n\t\toutput: number;\n\t\tcacheWrite: number;\n\t\tcacheRead: number;\n\t\t/**\n\t\t * The cache-write TTL breakdown (#213). The three sum to `cacheWrite`,\n\t\t * which stays the total.\n\t\t *\n\t\t * The analyzer has held this split since #126 and the wire merged it, so\n\t\t * the backend's read-time repricer had to charge every write at the cheap\n\t\t * 5-minute rate - about 8% low for Claude Code. Absent only when this\n\t\t * harness reports no cache writes at all.\n\t\t */\n\t\tcacheWriteTtl?: {\n\t\t\tfiveMinute: number;\n\t\t\toneHour: number;\n\t\t\t/** Writes the harness reported with no TTL. Priced at the 5-minute rate. */\n\t\t\tunsplit: number;\n\t\t};\n\t};\n\tapiEquivalentUSD?: number;\n\t/**\n\t * The table that produced `apiEquivalentUSD` - present exactly when the\n\t * dollars are (#136). Per model, not per payload: one opencode payload mixes\n\t * vendors, so a single top-level id would cite one table for dollars drawn\n\t * from two.\n\t */\n\tpricingTable?: string;\n};\n\nexport type PayloadAtom = {\n\tname: string;\n\tcallShare: number;\n\t/**\n\t * The absolute invocation count behind the share (#213). Its denominator is\n\t * `inventory.calls` for the same category, NOT the sum of the published\n\t * atoms: shares are computed over every observed call, withheld ones\n\t * included, and the count keeps that property.\n\t */\n\tcalls: number;\n};\n\nexport type PayloadInventory = {\n\tbuiltinTools: PayloadAtom[];\n\tmcpServers: PayloadAtom[];\n\tskills: PayloadAtom[];\n\tsubagents: PayloadAtom[];\n\tslashCommands: PayloadAtom[];\n\t/** DISTINCT names withheld per category, so the gap in the shares is explained. */\n\twithheld: {\n\t\tbuiltinTools: number;\n\t\tmcpServers: number;\n\t\tskills: number;\n\t\tsubagents: number;\n\t\tslashCommands: number;\n\t};\n\t/**\n\t * Every observed call per category, withheld names included (#213) - the\n\t * denominator the shares were computed over. Withheld calls are this total\n\t * minus the published counts, so the absolute figures explain their own gap\n\t * the way `withheld` explains the shares'.\n\t */\n\tcalls: {\n\t\tbuiltinTools: number;\n\t\tmcpServers: number;\n\t\tskills: number;\n\t\tsubagents: number;\n\t\tslashCommands: number;\n\t};\n};\n\nexport type MeasuredPayload = {\n\tschemaVersion: 2;\n\t/** Client clock. The server stamps its own `receivedAt` (#33 decision 6). */\n\tcapturedAt: number;\n\twindow: { days: number; from: string; to: string };\n\tharness: { name: string; version: string | null };\n\t/**\n\t * The one table the models' citations agree on, or `null` - when\n\t * `publishCost` is off, when nothing priced, and when a mixed-vendor payload\n\t * cites several tables (the per-model `pricingTable` fields carry the truth,\n\t * and joining them here would blow the server's 64-character name bound).\n\t */\n\tpricingTable: string | null;\n\tactivity: {\n\t\tsessions: number;\n\t\t/** Sorted UTC dates inside the declared window. */\n\t\tactiveDayDates: string[];\n\t\t/** Sorted project workspace identifiers. Project paths never travel. */\n\t\tprojectKeys: string[];\n\t\ttotalTokens: number;\n\t\tcacheHitShare: number;\n\t\tsubagentShare: number;\n\t};\n\tmodels: PayloadModel[];\n\tinventory: PayloadInventory;\n\tcoverage: {\n\t\tfilesScanned: number;\n\t\tfilesUnreadable: number;\n\t\tlinesParsed: number;\n\t\tlinesFailed: number;\n\t};\n\texcludedTokens: { unpriced: number; synthetic: number };\n};\n\n// ---------------------------------------------------------------------------\n// Sanitization\n// ---------------------------------------------------------------------------\n\n/**\n * Model ids are exempt from the allowlist (#33 decision 3) precisely because\n * they are vendor-assigned: on the day a new Claude model ships, fail-closing it\n * would make its tokens silently vanish from every sync and understate cost with\n * no visible cause. Exempt is not unchecked, though - the id still becomes a\n * database key and a rendered string, so charset and length are bounded here.\n */\nconst MODEL_ID_UNSAFE_RE = /[^A-Za-z0-9._:-]+/g;\nconst MODEL_ID_MAX = 64;\n\nexport function sanitizeModelId(id: string): string {\n\tconst collapsed = cleanName(id)\n\t\t.replace(MODEL_ID_UNSAFE_RE, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\tif (collapsed.length === 0) return \"unknown\";\n\treturn collapsed.length > MODEL_ID_MAX\n\t\t? collapsed.slice(0, MODEL_ID_MAX)\n\t\t: collapsed;\n}\n\nconst round4 = (n: number): number => Math.round(n * 10_000) / 10_000;\nconst round2 = (n: number): number => Math.round(n * 100) / 100;\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\n// ---------------------------------------------------------------------------\n// Inventory\n// ---------------------------------------------------------------------------\n\nconst toAtoms = (pairs: ReadonlyArray<readonly [string, number]>): Atom[] =>\n\tpairs.map(([name, count]) => ({ name, count }));\n\n/**\n * Shares are computed over ALL observed calls, including withheld ones.\n *\n * Renormalizing over only the allowlisted atoms would make the published shares\n * sum to 1.0 and read as a complete inventory - a withheld MCP server carrying\n * 90% of the calls would leave no trace. Keeping the true denominator means the\n * shares sum to less than 1 exactly when something was withheld, and the\n * `withheld` counts say how many things.\n */\nfunction buildCategory(\n\tobserved: ReadonlyArray<readonly [string, number]>,\n\tcurated: ReadonlySet<string>,\n\toptIns: readonly string[],\n\tdenominator: number,\n): { atoms: PayloadAtom[]; withheld: number; keptPrivate: KeptPrivateAtom[] } {\n\t// The union is where #42 decision 1 lands: a name publishes if it is curated\n\t// OR the owner ticked it. Filtering itself is unchanged - still client-side,\n\t// still fail-closed, still before the send. What moves is who judged the name.\n\tconst publishable = new Set([...curated, ...optIns]);\n\tconst {\n\t\tallowed: kept,\n\t\tkeptPrivate,\n\t\twithheld,\n\t} = filterAtoms(toAtoms(observed), { publishable, curated });\n\treturn {\n\t\tatoms: kept.map((a) => ({\n\t\t\tname: a.name,\n\t\t\tcallShare: denominator ? round4(a.count / denominator) : 0,\n\t\t\tcalls: a.count,\n\t\t})),\n\t\twithheld,\n\t\tkeptPrivate,\n\t};\n}\n\nconst sumCounts = (pairs: ReadonlyArray<readonly [string, number]>): number => {\n\tlet n = 0;\n\tfor (const [, c] of pairs) n += c;\n\treturn n;\n};\n\n// ---------------------------------------------------------------------------\n// Models\n// ---------------------------------------------------------------------------\n\ntype ModelGroup = {\n\tid: string;\n\ttotalTokens: number;\n\tinput: number;\n\toutput: number;\n\tcacheWrite: number;\n\tcacheWrite5m: number;\n\tcacheWrite1h: number;\n\tcacheWriteUnsplit: number;\n\tcacheRead: number;\n\tcostUSD: number;\n\tunpricedTokens: number;\n\tanyUnpriceable: boolean;\n\t/** The table citing this group's rates; every row shares it (one base model). */\n\ttable: string | null;\n};\n\n/**\n * Collapse the analyzer's pricing keys into vendor-assigned ids.\n *\n * The analyzer prices fast mode under a synthetic `claude-opus-5#fast` key\n * because it bills at a different rate ($10/$50 vs $5/$25). That suffix is OURS,\n * not the vendor's, so publishing it would hand the server an id that cannot\n * resolve against the models catalog - the exact silent-disappearance failure\n * decision 3 exists to prevent. The rows are therefore merged back onto the base\n * id here. Cost stays exact because it was already accumulated per response at\n * the fast rate; what is lost is the fast-mode share itself, which the payload\n * has no field for and which is a candidate for a later schema bump.\n */\nfunction groupModels(rows: readonly ModelRow[]): ModelGroup[] {\n\tconst groups = new Map<string, ModelGroup>();\n\tfor (const r of rows) {\n\t\tconst id = sanitizeModelId(baseModelId(r.modelKey));\n\t\tlet g = groups.get(id);\n\t\tif (!g) {\n\t\t\tg = {\n\t\t\t\tid,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\tcacheWrite5m: 0,\n\t\t\t\tcacheWrite1h: 0,\n\t\t\t\tcacheWriteUnsplit: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcostUSD: 0,\n\t\t\t\tunpricedTokens: 0,\n\t\t\t\tanyUnpriceable: false,\n\t\t\t\ttable: null,\n\t\t\t};\n\t\t\tgroups.set(id, g);\n\t\t}\n\t\tg.table ??= pricingTableFor(r.modelKey);\n\t\tg.totalTokens += r.totalTokens;\n\t\tg.input += r.tokens.input;\n\t\tg.output += r.tokens.output;\n\t\tg.cacheWrite5m += r.tokens.cacheWrite5m;\n\t\tg.cacheWrite1h += r.tokens.cacheWrite1h;\n\t\tg.cacheWriteUnsplit += r.tokens.cacheWriteUnsplit;\n\t\tg.cacheWrite +=\n\t\t\tr.tokens.cacheWrite5m +\n\t\t\tr.tokens.cacheWrite1h +\n\t\t\tr.tokens.cacheWriteUnsplit;\n\t\tg.cacheRead += r.tokens.cacheRead;\n\t\tg.costUSD += r.costUSD ?? 0;\n\t\tg.unpricedTokens += r.unpricedTokens;\n\t\tif (r.costUSD === null) g.anyUnpriceable = true;\n\t}\n\treturn [...groups.values()].sort(\n\t\t(a, b) => b.totalTokens - a.totalTokens || a.id.localeCompare(b.id),\n\t);\n}\n\nfunction buildModels(\n\trows: readonly ModelRow[],\n\ttotalTokens: number,\n\tpublishCost: boolean,\n): PayloadModel[] {\n\treturn groupModels(rows).map((g) => {\n\t\tconst model: PayloadModel = {\n\t\t\tid: g.id,\n\t\t\ttokenShare: totalTokens ? round4(g.totalTokens / totalTokens) : 0,\n\t\t\ttokens: {\n\t\t\t\tinput: g.input,\n\t\t\t\toutput: g.output,\n\t\t\t\tcacheWrite: g.cacheWrite,\n\t\t\t\tcacheRead: g.cacheRead,\n\t\t\t\t// All three or none (#213), so a reader never sees half a\n\t\t\t\t// breakdown. Omitted when there were no cache writes at all -\n\t\t\t\t// three zeros state nothing the total does not already state.\n\t\t\t\t...(g.cacheWrite > 0\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tcacheWriteTtl: {\n\t\t\t\t\t\t\t\tfiveMinute: g.cacheWrite5m,\n\t\t\t\t\t\t\t\toneHour: g.cacheWrite1h,\n\t\t\t\t\t\t\t\tunsplit: g.cacheWriteUnsplit,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t};\n\t\t// Absent, not zero: a partially-priced model reporting a dollar figure\n\t\t// would understate without saying so. `excludedTokens.unpriced` carries\n\t\t// the tokens that were left out. Dollars and their citation travel\n\t\t// together (#136) - a figure without its table may not render anywhere.\n\t\tif (\n\t\t\tpublishCost &&\n\t\t\t!g.anyUnpriceable &&\n\t\t\tg.unpricedTokens === 0 &&\n\t\t\tg.table !== null\n\t\t) {\n\t\t\tmodel.apiEquivalentUSD = round2(g.costUSD);\n\t\t\tmodel.pricingTable = g.table;\n\t\t}\n\t\treturn model;\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Build\n// ---------------------------------------------------------------------------\n\nexport type BuildPayloadInput = {\n\taggregate: Aggregate;\n\tstats: ScanStats;\n\tsyncConfig: SyncConfig;\n\t/** Client clock, epoch ms. The same value used to derive the scan window. */\n\tnow: number;\n\twindowDays: number;\n\t/** The adapter's payload discriminator, e.g. `\"claude-code\"` (#66). */\n\tharnessName: string;\n\t/** The adapter's fail-closed vendor tool set (#66 decision 3). */\n\tbuiltinTools: ReadonlySet<string>;\n\t/** Resolve one local project directory to its persistent opaque id. */\n\tprojectWorkspaceId: (directory: string) => string;\n};\n\nexport type BuiltPayload = {\n\tpayload: MeasuredPayload;\n\t/** The same numbers unfiltered, for the local report and the approve gate. */\n\tfinalized: Finalized;\n\t/**\n\t * Every observed name that will NOT publish, by category - the gate's review\n\t * list (#42 decision 1, wired in #44).\n\t *\n\t * This is the one thing here that is deliberately NOT in the payload. It is\n\t * the list of names the user has not agreed to publish, so it stays on the\n\t * machine; the payload carries only the per-category COUNT.\n\t */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n};\n\nexport function buildPayload(input: BuildPayloadInput): BuiltPayload {\n\tconst {\n\t\taggregate: agg,\n\t\tstats,\n\t\tsyncConfig,\n\t\tnow,\n\t\twindowDays,\n\t\tharnessName,\n\t\tbuiltinTools,\n\t\tprojectWorkspaceId,\n\t} = input;\n\tconst finalized = finalize(agg);\n\tconst { publishCost, allowlist, optIns } = syncConfig;\n\n\tconst fromMs = windowStartMs(now, windowDays);\n\tconst from = utcDate(fromMs);\n\tconst to = utcDate(now);\n\n\t// Limited to the reported window rather than copied from the aggregate: a\n\t// clock-skewed, imported, or restored transcript dated in the future would\n\t// otherwise put an impossible date in a deterministic payload.\n\tconst activeDayDates = [...agg.activeDays]\n\t\t.filter((d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d) && d >= from && d <= to)\n\t\t.sort();\n\tconst projectKeys = [\n\t\t...new Set(\n\t\t\t[...agg.projectDirs].map((directory) => projectWorkspaceId(directory)),\n\t\t),\n\t].sort();\n\tif (\n\t\tprojectKeys.length > 1_000 ||\n\t\tprojectKeys.some((key) => !/^[A-Za-z0-9_-]{22}$/.test(key))\n\t) {\n\t\tthrow new Error(\n\t\t\t\"Project workspace identifiers must be 22-character base64url strings\",\n\t\t);\n\t}\n\n\t// One denominator per category, held rather than inlined: each is both the\n\t// divisor for its shares and the total the payload publishes (#213), and the\n\t// two must be the same number or the absolute counts would not add up.\n\tconst observedCalls = {\n\t\tbuiltinTools: finalized.totalToolCalls,\n\t\tmcpServers: sumCounts(finalized.mcpServers),\n\t\tskills: sumCounts(finalized.skills),\n\t\tsubagents: sumCounts(finalized.subagents),\n\t\tslashCommands: sumCounts(finalized.slashCommands),\n\t};\n\tconst builtins = buildCategory(\n\t\tfinalized.tools,\n\t\tbuiltinTools,\n\t\toptIns.builtinTools,\n\t\tobservedCalls.builtinTools,\n\t);\n\tconst mcp = buildCategory(\n\t\tfinalized.mcpServers,\n\t\tnew Set(allowlist.mcpServers),\n\t\toptIns.mcpServers,\n\t\tobservedCalls.mcpServers,\n\t);\n\tconst skills = buildCategory(\n\t\tfinalized.skills,\n\t\tnew Set(allowlist.skills),\n\t\toptIns.skills,\n\t\tobservedCalls.skills,\n\t);\n\tconst subagents = buildCategory(\n\t\tfinalized.subagents,\n\t\tnew Set(allowlist.subagents),\n\t\toptIns.subagents,\n\t\tobservedCalls.subagents,\n\t);\n\tconst slash = buildCategory(\n\t\tfinalized.slashCommands,\n\t\tnew Set(allowlist.slashCommands),\n\t\toptIns.slashCommands,\n\t\tobservedCalls.slashCommands,\n\t);\n\n\tconst models = buildModels(\n\t\tfinalized.models,\n\t\tfinalized.totalTokens,\n\t\tpublishCost,\n\t);\n\t// The citation lives on each model (#136). The top-level field survives for\n\t// readers of the old shape and states the one table everything agrees on -\n\t// never a false single citation over a mixed payload.\n\tconst citedTables = [\n\t\t...new Set(models.flatMap((m) => (m.pricingTable ? [m.pricingTable] : []))),\n\t];\n\n\tconst payload: MeasuredPayload = {\n\t\tschemaVersion: SCHEMA_VERSION,\n\t\tcapturedAt: now,\n\t\twindow: { days: windowDays, from, to },\n\t\tharness: {\n\t\t\tname: harnessName,\n\t\t\tversion:\n\t\t\t\tfinalized.harnessVersion === null\n\t\t\t\t\t? null\n\t\t\t\t\t: sanitizeModelId(finalized.harnessVersion),\n\t\t},\n\t\tpricingTable: citedTables.length === 1 ? citedTables[0] : null,\n\t\tactivity: {\n\t\t\tsessions: finalized.sessions,\n\t\t\tactiveDayDates,\n\t\t\tprojectKeys,\n\t\t\ttotalTokens: finalized.totalTokens,\n\t\t\tcacheHitShare: round4(finalized.cacheHitShare),\n\t\t\tsubagentShare: round4(finalized.sidechainShare),\n\t\t},\n\t\tmodels,\n\t\tinventory: {\n\t\t\tbuiltinTools: builtins.atoms,\n\t\t\tmcpServers: mcp.atoms,\n\t\t\tskills: skills.atoms,\n\t\t\tsubagents: subagents.atoms,\n\t\t\tslashCommands: slash.atoms,\n\t\t\twithheld: {\n\t\t\t\tbuiltinTools: builtins.withheld,\n\t\t\t\tmcpServers: mcp.withheld,\n\t\t\t\tskills: skills.withheld,\n\t\t\t\tsubagents: subagents.withheld,\n\t\t\t\tslashCommands: slash.withheld,\n\t\t\t},\n\t\t\tcalls: observedCalls,\n\t\t},\n\t\tcoverage: {\n\t\t\tfilesScanned: stats.filesRead,\n\t\t\tfilesUnreadable: stats.filesUnreadable,\n\t\t\tlinesParsed: agg.lines - agg.parseErrors,\n\t\t\tlinesFailed: agg.parseErrors,\n\t\t},\n\t\texcludedTokens: {\n\t\t\tunpriced: finalized.unpricedTokens,\n\t\t\tsynthetic: agg.syntheticTokens,\n\t\t},\n\t};\n\n\treturn {\n\t\tpayload,\n\t\tfinalized,\n\t\tkeptPrivate: {\n\t\t\tbuiltinTools: builtins.keptPrivate,\n\t\t\tmcpServers: mcp.keptPrivate,\n\t\t\tskills: skills.keptPrivate,\n\t\t\tsubagents: subagents.keptPrivate,\n\t\t\tslashCommands: slash.keptPrivate,\n\t\t},\n\t};\n}\n\n/**\n * What `POST /api/cli/sync` takes: one sealed payload PER DETECTED HARNESS,\n * one unsealed half shared across them (#66 decision 5). The batch is atomic\n * server-side, so two harnesses cannot wipe each other's staged names - which\n * is what two sequential per-harness publishes would have done, because the\n * staged list is a whole-list replace per stack.\n */\nexport type SyncBody = {\n\tpayloads: MeasuredPayload[];\n\tkeptPrivate?: Record<NameCategory, KeptPrivateAtom[]>;\n\t/**\n\t * The machine's standing auto-sync opt-in (#78). Not measurement and not a\n\t * name - it is the one bit of local state the backend cannot otherwise see,\n\t * and `auto_sync_enabled` has nothing to fire on without it.\n\t *\n\t * It rides BESIDE the payloads, never inside one: the payload validator is\n\t * closed, and that closedness is the privacy claim.\n\t */\n\tautoSync?: { enabled: boolean; frequencyHours: number };\n\t/**\n\t * How this sync fired (#102, sent by #103). `auto` means a SessionStart hook\n\t * ran it with nobody watching; `manual` means a human typed the command.\n\t *\n\t * The server stamps `lastAutoSyncAt` from it, so the web switch can tell\n\t * on-and-working from on-but-never-fired. It rides beside the payloads for\n\t * the same reason `autoSync` does: the payload validator is closed.\n\t */\n\ttrigger?: SyncTrigger;\n\t/**\n\t * The measured days (#307, ADR-0010): one row per UTC date holding the\n\t * usage half and the workflow half under ONE version, `measured-days/v1`.\n\t * Only the dates the server lacks or holds differently ride here, plus\n\t * today; the manifest decides.\n\t *\n\t * It rides BESIDE the payloads for the reasons the workflow section did: the\n\t * Git half is per machine, not per harness, and the closed payload\n\t * validator is the privacy claim (#33). Each consent bit strips its own\n\t * half on the machine: `publishWorkflow` off means no day carries a\n\t * `workflow` block, `publishCost` off means no model carries `usd`.\n\t */\n\tmeasuredDays?: PayloadMeasuredDays;\n\t/**\n\t * The workflow section of the wire before #307. This CLI no longer sets it;\n\t * the workflow blocks ride inside `measuredDays`. The field stays so an old\n\t * body still type-checks.\n\t */\n\tworkflow?: PayloadWorkflow;\n\t/**\n\t * The version of this CLI (#213).\n\t *\n\t * It answers one operational question that had no answer: how many machines\n\t * are still on an old wire. `cliVersion` reached PostHog and nothing else,\n\t * so no query over published rows could count them - which is the exact\n\t * question a wire bump asks.\n\t */\n\tcliVersion?: string;\n};\n\n/**\n * The workflow section as it goes on the wire (#285): per-day rows of\n * combinable atoms, plus the machine's clock. `WorkflowExtraction` is already\n * that shape, and nothing local survives it, so the wire type restates it\n * rather than deriving it: the two ends must describe the same bytes.\n */\nexport type PayloadWorkflow = {\n\taggregateVersion: string;\n\tutcOffsetMinutes: number;\n\tdays: WorkflowDay[];\n};\n\n/**\n * Put an extraction on the legacy `workflow` body field. The CLI no longer\n * sends that field (#307); this stays for the server-side contract test that\n * checks the day shape against the validator.\n */\nexport function toPayloadWorkflow(\n\textraction: WorkflowExtraction,\n): PayloadWorkflow {\n\treturn {\n\t\taggregateVersion: extraction.aggregateVersion,\n\t\tutcOffsetMinutes: extraction.utcOffsetMinutes,\n\t\tdays: extraction.days,\n\t};\n}\n\n/** The day rows on the wire (#307), plus the machine's clock. */\nexport type PayloadMeasuredDays = {\n\taggregateVersion: typeof MEASURED_DAYS_V1;\n\t/** Minutes EAST of UTC, as `PayloadWorkflow` carried it (#218). */\n\tutcOffsetMinutes: number;\n\tdays: MeasuredDay[];\n};\n\n/**\n * Apply both consent bits to the day rows. Idempotent and pure, so the stage\n * runs it BEFORE fingerprinting (the fingerprint must hash the bytes that go)\n * and `buildSyncBody` runs it again as the last line of defense.\n *\n * `publishWorkflow` off drops every `workflow` block. `publishCost` off drops\n * `usd` and `pricingTable` from every model. A config the machine could not\n * fetch reads as both off.\n */\nexport function applyDayConsent(\n\tdays: readonly MeasuredDay[],\n\tsyncConfig: Pick<SyncConfig, \"publishCost\" | \"publishWorkflow\">,\n): MeasuredDay[] {\n\treturn days.map((day) => {\n\t\tconst { workflow, usage, ...rest } = day;\n\t\tconst out: MeasuredDay = { ...rest };\n\t\tif (usage) {\n\t\t\tout.usage = syncConfig.publishCost\n\t\t\t\t? usage\n\t\t\t\t: {\n\t\t\t\t\t\tharnesses: usage.harnesses.map((h) => ({\n\t\t\t\t\t\t\t...h,\n\t\t\t\t\t\t\tmodels: h.models.map(\n\t\t\t\t\t\t\t\t({ usd: _usd, pricingTable: _table, ...model }) => model,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t})),\n\t\t\t\t\t};\n\t\t}\n\t\tif (workflow && syncConfig.publishWorkflow) out.workflow = workflow;\n\t\treturn out;\n\t});\n}\n\n/** The two ways a sync can fire. Absent on an old CLI, and that reads as manual. */\nexport type SyncTrigger = \"manual\" | \"auto\";\n\n/**\n * Union the per-harness kept-private lists into the one list the wire carries.\n *\n * One list, not one per harness, because consent is per NAME (#66 decision 5):\n * the owner ticks \"alp-river\", not \"alp-river as seen by Codex\". Counts merge\n * by (category, name); the group survives from whichever harness saw it first.\n */\nexport function mergeKeptPrivate(\n\thalves: ReadonlyArray<Record<NameCategory, KeptPrivateAtom[]>>,\n): Record<NameCategory, KeptPrivateAtom[]> {\n\tconst out = {} as Record<NameCategory, KeptPrivateAtom[]>;\n\tfor (const category of NAME_CATEGORIES) {\n\t\tconst merged = new Map<string, KeptPrivateAtom>();\n\t\tfor (const half of halves) {\n\t\t\tfor (const atom of half[category]) {\n\t\t\t\tconst held = merged.get(atom.name);\n\t\t\t\tif (held) held.count += atom.count;\n\t\t\t\telse merged.set(atom.name, { ...atom });\n\t\t\t}\n\t\t}\n\t\tout[category] = [...merged.values()].sort(\n\t\t\t(a, b) => b.count - a.count || a.name.localeCompare(b.name),\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * Assemble the request body from the built payloads, one per detected harness.\n *\n * The two halves ride in ONE request (#48): a second call would let them drift\n * against a newer snapshot. They stay SEPARATE objects because the payload's\n * validator is closed and rejects any extra key - that closedness is the privacy\n * claim, so a kept-private name may sit beside the payloads and never inside one.\n *\n * The switch is read from the sync config the server just served. Off - or a\n * config the machine could not fetch, which reads as off - sends the payloads\n * alone and the names stay on the machine.\n */\nexport function buildSyncBody(\n\tbuilt: readonly BuiltPayload[],\n\tsyncConfig: SyncConfig,\n\tautoSync?: { enabled: boolean; frequencyHours: number },\n\ttrigger: SyncTrigger = \"manual\",\n\tmeasuredDays?: PayloadMeasuredDays,\n\tcliVersion?: string,\n): SyncBody {\n\tconst payloads = built.map((b) => b.payload);\n\tconst base: SyncBody = autoSync\n\t\t? { payloads, autoSync, trigger }\n\t\t: { payloads, trigger };\n\t// THE CONSENT GATES, APPLIED HERE TOO (#213, #307). The stage already\n\t// stripped what the owner declined before it fingerprinted the days; this\n\t// pass is idempotent and keeps the promise even for a caller that did not.\n\tconst withDays: SyncBody = measuredDays\n\t\t? {\n\t\t\t\t...base,\n\t\t\t\tmeasuredDays: {\n\t\t\t\t\taggregateVersion: MEASURED_DAYS_V1,\n\t\t\t\t\tutcOffsetMinutes: measuredDays.utcOffsetMinutes,\n\t\t\t\t\tdays: applyDayConsent(measuredDays.days, syncConfig),\n\t\t\t\t},\n\t\t\t}\n\t\t: base;\n\tconst withVersion: SyncBody = cliVersion\n\t\t? { ...withDays, cliVersion }\n\t\t: withDays;\n\tif (!syncConfig.reviewKeptPrivate) return withVersion;\n\treturn {\n\t\t...withVersion,\n\t\tkeptPrivate: mergeKeptPrivate(built.map((b) => b.keptPrivate)),\n\t};\n}\n","import {\n\tclassifyEvent,\n\tderiveSessionPhases,\n\tEFFORT_LEVELS,\n\ttype EffortLevel,\n\teffortLevelOf,\n\ttype HarnessDay,\n\ttype HarnessEvent,\n\ttype HarnessName,\n\tLOG_BUCKETS_V1,\n\tLOG_BUCKETS_V2,\n\tlogBucket,\n\tlogBucketV2,\n\tPHASE_RULES_V1,\n\tPHASES,\n\ttype PhaseId,\n\ttype SessionLengthBucket,\n\tUNKNOWN_GATE,\n\tWORKFLOW_AGGREGATES_V3,\n} from \"@aistack/workflow-rules\";\nimport { sanitizeModelId } from \"../harness/shared/payload.js\";\n\nexport const WORKFLOW_AGGREGATE_VERSION = WORKFLOW_AGGREGATES_V3;\n\n/**\n * The first call of a session, split (#358). `harnessTokens` is what the\n * call read from a cache another session had already filled: the system\n * prompt and the tool definitions. `instructionsTokens` is what it wrote or\n * sent fresh: project instructions, memory, skills, agents, the first prompt.\n */\nexport type FirstCallSplit = {\n\tharnessTokens: number;\n\tinstructionsTokens: number;\n};\n\nexport type WorkflowObservation = {\n\tsession: string;\n\tprojectWorkspace?: string;\n\ttsMs: number;\n\tparentSession?: string;\n\tsidechain?: boolean;\n} & (\n\t| { type: \"event\"; tool: string; arg?: string; batchId?: string }\n\t| {\n\t\t\ttype: \"response\";\n\t\t\tresponseId?: string;\n\t\t\tmodel?: string;\n\t\t\tthinkingTokens?: number;\n\t\t\tresponseTokens?: number;\n\t\t\troutingTokens?: number;\n\t\t\teffort?: string;\n\t\t\tdurationSec?: number;\n\t\t\t/** What the request carried in: fresh input plus cache reads and writes. */\n\t\t\tcontextTokens?: number;\n\t\t\t/** The window the harness logged for this call, when it logs one. */\n\t\t\tcontextWindow?: number;\n\t\t\t/** Present on the first call of the session only. */\n\t\t\tfirstCall?: FirstCallSplit;\n\t }\n\t| { type: \"turn\"; turnId?: string; questionBack: boolean }\n\t/** A compaction boundary the harness logged. Lands on the day of the event. */\n\t| { type: \"compaction\" }\n);\n\n/** One harness's reading for one UTC day, with the day it belongs to. */\nexport type HarnessDayRow = HarnessDay & { date: string };\n\n/**\n * One harness's workflow reading over the sync window: one row per UTC day\n * that saw a session start, an event, or a response (#285).\n *\n * THE GATE IS OVER THE WHOLE WINDOW. \"`phase-rules/v1` ships only when a\n * harness has 20 percent unknown time or less\" (map notes), and a day is too\n * small a sample to judge that on: a quiet day with one unclassified command\n * would fail alone and pass inside its month. The extraction strips `phase`\n * from every day of a harness that fails, so the wire carries no phase atoms\n * a window could fold into a playbook the gate refused.\n */\nexport type HarnessWorkflowAggregate = {\n\taggregateVersion: typeof WORKFLOW_AGGREGATE_VERSION;\n\tharness: HarnessName;\n\tgate: {\n\t\truleVersion: typeof PHASE_RULES_V1;\n\t\tpublishable: boolean;\n\t\tsessions: number;\n\t\tunknownShare: number;\n\t};\n\tdays: HarnessDayRow[];\n};\n\n/** Raw local keys used to join harness activity to Git. Never serialize this value. */\nexport type WorkflowLocalSources = {\n\tprojectWorkspaces: Set<string>;\n\tactiveProjectDays: Map<string, Set<string>>;\n};\n\nexport function createWorkflowLocalSources(): WorkflowLocalSources {\n\treturn { projectWorkspaces: new Set(), activeProjectDays: new Map() };\n}\n\ntype SessionState = {\n\tevents: Array<{ event: HarnessEvent; batchId?: string }>;\n\tresponses: Map<\n\t\tstring,\n\t\t{\n\t\t\tmodel?: string;\n\t\t\tthinkingTokens?: number;\n\t\t\tresponseTokens?: number;\n\t\t\troutingTokens?: number;\n\t\t\teffort?: string;\n\t\t\tdurationSec?: number;\n\t\t\tcontextTokens?: number;\n\t\t\tcontextWindow?: number;\n\t\t\tfirstCall?: FirstCallSplit;\n\t\t\ttsMs: number;\n\t\t}\n\t>;\n\tnextAnonymousResponse: number;\n\tturns: Map<string, boolean>;\n\tnextAnonymousTurn: number;\n\tprojectWorkspaces: Set<string>;\n\tparentSession: string | undefined;\n\tsidechain: boolean;\n\tfirstTs: number | undefined;\n\tlastTs: number | undefined;\n};\n\nconst emptyPhase = (): Record<PhaseId, number> => ({\n\tscout: 0,\n\tbuild: 0,\n\tverify: 0,\n\thandoff: 0,\n\tunknown: 0,\n});\n\nconst finiteNonnegative = (value: number | undefined): number =>\n\tvalue !== undefined && Number.isFinite(value) && value > 0 ? value : 0;\n\nconst bump = <K>(map: Map<K, number>, key: K, amount = 1): void => {\n\tmap.set(key, (map.get(key) ?? 0) + amount);\n};\n\nexport const utcDateOf = (ms: number): string =>\n\tnew Date(ms).toISOString().slice(0, 10);\n\n/** A bucket histogram as sorted rows, the count under the caller's field name. */\nfunction asBuckets<K extends string>(\n\tmap: Map<number, number>,\n\tfield: K,\n): ({ bucket: number } & Record<K, number>)[] {\n\treturn [...map]\n\t\t.map(\n\t\t\t([bucket, count]) =>\n\t\t\t\t({ bucket, [field]: count }) as { bucket: number } & Record<K, number>,\n\t\t)\n\t\t.sort((a, b) => a.bucket - b.bucket);\n}\n\nconst PHASE_RANK: Record<PhaseId, number> = {\n\tverify: 4,\n\thandoff: 3,\n\tbuild: 2,\n\tscout: 1,\n\tunknown: 0,\n};\n\nfunction reduceEventBatches(\n\trecorded: SessionState[\"events\"],\n\tharness: HarnessName,\n): HarnessEvent[] {\n\tconst sorted = [...recorded].sort((a, b) => a.event[0] - b.event[0]);\n\tconst output: HarnessEvent[] = [];\n\tconst batchIndexes = new Map<string, number>();\n\tfor (const row of sorted) {\n\t\tif (!row.batchId) {\n\t\t\toutput.push(row.event);\n\t\t\tcontinue;\n\t\t}\n\t\tconst existingIndex = batchIndexes.get(row.batchId);\n\t\tif (existingIndex === undefined) {\n\t\t\tbatchIndexes.set(row.batchId, output.length);\n\t\t\toutput.push(row.event);\n\t\t\tcontinue;\n\t\t}\n\t\tconst existing = output[existingIndex];\n\t\tif (!existing) continue;\n\t\tconst existingPhase = classifyEvent(\n\t\t\texisting[1],\n\t\t\texisting[2],\n\t\t\tnull,\n\t\t\tPHASE_RULES_V1,\n\t\t\tharness,\n\t\t).phase;\n\t\tconst candidatePhase = classifyEvent(\n\t\t\trow.event[1],\n\t\t\trow.event[2],\n\t\t\tnull,\n\t\t\tPHASE_RULES_V1,\n\t\t\tharness,\n\t\t).phase;\n\t\tif (PHASE_RANK[candidatePhase] > PHASE_RANK[existingPhase]) {\n\t\t\toutput[existingIndex] = [existing[0], row.event[1], row.event[2]];\n\t\t}\n\t}\n\treturn output;\n}\n\nconst hasVerifyRun = (\n\tevents: readonly HarnessEvent[],\n\tharness: HarnessName,\n): boolean =>\n\tevents.some(\n\t\t(event) =>\n\t\t\tderiveSessionPhases([event], PHASE_RULES_V1, harness).phaseEvents.verify >\n\t\t\t0,\n\t);\n\nfunction shellIncludes(arg: string, head: string): boolean {\n\treturn arg\n\t\t.split(/(?:&&|\\|\\||;|\\|)/)\n\t\t.some((part) => part.trim() === head || part.trim().startsWith(`${head} `));\n}\n\nfunction sessionState(): SessionState {\n\treturn {\n\t\tevents: [],\n\t\tresponses: new Map(),\n\t\tnextAnonymousResponse: 0,\n\t\tturns: new Map(),\n\t\tnextAnonymousTurn: 0,\n\t\tprojectWorkspaces: new Set(),\n\t\tparentSession: undefined,\n\t\tsidechain: false,\n\t\tfirstTs: undefined,\n\t\tlastTs: undefined,\n\t};\n}\n\n/** The accumulators behind one day's row, before they become plain arrays. */\ntype DayState = {\n\tsessions: number;\n\tstartHours: Map<number, number>;\n\tphase: {\n\t\tsessions: number;\n\t\tphaseSec: Record<PhaseId, number>;\n\t\tphaseEvents: Record<PhaseId, number>;\n\t\twaitingSec: number;\n\t\tidleSec: number;\n\t\tsessionsWithVerify: number;\n\t\tsessionsWithHandoff: number;\n\t\tlengths: Map<number, SessionLengthBucket>;\n\t};\n\trouting: { main: Map<string, number>; subagents: Map<string, number> };\n\thasRouting: boolean;\n\tdelegation: {\n\t\tmainToolCalls: number;\n\t\tsubagentToolCalls: number;\n\t\twidestFanOut: number;\n\t\tmostSubagents: number;\n\t};\n\thasDelegation: boolean;\n\tactivity: Map<string, number>;\n\teffort: Map<EffortLevel, number>;\n\thasEffort: boolean;\n\tthinking: { thinkingTokens: number; responseTokens: number };\n\thasThinking: boolean;\n\tturnDurations: Map<number, number>;\n\thasDurations: boolean;\n\tquestions: { asked: number; turns: number };\n\thasQuestions: boolean;\n\twebSearches: number;\n\thasWebSearches: boolean;\n\tcontext: {\n\t\tcalls: { main: Map<number, number>; subagents: Map<number, number> };\n\t\tfirstCalls: Map<number, number>;\n\t\tfirstCallHarnessTokens: number;\n\t\tfirstCallInstructionsTokens: number;\n\t\tfirstCallCount: number;\n\t\tmaxContext: number;\n\t\tcompactions: number;\n\t\twindow: { tsMs: number; window: number } | undefined;\n\t};\n\thasContext: boolean;\n};\n\nfunction dayState(): DayState {\n\treturn {\n\t\tsessions: 0,\n\t\tstartHours: new Map(),\n\t\tphase: {\n\t\t\tsessions: 0,\n\t\t\tphaseSec: emptyPhase(),\n\t\t\tphaseEvents: emptyPhase(),\n\t\t\twaitingSec: 0,\n\t\t\tidleSec: 0,\n\t\t\tsessionsWithVerify: 0,\n\t\t\tsessionsWithHandoff: 0,\n\t\t\tlengths: new Map(),\n\t\t},\n\t\trouting: { main: new Map(), subagents: new Map() },\n\t\thasRouting: false,\n\t\tdelegation: {\n\t\t\tmainToolCalls: 0,\n\t\t\tsubagentToolCalls: 0,\n\t\t\twidestFanOut: 0,\n\t\t\tmostSubagents: 0,\n\t\t},\n\t\thasDelegation: false,\n\t\tactivity: new Map(),\n\t\teffort: new Map(),\n\t\thasEffort: false,\n\t\tthinking: { thinkingTokens: 0, responseTokens: 0 },\n\t\thasThinking: false,\n\t\tturnDurations: new Map(),\n\t\thasDurations: false,\n\t\tquestions: { asked: 0, turns: 0 },\n\t\thasQuestions: false,\n\t\twebSearches: 0,\n\t\thasWebSearches: false,\n\t\tcontext: {\n\t\t\tcalls: { main: new Map(), subagents: new Map() },\n\t\t\tfirstCalls: new Map(),\n\t\t\tfirstCallHarnessTokens: 0,\n\t\t\tfirstCallInstructionsTokens: 0,\n\t\t\tfirstCallCount: 0,\n\t\t\tmaxContext: 0,\n\t\t\tcompactions: 0,\n\t\t\twindow: undefined,\n\t\t},\n\t\thasContext: false,\n\t};\n}\n\nexport type HarnessWorkflowReducer = {\n\tingest(observation: WorkflowObservation): void;\n\tfinish(): HarnessWorkflowAggregate;\n};\n\n/**\n * Reduce one harness's observations into per-day rows of combinable atoms.\n *\n * A SESSION BELONGS TO THE UTC DAY IT STARTED. Its phase seconds, its length\n * bucket, its model tokens, its effort and thinking and turn figures all land\n * on that day, so a session spanning midnight counts once. Event cells and web\n * searches land on the day of the event, so the heatmap stays exact.\n *\n * Nothing that names a path, a session, a command or a timestamp survives\n * `finish()`: the wire carries counts, sums, maxes and bucket indexes.\n */\nexport function createHarnessWorkflowReducer(\n\tharness: HarnessName,\n\tlocalSources: WorkflowLocalSources = createWorkflowLocalSources(),\n): HarnessWorkflowReducer {\n\tconst sessions = new Map<string, SessionState>();\n\tconst eventCells = new Map<string, Map<string, number>>();\n\tconst webSearchesByDate = new Map<string, number>();\n\tconst compactionsByDate = new Map<string, number>();\n\tconst eventDates = new Set<string>();\n\tlet finished: HarnessWorkflowAggregate | undefined;\n\n\tconst getSession = (key: string): SessionState => {\n\t\tlet state = sessions.get(key);\n\t\tif (!state) {\n\t\t\tstate = sessionState();\n\t\t\tsessions.set(key, state);\n\t\t}\n\t\treturn state;\n\t};\n\n\treturn {\n\t\tingest(observation): void {\n\t\t\tif (finished) return;\n\t\t\tif (!Number.isFinite(observation.tsMs)) return;\n\t\t\tconst state = getSession(observation.session);\n\t\t\tstate.firstTs =\n\t\t\t\tstate.firstTs === undefined\n\t\t\t\t\t? observation.tsMs\n\t\t\t\t\t: Math.min(state.firstTs, observation.tsMs);\n\t\t\tstate.lastTs =\n\t\t\t\tstate.lastTs === undefined\n\t\t\t\t\t? observation.tsMs\n\t\t\t\t\t: Math.max(state.lastTs, observation.tsMs);\n\t\t\tstate.parentSession ??= observation.parentSession;\n\t\t\tstate.sidechain ||= observation.sidechain === true;\n\t\t\tconst at = new Date(observation.tsMs);\n\t\t\tconst date = utcDateOf(observation.tsMs);\n\t\t\tif (observation.projectWorkspace) {\n\t\t\t\tstate.projectWorkspaces.add(observation.projectWorkspace);\n\t\t\t\tlocalSources.projectWorkspaces.add(observation.projectWorkspace);\n\t\t\t}\n\n\t\t\tif (observation.type === \"event\") {\n\t\t\t\tconst arg = observation.arg ?? \"\";\n\t\t\t\tstate.events.push({\n\t\t\t\t\tevent: [observation.tsMs, observation.tool, arg],\n\t\t\t\t\t...(observation.batchId ? { batchId: observation.batchId } : {}),\n\t\t\t\t});\n\t\t\t\teventDates.add(date);\n\t\t\t\tconst cells = eventCells.get(date) ?? new Map<string, number>();\n\t\t\t\tbump(cells, `${at.getUTCDay()}:${at.getUTCHours()}`);\n\t\t\t\teventCells.set(date, cells);\n\t\t\t\tif ([\"WebSearch\", \"web_search\", \"websearch\"].includes(observation.tool))\n\t\t\t\t\tbump(webSearchesByDate, date);\n\t\t\t} else if (observation.type === \"response\") {\n\t\t\t\tconst responseId =\n\t\t\t\t\tobservation.responseId ??\n\t\t\t\t\t`anonymous:${state.nextAnonymousResponse++}`;\n\t\t\t\tconst duration = finiteNonnegative(observation.durationSec);\n\t\t\t\tconst contextTokens = finiteNonnegative(observation.contextTokens);\n\t\t\t\tconst contextWindow = finiteNonnegative(observation.contextWindow);\n\t\t\t\tconst response = {\n\t\t\t\t\ttsMs: observation.tsMs,\n\t\t\t\t\t...(observation.model ? { model: observation.model } : {}),\n\t\t\t\t\t...(observation.thinkingTokens !== undefined\n\t\t\t\t\t\t? { thinkingTokens: finiteNonnegative(observation.thinkingTokens) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(observation.responseTokens !== undefined\n\t\t\t\t\t\t? { responseTokens: finiteNonnegative(observation.responseTokens) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(observation.routingTokens !== undefined\n\t\t\t\t\t\t? { routingTokens: finiteNonnegative(observation.routingTokens) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(observation.effort ? { effort: observation.effort } : {}),\n\t\t\t\t\t...(duration > 0 ? { durationSec: duration } : {}),\n\t\t\t\t\t...(observation.contextTokens !== undefined ? { contextTokens } : {}),\n\t\t\t\t\t...(contextWindow > 0 ? { contextWindow } : {}),\n\t\t\t\t\t...(observation.firstCall\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tfirstCall: {\n\t\t\t\t\t\t\t\t\tharnessTokens: finiteNonnegative(\n\t\t\t\t\t\t\t\t\t\tobservation.firstCall.harnessTokens,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tinstructionsTokens: finiteNonnegative(\n\t\t\t\t\t\t\t\t\t\tobservation.firstCall.instructionsTokens,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t};\n\t\t\t\tconst existing = state.responses.get(responseId);\n\t\t\t\tconst magnitude = (value: typeof response): number =>\n\t\t\t\t\tvalue.routingTokens ??\n\t\t\t\t\t(value.thinkingTokens ?? 0) + (value.responseTokens ?? 0);\n\t\t\t\tif (!existing || magnitude(response) > magnitude(existing)) {\n\t\t\t\t\tstate.responses.set(responseId, response);\n\t\t\t\t}\n\t\t\t} else if (observation.type === \"turn\") {\n\t\t\t\tconst turnId =\n\t\t\t\t\tobservation.turnId ?? `anonymous:${state.nextAnonymousTurn++}`;\n\t\t\t\tstate.turns.set(turnId, observation.questionBack);\n\t\t\t} else {\n\t\t\t\tbump(compactionsByDate, date);\n\t\t\t}\n\t\t},\n\n\t\tfinish(): HarnessWorkflowAggregate {\n\t\t\tif (finished) return finished;\n\t\t\tconst days = new Map<string, DayState>();\n\t\t\tconst dayOf = (date: string): DayState => {\n\t\t\t\tlet state = days.get(date);\n\t\t\t\tif (!state) {\n\t\t\t\t\tstate = dayState();\n\t\t\t\t\tdays.set(date, state);\n\t\t\t\t}\n\t\t\t\treturn state;\n\t\t\t};\n\n\t\t\tconst windowPhaseSec = emptyPhase();\n\t\t\tlet phaseSessionCount = 0;\n\n\t\t\tfor (const state of sessions.values()) {\n\t\t\t\tif (state.firstTs === undefined) continue;\n\t\t\t\tconst day = dayOf(utcDateOf(state.firstTs));\n\t\t\t\tconst events = reduceEventBatches(state.events, harness);\n\t\t\t\tconst responses = [...state.responses.values()];\n\n\t\t\t\tday.sessions++;\n\t\t\t\tconst startHour = new Date(state.firstTs).getUTCHours();\n\t\t\t\tday.startHours.set(startHour, (day.startHours.get(startHour) ?? 0) + 1);\n\n\t\t\t\t// The phase reading of this session.\n\t\t\t\tconst phases = deriveSessionPhases(events, PHASE_RULES_V1, harness);\n\t\t\t\tif (state.events.length > 0) phaseSessionCount++;\n\t\t\t\tday.phase.sessions++;\n\t\t\t\tfor (const phase of PHASES) {\n\t\t\t\t\tday.phase.phaseSec[phase] += phases.phaseSec[phase];\n\t\t\t\t\tday.phase.phaseEvents[phase] += phases.phaseEvents[phase];\n\t\t\t\t\twindowPhaseSec[phase] += phases.phaseSec[phase];\n\t\t\t\t}\n\t\t\t\tday.phase.waitingSec += phases.waitingSec;\n\t\t\t\tday.phase.idleSec += phases.idleSec;\n\t\t\t\tif (phases.phaseEvents.verify > 0) day.phase.sessionsWithVerify++;\n\t\t\t\tif (phases.phaseEvents.handoff > 0) day.phase.sessionsWithHandoff++;\n\n\t\t\t\tconst measuredSec = PHASES.reduce(\n\t\t\t\t\t(sum, phase) => sum + phases.phaseSec[phase],\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\tconst bucket = logBucket(measuredSec / 60);\n\t\t\t\tconst merged = events.some(\n\t\t\t\t\t([, tool, arg]) =>\n\t\t\t\t\t\t[\"Bash\", \"bash\", \"shell\", \"local_shell\", \"exec_command\"].includes(\n\t\t\t\t\t\t\ttool,\n\t\t\t\t\t\t) && shellIncludes(arg, \"gh pr merge\"),\n\t\t\t\t);\n\t\t\t\tconst verified = hasVerifyRun(events, harness);\n\t\t\t\tconst openedWithScout =\n\t\t\t\t\t(events[0]\n\t\t\t\t\t\t? deriveSessionPhases([events[0]], PHASE_RULES_V1, harness)\n\t\t\t\t\t\t\t\t.phaseEvents.scout\n\t\t\t\t\t\t: 0) > 0;\n\t\t\t\tconst length = day.phase.lengths.get(bucket) ?? {\n\t\t\t\t\tbucket,\n\t\t\t\t\tsessions: 0,\n\t\t\t\t\tphaseSec: emptyPhase(),\n\t\t\t\t\tmerged: 0,\n\t\t\t\t\tverified: 0,\n\t\t\t\t\tmergedVerified: 0,\n\t\t\t\t\topenedWithScout: 0,\n\t\t\t\t};\n\t\t\t\tlength.sessions++;\n\t\t\t\tfor (const phase of PHASES)\n\t\t\t\t\tlength.phaseSec[phase] += phases.phaseSec[phase];\n\t\t\t\tif (merged) length.merged++;\n\t\t\t\tif (verified) length.verified++;\n\t\t\t\tif (merged && verified) length.mergedVerified++;\n\t\t\t\tif (openedWithScout) length.openedWithScout++;\n\t\t\t\tday.phase.lengths.set(bucket, length);\n\n\t\t\t\t// Routing and delegation.\n\t\t\t\tconst routing =\n\t\t\t\t\tstate.sidechain || state.parentSession ? \"subagents\" : \"main\";\n\t\t\t\tfor (const response of responses) {\n\t\t\t\t\tif (!response.model) continue;\n\t\t\t\t\tday.hasRouting = true;\n\t\t\t\t\tbump(\n\t\t\t\t\t\tday.routing[routing],\n\t\t\t\t\t\tresponse.model,\n\t\t\t\t\t\tresponse.routingTokens ?? response.responseTokens ?? 0,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (routing === \"subagents\") {\n\t\t\t\t\tday.delegation.subagentToolCalls += state.events.length;\n\t\t\t\t\tday.hasDelegation ||= state.events.length > 0;\n\t\t\t\t} else day.delegation.mainToolCalls += state.events.length;\n\n\t\t\t\t// Effort, thinking, turn durations and questions.\n\t\t\t\tfor (const response of responses) {\n\t\t\t\t\tif (response.effort) {\n\t\t\t\t\t\tday.hasEffort = true;\n\t\t\t\t\t\tconst level = effortLevelOf(response.effort);\n\t\t\t\t\t\tday.effort.set(level, (day.effort.get(level) ?? 0) + 1);\n\t\t\t\t\t}\n\t\t\t\t\tif (response.thinkingTokens !== undefined) {\n\t\t\t\t\t\tday.hasThinking = true;\n\t\t\t\t\t\tday.thinking.thinkingTokens += response.thinkingTokens;\n\t\t\t\t\t\tday.thinking.responseTokens += response.responseTokens ?? 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (response.durationSec !== undefined) {\n\t\t\t\t\t\tday.hasDurations = true;\n\t\t\t\t\t\tconst durationBucket = logBucket(response.durationSec);\n\t\t\t\t\t\tday.turnDurations.set(\n\t\t\t\t\t\t\tdurationBucket,\n\t\t\t\t\t\t\t(day.turnDurations.get(durationBucket) ?? 0) + 1,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (harness !== \"pi-mono\") {\n\t\t\t\t\tday.hasQuestions = true;\n\t\t\t\t\tday.questions.turns += state.turns.size;\n\t\t\t\t\tday.questions.asked += [...state.turns.values()].filter(\n\t\t\t\t\t\tBoolean,\n\t\t\t\t\t).length;\n\t\t\t\t}\n\n\t\t\t\t// Per-call context (#358), on the session's start day like every\n\t\t\t\t// other response figure. First calls count on main sessions only:\n\t\t\t\t// the reading splits the MAIN median call, and a subagent's first\n\t\t\t\t// call carries a different fixed part.\n\t\t\t\tfor (const response of responses) {\n\t\t\t\t\tif (response.contextTokens === undefined) continue;\n\t\t\t\t\tday.hasContext = true;\n\t\t\t\t\tconst context = day.context;\n\t\t\t\t\tbump(context.calls[routing], logBucketV2(response.contextTokens));\n\t\t\t\t\tcontext.maxContext = Math.max(\n\t\t\t\t\t\tcontext.maxContext,\n\t\t\t\t\t\tresponse.contextTokens,\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\tresponse.contextWindow !== undefined &&\n\t\t\t\t\t\t(context.window === undefined ||\n\t\t\t\t\t\t\tresponse.tsMs >= context.window.tsMs)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcontext.window = {\n\t\t\t\t\t\t\ttsMs: response.tsMs,\n\t\t\t\t\t\t\twindow: response.contextWindow,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (response.firstCall && routing === \"main\") {\n\t\t\t\t\t\tbump(context.firstCalls, logBucketV2(response.contextTokens));\n\t\t\t\t\t\tcontext.firstCallHarnessTokens += response.firstCall.harnessTokens;\n\t\t\t\t\t\tcontext.firstCallInstructionsTokens +=\n\t\t\t\t\t\t\tresponse.firstCall.instructionsTokens;\n\t\t\t\t\t\tcontext.firstCallCount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Compactions, on the day of the boundary.\n\t\t\tfor (const [date, compactions] of compactionsByDate) {\n\t\t\t\tconst day = dayOf(date);\n\t\t\t\tday.hasContext = true;\n\t\t\t\tday.context.compactions += compactions;\n\t\t\t}\n\n\t\t\t// Event cells and web searches, on the day of the event.\n\t\t\tfor (const date of eventDates) {\n\t\t\t\tconst day = dayOf(date);\n\t\t\t\tfor (const [key, events] of eventCells.get(date) ?? []) {\n\t\t\t\t\tbump(day.activity, key, events);\n\t\t\t\t}\n\t\t\t\tif (harness !== \"pi-mono\") {\n\t\t\t\t\tday.hasWebSearches = true;\n\t\t\t\t\tday.webSearches = webSearchesByDate.get(date) ?? 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fan-out, on the parent's start day.\n\t\t\tconst childrenByParent = new Map<string, SessionState[]>();\n\t\t\tfor (const state of sessions.values()) {\n\t\t\t\tif (!state.parentSession) continue;\n\t\t\t\tconst children = childrenByParent.get(state.parentSession) ?? [];\n\t\t\t\tchildren.push(state);\n\t\t\t\tchildrenByParent.set(state.parentSession, children);\n\t\t\t}\n\t\t\tfor (const [parentKey, children] of childrenByParent) {\n\t\t\t\tconst parent = sessions.get(parentKey);\n\t\t\t\tconst anchor =\n\t\t\t\t\tparent?.firstTs ??\n\t\t\t\t\tMath.min(...children.map((child) => child.firstTs ?? Infinity));\n\t\t\t\tif (!Number.isFinite(anchor)) continue;\n\t\t\t\tconst day = dayOf(utcDateOf(anchor));\n\t\t\t\tday.hasDelegation = true;\n\t\t\t\tday.delegation.mostSubagents = Math.max(\n\t\t\t\t\tday.delegation.mostSubagents,\n\t\t\t\t\tchildren.length,\n\t\t\t\t);\n\t\t\t\tconst boundaries = children.flatMap((child) => [\n\t\t\t\t\t{ ts: child.firstTs ?? 0, delta: 1 },\n\t\t\t\t\t{ ts: child.lastTs ?? child.firstTs ?? 0, delta: -1 },\n\t\t\t\t]);\n\t\t\t\tboundaries.sort((a, b) => a.ts - b.ts || b.delta - a.delta);\n\t\t\t\tlet active = 0;\n\t\t\t\tfor (const boundary of boundaries) {\n\t\t\t\t\tactive += boundary.delta;\n\t\t\t\t\tday.delegation.widestFanOut = Math.max(\n\t\t\t\t\t\tday.delegation.widestFanOut,\n\t\t\t\t\t\tactive,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// The workspace-day marks Git and the parallel-project count read.\n\t\t\tlocalSources.activeProjectDays.clear();\n\t\t\tfor (const state of sessions.values()) {\n\t\t\t\tif (state.firstTs === undefined || state.lastTs === undefined) continue;\n\t\t\t\tlet day = Date.parse(`${utcDateOf(state.firstTs)}T00:00:00Z`);\n\t\t\t\tconst lastDay = Date.parse(`${utcDateOf(state.lastTs)}T00:00:00Z`);\n\t\t\t\twhile (day <= lastDay) {\n\t\t\t\t\tconst date = utcDateOf(day);\n\t\t\t\t\tconst projects =\n\t\t\t\t\t\tlocalSources.activeProjectDays.get(date) ?? new Set();\n\t\t\t\t\tfor (const project of state.projectWorkspaces) projects.add(project);\n\t\t\t\t\tif (projects.size > 0)\n\t\t\t\t\t\tlocalSources.activeProjectDays.set(date, projects);\n\t\t\t\t\tday += 86_400_000;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst attributed = PHASES.reduce(\n\t\t\t\t(sum, phase) => sum + windowPhaseSec[phase],\n\t\t\t\t0,\n\t\t\t);\n\t\t\tconst unknown =\n\t\t\t\tattributed === 0 ? 0 : windowPhaseSec.unknown / attributed;\n\t\t\tconst routesModels =\n\t\t\t\tharness === \"claude-code\" ||\n\t\t\t\tharness === \"opencode\" ||\n\t\t\t\tharness === \"grok-build\" ||\n\t\t\t\tharness === \"cursor\";\n\n\t\t\tconst asRows = (map: Map<string, number>) => {\n\t\t\t\tconst safe = new Map<string, number>();\n\t\t\t\tfor (const [model, tokens] of map) {\n\t\t\t\t\tbump(safe, sanitizeModelId(model), tokens);\n\t\t\t\t}\n\t\t\t\treturn [...safe]\n\t\t\t\t\t.map(([model, tokens]) => ({ model, tokens }))\n\t\t\t\t\t.sort(\n\t\t\t\t\t\t(a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model),\n\t\t\t\t\t);\n\t\t\t};\n\n\t\t\tfinished = {\n\t\t\t\taggregateVersion: WORKFLOW_AGGREGATE_VERSION,\n\t\t\t\tharness,\n\t\t\t\tgate: {\n\t\t\t\t\truleVersion: PHASE_RULES_V1,\n\t\t\t\t\tpublishable: phaseSessionCount > 0 && unknown <= UNKNOWN_GATE,\n\t\t\t\t\tsessions: sessions.size,\n\t\t\t\t\tunknownShare: unknown,\n\t\t\t\t},\n\t\t\t\tdays: [...days]\n\t\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t\t.map(([date, day]) => ({\n\t\t\t\t\t\tdate,\n\t\t\t\t\t\tharness,\n\t\t\t\t\t\tsessions: day.sessions,\n\t\t\t\t\t\tstartHours: [...day.startHours]\n\t\t\t\t\t\t\t.map(([hourUtc, count]) => ({ hourUtc, sessions: count }))\n\t\t\t\t\t\t\t.sort((a, b) => a.hourUtc - b.hourUtc),\n\t\t\t\t\t\t...(day.phase.sessions > 0\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tphase: {\n\t\t\t\t\t\t\t\t\t\truleVersion: PHASE_RULES_V1,\n\t\t\t\t\t\t\t\t\t\tsessions: day.phase.sessions,\n\t\t\t\t\t\t\t\t\t\tphaseSec: day.phase.phaseSec,\n\t\t\t\t\t\t\t\t\t\tphaseEvents: day.phase.phaseEvents,\n\t\t\t\t\t\t\t\t\t\twaitingSec: day.phase.waitingSec,\n\t\t\t\t\t\t\t\t\t\tidleSec: day.phase.idleSec,\n\t\t\t\t\t\t\t\t\t\tsessionsWithVerify: day.phase.sessionsWithVerify,\n\t\t\t\t\t\t\t\t\t\tsessionsWithHandoff: day.phase.sessionsWithHandoff,\n\t\t\t\t\t\t\t\t\t\tbucketRuleVersion: LOG_BUCKETS_V1,\n\t\t\t\t\t\t\t\t\t\tlengths: [...day.phase.lengths.values()].sort(\n\t\t\t\t\t\t\t\t\t\t\t(a, b) => a.bucket - b.bucket,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(routesModels && day.hasRouting\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\trouting: {\n\t\t\t\t\t\t\t\t\t\tmain: asRows(day.routing.main),\n\t\t\t\t\t\t\t\t\t\tsubagents: asRows(day.routing.subagents),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(day.hasDelegation ? { delegation: day.delegation } : {}),\n\t\t\t\t\t\tactivity: [...day.activity]\n\t\t\t\t\t\t\t.map(([key, events]) => {\n\t\t\t\t\t\t\t\tconst [weekdayUtc, hourUtc] = key.split(\":\").map(Number);\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tweekdayUtc: weekdayUtc ?? 0,\n\t\t\t\t\t\t\t\t\thourUtc: hourUtc ?? 0,\n\t\t\t\t\t\t\t\t\tevents,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t\t(a, b) => a.weekdayUtc - b.weekdayUtc || a.hourUtc - b.hourUtc,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t...(day.hasEffort\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\teffort: EFFORT_LEVELS.flatMap((level) => {\n\t\t\t\t\t\t\t\t\t\tconst turns = day.effort.get(level) ?? 0;\n\t\t\t\t\t\t\t\t\t\treturn turns > 0 ? [{ level, turns }] : [];\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(day.hasThinking ? { thinking: day.thinking } : {}),\n\t\t\t\t\t\t...(day.hasDurations\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tturnDurations: {\n\t\t\t\t\t\t\t\t\t\tbucketRuleVersion: LOG_BUCKETS_V1,\n\t\t\t\t\t\t\t\t\t\tbuckets: [...day.turnDurations]\n\t\t\t\t\t\t\t\t\t\t\t.map(([bucket, turns]) => ({ bucket, turns }))\n\t\t\t\t\t\t\t\t\t\t\t.sort((a, b) => a.bucket - b.bucket),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...(day.hasQuestions ? { questions: day.questions } : {}),\n\t\t\t\t\t\t...(day.hasWebSearches ? { webSearches: day.webSearches } : {}),\n\t\t\t\t\t\t...(day.hasContext\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\t\t\t\tbucketRuleVersion: LOG_BUCKETS_V2,\n\t\t\t\t\t\t\t\t\t\tcalls: {\n\t\t\t\t\t\t\t\t\t\t\tmain: asBuckets(day.context.calls.main, \"calls\"),\n\t\t\t\t\t\t\t\t\t\t\tsubagents: asBuckets(\n\t\t\t\t\t\t\t\t\t\t\t\tday.context.calls.subagents,\n\t\t\t\t\t\t\t\t\t\t\t\t\"calls\",\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tfirstCalls: {\n\t\t\t\t\t\t\t\t\t\t\tmain: asBuckets(day.context.firstCalls, \"sessions\"),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tfirstCallHarnessTokens: day.context.firstCallHarnessTokens,\n\t\t\t\t\t\t\t\t\t\tfirstCallInstructionsTokens:\n\t\t\t\t\t\t\t\t\t\t\tday.context.firstCallInstructionsTokens,\n\t\t\t\t\t\t\t\t\t\tfirstCallCount: day.context.firstCallCount,\n\t\t\t\t\t\t\t\t\t\tmaxContext: day.context.maxContext,\n\t\t\t\t\t\t\t\t\t\tcompactions: day.context.compactions,\n\t\t\t\t\t\t\t\t\t\t...(day.context.window\n\t\t\t\t\t\t\t\t\t\t\t? { window: day.context.window.window }\n\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t})),\n\t\t\t};\n\t\t\tsessions.clear();\n\t\t\teventCells.clear();\n\t\t\twebSearchesByDate.clear();\n\t\t\tcompactionsByDate.clear();\n\t\t\teventDates.clear();\n\t\t\treturn finished;\n\t\t},\n\t};\n}\n","// Pure fold over parsed Claude Code transcript records. No I/O, no console.\n//\n// Wayfinder ticket #37 (map #29), productizing the #32 prototype. Field\n// semantics come from docs/research/claude-code-transcripts-2026-07.md (#30),\n// as corrected by #32 and #33. Every field is treated as untrusted and\n// optional: records arrive as `unknown` and are narrowed here.\n//\n// The shared aggregate/finalize machinery lives in ../shared/aggregate.ts\n// (#67); this file owns what is CLAUDE-specific - the record shapes, and the\n// response dedup below.\n//\n// THE LOAD-BEARING SUBTLETY - read before touching `ingestAssistant`.\n// Claude Code writes ONE API response as SEVERAL JSONL records: each carries a\n// distinct content block (thinking, then tool_use, then tool_use...) and a\n// *cumulative* `usage` snapshot that grows with each record. Measured on a real\n// corpus: 20,073 of 44,280 response groups have differing usage across their\n// records, 20,071 of them monotonically increasing.\n//\n// So there are three wrong ways to count and one right way:\n// - sum every record -> ~2x over\n// - keep the first record -> ~2.1x under\n// - keep the last record -> right, but relies on file order\n// - keep the largest total -> right, order-independent <- this\n// Keeping the largest total is also ccusage's documented rule\n// (`should_replace_deduped_entry`).\n//\n// THE SECOND SUBTLETY - cost accumulates HERE, not in `finalize`.\n// Decision 8 of #33 made pricing time-aware, so a response is priced at the\n// rate in effect at its own timestamp. Summing tokens per model and pricing\n// once at the end cannot express a mid-window rate change, so each response's\n// cost is computed as it is ingested and un-applied on replace.\n\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\temptyUsage,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\tnoteSyntheticTokens,\n\tnoteUsageResponse,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n// Re-exported for the existing import sites (tests, stage, summary); the\n// definitions moved to ../shared/aggregate.ts in #67.\nexport {\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"../shared/aggregate.js\";\n\ntype Entry = {\n\tmodelKey: string;\n\tcounts: TokenCounts;\n\t/** `null` = no rate applied at this response's timestamp. */\n\tcostUSD: number | null;\n};\n\n/** One API response's full contribution, kept so it can be un-applied on replace. */\ntype Contribution = {\n\tentries: Entry[];\n\ttotal: number;\n\tsidechain: boolean;\n\t/** The response's own timestamp; the day its tokens belong to (#307). */\n\ttsMs: number | null;\n\twebSearch: number;\n\twebFetch: number;\n\t/** Iteration types that mirrored top-level usage, for the diagnostics line. */\n\tmirroredIterationTypes: Array<[string, number]>;\n\t/** Iterations naming a different model, attributed to that model (#33 dec. 9). */\n\tfallbackAttempts: number;\n\t/** Mirror-suspected iterations with no `model` field - skipped, not billed. */\n\tuntypedMirrors: number;\n};\n\ntype SeenEntry = { requestId: string | null; contribution: Contribution };\n\n/**\n * The Claude adapter's aggregate: the shared fold target, with `seen` keyed\n * by `message.id` holding this adapter's replay/continuation bookkeeping.\n */\nexport type Aggregate = SharedAggregate<SeenEntry> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n\tworkflowSeenCalls: Set<string>;\n\tworkflowSeenTurns: Set<string>;\n\t/**\n\t * The `message.id` of the first API call seen per workflow session key\n\t * (#358), so every record of that response carries the first-call split\n\t * and no later response does. `null` marks a session whose first call was\n\t * seen without an id, or before the window opened (`noteRecordBeforeWindow`).\n\t */\n\tcontextFirstCall: Map<string, string | null>;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<SeenEntry>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"claude-code\", workflowLocal),\n\t\tworkflowLocal,\n\t\tworkflowSeenCalls: new Set<string>(),\n\t\tworkflowSeenTurns: new Set<string>(),\n\t\tcontextFirstCall: new Map<string, string | null>(),\n\t});\n}\n\n/** The session key the workflow reducer sees: subagents get their own. */\nfunction workflowSessionKey(rec: Obj): string | null {\n\tconst baseSession = asStr(rec.sessionId);\n\tif (!baseSession) return null;\n\tif (rec.isSidechain !== true) return baseSession;\n\treturn `${baseSession}:agent:${asStr(rec.agentId) ?? \"unknown\"}`;\n}\n\n/** True for a record that is one API call: assistant, with usage, not the harness's own pseudo-model. */\nfunction isApiCall(rec: Obj): boolean {\n\tif (asStr(rec.type) !== \"assistant\") return false;\n\tconst msg = asObj(rec.message);\n\tif (!msg || !asObj(msg.usage)) return false;\n\treturn !(asName(msg.model) ?? \"\").startsWith(\"<\");\n}\n\n/**\n * Note a record the scan skipped because it predates the window. The only\n * fact that matters here is that the session already made a call, so the\n * first call the window does see is not the session's first (#358). Without\n * this a session resumed inside the window would file a mid-conversation\n * call as its startup overhead.\n */\nexport function noteRecordBeforeWindow(agg: Aggregate, raw: unknown): void {\n\tconst rec = asObj(raw);\n\tif (!rec || !isApiCall(rec)) return;\n\tconst session = workflowSessionKey(rec);\n\tif (session && !agg.contextFirstCall.has(session)) {\n\t\tagg.contextFirstCall.set(session, null);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Ingest\n// ---------------------------------------------------------------------------\n\nexport type IngestContext = { projectDir: string };\n\n/** Read the local project workspace directory from one untrusted record. */\nexport function projectWorkspaceDirectory(raw: unknown): string | null {\n\tconst rec = asObj(raw);\n\treturn rec ? asStr(rec.cwd) : null;\n}\n\n/** Fold one parsed JSONL record into the aggregate. */\nexport function ingestRecord(\n\tagg: Aggregate,\n\traw: unknown,\n\tctx: IngestContext,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\n\tagg.records++;\n\tconst projectDir = projectWorkspaceDirectory(rec) ?? ctx.projectDir;\n\tagg.projectDirs.add(projectDir);\n\n\tconst version = asStr(rec.version);\n\tif (version) agg.ccVersions.add(cleanName(version));\n\tconst sessionId = asStr(rec.sessionId);\n\tif (sessionId) agg.sessions.add(sessionId);\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) {\n\t\t\ttsMs = ts;\n\t\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\t\tagg.firstTs = agg.firstTs === null ? ts : Math.min(agg.firstTs, ts);\n\t\t\tagg.lastTs = agg.lastTs === null ? ts : Math.max(agg.lastTs, ts);\n\t\t}\n\t}\n\tif (sessionId) noteSessionStart(agg, sessionId, tsMs);\n\tnoteProjectDay(agg, projectDir, tsMs);\n\n\tconst type = asStr(rec.type);\n\tif (type === \"assistant\") {\n\t\tingestClaudeWorkflow(agg, rec, ctx, tsMs);\n\t\tingestAssistant(agg, rec, tsMs);\n\t} else if (type === \"user\") ingestUser(agg, rec);\n\telse if (type === \"system\") {\n\t\tingestClaudeTurnDuration(agg, rec, ctx, tsMs);\n\t\tingestClaudeCompaction(agg, rec, ctx, tsMs);\n\t}\n}\n\n/**\n * A compaction boundary: `{type: \"system\", subtype: \"compact_boundary\"}`\n * (#358). Counted on the day of the boundary; the sizes it carries are not\n * on the wire.\n */\nfunction ingestClaudeCompaction(\n\tagg: Aggregate,\n\trec: Obj,\n\tctx: IngestContext,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null || asStr(rec.subtype) !== \"compact_boundary\") return;\n\tconst session = workflowSessionKey(rec);\n\tif (!session) return;\n\tagg.workflow.ingest({\n\t\ttype: \"compaction\",\n\t\tsession,\n\t\tprojectWorkspace: projectWorkspaceDirectory(rec) ?? ctx.projectDir,\n\t\ttsMs,\n\t\t...(rec.isSidechain === true ? { sidechain: true } : {}),\n\t});\n}\n\n/** What the request carried in: fresh input plus every cache write and read. */\nfunction contextOf(counts: TokenCounts): number {\n\treturn (\n\t\tcounts.input +\n\t\tcounts.cacheWrite5m +\n\t\tcounts.cacheWrite1h +\n\t\tcounts.cacheWriteUnsplit +\n\t\tcounts.cacheRead\n\t);\n}\n\nfunction ingestClaudeTurnDuration(\n\tagg: Aggregate,\n\trec: Obj,\n\tctx: IngestContext,\n\ttsMs: number | null,\n): void {\n\tif (\n\t\ttsMs === null ||\n\t\tasStr(rec.subtype) !== \"turn_duration\" ||\n\t\tasNum(rec.durationMs) <= 0\n\t) {\n\t\treturn;\n\t}\n\tconst session = asStr(rec.sessionId);\n\tif (!session) return;\n\tagg.workflow.ingest({\n\t\ttype: \"response\",\n\t\tsession,\n\t\tprojectWorkspace: projectWorkspaceDirectory(rec) ?? ctx.projectDir,\n\t\ttsMs,\n\t\t...(asStr(rec.uuid) ? { responseId: `duration:${asStr(rec.uuid)}` } : {}),\n\t\tdurationSec: asNum(rec.durationMs) / 1000,\n\t});\n}\n\nfunction ingestClaudeWorkflow(\n\tagg: Aggregate,\n\trec: Obj,\n\tctx: IngestContext,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null) return;\n\tconst baseSession = asStr(rec.sessionId);\n\tconst msg = asObj(rec.message);\n\tif (!baseSession || !msg) return;\n\tconst projectWorkspace = projectWorkspaceDirectory(rec) ?? ctx.projectDir;\n\tconst sidechain = rec.isSidechain === true;\n\tconst agentId = asStr(rec.agentId);\n\tconst session = sidechain\n\t\t? `${baseSession}:agent:${agentId ?? \"unknown\"}`\n\t\t: baseSession;\n\tconst parentSession = sidechain ? baseSession : undefined;\n\tconst usage = asObj(msg.usage);\n\tconst counts = usage ? readCounts(usage) : null;\n\tconst messageId = asStr(msg.id);\n\tconst newTurn = messageId ? !agg.workflowSeenTurns.has(messageId) : true;\n\tif (messageId) agg.workflowSeenTurns.add(messageId);\n\n\t// Per-call context (#358). The first call of a session is the first API\n\t// call seen under its key; every record of that response carries the split\n\t// (the records of one response share one context), and a response seen\n\t// before the window opened has already claimed the slot with `null`.\n\tlet context: {\n\t\tcontextTokens: number;\n\t\tfirstCall?: { harnessTokens: number; instructionsTokens: number };\n\t} | null = null;\n\tif (counts && isApiCall(rec)) {\n\t\tconst held = agg.contextFirstCall.get(session);\n\t\tlet first = false;\n\t\tif (held === undefined) {\n\t\t\tagg.contextFirstCall.set(session, messageId);\n\t\t\tfirst = true;\n\t\t} else if (held !== null && held === messageId) first = true;\n\t\tcontext = {\n\t\t\tcontextTokens: contextOf(counts),\n\t\t\t...(first\n\t\t\t\t? {\n\t\t\t\t\t\tfirstCall: {\n\t\t\t\t\t\t\tharnessTokens: counts.cacheRead,\n\t\t\t\t\t\t\tinstructionsTokens: contextOf(counts) - counts.cacheRead,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tagg.workflow.ingest({\n\t\ttype: \"response\",\n\t\tsession,\n\t\tprojectWorkspace,\n\t\ttsMs,\n\t\tsidechain,\n\t\t...(parentSession ? { parentSession } : {}),\n\t\t...(messageId ? { responseId: messageId } : {}),\n\t\t...(asName(msg.model) ? { model: asName(msg.model) as string } : {}),\n\t\t...(counts ? { responseTokens: counts.output } : {}),\n\t\t...(counts ? { routingTokens: countsTotal(counts) } : {}),\n\t\t...((asStr(rec.effort) ?? asStr(msg.effort))\n\t\t\t? { effort: (asStr(rec.effort) ?? asStr(msg.effort)) as string }\n\t\t\t: {}),\n\t\t...(context ?? {}),\n\t});\n\n\tconst tools: Obj[] = [];\n\tfor (const rawBlock of asArr(msg.content)) {\n\t\tconst block = asObj(rawBlock);\n\t\tif (block && asStr(block.type) === \"tool_use\") tools.push(block);\n\t}\n\tfor (const block of tools) {\n\t\tconst id = asStr(block.id);\n\t\tif (id) {\n\t\t\tif (agg.workflowSeenCalls.has(id)) continue;\n\t\t\tagg.workflowSeenCalls.add(id);\n\t\t}\n\t\tconst name = asName(block.name);\n\t\tif (!name) continue;\n\t\tconst input = asObj(block.input) ?? {};\n\t\tlet arg = \"\";\n\t\tif (name === \"Skill\") arg = asStr(input.skill) ?? \"\";\n\t\telse if (name === \"Agent\" || name === \"Task\")\n\t\t\targ = asStr(input.subagent_type) ?? \"\";\n\t\telse if (name === \"Bash\") arg = asStr(input.command) ?? \"\";\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"event\",\n\t\t\tsession,\n\t\t\tprojectWorkspace,\n\t\t\ttsMs,\n\t\t\tsidechain,\n\t\t\t...(parentSession ? { parentSession } : {}),\n\t\t\ttool: name === \"Task\" ? \"Agent\" : name,\n\t\t\targ,\n\t\t\t...(messageId ? { batchId: messageId } : {}),\n\t\t});\n\t}\n\tif (tools.length > 0 || newTurn) {\n\t\tconst lastTool = tools.at(-1);\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession,\n\t\t\tprojectWorkspace,\n\t\t\ttsMs,\n\t\t\tsidechain,\n\t\t\t...(parentSession ? { parentSession } : {}),\n\t\t\t...(messageId ? { turnId: messageId } : {}),\n\t\t\tquestionBack: [\"AskUserQuestion\", \"ExitPlanMode\"].includes(\n\t\t\t\tasName(lastTool?.name) ?? \"\",\n\t\t\t),\n\t\t});\n\t}\n}\n\nfunction ingestAssistant(agg: Aggregate, rec: Obj, tsMs: number | null): void {\n\tagg.assistantRecords++;\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\n\tconst messageId = asStr(msg.id);\n\tconst requestId = asStr(rec.requestId);\n\tconst existing = messageId === null ? undefined : agg.seen.get(messageId);\n\t// A genuine replay is the same message.id under a NEW requestId. Its records\n\t// repeat content already counted; a continuation's records do not.\n\tconst isReplay = existing !== undefined && existing.requestId !== requestId;\n\n\t// Content blocks are counted per RECORD, deliberately outside the token\n\t// fold: the records of ONE response carry disjoint blocks (verified across\n\t// 44,478 groups - zero overlap), so folding them would drop real blocks.\n\t// Replays are the exception and must be skipped, because `tool_use` has\n\t// `block.id` to dedup on but thinking/text blocks have no identity at all.\n\tif (!isReplay) ingestContentBlocks(agg, msg.content);\n\n\tconst usage = asObj(msg.usage);\n\tif (!usage) return;\n\n\tconst model = asName(msg.model) ?? \"(unknown)\";\n\t// `<synthetic>` is the harness's own pseudo-model for records it generates\n\t// itself. Not a tool the user chose - excluded from inventory and pricing,\n\t// but its tokens are surfaced rather than silently dropped.\n\tif (model.startsWith(\"<\")) {\n\t\tagg.syntheticRecords++;\n\t\tconst synthetic = countsTotal(readCounts(usage));\n\t\tagg.syntheticTokens += synthetic;\n\t\tnoteSyntheticTokens(agg, tsMs, synthetic);\n\t\treturn;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst sidechain = rec.isSidechain === true;\n\tconst contribution = buildContribution(usage, model, sidechain, tsMs);\n\n\tif (messageId === null) {\n\t\t// No dedup key available - count it and record that we were unprotected.\n\t\tagg.unkeyedResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\treturn;\n\t}\n\n\tif (existing === undefined) {\n\t\tagg.distinctResponses++;\n\t\tacceptContribution(agg, contribution);\n\t\tagg.seen.set(messageId, { requestId, contribution });\n\t\treturn;\n\t}\n\n\tif (isReplay) agg.realReplaysFolded++;\n\telse agg.continuationsFolded++;\n\n\tif (!supersedes(contribution, existing.contribution)) return;\n\n\tagg.supersededByLarger++;\n\tretractContribution(agg, existing.contribution);\n\tacceptContribution(agg, contribution);\n\t// Keep the FIRST-seen requestId, not this record's. If a genuine replay wins\n\t// on tokens, overwriting it would make the replay's own later records compare\n\t// equal to the stored id, read as continuations, and get their thinking/text\n\t// blocks counted a second time - reopening exactly what the `isReplay` gate\n\t// above exists to close. (tool_use survives either way via `block.id`.)\n\tagg.seen.set(messageId, { requestId: existing.requestId, contribution });\n}\n\n/**\n * Apply a contribution and tally its diagnostics. Paired with\n * `retractContribution` so every per-response census stays per-RESPONSE rather\n * than per-record - these used to be bumped while merely *building* a\n * contribution, which counted every folded continuation too.\n */\nfunction acceptContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, +1);\n}\n\nfunction retractContribution(agg: Aggregate, c: Contribution): void {\n\tapplyContribution(agg, c, -1);\n}\n\n/**\n * ccusage's collision rule: a non-sidechain copy beats a sidechain one;\n * otherwise the larger token total wins. Order-independent by construction,\n * so the result does not depend on filesystem traversal order.\n */\nfunction supersedes(next: Contribution, prev: Contribution): boolean {\n\tif (prev.sidechain !== next.sidechain)\n\t\treturn prev.sidechain && !next.sidechain;\n\treturn next.total > prev.total;\n}\n\nfunction readCounts(usage: Obj): TokenCounts {\n\tconst t: TokenCounts = {\n\t\tinput: asNum(usage.input_tokens),\n\t\toutput: asNum(usage.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: asNum(usage.cache_read_input_tokens),\n\t};\n\tconst cacheWriteTotal = asNum(usage.cache_creation_input_tokens);\n\tconst cc = asObj(usage.cache_creation);\n\tif (cc) {\n\t\tt.cacheWrite5m = asNum(cc.ephemeral_5m_input_tokens);\n\t\tt.cacheWrite1h = asNum(cc.ephemeral_1h_input_tokens);\n\t\tconst residual = cacheWriteTotal - (t.cacheWrite5m + t.cacheWrite1h);\n\t\tif (residual > 0) t.cacheWriteUnsplit = residual;\n\t} else {\n\t\tt.cacheWriteUnsplit = cacheWriteTotal;\n\t}\n\treturn t;\n}\n\n/** `usage.speed === \"fast\"` prices under a separate, higher rate. */\nfunction modelKeyFor(model: string, speed: string | null): string {\n\treturn normalizeModel(speed === \"fast\" ? `${model}#fast` : model);\n}\n\nfunction makeEntry(\n\tmodelKey: string,\n\tcounts: TokenCounts,\n\ttsMs: number | null,\n): Entry {\n\treturn {\n\t\tmodelKey,\n\t\tcounts,\n\t\tcostUSD: apiEquivalentCost(modelKey, counts, tsMs),\n\t};\n}\n\nfunction buildContribution(\n\tusage: Obj,\n\tmodel: string,\n\tsidechain: boolean,\n\ttsMs: number | null,\n): Contribution {\n\tconst modelKey = modelKeyFor(model, asStr(usage.speed));\n\tconst entries: Entry[] = [makeEntry(modelKey, readCounts(usage), tsMs)];\n\tconst mirrored = new Map<string, number>();\n\tlet fallbackAttempts = 0;\n\tlet untypedMirrors = 0;\n\n\tfor (const rawIt of asArr(usage.iterations)) {\n\t\tconst it = asObj(rawIt);\n\t\tif (!it) continue;\n\t\tconst itType = asName(it.type) ?? \"(untyped)\";\n\t\tconst itModel = asName(it.model);\n\t\tconst itKey =\n\t\t\titModel === null ? null : modelKeyFor(itModel, asStr(it.speed));\n\n\t\t// Advisor iterations are a genuinely separate billed call under their own\n\t\t// model, never a mirror of top-level usage (ccusage prices them apart).\n\t\tif (itType === \"advisor_message\") {\n\t\t\tentries.push(makeEntry(itKey ?? modelKey, readCounts(it), tsMs));\n\t\t\tcontinue;\n\t\t}\n\n\t\t// #33 decision 9, SHARPENED - read the whole comment before touching this.\n\t\t//\n\t\t// The prototype skipped EVERY `type: \"message\"` iteration as a mirror of\n\t\t// top-level usage, which was correct by luck rather than construction: a\n\t\t// real `fallback_message` record showed top-level usage equal to the\n\t\t// fallback iteration EXACTLY, while a sibling `type: message` iteration\n\t\t// named a DIFFERENT model and carried tokens recorded nowhere else. So\n\t\t// `message.model` is already the serving model, and the mirror test is the\n\t\t// MODEL, not the type.\n\t\t//\n\t\t// #33 phrased the fix as \"skip it only when `iter.model === message.model`\".\n\t\t// Taken literally that is a ~2x overcount, because the corpus says the\n\t\t// `model` field is almost never there: of 63,638 non-advisor iterations,\n\t\t// 63,634 carry NO `model` at all - and all 63,634 are byte-exact mirrors of\n\t\t// their record's top-level usage (measured: zero differ). They carry 7.24\n\t\t// BILLION tokens, nearly double the corpus total, so attributing them as\n\t\t// separate entries would roughly double both tokens and cost. Only 8\n\t\t// iterations name a model: 4 matching (the `fallback_message` entries) and\n\t\t// 4 differing (the real first attempts).\n\t\t//\n\t\t// So the operative rule is: SKIP UNLESS THE ITERATION NAMES A DIFFERENT\n\t\t// MODEL. Absent is treated as matching - mis-attributing is a double-bill,\n\t\t// skipping is at worst an undercount, and the measurement above says it is\n\t\t// not even that.\n\t\tif (itKey === null) {\n\t\t\tuntypedMirrors++;\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tif (itKey === modelKey) {\n\t\t\tbump(mirrored, itType);\n\t\t\tcontinue;\n\t\t}\n\t\tentries.push(makeEntry(itKey, readCounts(it), tsMs));\n\t\tfallbackAttempts++;\n\t}\n\n\tconst serverTools = asObj(usage.server_tool_use);\n\treturn {\n\t\tentries,\n\t\ttotal: entries.reduce((a, e) => a + countsTotal(e.counts), 0),\n\t\tsidechain,\n\t\ttsMs,\n\t\twebSearch: serverTools ? asNum(serverTools.web_search_requests) : 0,\n\t\twebFetch: serverTools ? asNum(serverTools.web_fetch_requests) : 0,\n\t\tmirroredIterationTypes: [...mirrored],\n\t\tfallbackAttempts,\n\t\tuntypedMirrors,\n\t};\n}\n\n/** Add (sign +1) or remove (sign -1) a response's contribution from the totals. */\nfunction applyContribution(\n\tagg: Aggregate,\n\tc: Contribution,\n\tsign: 1 | -1,\n): void {\n\tc.entries.forEach(({ modelKey, counts, costUSD }, i) => {\n\t\tlet m = agg.byModel.get(modelKey);\n\t\tif (!m) {\n\t\t\tm = emptyUsage();\n\t\t\tagg.byModel.set(modelKey, m);\n\t\t}\n\t\t// One response is one message, even when a fallback attempt or an advisor\n\t\t// iteration attributes tokens to a second model - counting per entry would\n\t\t// inflate the response total past distinctResponses.\n\t\tif (i === 0) m.messages += sign;\n\t\tm.input += sign * counts.input;\n\t\tm.output += sign * counts.output;\n\t\tm.cacheWrite5m += sign * counts.cacheWrite5m;\n\t\tm.cacheWrite1h += sign * counts.cacheWrite1h;\n\t\tm.cacheWriteUnsplit += sign * counts.cacheWriteUnsplit;\n\t\tm.cacheRead += sign * counts.cacheRead;\n\t\tif (costUSD === null) m.unpricedTokens += sign * countsTotal(counts);\n\t\telse m.costUSD += sign * costUSD;\n\t\tnoteUsageResponse(\n\t\t\tagg,\n\t\t\t{ tsMs: c.tsMs, modelKey, counts, costUSD, sidechain: c.sidechain },\n\t\t\tsign,\n\t\t);\n\t});\n\tif (c.sidechain) agg.sidechainTokens += sign * c.total;\n\telse agg.mainTokens += sign * c.total;\n\tagg.webSearchRequests += sign * c.webSearch;\n\tagg.webFetchRequests += sign * c.webFetch;\n\tagg.fallbackAttempts += sign * c.fallbackAttempts;\n\tagg.untypedMirrors += sign * c.untypedMirrors;\n\tfor (const [type, count] of c.mirroredIterationTypes) {\n\t\tbump(agg.mirroredIterationTypes, type, sign * count);\n\t}\n}\n\nfunction ingestContentBlocks(agg: Aggregate, content: unknown): void {\n\tfor (const rawBlock of asArr(content)) {\n\t\tconst block = asObj(rawBlock);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") agg.thinkingBlocks++;\n\t\telse if (type === \"text\") agg.textBlocks++;\n\t\telse if (type === \"tool_use\") ingestToolUse(agg, block);\n\t}\n}\n\nfunction ingestToolUse(agg: Aggregate, block: Obj): void {\n\tconst name = asName(block.name);\n\tif (!name) return;\n\n\t// `toolu_...` block ids are globally unique, which makes this key both\n\t// collision-proof and replay-proof without a record-level prefix. A block\n\t// with no id is skipped rather than folded under a name-only key, which\n\t// would silently collapse every call to that tool into one.\n\tconst blockId = asStr(block.id);\n\tif (!blockId) {\n\t\tagg.toolBlocksWithoutId++;\n\t\treturn;\n\t}\n\tif (agg.toolCallDedup.has(blockId)) return;\n\tagg.toolCallDedup.add(blockId);\n\n\tconst input = asObj(block.input) ?? {};\n\n\tif (name.startsWith(\"mcp__\")) {\n\t\tconst parts = name.slice(\"mcp__\".length).split(\"__\");\n\t\tbump(agg.mcpServerCalls, parts[0] || \"(unknown)\");\n\t\tbump(agg.mcpToolCalls, name);\n\t\treturn;\n\t}\n\tif (name === \"Skill\") {\n\t\tbump(agg.skillCalls, asName(input.skill) ?? \"(unnamed)\");\n\t\tbump(agg.toolCalls, \"Skill\");\n\t\treturn;\n\t}\n\t// `Task` is the pre-rename spelling of `Agent`.\n\tif (name === \"Agent\" || name === \"Task\") {\n\t\tbump(agg.subagentCalls, asName(input.subagent_type) ?? \"(default)\");\n\t\tbump(agg.toolCalls, \"Agent\");\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, name);\n}\n\nconst SLASH_RE = /<command-name>\\/?([^<\\n\\r]{1,64})<\\/command-name>/g;\n\nfunction ingestUser(agg: Aggregate, rec: Obj): void {\n\tconst msg = asObj(rec.message);\n\tif (!msg) return;\n\tconst content = msg.content;\n\n\tlet text = \"\";\n\tif (typeof content === \"string\") text = content;\n\telse {\n\t\tfor (const rawBlock of asArr(content)) {\n\t\t\tconst block = asObj(rawBlock);\n\t\t\tif (!block) continue;\n\t\t\tif (asStr(block.type) === \"text\") text += asStr(block.text) ?? \"\";\n\t\t}\n\t}\n\tif (!text.includes(\"<command-name>\")) return;\n\n\t// `matchAll` over `exec` in a loop: the regex is module-level and `g`-flagged,\n\t// so an `exec` loop carries a shared `lastIndex` that a forgotten reset turns\n\t// into records being skipped at random.\n\tfor (const match of text.matchAll(SLASH_RE)) {\n\t\tbump(agg.slashCommands, cleanName(match[1]));\n\t}\n}\n","// I/O shell around the pure analyzer: find transcript roots, stream JSONL, hand\n// each parsed record to ingestRecord. Nothing leaves this machine.\n//\n// Wayfinder ticket #37 (map #29).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. Two consequences visible here:\n// project directories are counted but their names never escape this module, and\n// read errors are swallowed rather than thrown, because the error object carries\n// the absolute path and the munged project directory.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport {\n\temptyScanStats,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tingestRecord,\n\tnoteRecordBeforeWindow,\n\tprojectWorkspaceDirectory,\n} from \"./analyzer.js\";\n\nexport { type ScanStats, windowStartMs };\n\n/** Discovery order mirrors ccusage's adapter: CLAUDE_CONFIG_DIR, then the defaults. */\nexport function transcriptRoots(): string[] {\n\tconst env = process.env.CLAUDE_CONFIG_DIR;\n\tif (env) {\n\t\treturn env\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((s) => path.join(s, \"projects\"));\n\t}\n\tconst roots = [path.join(homedir(), \".claude\", \"projects\")];\n\tconst xdg = process.env.XDG_CONFIG_HOME ?? path.join(homedir(), \".config\");\n\troots.push(path.join(xdg, \"claude\", \"projects\"));\n\treturn roots;\n}\n\n/** What counts as a Claude Code transcript. Shared with `detect` (#101). */\nexport function isTranscriptFile(basename: string): boolean {\n\treturn basename.endsWith(\".jsonl\");\n}\n\n/** Recursive *.jsonl walk - the nested `<sessionId>/subagents/` layout is real. */\nasync function* walkJsonl(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkJsonl(full);\n\t\telse if (e.isFile() && isTranscriptFile(e.name)) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only ingest records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\n/**\n * KNOWN PERFORMANCE FLOOR - measured, decided, deliberately not fixed.\n *\n * Enumeration walks every project directory and `realpath`+`stat`s every file\n * BEFORE the mtime filter can skip anything, so a narrow window still pays a\n * floor proportional to TOTAL history (~60 ms over 3,206 files today, growing\n * linearly). The obvious fix - prune whole project directories by directory\n * mtime - is UNSOUND, and this was verified rather than assumed: appending to a\n * file does not update its parent directory's mtime (only adding, removing, or\n * renaming an entry does). A session resumed with `--resume` appends to a\n * transcript created before the window opened, inside a directory whose mtime\n * never moves, so dir-mtime pruning would silently drop live in-window records\n * - a wrong number, which is worse than a slow one for a tool whose whole claim\n * is measured-not-claimed.\n *\n * The sound version is a persisted enumeration cache, which cuts against #33\n * decision 1 (a sync is a stateless snapshot-replace, no durable client scan\n * state). So: DEFERRED. 60 ms is two orders of magnitude under the ~3 s full\n * scan and invisible next to the send round-trip; it becomes worth revisiting\n * only when total history reaches a scale where the floor dominates.\n */\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\t// Roots can overlap (CLAUDE_CONFIG_DIR may repeat a dir; ~/.claude and\n\t// ~/.config/claude may be symlinked together). Without this guard the same\n\t// file is ingested twice and the record/line/block counters silently double.\n\tconst visited = new Set<string>();\n\tconst projectWorkspaces = new Map<string, string>();\n\n\tfor (const root of opts.roots ?? transcriptRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkJsonl(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Transcripts are append-only and chronological, so a file untouched\n\t\t\t// since the window opened cannot hold an in-window record. This is what\n\t\t\t// makes a narrow window actually cheaper rather than merely narrower.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat - fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project dir = first path segment under projects/ (privacy-sensitive:\n\t\t\t// it is a munged absolute path, so it is only ever counted, never shown).\n\t\t\tconst rel = path.relative(root, file);\n\t\t\tconst projectDir = rel.split(path.sep)[0] ?? \"(root)\";\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tawait ingestFile(\n\t\t\t\t\tagg,\n\t\t\t\t\tfile,\n\t\t\t\t\tprojectDir,\n\t\t\t\t\tprojectWorkspaces,\n\t\t\t\t\topts.sinceMs,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\tprojectDir: string,\n\tprojectWorkspaces: Map<string, string>,\n\tsinceMs?: number,\n): Promise<void> {\n\tlet projectWorkspace = projectWorkspaces.get(projectDir) ?? projectDir;\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tfor await (const line of rl) {\n\t\tif (!line) continue;\n\t\tagg.lines++;\n\t\tlet rec: unknown;\n\t\ttry {\n\t\t\trec = JSON.parse(line);\n\t\t} catch {\n\t\t\tagg.parseErrors++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst cwd = projectWorkspaceDirectory(rec);\n\t\tif (cwd) {\n\t\t\tagg.projectDirs.delete(projectDir);\n\t\t\tprojectWorkspace = cwd;\n\t\t\tprojectWorkspaces.set(projectDir, cwd);\n\t\t}\n\t\tif (sinceMs !== undefined) {\n\t\t\tconst ts =\n\t\t\t\trec &&\n\t\t\t\ttypeof rec === \"object\" &&\n\t\t\t\t\"timestamp\" in rec &&\n\t\t\t\ttypeof (rec as { timestamp?: unknown }).timestamp === \"string\"\n\t\t\t\t\t? Date.parse((rec as { timestamp: string }).timestamp)\n\t\t\t\t\t: Number.NaN;\n\t\t\tif (Number.isNaN(ts) || ts < sinceMs) {\n\t\t\t\t// Skipped, but a call before the window still means the session's\n\t\t\t\t// first call is not the one the window will see (#358).\n\t\t\t\tnoteRecordBeforeWindow(agg, rec);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tingestRecord(agg, rec, { projectDir: projectWorkspace });\n\t}\n}\n","// The Claude Code harness behind the seam (#67). The parsing lives in\n// analyzer.ts/scan.ts, unchanged from the single-harness era; this file only\n// gives it the adapter shape.\n\nimport { BUILTIN_TOOLS } from \"../shared/allowlist.js\";\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isTranscriptFile, scan, transcriptRoots } from \"./scan.js\";\n\nexport const CLAUDE_HARNESS_NAME = \"claude-code\";\n\nexport const claudeAdapter: HarnessAdapter = {\n\tname: CLAUDE_HARNESS_NAME,\n\tbuiltinTools: BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? transcriptRoots(),\n\t\t\tisTranscriptFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","// Pure fold over parsed Codex CLI rollout lines. No I/O, no console.\n//\n// Wayfinder ticket #67 (map #60). Field semantics come from\n// docs/research/codex-session-log-anatomy-2026-08.md (#65) as locked by the\n// wire-format grilling #66. Every field is untrusted and optional: lines\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING SUBTLETY - Claude's cumulative gotcha, INVERTED.\n// Claude Code logs per-message usage that can repeat across snapshot records,\n// so its analyzer dedups by message id. Codex logs a `token_count` event whose\n// `total_token_usage` is the CUMULATIVE session sum - summing it across a\n// session's 20+ events overcounts by orders of magnitude. The rule locked in\n// #66: sum `last_token_usage` (the per-response delta) and never read the\n// totals. Deltas also carry the cached/non-cached split each response's cost\n// needs, which the cumulative figure re-counts every turn.\n//\n// Attribution: `token_count` events carry no model. Each delta is attributed\n// to the model of the nearest preceding `turn_context` in the same file.\n//\n// TokenCounts mapping (#66 decision 6): `cached_input_tokens` is a SUBSET of\n// `input_tokens`, so `input = input_tokens - cached_input`, `cacheRead =\n// cached_input`, and `cacheWrite = 0` - Codex reports no cache writes, and a\n// zero write prices correctly with zero pricing-code changes.\n\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Obj,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/** Codex needs no response dedup bookkeeping - deltas count once by construction. */\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n\tworkflowSeenCalls: Set<string>;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"codex\", workflowLocal),\n\t\tworkflowLocal,\n\t\tworkflowSeenCalls: new Set<string>(),\n\t});\n}\n\n/**\n * Per-file fold state. A rollout file is one session; the session id, CLI\n * version, cwd and current model are context lines that may sit BEFORE the\n * window opens, so they update state unconditionally and are only counted\n * when an in-window line lands (`noteActivity`).\n */\nexport type FileState = {\n\tsessionId: string | null;\n\tcliVersion: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding `turn_context`. */\n\tmodelKey: string | null;\n\teffort: string | null;\n\tresponseIndex: number;\n\tcurrentQuestionBack: boolean;\n\t/** True once any in-window line was counted for this file. */\n\tcounted: boolean;\n\t/** Timestamp of the file's `session_meta` line, for the replay guard. */\n\tmetaTsMs: number | null;\n\t/**\n\t * True on a rollout forked from another (`forked_from_id`). Its first\n\t * genuine call already carries the parent's history, so it is no first\n\t * call for the context split (#358); its calls still count.\n\t */\n\tforked: boolean;\n\t/** True once a genuine (non-replayed, nonzero) response was seen, in window or not. */\n\tsawResponse: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn {\n\t\tsessionId: null,\n\t\tcliVersion: null,\n\t\tcwd: null,\n\t\tmodelKey: null,\n\t\teffort: null,\n\t\tresponseIndex: 0,\n\t\tcurrentQuestionBack: false,\n\t\tcounted: false,\n\t\tmetaTsMs: null,\n\t\tforked: false,\n\t\tsawResponse: false,\n\t};\n}\n\n/**\n * The per-response delta of a `token_count` event, or null when the line is\n * a rate-limit-only refresh (zero delta) or replayed parent history (see\n * `FORK_REPLAY_WINDOW_MS`). Shared by the window-independent first-call\n * bookkeeping and the in-window fold, so both see the same responses.\n */\nfunction genuineDelta(\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n): { counts: TokenCounts; last: Obj; contextWindow: number } | null {\n\tif (asStr(payload.type) !== \"token_count\") return null;\n\tconst info = asObj(payload.info);\n\tconst last = info ? asObj(info.last_token_usage) : null;\n\tif (!last) return null;\n\n\tconst inputTotal = asNum(last.input_tokens);\n\tconst cached = Math.min(asNum(last.cached_input_tokens), inputTotal);\n\tconst counts: TokenCounts = {\n\t\tinput: inputTotal - cached,\n\t\toutput: asNum(last.output_tokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: 0,\n\t\tcacheRead: cached,\n\t};\n\t// A zero delta is a rate-limit-only refresh, not a response.\n\tif (countsTotal(counts) === 0) return null;\n\n\t// Replayed parent history (see FORK_REPLAY_WINDOW_MS): a `token_count`\n\t// stamped within the fork's write burst was counted by the parent rollout.\n\t// The replay often carries the parent's `turn_context` lines too, so the\n\t// timestamp is the guard; the `modelKey` check in `ingestEvent` only\n\t// backstops a replay whose head carried usage before any `turn_context`.\n\tif (\n\t\ttsMs !== null &&\n\t\tstate.metaTsMs !== null &&\n\t\ttsMs - state.metaTsMs < FORK_REPLAY_WINDOW_MS\n\t)\n\t\treturn null;\n\treturn {\n\t\tcounts,\n\t\tlast,\n\t\tcontextWindow: info ? asNum(info.model_context_window) : 0,\n\t};\n}\n\n/**\n * THE FORK-REPLAY GUARD. Codex forked threads (observed in codex-tui 0.151.0,\n * `forked_from_id` in `session_meta`) replay the parent's history into the new\n * rollout - `token_count` events included - re-stamped at fork creation. The\n * parent rollout already counted those deltas, so counting the replay double\n * counts them; on one machine the replays held 763M fresh tokens over 30 days.\n *\n * The replay is a synchronous write burst: across 166 local files every\n * replayed `token_count` sat within 144ms of the `session_meta` timestamp\n * (median 3ms), while the earliest GENUINE response of any session landed\n * 1,035ms after (5th percentile 5.3s) - a real response needs a network round\n * trip. 500ms splits the two populations with a wide margin on both sides.\n */\nconst FORK_REPLAY_WINDOW_MS = 500;\n\n/**\n * Fold one parsed rollout line into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context lines\n * (session_meta, turn_context) must update `state` even when they predate the\n * window - a session resumed today attributes today's deltas to a model named\n * last week.\n */\nexport function ingestLine(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tlet tsMs: number | null = null;\n\tconst timestamp = asStr(rec.timestamp);\n\tif (timestamp) {\n\t\tconst ts = Date.parse(timestamp);\n\t\tif (!Number.isNaN(ts)) tsMs = ts;\n\t}\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\n\tconst type = asStr(rec.type);\n\tconst payload = asObj(rec.payload);\n\n\tif (type === \"session_meta\" && payload) {\n\t\tstate.sessionId =\n\t\t\tasStr(payload.id) ?? asStr(payload.session_id) ?? state.sessionId;\n\t\tstate.cliVersion = asStr(payload.cli_version) ?? state.cliVersion;\n\t\tstate.cwd = asStr(payload.cwd) ?? state.cwd;\n\t\tstate.metaTsMs = tsMs ?? state.metaTsMs;\n\t\tstate.forked =\n\t\t\tpayload.forked_from_id !== undefined && payload.forked_from_id !== null;\n\t} else if (type === \"turn_context\" && payload) {\n\t\tconst model = asName(payload.model);\n\t\tif (model) state.modelKey = normalizeModel(model);\n\t\tstate.effort = asStr(payload.effort) ?? state.effort;\n\t}\n\n\t// The first genuine response of a file is a fact about the whole file, so\n\t// it is noted before the window filter: a session resumed inside the\n\t// window must not file a mid-conversation call as its first (#358).\n\tlet firstCall = false;\n\tif (type === \"event_msg\" && payload && genuineDelta(payload, state, tsMs)) {\n\t\tfirstCall = !state.sawResponse;\n\t\tstate.sawResponse = true;\n\t}\n\n\tif (!inWindow) return;\n\n\tif (tsMs !== null && timestamp) {\n\t\tagg.activeDays.add(timestamp.slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state, tsMs);\n\tnoteProjectDay(agg, state.cwd ?? \"(unknown)\", tsMs);\n\n\tif (type === \"event_msg\" && payload)\n\t\tingestEvent(agg, payload, state, tsMs, firstCall);\n\telse if (type === \"response_item\" && payload)\n\t\tingestItem(agg, payload, state, tsMs);\n\telse if (type === \"compacted\" && tsMs !== null && state.sessionId) {\n\t\t// A `compacted` rollout line is one compaction boundary (#358). The\n\t\t// zero-delta `token_count` that follows it stays skipped as usage.\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"compaction\",\n\t\t\tsession: state.sessionId,\n\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\ttsMs,\n\t\t});\n\t}\n}\n\n/** Count the file's session/version/cwd once, on its first in-window line. */\nfunction noteActivity(\n\tagg: Aggregate,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (state.sessionId) noteSessionStart(agg, state.sessionId, tsMs);\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\tif (state.cliVersion) agg.ccVersions.add(cleanName(state.cliVersion));\n\t// Counted, never published - same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n// ---------------------------------------------------------------------------\n// Usage - token_count deltas\n// ---------------------------------------------------------------------------\n\nfunction ingestEvent(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n\tfirstCall: boolean,\n): void {\n\tconst delta = genuineDelta(payload, state, tsMs);\n\tif (!delta) return;\n\tconst { counts, last, contextWindow } = delta;\n\tconst total = countsTotal(counts);\n\tif (state.modelKey === null) return;\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\n\tconst modelKey = state.modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t\t1,\n\t\t{ tsMs },\n\t);\n\t// Codex rollouts carry no sidechain flag; everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 rather than a guess.\n\tagg.mainTokens += total;\n\tif (tsMs !== null && state.sessionId) {\n\t\tconst responseId = `${state.sessionId}:response:${state.responseIndex++}`;\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"response\",\n\t\t\tsession: state.sessionId,\n\t\t\tresponseId,\n\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\ttsMs,\n\t\t\tmodel: modelKey,\n\t\t\tresponseTokens: counts.output,\n\t\t\troutingTokens: total,\n\t\t\tthinkingTokens: asNum(last.reasoning_output_tokens),\n\t\t\t...(state.effort ? { effort: state.effort } : {}),\n\t\t\t// The context at this call is `input_tokens` (cached is a subset).\n\t\t\t// On the first call the cached part is the harness (base\n\t\t\t// instructions and tool specs, cached by an earlier session) and\n\t\t\t// the fresh part is the instructions: AGENTS.md, environment,\n\t\t\t// skills and the first prompt. Same method as Claude Code (#358).\n\t\t\tcontextTokens: counts.input + counts.cacheRead,\n\t\t\t...(contextWindow > 0 ? { contextWindow } : {}),\n\t\t\t...(firstCall && !state.forked\n\t\t\t\t? {\n\t\t\t\t\t\tfirstCall: {\n\t\t\t\t\t\t\tharnessTokens: counts.cacheRead,\n\t\t\t\t\t\t\tinstructionsTokens: counts.input,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t});\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession: state.sessionId,\n\t\t\tturnId: responseId,\n\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\ttsMs,\n\t\t\tquestionBack: state.currentQuestionBack,\n\t\t});\n\t\tstate.currentQuestionBack = false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Inventory - response_item tool calls\n// ---------------------------------------------------------------------------\n\n/**\n * MCP tools reach the model as `<server>__<tool>` (MCP_TOOL_NAME_DELIMITER in\n * the Codex source); split on the FIRST `__` to recover the server. Over-long\n * names get a hash suffix on the TOOL side, so the server segment survives.\n */\nfunction ingestCall(agg: Aggregate, name: string, callId: string | null): void {\n\tif (callId) {\n\t\tif (agg.toolCallDedup.has(callId)) return;\n\t\tagg.toolCallDedup.add(callId);\n\t}\n\tconst sep = name.indexOf(\"__\");\n\tif (sep > 0) {\n\t\tbump(agg.mcpServerCalls, cleanName(name.slice(0, sep)));\n\t\tbump(agg.mcpToolCalls, cleanName(name));\n\t\treturn;\n\t}\n\tbump(agg.toolCalls, cleanName(name));\n}\n\nfunction ingestItem(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tconst type = asStr(payload.type);\n\tif (type === \"function_call\" || type === \"custom_tool_call\") {\n\t\tconst name = asName(payload.name);\n\t\tif (!name) return;\n\t\tconst callId = asStr(payload.call_id) ?? asStr(payload.id);\n\t\tingestCall(agg, name, callId);\n\t\tingestWorkflowCall(agg, payload, name, callId, state, tsMs);\n\t\treturn;\n\t}\n\t// Non-function tool items publish under stable synthetic names that live in\n\t// CODEX_BUILTIN_TOOLS, so they survive the fail-closed filter.\n\tif (type === \"local_shell_call\") {\n\t\tconst callId = asStr(payload.call_id) ?? asStr(payload.id);\n\t\tingestCall(agg, \"local_shell\", callId);\n\t\tingestWorkflowCall(agg, payload, \"local_shell\", callId, state, tsMs);\n\t} else if (type === \"web_search_call\") {\n\t\tagg.webSearchRequests++;\n\t\tingestCall(agg, \"web_search\", asStr(payload.id));\n\t\tingestWorkflowCall(\n\t\t\tagg,\n\t\t\tpayload,\n\t\t\t\"web_search\",\n\t\t\tasStr(payload.id),\n\t\t\tstate,\n\t\t\ttsMs,\n\t\t);\n\t} else if (type === \"tool_search_call\") {\n\t\tingestCall(agg, \"tool_search\", asStr(payload.id));\n\t\tingestWorkflowCall(\n\t\t\tagg,\n\t\t\tpayload,\n\t\t\t\"tool_search\",\n\t\t\tasStr(payload.id),\n\t\t\tstate,\n\t\t\ttsMs,\n\t\t);\n\t}\n}\n\nfunction ingestWorkflowCall(\n\tagg: Aggregate,\n\tpayload: Obj,\n\tname: string,\n\tcallId: string | null,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (tsMs === null || !state.sessionId) return;\n\tif (callId) {\n\t\tif (agg.workflowSeenCalls.has(callId)) return;\n\t\tagg.workflowSeenCalls.add(callId);\n\t}\n\tstate.currentQuestionBack = name === \"request_user_input\";\n\tlet arg = \"\";\n\tif (\n\t\t[\"exec_command\", \"shell\", \"container.exec\", \"local_shell\"].includes(name)\n\t) {\n\t\tconst raw =\n\t\t\tname === \"local_shell\"\n\t\t\t\t? asObj(payload.action)?.command\n\t\t\t\t: (payload.arguments ?? payload.input);\n\t\tif (Array.isArray(raw)) arg = unwrapShellCommand(raw.map(String));\n\t\telse if (typeof raw === \"string\" && !raw.trim().startsWith(\"{\")) arg = raw;\n\t\telse {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(String(raw ?? \"{}\")) as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tunknown\n\t\t\t\t>;\n\t\t\t\tconst command = parsed.cmd ?? parsed.command;\n\t\t\t\targ = Array.isArray(command)\n\t\t\t\t\t? unwrapShellCommand(command.map(String))\n\t\t\t\t\t: typeof command === \"string\"\n\t\t\t\t\t\t? command\n\t\t\t\t\t\t: \"\";\n\t\t\t} catch {\n\t\t\t\targ = \"\";\n\t\t\t}\n\t\t}\n\t}\n\tagg.workflow.ingest({\n\t\ttype: \"event\",\n\t\tsession: state.sessionId,\n\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\ttsMs,\n\t\ttool: name,\n\t\targ,\n\t});\n}\n\nfunction unwrapShellCommand(command: string[]): string {\n\tif (\n\t\tcommand.length >= 3 &&\n\t\t/^(bash|sh|zsh)$/.test(command[0] ?? \"\") &&\n\t\t/^-l?c$/.test(command[1] ?? \"\")\n\t)\n\t\treturn command.slice(2).join(\" \");\n\treturn command.join(\" \");\n}\n\n/**\n * Static MCP inventory from `~/.codex/config.toml` (#66 decision 3): a\n * configured server the window never called still exists. Zero-count entries\n * ride into the inventory (callShare 0) without inventing calls.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(raw);\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t}\n}\n","// I/O shell around the pure Codex analyzer: find rollout files, stream JSONL\n// (plain or zstd), hand each parsed line to ingestLine. Nothing leaves this\n// machine.\n//\n// Wayfinder ticket #67 (map #60), semantics from #65/#66.\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. `~/.codex/history.jsonl` holds raw\n// prompt text and is NEVER opened here; read errors are swallowed rather than\n// thrown, because the error object carries the absolute path.\n\nimport { type Dirent, readFileSync } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport * as zlib from \"node:zlib\";\n\nimport { parse as parseToml } from \"smol-toml\";\n\nimport { asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tingestLine,\n\tnoteConfiguredMcpServers,\n} from \"./analyzer.js\";\n\n/** `$CODEX_HOME` honored, `~/.codex` the default - mirrors the Codex source. */\nexport function codexHome(): string {\n\treturn process.env.CODEX_HOME || path.join(homedir(), \".codex\");\n}\n\n/**\n * Only `sessions/` is read. `archived_sessions/` is deliberately excluded: an\n * archived session was removed from the user's working set, and the rolling\n * window makes old ones irrelevant anyway. `history.jsonl` is raw prompts and\n * is out of bounds entirely.\n */\nexport function rolloutRoots(): string[] {\n\treturn [path.join(codexHome(), \"sessions\")];\n}\n\nconst ROLLOUT_RE = /^rollout-.*\\.jsonl(\\.zst)?$/;\n\n/** What counts as a Codex rollout. Shared with `detect` (#101). */\nexport function isRolloutFile(basename: string): boolean {\n\treturn ROLLOUT_RE.test(basename);\n}\n\n/** Recursive rollout walk - the YYYY/MM/DD nesting is real. */\nasync function* walkRollouts(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkRollouts(full);\n\t\telse if (e.isFile() && isRolloutFile(e.name)) yield full;\n\t}\n}\n\n/**\n * zstd support landed in node:zlib after the CLI's original Node 18 floor,\n * so it is feature-detected. On an old runtime a `.zst` rollout counts as\n * unreadable - a visible coverage figure, never a silent skip.\n */\nconst zstdDecompress: ((buf: Buffer) => Buffer) | null =\n\ttypeof (zlib as { zstdDecompressSync?: unknown }).zstdDecompressSync ===\n\t\"function\"\n\t\t? (buf) =>\n\t\t\t\t(\n\t\t\t\t\tzlib as unknown as { zstdDecompressSync: (b: Buffer) => Buffer }\n\t\t\t\t).zstdDecompressSync(buf)\n\t\t: null;\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n\t/** Override the config.toml path. Tests only. */\n\tconfigFile?: string;\n\t/** Override the file reader. Tests only. */\n\treadFileImpl?: (file: string) => Buffer | string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\n\tfor (const root of opts.roots ?? rolloutRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkRollouts(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\t// Dedup key = resolved path with `.zst` stripped. Codex's compression\n\t\t\t// worker leaves `foo.jsonl` and `foo.jsonl.zst` coexisting for a moment\n\t\t\t// (rename before unlink, #73 §4) - one session, two names. Keying on\n\t\t\t// the stem makes the second listing a duplicate, not a double count.\n\t\t\tconst dedupKey = resolved.endsWith(\".zst\")\n\t\t\t\t? resolved.slice(0, -\".zst\".length)\n\t\t\t\t: resolved;\n\t\t\tif (visited.has(dedupKey)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(dedupKey);\n\n\t\t\t// Rollouts are append-only and chronological, so a file untouched since\n\t\t\t// the window opened cannot hold an in-window record.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat - fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\tconst outcome = ingestWithRetry(agg, file, opts);\n\t\t\tif (!outcome.ok) {\n\t\t\t\t// Never rethrown: the error object carries the absolute path. The\n\t\t\t\t// stats keep a relative path and a bare error class instead (#75).\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tif (outcome.reason === \"zstd-unsupported\") stats.filesZstdUnsupported++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: outcome.reason,\n\t\t\t\t});\n\t\t\t} else if (!outcome.genuine) {\n\t\t\t\t// Fingerprint failure (#73): another tool wrote this file. Its usage\n\t\t\t\t// stayed out of the aggregate entirely.\n\t\t\t\tstats.filesForeign++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tconst seen = stats.foreignOriginators.get(outcome.originator) ?? 0;\n\t\t\t\tstats.foreignOriginators.set(outcome.originator, seen + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\treadConfiguredMcpServers(agg, opts.configFile);\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\ntype IngestOutcome =\n\t| { ok: true; genuine: true }\n\t| { ok: true; genuine: false; originator: string }\n\t| { ok: false; reason: string };\n\n/**\n * A read failure classified WITHOUT the error object's message or stack -\n * both carry the absolute path, which never leaves this module. `code` is a\n * bare class name (`ENOENT`, `EACCES`, `zstd-unsupported`, `zstd-corrupt`).\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n/**\n * The compression race (#73 §4): codex's background worker compresses a\n * rollout to `.zst` and then unlinks the plain `.jsonl`, so a file listed by\n * the walk can be gone at read time. Mirror codex's own reader: on `ENOENT`,\n * try the `.zst` sibling once before counting the file unreadable.\n */\nfunction ingestWithRetry(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\ttry {\n\t\treturn ingestFile(agg, file, opts);\n\t} catch (e) {\n\t\tif (errorClass(e) === \"ENOENT\" && !file.endsWith(\".zst\")) {\n\t\t\ttry {\n\t\t\t\treturn ingestFile(agg, `${file}.zst`, opts);\n\t\t\t} catch (e2) {\n\t\t\t\treturn { ok: false, reason: errorClass(e2) };\n\t\t\t}\n\t\t}\n\t\treturn { ok: false, reason: errorClass(e) };\n\t}\n}\n\n/**\n * Whole-file read rather than a stream: a `.zst` rollout must be decompressed\n * as one buffer anyway, and rollout files are single sessions - megabytes,\n * not gigabytes. The lines are parsed BEFORE any of them folds into the\n * aggregate, because the fingerprint verdict (#73) arrives only at end of\n * file: a foreign file must leave the aggregate untouched.\n */\nfunction ingestFile(\n\tagg: Aggregate,\n\tfile: string,\n\topts: ScanOptions,\n): IngestOutcome {\n\tconst readFile = opts.readFileImpl ?? readFileSync;\n\tlet text: string;\n\tif (file.endsWith(\".zst\")) {\n\t\tif (zstdDecompress === null) throw readError(\"zstd-unsupported\");\n\t\tconst raw = readFile(file);\n\t\ttry {\n\t\t\ttext = zstdDecompress(\n\t\t\t\tBuffer.isBuffer(raw) ? raw : Buffer.from(raw),\n\t\t\t).toString(\"utf8\");\n\t\t} catch {\n\t\t\tthrow readError(\"zstd-corrupt\");\n\t\t}\n\t} else {\n\t\ttext = readFile(file).toString(\"utf8\");\n\t}\n\n\tconst records: unknown[] = [];\n\tlet nonEmptyLines = 0;\n\tlet parseErrors = 0;\n\tfor (const line of text.split(\"\\n\")) {\n\t\tif (!line) continue;\n\t\tnonEmptyLines++;\n\t\ttry {\n\t\t\trecords.push(JSON.parse(line));\n\t\t} catch {\n\t\t\tparseErrors++;\n\t\t}\n\t}\n\n\tconst verdict = classifyRollout(records);\n\tif (!verdict.genuine) return { ok: true, ...verdict };\n\n\tagg.lines += nonEmptyLines;\n\tagg.parseErrors += parseErrors;\n\tconst state = createFileState();\n\tfor (const rec of records) ingestLine(agg, rec, state, opts.sinceMs);\n\treturn { ok: true, genuine: true };\n}\n\n/**\n * The genuine-rollout fingerprint (#73, source-pinned at rust-v0.146.0): the\n * codex-rs recorder always writes `session_meta` first, and every real user\n * turn persists a `turn_context` before its usage lands. Newer Codex (observed\n * in 0.151.0) broke the ORDER half of that invariant: a forked thread replays\n * the parent's history - `token_count` events included - ahead of its first\n * new turn, so usage may sit before any `turn_context`. The analyzer skips\n * that replayed head; here the rule weakens to presence: a file that carries\n * a `token_count` but no `turn_context` ANYWHERE was not written by Codex\n * CLI. Negative by construction - it detects \"not genuine\", never \"written by\n * tool X\"; the originator label is diagnostic only.\n */\nfunction classifyRollout(\n\trecords: readonly unknown[],\n): { genuine: true } | { genuine: false; originator: string } {\n\tlet originator: string | null = null;\n\tlet sawTurnContext = false;\n\tlet sawTokenCount = false;\n\tlet genuine = records.length > 0;\n\tfor (const [i, raw] of records.entries()) {\n\t\tconst rec = asObj(raw);\n\t\tconst type = rec ? asStr(rec.type) : null;\n\t\tconst payload = rec ? asObj(rec.payload) : null;\n\t\tif (i === 0 && type !== \"session_meta\") genuine = false;\n\t\tif (type === \"session_meta\" && payload && originator === null) {\n\t\t\toriginator = asStr(payload.originator);\n\t\t} else if (type === \"turn_context\") {\n\t\t\tsawTurnContext = true;\n\t\t} else if (\n\t\t\ttype === \"event_msg\" &&\n\t\t\tpayload &&\n\t\t\tasStr(payload.type) === \"token_count\"\n\t\t) {\n\t\t\tsawTokenCount = true;\n\t\t}\n\t}\n\tif (sawTokenCount && !sawTurnContext) genuine = false;\n\tif (genuine) return { genuine: true };\n\treturn { genuine: false, originator: originator ?? \"(none)\" };\n}\n\n/**\n * The static half of the MCP inventory (#66 decision 3): `[mcp_servers.*]`\n * in `~/.codex/config.toml`. Unreadable or absent config is silence, not an\n * error - the observed half stands on its own.\n */\nfunction readConfiguredMcpServers(agg: Aggregate, configFile?: string): void {\n\tconst file = configFile ?? path.join(codexHome(), \"config.toml\");\n\tlet names: string[] = [];\n\ttry {\n\t\tconst parsed = parseToml(readFileSync(file, \"utf8\"));\n\t\tconst servers = parsed.mcp_servers;\n\t\tif (servers && typeof servers === \"object\" && !Array.isArray(servers)) {\n\t\t\tnames = Object.keys(servers);\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n\tnoteConfiguredMcpServers(agg, names);\n}\n","// The Codex CLI harness behind the seam (#66 decision 6, built in #67).\n\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isRolloutFile, rolloutRoots, scan } from \"./scan.js\";\n\nexport const CODEX_HARNESS_NAME = \"codex\";\n\n/**\n * Codex's vendor-assigned tool surface, as observed in rollouts and pinned in\n * the Codex source (#65 §4). Same fail-closed mechanism as Claude's\n * BUILTIN_TOOLS: a literal set, never a pattern - an unknown-but-real\n * built-in withheld as a count is a small loss; an unknown-and-user-named\n * tool published verbatim is the leak this prevents. The last four are the\n * stable synthetic names the analyzer assigns to non-`function_call` items.\n *\n * Codex v1 publishes builtinTools and mcpServers ONLY (#66 decision 3):\n * slash commands verifiably never reach rollouts, and the skill/subagent\n * surfaces are unverified - those categories ship as empty arrays, absorbed\n * with no schema change if a later Codex version logs them.\n */\nexport const CODEX_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"exec_command\",\n\t\"grep_command\",\n\t\"list_dir\",\n\t\"read_file\",\n\t\"request_user_input\",\n\t\"shell\",\n\t\"unified_exec\",\n\t\"update_plan\",\n\t\"view_image\",\n\t\"write_stdin\",\n\t// synthetic names for non-function_call response items\n\t\"local_shell\",\n\t\"web_search\",\n\t\"tool_search\",\n]);\n\nexport const codexAdapter: HarnessAdapter = {\n\tname: CODEX_HARNESS_NAME,\n\tbuiltinTools: CODEX_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? rolloutRoots(),\n\t\t\tisRolloutFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { asObj } from \"../shared/aggregate.js\";\nimport { type Account, digest, fetchWindow } from \"./account.js\";\nimport { type Contribution, count, timestamp } from \"./evidence.js\";\n\nexport type CachedWindow = {\n\tfrom: number;\n\tto: number;\n\tfetchedAt: number;\n\tevents: Contribution[];\n};\nexport type CursorCache = {\n\tversion: 1;\n\taccount: string | null;\n\twindows: Record<string, CachedWindow>;\n\tlocal: Contribution[];\n\tsessions: string[];\n};\nexport const emptyCache = (): CursorCache => ({\n\tversion: 1,\n\taccount: null,\n\twindows: {},\n\tlocal: [],\n\tsessions: [],\n});\nexport const cacheFile = (root: string, store: string) =>\n\tpath.join(\n\t\thomedir(),\n\t\t\".config\",\n\t\t\"aistack\",\n\t\t\"cursor\",\n\t\t`${digest(`${root}\\0${store}`)}.json`,\n\t);\n\nfunction validContribution(value: unknown): value is Contribution {\n\tconst row = asObj(value);\n\tconst buckets = asObj(row?.buckets);\n\treturn (\n\t\t!!row &&\n\t\ttypeof row.session === \"string\" &&\n\t\ttypeof row.model === \"string\" &&\n\t\ttimestamp(row.tsMs) !== null &&\n\t\t(row.id === undefined || typeof row.id === \"string\") &&\n\t\t(row.source === \"api\" || row.source === \"local\") &&\n\t\t!!buckets &&\n\t\t[\"input\", \"output\", \"cacheRead\", \"cacheWrite\"].every(\n\t\t\t(k) => buckets[k] === undefined || count(buckets[k]) !== undefined,\n\t\t)\n\t);\n}\nexport async function loadCache(\n\tfile: string,\n): Promise<{ value: CursorCache; complete: boolean }> {\n\ttry {\n\t\tconst raw = asObj(JSON.parse(await readFile(file, \"utf8\")));\n\t\tconst windows = asObj(raw?.windows);\n\t\tif (\n\t\t\traw?.version !== 1 ||\n\t\t\t!(raw.account === null || typeof raw.account === \"string\") ||\n\t\t\t!Array.isArray(raw.sessions) ||\n\t\t\t!raw.sessions.every((id) => typeof id === \"string\") ||\n\t\t\t!Array.isArray(raw.local) ||\n\t\t\t!raw.local.every(validContribution) ||\n\t\t\t!windows\n\t\t)\n\t\t\tthrow new Error(\"Invalid cache\");\n\t\tfor (const value of Object.values(windows)) {\n\t\t\tconst w = asObj(value);\n\t\t\tif (\n\t\t\t\t!w ||\n\t\t\t\ttimestamp(w.from) === null ||\n\t\t\t\ttimestamp(w.to) === null ||\n\t\t\t\ttimestamp(w.fetchedAt) === null ||\n\t\t\t\t!Array.isArray(w.events) ||\n\t\t\t\t!w.events.every(validContribution)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Invalid window\");\n\t\t}\n\t\treturn { value: raw as CursorCache, complete: true };\n\t} catch (error) {\n\t\treturn {\n\t\t\tvalue: emptyCache(),\n\t\t\tcomplete: (error as NodeJS.ErrnoException).code === \"ENOENT\",\n\t\t};\n\t}\n}\nexport async function saveCache(\n\tfile: string,\n\tvalue: CursorCache,\n): Promise<void> {\n\tawait mkdir(path.dirname(file), { recursive: true, mode: 0o700 });\n\tconst temporary = `${file}.${process.pid}.${Date.now()}.tmp`;\n\tawait writeFile(temporary, JSON.stringify(value), { mode: 0o600 });\n\tawait rename(temporary, file);\n}\n\nconst WINDOW_MS = 30 * 86_400_000;\n/** Complete normalized windows replace atomically. ID-less multiplicity is never hashed away. */\nexport async function refreshAccount(input: {\n\tcache: CursorCache;\n\taccount: Account | null;\n\tsinceMs: number;\n\tnow: number;\n\tsessionIds: Set<string>;\n\tfetchImpl?: typeof fetch;\n}): Promise<CursorCache> {\n\tconst { cache, account, now, sessionIds } = input;\n\tif (!account) return cache;\n\tconst changed = cache.account !== account.scope;\n\tconst next: CursorCache = {\n\t\t...cache,\n\t\taccount: account.scope,\n\t\twindows: changed ? {} : { ...cache.windows },\n\t};\n\t// A complete query can still omit activity older than the service's retention.\n\t// Preserve empty historical windows previously populated; refresh today's window normally.\n\tfor (\n\t\tlet from = Math.floor(input.sinceMs / WINDOW_MS) * WINDOW_MS;\n\t\tfrom <= now;\n\t\tfrom += WINDOW_MS\n\t) {\n\t\tconst to = Math.min(from + WINDOW_MS, now + 1);\n\t\tconst key = String(from);\n\t\tconst previous = next.windows[key];\n\t\tif (previous && now - previous.fetchedAt < 300_000) continue;\n\t\ttry {\n\t\t\tconst events = (\n\t\t\t\tawait fetchWindow(account, from, to, input.fetchImpl)\n\t\t\t).filter((e) => sessionIds.has(e.session));\n\t\t\tif (previous?.events.length && events.length === 0 && to <= now) continue;\n\t\t\tnext.windows[key] = { from, to, fetchedAt: now, events };\n\t\t} catch {\n\t\t\t// A first run may still publish complete local evidence. Existing enrichment survives.\n\t\t\t// Stop after an auth/network failure, rather than retrying every historical window.\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn next;\n}\nexport function cachedEvents(\n\tcache: CursorCache,\n\tsessionIds: Set<string>,\n): Contribution[] {\n\tconst seen = new Set<string>();\n\treturn Object.values(cache.windows)\n\t\t.sort((a, b) => b.fetchedAt - a.fetchedAt || b.from - a.from)\n\t\t.flatMap((w) => w.events)\n\t\t.filter((e) => {\n\t\t\tif (!sessionIds.has(e.session) || (e.id && seen.has(e.id))) return false;\n\t\t\tif (e.id) seen.add(e.id);\n\t\t\treturn true;\n\t\t});\n}\n","import { createHash } from \"node:crypto\";\nimport { asObj, asStr } from \"../shared/aggregate.js\";\nimport { type Contribution, count, timestamp } from \"./evidence.js\";\nimport { globalDb } from \"./local.js\";\n\nexport type Account = { scope: string; cookie: string };\nexport const digest = (value: string) =>\n\tcreateHash(\"sha256\").update(value).digest(\"hex\");\n/** Only the credential already stored in SQLite is unattended on every platform.\n * Keychain APIs may display an OS prompt, so this path deliberately never calls them.\n */\nexport async function existingAccount(root: string): Promise<Account | null> {\n\tlet db: import(\"node:sqlite\").DatabaseSync | undefined;\n\ttry {\n\t\tconst { DatabaseSync } = await import(\"node:sqlite\");\n\t\tdb = new DatabaseSync(globalDb(root), { readOnly: true });\n\t\tconst row = db\n\t\t\t.prepare(\"SELECT value FROM ItemTable WHERE key = ?\")\n\t\t\t.get(\"cursorAuth/accessToken\");\n\t\tif (typeof row?.value !== \"string\") return null;\n\t\tconst token = row.value.trim().replace(/^\"|\"$/g, \"\");\n\t\tconst claims = asObj(\n\t\t\tJSON.parse(\n\t\t\t\tBuffer.from(token.split(\".\")[1] ?? \"\", \"base64url\").toString(\"utf8\"),\n\t\t\t),\n\t\t);\n\t\tconst subject = asStr(claims?.sub);\n\t\tif (\n\t\t\t!subject ||\n\t\t\tclaims?.type === \"api_key_token\" ||\n\t\t\t!/^[\\w|:-]+$/.test(subject) ||\n\t\t\t!/^[A-Za-z0-9_.-]+$/.test(token)\n\t\t)\n\t\t\treturn null;\n\t\tconst user = subject.split(\"|\").at(-1);\n\t\tif (!user) return null;\n\t\t// Expired access still identifies the account, so it cannot fall back to a different account cache.\n\t\treturn { scope: digest(subject), cookie: `${user}%3A%3A${token}` };\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tdb?.close();\n\t}\n}\n\nexport function apiContribution(value: unknown): Contribution | null {\n\tconst raw = asObj(value);\n\tif (!raw) throw new Error(\"Invalid Cursor event\");\n\tconst session = asStr(raw.conversationId);\n\tif (!session || raw.cloudAgentId || session.startsWith(\"bc-\")) return null;\n\tconst tsMs = timestamp(raw.timestamp);\n\tconst usage = raw.tokenUsage == null ? {} : asObj(raw.tokenUsage);\n\tif (tsMs === null || !usage) throw new Error(\"Invalid Cursor event\");\n\tconst buckets: Contribution[\"buckets\"] = {};\n\tfor (const [key, field] of [\n\t\t[\"input\", \"inputTokens\"],\n\t\t[\"output\", \"outputTokens\"],\n\t\t[\"cacheRead\", \"cacheReadTokens\"],\n\t\t[\"cacheWrite\", \"cacheWriteTokens\"],\n\t] as const) {\n\t\tif (usage[field] === undefined || usage[field] === null) continue;\n\t\tconst value = count(usage[field]);\n\t\tif (value === undefined) throw new Error(\"Invalid Cursor token bucket\");\n\t\tbuckets[key] = value;\n\t}\n\tconst id = asStr(raw.id) ?? asStr(raw.eventId);\n\treturn {\n\t\tsession,\n\t\t...(id ? { id } : {}),\n\t\ttsMs,\n\t\tmodel: asStr(raw.model) ?? \"unknown\",\n\t\tbuckets,\n\t\tsource: \"api\",\n\t};\n}\n\n/** A capped, repeated, malformed or failed page rejects the entire fixed query window. */\nexport async function fetchWindow(\n\taccount: Account,\n\tfrom: number,\n\tto: number,\n\tfetchImpl: typeof fetch = fetch,\n): Promise<Contribution[]> {\n\tconst out: Contribution[] = [];\n\tconst pages = new Set<string>();\n\tconst eventIds = new Set<string>();\n\tlet total: number | undefined;\n\tlet retrieved = 0;\n\tconst pageSize = 100;\n\tfor (let page = 1; page <= 100; page++) {\n\t\tconst response = await fetchImpl(\n\t\t\t\"https://cursor.com/api/dashboard/get-filtered-usage-events\",\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\tredirect: \"error\",\n\t\t\t\tsignal: AbortSignal.timeout(15_000),\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tOrigin: \"https://cursor.com\",\n\t\t\t\t\tCookie: `WorkosCursorSessionToken=${account.cookie}`,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tstartDate: from,\n\t\t\t\t\tendDate: to - 1,\n\t\t\t\t\tpage,\n\t\t\t\t\tpageSize,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t\tif (!response.ok) throw new Error(\"Cursor usage unavailable\");\n\t\tconst payload = asObj(await response.json());\n\t\tconst events = payload?.usageEventsDisplay;\n\t\tif (!Array.isArray(events) || events.length > pageSize)\n\t\t\tthrow new Error(\"Invalid Cursor page\");\n\t\tif (payload?.totalUsageEventsCount !== undefined) {\n\t\t\tconst reported = count(payload.totalUsageEventsCount);\n\t\t\tif (\n\t\t\t\treported === undefined ||\n\t\t\t\t!Number.isInteger(reported) ||\n\t\t\t\t(total !== undefined && total !== reported)\n\t\t\t)\n\t\t\t\tthrow new Error(\"Cursor page count changed\");\n\t\t\ttotal = reported;\n\t\t}\n\t\tif (events.length) {\n\t\t\tconst key = digest(JSON.stringify(events));\n\t\t\tif (pages.has(key)) throw new Error(\"Repeated Cursor page\");\n\t\t\tpages.add(key);\n\t\t}\n\t\tretrieved += events.length;\n\t\tfor (const value of events) {\n\t\t\tconst event = apiContribution(value);\n\t\t\tif (!event) continue;\n\t\t\tif (event.tsMs < from || event.tsMs >= to)\n\t\t\t\tthrow new Error(\"Cursor event outside query\");\n\t\t\tif (event.id && eventIds.has(event.id)) continue;\n\t\t\tif (event.id) eventIds.add(event.id);\n\t\t\tout.push(event);\n\t\t}\n\t\tif (total !== undefined && retrieved > total)\n\t\t\tthrow new Error(\"Invalid Cursor page count\");\n\t\tif (\n\t\t\t(total !== undefined && retrieved === total) ||\n\t\t\t(total === undefined && events.length < pageSize)\n\t\t)\n\t\t\treturn out;\n\t\tif (!events.length) throw new Error(\"Incomplete Cursor pages\");\n\t}\n\tthrow new Error(\"Cursor page limit\");\n}\n","import type { Message, Session } from \"cursor-history\";\nimport { asObj, asStr } from \"../shared/aggregate.js\";\n\nexport type Buckets = {\n\tinput?: number;\n\toutput?: number;\n\tcacheRead?: number;\n\tcacheWrite?: number;\n};\nexport type TokenEvidence = Buckets & {\n\tinputSource?: \"reported\" | \"context\" | \"dry-run\" | \"text\";\n\toutputSource?: \"reported\" | \"text\";\n};\nexport type Contribution = {\n\tsession: string;\n\tid?: string;\n\tnativeId?: string;\n\ttsMs: number;\n\tmodel: string;\n\tbuckets: TokenEvidence;\n\tsource: \"api\" | \"local\";\n\tsidechain?: boolean;\n};\nexport type LocalSession = {\n\tsession: Session;\n\t/** Keyed raw token scalars only. The reader's flattened pair loses zero/presence. */\n\ttokens: Map<string, TokenEvidence>;\n};\n\nexport function timestamp(value: unknown): number | null {\n\tconst n =\n\t\ttypeof value === \"number\"\n\t\t\t? value\n\t\t\t: typeof value === \"string\"\n\t\t\t\t? /^\\d+$/.test(value)\n\t\t\t\t\t? Number(value)\n\t\t\t\t\t: Date.parse(value)\n\t\t\t\t: NaN;\n\treturn Number.isFinite(n) && n > 0 && n < 8.64e15 ? n : null;\n}\nexport function count(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value >= 0\n\t\t? value\n\t\t: undefined;\n}\n\n/** Direct buckets win individually, including explicit zero. No cents conversion. */\nexport function tokenEvidence(value: unknown): TokenEvidence {\n\tconst raw = asObj(value) ?? {};\n\tconst direct = asObj(raw.tokenCount);\n\tconst usage = asObj(raw.usage);\n\tfor (const [obj, fields] of [\n\t\t[\n\t\t\tdirect,\n\t\t\t[\"inputTokens\", \"outputTokens\", \"cacheReadTokens\", \"cacheWriteTokens\"],\n\t\t],\n\t\t[\n\t\t\tusage,\n\t\t\t[\n\t\t\t\t\"input_tokens\",\n\t\t\t\t\"output_tokens\",\n\t\t\t\t\"cache_read_input_tokens\",\n\t\t\t\t\"cache_creation_input_tokens\",\n\t\t\t],\n\t\t],\n\t] as const) {\n\t\tfor (const field of fields)\n\t\t\tif (obj?.[field] != null && count(obj[field]) === undefined)\n\t\t\t\tthrow new Error(\"Invalid Cursor token evidence\");\n\t}\n\tconst out: TokenEvidence = {};\n\tout.input = count(direct?.inputTokens) ?? count(usage?.input_tokens);\n\tout.output = count(direct?.outputTokens) ?? count(usage?.output_tokens);\n\tout.cacheRead =\n\t\tcount(direct?.cacheReadTokens) ?? count(usage?.cache_read_input_tokens);\n\tout.cacheWrite =\n\t\tcount(direct?.cacheWriteTokens) ??\n\t\tcount(usage?.cache_creation_input_tokens);\n\tif (out.input !== undefined) out.inputSource = \"reported\";\n\tif (out.output !== undefined) out.outputSource = \"reported\";\n\tif (out.input === undefined) {\n\t\tconst context = count(asObj(raw.contextWindowStatusAtCreation)?.tokensUsed);\n\t\tif (context !== undefined) {\n\t\t\tout.input = context;\n\t\t\tout.inputSource = \"context\";\n\t\t} else if (typeof raw.promptDryRunInfo === \"string\") {\n\t\t\ttry {\n\t\t\t\tconst dry = asObj(JSON.parse(raw.promptDryRunInfo));\n\t\t\t\tconst estimate =\n\t\t\t\t\tcount(asObj(dry?.fullConversationTokenCount)?.numTokens) ??\n\t\t\t\t\tcount(asObj(dry?.userMessageTokenCount)?.numTokens);\n\t\t\t\tif (estimate !== undefined) {\n\t\t\t\t\tout.input = estimate;\n\t\t\t\t\tout.inputSource = \"dry-run\";\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* optional estimate */\n\t\t\t}\n\t\t}\n\t}\n\treturn out;\n}\n\nconst directTimes = new Set([\n\t\"composer-created-at\",\n\t\"composer-timing\",\n\t\"store-turn-timing\",\n]);\n/** Interpolate only date attribution. This does not establish call latency. */\nexport function messageTimes(\n\tsession: Session,\n\tapi: Contribution[] = [],\n): Array<number | null> {\n\tconst times = session.messages.map((m) =>\n\t\tdirectTimes.has(m.timestampSource ?? \"\") ? timestamp(m.timestamp) : null,\n\t);\n\tconst anchors: Array<{ index: number; time: number }> = [];\n\tfor (const [index, time] of times.entries())\n\t\tif (time !== null) anchors.push({ index, time });\n\tconst start =\n\t\tsession.createdAtSource !== \"epoch-unknown\"\n\t\t\t? timestamp(session.timestamp)\n\t\t\t: null;\n\tconst end =\n\t\tsession.lastUpdatedAtSource !== \"epoch-unknown\"\n\t\t\t? timestamp(session.metadata?.lastModified)\n\t\t\t: null;\n\tconst apiTimes = api\n\t\t.filter((e) => e.session === session.id)\n\t\t.map((e) => e.tsMs);\n\tif (!anchors.some((a) => a.index === 0)) {\n\t\tconst time = start ?? (apiTimes.length ? Math.min(...apiTimes) : null);\n\t\tif (time !== null) anchors.unshift({ index: -1, time });\n\t}\n\tconst last = session.messages.length;\n\tif (last && !anchors.some((a) => a.index === last - 1)) {\n\t\tconst time = end ?? (apiTimes.length ? Math.max(...apiTimes) : null);\n\t\tif (time !== null) anchors.push({ index: last, time });\n\t}\n\tfor (let i = 0; i < times.length; i++) {\n\t\tif (times[i] !== null) continue;\n\t\tconst before = anchors.filter((a) => a.index < i).at(-1);\n\t\tconst after = anchors.find((a) => a.index > i);\n\t\tif (before && after && after.time >= before.time)\n\t\t\ttimes[i] = Math.round(\n\t\t\t\tbefore.time +\n\t\t\t\t\t((after.time - before.time) * (i - before.index)) /\n\t\t\t\t\t\t(after.index - before.index),\n\t\t\t);\n\t\telse times[i] = before?.time ?? after?.time ?? null;\n\t}\n\treturn times;\n}\n\nexport function localContributions(\n\tlocal: LocalSession,\n\tapi: Contribution[] = [],\n): Contribution[] {\n\tconst { session, tokens } = local;\n\tconst times = messageTimes(session, api);\n\tconst out: Contribution[] = [];\n\tlet pending: Message | undefined;\n\tconst seen = new Set<string>();\n\tfor (const [i, message] of session.messages.entries()) {\n\t\tif (message.id && seen.has(message.id)) continue;\n\t\tif (message.id) seen.add(message.id);\n\t\tif (message.role === \"user\") {\n\t\t\tpending = message;\n\t\t\tcontinue;\n\t\t}\n\t\tconst own = tokens.get(message.id ?? \"\") ?? {};\n\t\tconst user = tokens.get(pending?.id ?? \"\") ?? {};\n\t\tconst input = own.input !== undefined ? own : user;\n\t\tconst buckets: TokenEvidence = {\n\t\t\t...own,\n\t\t\tinput:\n\t\t\t\tinput.input ??\n\t\t\t\t(pending ? Math.ceil(pending.content.length / 4) : undefined),\n\t\t\tinputSource: input.inputSource ?? (pending ? \"text\" : undefined),\n\t\t\toutput: own.output ?? Math.ceil(message.content.length / 4),\n\t\t\toutputSource: own.outputSource ?? \"text\",\n\t\t};\n\t\tpending = undefined;\n\t\tconst tsMs = times[i];\n\t\tif (tsMs === null) continue;\n\t\tout.push({\n\t\t\tsession: session.id,\n\t\t\t...(message.id ? { id: message.id } : {}),\n\t\t\t...(message.identityOrigin === \"composer-native\" && message.id\n\t\t\t\t? { nativeId: message.id }\n\t\t\t\t: {}),\n\t\t\ttsMs,\n\t\t\tmodel: asStr(message.model) ?? \"unknown\",\n\t\t\tbuckets,\n\t\t\tsource: \"local\",\n\t\t\t...(message.isSidechain ? { sidechain: true } : {}),\n\t\t});\n\t}\n\treturn out;\n}\n\n/** API events without a response join supersede the conversation's UTC-day total. */\nexport function reconcile(\n\tlocal: Contribution[],\n\tapi: Contribution[],\n): Contribution[] {\n\tconst grain = (c: Contribution) =>\n\t\t`${c.session}\\0${new Date(c.tsMs).toISOString().slice(0, 10)}`;\n\tconst reported = api.filter((row) =>\n\t\tObject.values(row.buckets).some((value) => typeof value === \"number\"),\n\t);\n\tconst grains = new Set(reported.map(grain));\n\treturn [...local.filter((c) => !grains.has(grain(c))), ...reported];\n}\n","import { stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport type { Session, SessionReadContext } from \"cursor-history\";\nimport { asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype LocalSession,\n\ttype TokenEvidence,\n\ttokenEvidence,\n} from \"./evidence.js\";\n\n/** Cursor's user-data override is a workspaceStorage path, as in cursor-history. */\nexport function dataPath(\n\tenv = process.env,\n\thome = homedir(),\n\tplatform = process.platform,\n): string {\n\tif (env.CURSOR_DATA_PATH) return path.resolve(env.CURSOR_DATA_PATH);\n\tconst base =\n\t\tplatform === \"darwin\"\n\t\t\t? path.join(home, \"Library\", \"Application Support\")\n\t\t\t: platform === \"win32\"\n\t\t\t\t? env.APPDATA || path.join(home, \"AppData\", \"Roaming\")\n\t\t\t\t: env.XDG_CONFIG_HOME || path.join(home, \".config\");\n\treturn path.join(base, \"Cursor\", \"User\", \"workspaceStorage\");\n}\nexport const storeRoot = () =>\n\tprocess.env.CURSOR_STORE_ROOT || path.join(homedir(), \".cursor\");\nexport const globalDb = (root: string) =>\n\tpath.join(path.dirname(root), \"globalStorage\", \"state.vscdb\");\n\nasync function exists(file: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(file);\n\t\treturn true;\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code !== \"ENOENT\";\n\t}\n}\nexport async function hasLocalSource(root = dataPath()): Promise<boolean> {\n\treturn (\n\t\tawait Promise.all(\n\t\t\t[\n\t\t\t\troot,\n\t\t\t\tglobalDb(root),\n\t\t\t\tpath.join(storeRoot(), \"projects\"),\n\t\t\t\tpath.join(storeRoot(), \"chats\"),\n\t\t\t\tpath.join(storeRoot(), \"acp-sessions\"),\n\t\t\t].map(exists),\n\t\t)\n\t).some(Boolean);\n}\n\ntype Sqlite = import(\"node:sqlite\").DatabaseSync;\n/** Supplemental read only for fields the selected reader flattens. No transcript parser. */\nexport async function readTokenEvidence(\n\troot: string,\n\tsession: Session,\n): Promise<Map<string, TokenEvidence>> {\n\tconst out = new Map<string, TokenEvidence>();\n\tif (!session.messages.some((m) => m.identityOrigin?.startsWith(\"composer\")))\n\t\treturn out;\n\tconst file = globalDb(root);\n\tif (!(await exists(file))) return out;\n\tconst { DatabaseSync } = await import(\"node:sqlite\");\n\tconst db: Sqlite = new DatabaseSync(file, { readOnly: true });\n\ttry {\n\t\tdb.exec(\"BEGIN\");\n\t\tconst table = db\n\t\t\t.prepare(\n\t\t\t\t\"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'cursorDiskKV'\",\n\t\t\t)\n\t\t\t.get();\n\t\tif (!table) return out;\n\t\tconst query = db.prepare(`SELECT json_object(\n\t\t\t'tokenCount', json_extract(value, '$.tokenCount'),\n\t\t\t'usage', json_extract(value, '$.usage'),\n\t\t\t'contextWindowStatusAtCreation', json_extract(value, '$.contextWindowStatusAtCreation'),\n\t\t\t'promptDryRunInfo', json_extract(value, '$.promptDryRunInfo')) AS evidence\n\t\t\tFROM cursorDiskKV WHERE key = ?`);\n\t\tfor (const message of session.messages) {\n\t\t\tif (!message.id || message.identityOrigin !== \"composer-native\") continue;\n\t\t\tconst row = query.get(`bubbleId:${session.id}:${message.id}`);\n\t\t\tif (typeof row?.evidence === \"string\")\n\t\t\t\tout.set(message.id, tokenEvidence(JSON.parse(row.evidence)));\n\t\t}\n\t\t// Older Composer headers can retain inline bubbles instead of split keys.\n\t\tconst header = db\n\t\t\t.prepare(\n\t\t\t\t\"SELECT json_extract(value, '$.conversation') AS conversation FROM cursorDiskKV WHERE key = ?\",\n\t\t\t)\n\t\t\t.get(`composerData:${session.id}`);\n\t\tif (typeof header?.conversation === \"string\") {\n\t\t\tconst conversation: unknown = JSON.parse(header.conversation);\n\t\t\tif (Array.isArray(conversation))\n\t\t\t\tfor (const [index, raw] of conversation.entries()) {\n\t\t\t\t\tconst obj = asObj(raw);\n\t\t\t\t\tconst id = asStr(obj?.bubbleId) ?? asStr(obj?.id) ?? `msg:${index}`;\n\t\t\t\t\tif (!out.has(id)) out.set(id, tokenEvidence(raw));\n\t\t\t\t}\n\t\t}\n\t\treturn out;\n\t} finally {\n\t\tdb.close();\n\t}\n}\n\nasync function sourceStamp(root: string): Promise<string> {\n\tconst values = await Promise.all(\n\t\t[globalDb(root), `${globalDb(root)}-wal`].map(async (file) => {\n\t\t\ttry {\n\t\t\t\tconst info = await stat(file);\n\t\t\t\treturn `${info.size}:${info.mtimeMs}`;\n\t\t\t} catch {\n\t\t\t\treturn \"unavailable\";\n\t\t\t}\n\t\t}),\n\t);\n\treturn values.join(\"/\");\n}\n\nexport type LocalRead = {\n\tsessions: LocalSession[];\n\tcomplete: boolean;\n\tstats: ScanStats;\n};\n/** Errors never escape with paths, prompts or database values. */\nexport async function readLocal(\n\troot = dataPath(),\n\tonProgress?: (files: number) => void,\n): Promise<LocalRead> {\n\tconst stats = emptyScanStats();\n\tconst out: LocalRead = { sessions: [], complete: true, stats };\n\tif (!(await hasLocalSource(root))) return out;\n\tconst before = await sourceStamp(root);\n\tlet context: SessionReadContext | undefined;\n\ttry {\n\t\tconst reader = await import(\"cursor-history\");\n\t\tconst options = {\n\t\t\tdataPath: root,\n\t\t\tsqliteDriver: \"node:sqlite\" as const,\n\t\t\tonDiagnostic: () => {\n\t\t\t\tout.complete = false;\n\t\t\t},\n\t\t\tsignal: AbortSignal.timeout(120_000),\n\t\t};\n\t\tcontext = reader.createSessionReadContext(options);\n\t\tconst config = { ...options, readContext: context };\n\t\tlet offset = 0;\n\t\twhile (true) {\n\t\t\tconst page = await reader.listSessionSummaries({\n\t\t\t\t...config,\n\t\t\t\toffset,\n\t\t\t\tlimit: 100,\n\t\t\t});\n\t\t\tfor (const summary of page.data) {\n\t\t\t\tstats.filesFound++;\n\t\t\t\tif (summary.resolutionState === \"ambiguous\") {\n\t\t\t\t\tout.complete = false;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst session = await reader.getSession(summary.id, config);\n\t\t\t\t\tif (\n\t\t\t\t\t\tsession.resolutionState !== \"complete\" ||\n\t\t\t\t\t\tsession.messages.some((m) => m.metadata?.corrupted)\n\t\t\t\t\t)\n\t\t\t\t\t\tout.complete = false;\n\t\t\t\t\tconst tokens = await readTokenEvidence(root, session);\n\t\t\t\t\tout.sessions.push({ session, tokens });\n\t\t\t\t\tstats.filesRead++;\n\t\t\t\t\tonProgress?.(stats.filesRead);\n\t\t\t\t} catch {\n\t\t\t\t\tout.complete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t} finally {\n\t\t\t\t\tcontext.releaseSession(summary.id);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!page.pagination.hasMore) break;\n\t\t\tif (page.data.length === 0 || offset >= 100_000) {\n\t\t\t\tout.complete = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toffset += page.data.length;\n\t\t}\n\t} catch {\n\t\tout.complete = false;\n\t\tstats.filesUnreadable++;\n\t} finally {\n\t\ttry {\n\t\t\tawait context?.dispose();\n\t\t} catch {\n\t\t\tout.complete = false;\n\t\t}\n\t}\n\tif (before !== (await sourceStamp(root))) out.complete = false;\n\treturn out;\n}\n","import path from \"node:path\";\nimport {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tcountsTotal,\n\tcreateAggregate,\n\temptyUsage,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\tutcDateOf,\n} from \"../shared/aggregate.js\";\nimport type { HarnessScan, HarnessScanOptions } from \"../types.js\";\nimport { type Account, existingAccount } from \"./account.js\";\nimport {\n\tcachedEvents,\n\tcacheFile,\n\tloadCache,\n\trefreshAccount,\n\tsaveCache,\n} from \"./cache.js\";\nimport { localContributions, messageTimes, reconcile } from \"./evidence.js\";\nimport { dataPath, type LocalRead, readLocal, storeRoot } from \"./local.js\";\nimport { projectHistory } from \"./workflow.js\";\n\nexport type ScanOptions = HarnessScanOptions & {\n\troot?: string;\n\tcachePath?: string;\n\tnow?: number;\n\treadLocalImpl?: (\n\t\troot: string,\n\t\tonProgress?: (files: number) => void,\n\t) => Promise<LocalRead>;\n\taccountImpl?: (root: string) => Promise<Account | null>;\n\tfetchImpl?: typeof fetch;\n};\nexport async function scan(options: ScanOptions): Promise<HarnessScan> {\n\tconst root = options.root ?? dataPath();\n\tconst now = options.now ?? Date.now();\n\tconst file = options.cachePath ?? cacheFile(root, storeRoot());\n\tconst local = await (options.readLocalImpl ?? readLocal)(\n\t\troot,\n\t\toptions.onProgress,\n\t);\n\tconst loaded = await loadCache(file);\n\tlet complete = local.complete && loaded.complete;\n\tconst ids = new Set(local.sessions.map((s) => s.session.id));\n\t// Disappearing local history cannot prove a full replacement, even when usage is cached.\n\tif (\n\t\t[\n\t\t\t...loaded.value.local,\n\t\t\t...Object.values(loaded.value.windows).flatMap((w) => w.events),\n\t\t].some((row) => row.tsMs >= options.sinceMs && !ids.has(row.session))\n\t)\n\t\tcomplete = false;\n\tconst account = await (options.accountImpl ?? existingAccount)(root);\n\tconst cache = await refreshAccount({\n\t\tcache: loaded.value,\n\t\taccount,\n\t\tsinceMs: options.sinceMs,\n\t\tnow,\n\t\tsessionIds: ids,\n\t\tfetchImpl: options.fetchImpl,\n\t});\n\tconst api = cachedEvents(cache, ids);\n\tconst native = new Set<string>();\n\tconst contributions = local.sessions\n\t\t.flatMap((s) => localContributions(s, api))\n\t\t.filter((row) => {\n\t\t\tif (!row.nativeId) return true;\n\t\t\tif (native.has(row.nativeId)) return false;\n\t\t\tnative.add(row.nativeId);\n\t\t\treturn true;\n\t\t});\n\tconst dated = new Set(contributions.map((row) => row.session));\n\tif (\n\t\tloaded.value.local.some(\n\t\t\t(row) => row.tsMs >= options.sinceMs && !dated.has(row.session),\n\t\t)\n\t)\n\t\tcomplete = false;\n\tconst previousDates = new Map<string, Set<string>>();\n\tfor (const row of [\n\t\t...loaded.value.local,\n\t\t...cachedEvents(loaded.value, ids),\n\t]) {\n\t\tconst dates = previousDates.get(row.session) ?? new Set();\n\t\tdates.add(utcDateOf(row.tsMs));\n\t\tpreviousDates.set(row.session, dates);\n\t}\n\tif (complete) {\n\t\tcache.local = contributions;\n\t\tcache.sessions = [...ids].sort();\n\t\ttry {\n\t\t\tawait saveCache(file, cache);\n\t\t} catch {\n\t\t\tcomplete = false;\n\t\t}\n\t}\n\tconst aggregate = createAggregate();\n\tconst workflowLocal = createWorkflowLocalSources();\n\tconst workflow = createHarnessWorkflowReducer(\"cursor\", workflowLocal);\n\tconst sessions = new Map(local.sessions.map((s) => [s.session.id, s]));\n\tconst sessionDates = previousDates;\n\tfor (const { session } of local.sessions) {\n\t\tfor (const message of session.messages)\n\t\t\tif (message.model) {\n\t\t\t\tconst model = normalizeModel(message.model);\n\t\t\t\tif (!aggregate.byModel.has(model))\n\t\t\t\t\taggregate.byModel.set(model, emptyUsage());\n\t\t\t}\n\t\tconst project = session.canonicalWorkspacePath;\n\t\tif (project && path.isAbsolute(project)) aggregate.projectDirs.add(project);\n\t\tif (session.metadata?.cursorVersion)\n\t\t\taggregate.ccVersions.add(session.metadata.cursorVersion);\n\t\tconst times = messageTimes(session, api).filter(\n\t\t\t(t): t is number => t !== null && t >= options.sinceMs && t <= now,\n\t\t);\n\t\tif (times.length) {\n\t\t\taggregate.sessions.add(session.id);\n\t\t\tnoteSessionStart(aggregate, session.id, Math.min(...times));\n\t\t}\n\t}\n\tconst usage = reconcile(contributions, api);\n\tprojectHistory({\n\t\tlocals: local.sessions,\n\t\tapi,\n\t\tusage,\n\t\taggregate,\n\t\tworkflow: options.publishWorkflow === false ? undefined : workflow,\n\t\tsinceMs: options.sinceMs,\n\t\tnow,\n\t});\n\tfor (const contribution of usage) {\n\t\tconst { tsMs, buckets, session, sidechain } = contribution;\n\t\tconst dates = sessionDates.get(session) ?? new Set();\n\t\tdates.add(utcDateOf(tsMs));\n\t\tsessionDates.set(session, dates);\n\t\tif (tsMs < options.sinceMs || tsMs > now) continue;\n\t\tconst model = normalizeModel(contribution.model);\n\t\tconst counts: TokenCounts = {\n\t\t\tinput: buckets.input ?? 0,\n\t\t\toutput: buckets.output ?? 0,\n\t\t\tcacheRead: buckets.cacheRead ?? 0,\n\t\t\tcacheWrite5m: 0,\n\t\t\tcacheWrite1h: 0,\n\t\t\tcacheWriteUnsplit: buckets.cacheWrite ?? 0,\n\t\t};\n\t\t// Value the reported model at shared API rates. Cursor account charges\n\t\t// include plan accounting and never substitute for token valuation.\n\t\taddModelUsage(\n\t\t\taggregate,\n\t\t\tmodel,\n\t\t\tcounts,\n\t\t\tapiEquivalentCost(model, counts, tsMs),\n\t\t\t1,\n\t\t\t{ tsMs, sidechain },\n\t\t);\n\t\taggregate.records++;\n\t\taggregate.assistantRecords++;\n\t\taggregate.distinctResponses++;\n\t\taggregate.sessions.add(session);\n\t\taggregate.activeDays.add(utcDateOf(tsMs));\n\t\taggregate.firstTs = Math.min(aggregate.firstTs ?? tsMs, tsMs);\n\t\taggregate.lastTs = Math.max(aggregate.lastTs ?? tsMs, tsMs);\n\t\tif (sidechain) aggregate.sidechainTokens += countsTotal(counts);\n\t\telse aggregate.mainTokens += countsTotal(counts);\n\t\tnoteSessionStart(aggregate, session, tsMs);\n\t\tconst project = sessions.get(session)?.session.canonicalWorkspacePath;\n\t\tif (project && path.isAbsolute(project))\n\t\t\tnoteProjectDay(aggregate, project, tsMs);\n\t}\n\taggregate.files = local.stats.filesRead;\n\treturn {\n\t\taggregate,\n\t\tstats: local.stats,\n\t\tworkflow: workflow.finish(),\n\t\tworkflowLocal,\n\t\tscanComplete: complete,\n\t\tsessionDates,\n\t};\n}\n","import path from \"node:path\";\nimport { normalizeModel } from \"@aistack/pricing\";\nimport type { Message } from \"cursor-history\";\nimport { detectMcpServers } from \"../../mcp.js\";\nimport type { HarnessWorkflowReducer } from \"../../workflow/reducer.js\";\nimport { type Aggregate, asStr, bump } from \"../shared/aggregate.js\";\nimport {\n\ttype Contribution,\n\ttype LocalSession,\n\tmessageTimes,\n} from \"./evidence.js\";\n\n/** Native names stay in inventory; only the reducer sees these established equivalents. */\nconst TOOLS: Record<string, string> = {\n\tread_file: \"Read\",\n\tread_file_v2: \"Read\",\n\tlist_dir: \"Glob\",\n\tglob_file_search: \"Glob\",\n\tgrep: \"Grep\",\n\tsearch: \"Grep\",\n\tcodebase_search: \"Grep\",\n\tedit_file: \"Edit\",\n\tedit_file_v2: \"Edit\",\n\tsearch_replace: \"Edit\",\n\twrite: \"Write\",\n\twrite_file: \"Write\",\n\tdelete_file: \"Edit\",\n\trun_terminal_cmd: \"Bash\",\n\trun_terminal_command: \"Bash\",\n\texecute_command: \"Bash\",\n\tweb_search: \"WebSearch\",\n\tweb_fetch: \"WebFetch\",\n\task_question: \"ask_question\",\n\ttodo_write: \"TodoWrite\",\n\ttask: \"Task\",\n\tskill: \"Skill\",\n};\nexport const CURSOR_BUILTIN_TOOLS: ReadonlySet<string> = new Set(\n\tObject.keys(TOOLS),\n);\n\n/** Project retained history without treating an accumulated turn as a Context call. */\nexport function projectHistory(options: {\n\tlocals: LocalSession[];\n\tapi: Contribution[];\n\tusage: Contribution[];\n\taggregate: Aggregate;\n\tworkflow?: HarnessWorkflowReducer;\n\tsinceMs: number;\n\tnow: number;\n}): void {\n\tconst { locals, api, usage, aggregate, workflow, sinceMs, now } = options;\n\tconst nativeOwners = new Map<string, string>();\n\tfor (const { session } of locals)\n\t\tfor (const message of session.messages)\n\t\t\tif (\n\t\t\t\tmessage.id &&\n\t\t\t\tmessage.identityOrigin === \"composer-native\" &&\n\t\t\t\t!message.isSidechain &&\n\t\t\t\t!nativeOwners.has(message.id)\n\t\t\t)\n\t\t\t\tnativeOwners.set(message.id, session.id);\n\tconst seenMessages = new Set<string>();\n\tconst seenTools = new Set<string>();\n\tconst configuredServers = new Map<string, string[]>();\n\tconst scopes = new Map<\n\t\tstring,\n\t\t{ session: string; sidechain?: boolean; parentSession?: string }\n\t>();\n\tconst inWindow = (t: number | null): t is number =>\n\t\tt !== null && t >= sinceMs && t <= now;\n\tfor (const { session } of locals) {\n\t\tconst projectWorkspace =\n\t\t\tsession.canonicalWorkspacePath &&\n\t\t\tpath.isAbsolute(session.canonicalWorkspacePath)\n\t\t\t\t? session.canonicalWorkspacePath\n\t\t\t\t: undefined;\n\t\tconst times = messageTimes(session, api);\n\t\tconst scopeOf = (message: Message) => {\n\t\t\t// A sidechain bit establishes routing. Only an actual parent reference establishes fan-out.\n\t\t\tconst parent =\n\t\t\t\tmessage.parentMessageId && nativeOwners.get(message.parentMessageId);\n\t\t\treturn message.isSidechain\n\t\t\t\t? {\n\t\t\t\t\t\tsession: `${session.id}:sidechain`,\n\t\t\t\t\t\tsidechain: true,\n\t\t\t\t\t\t...(parent ? { parentSession: parent } : {}),\n\t\t\t\t\t}\n\t\t\t\t: { session: session.id };\n\t\t};\n\t\tfor (const [index, message] of session.messages.entries()) {\n\t\t\tconst identity =\n\t\t\t\tmessage.identityOrigin === \"composer-native\" && message.id\n\t\t\t\t\t? `native:${message.id}`\n\t\t\t\t\t: `${session.id}:${message.id ?? index}`;\n\t\t\tif (seenMessages.has(identity)) continue;\n\t\t\tseenMessages.add(identity);\n\t\t\tconst tsMs = times[index];\n\t\t\tconst scope = scopeOf(message);\n\t\t\tif (message.id) scopes.set(`${session.id}:${message.id}`, scope);\n\t\t\tif (tsMs !== null && !inWindow(tsMs)) continue;\n\t\t\tconst common = { ...scope, projectWorkspace, tsMs: tsMs ?? 0 };\n\t\t\tif (message.role === \"user\") {\n\t\t\t\tif (inWindow(tsMs))\n\t\t\t\t\tworkflow?.ingest({\n\t\t\t\t\t\t...common,\n\t\t\t\t\t\ttype: \"response\",\n\t\t\t\t\t\tresponseId: `anchor:${message.id ?? index}`,\n\t\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet questionBack = false;\n\t\t\tfor (const [toolIndex, call] of (message.toolCalls ?? []).entries()) {\n\t\t\t\tconst id =\n\t\t\t\t\tcall.identityOrigin === \"source-native\" && call.id\n\t\t\t\t\t\t? `native:${call.id}`\n\t\t\t\t\t\t: `${identity}:${call.id ?? toolIndex}`;\n\t\t\t\tif (seenTools.has(id)) continue;\n\t\t\t\tseenTools.add(id);\n\t\t\t\tbump(aggregate.toolCalls, call.name);\n\t\t\t\tconst tool = TOOLS[call.name] ?? call.name;\n\t\t\t\tconst arg =\n\t\t\t\t\tasStr(call.params?.command) ??\n\t\t\t\t\tasStr(call.params?.cmd) ??\n\t\t\t\t\tasStr(call.params?.skill) ??\n\t\t\t\t\tasStr(call.params?.subagent_type) ??\n\t\t\t\t\t\"\";\n\t\t\t\tif (tool === \"WebSearch\") aggregate.webSearchRequests++;\n\t\t\t\tif (tool === \"WebFetch\") aggregate.webFetchRequests++;\n\t\t\t\tif (tool === \"Skill\" && arg) bump(aggregate.skillCalls, arg);\n\t\t\t\tif (tool === \"Task\") bump(aggregate.subagentCalls, arg || \"(unknown)\");\n\t\t\t\tconst mcp = /^mcp__(.+?)__(.+)$/.exec(call.name);\n\t\t\t\tlet server = mcp?.[1];\n\t\t\t\tif (!server && call.name.startsWith(\"mcp_\")) {\n\t\t\t\t\tconst directory = projectWorkspace ?? process.cwd();\n\t\t\t\t\tlet names = configuredServers.get(directory);\n\t\t\t\t\tif (!names) {\n\t\t\t\t\t\tnames = detectMcpServers(directory)\n\t\t\t\t\t\t\t.filter((r) => r.group === \"cursor\")\n\t\t\t\t\t\t\t.map((r) => r.name)\n\t\t\t\t\t\t\t.sort((a, b) => b.length - a.length);\n\t\t\t\t\t\tconfiguredServers.set(directory, names);\n\t\t\t\t\t}\n\t\t\t\t\tserver = names.find((name) => call.name.startsWith(`mcp_${name}_`));\n\t\t\t\t}\n\t\t\t\tif (server) {\n\t\t\t\t\tbump(aggregate.mcpServerCalls, server);\n\t\t\t\t\tbump(aggregate.mcpToolCalls, call.name);\n\t\t\t\t}\n\t\t\t\tquestionBack ||= tool === \"ask_question\";\n\t\t\t\tif (inWindow(tsMs))\n\t\t\t\t\tworkflow?.ingest({\n\t\t\t\t\t\t...common,\n\t\t\t\t\t\ttype: \"event\",\n\t\t\t\t\t\ttool,\n\t\t\t\t\t\targ,\n\t\t\t\t\t\tbatchId: id,\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (inWindow(tsMs))\n\t\t\t\tworkflow?.ingest({\n\t\t\t\t\t...common,\n\t\t\t\t\ttype: \"turn\",\n\t\t\t\t\tturnId: message.id ?? `assistant:${index}`,\n\t\t\t\t\tquestionBack,\n\t\t\t\t});\n\t\t}\n\t}\n\tif (!workflow) return;\n\tconst projects = new Map(\n\t\tlocals.map(({ session }) => [session.id, session.canonicalWorkspacePath]),\n\t);\n\tfor (const [index, row] of usage.entries()) {\n\t\tif (!inWindow(row.tsMs)) continue;\n\t\tconst project = projects.get(row.session);\n\t\tconst scope =\n\t\t\trow.id && row.source === \"local\"\n\t\t\t\t? scopes.get(`${row.session}:${row.id}`)\n\t\t\t\t: undefined;\n\t\tworkflow.ingest({\n\t\t\t...(scope ?? {\n\t\t\t\tsession: row.sidechain ? `${row.session}:sidechain` : row.session,\n\t\t\t\tsidechain: row.sidechain,\n\t\t\t}),\n\t\t\t...(project && path.isAbsolute(project)\n\t\t\t\t? { projectWorkspace: project }\n\t\t\t\t: {}),\n\t\t\ttype: \"response\",\n\t\t\ttsMs: row.tsMs,\n\t\t\tresponseId: `${row.source}:${row.id ?? index}`,\n\t\t\tmodel: normalizeModel(row.model),\n\t\t\troutingTokens:\n\t\t\t\t(row.buckets.input ?? 0) +\n\t\t\t\t(row.buckets.output ?? 0) +\n\t\t\t\t(row.buckets.cacheRead ?? 0) +\n\t\t\t\t(row.buckets.cacheWrite ?? 0),\n\t\t});\n\t}\n}\n","import type { HarnessAdapter } from \"../types.js\";\nimport { cachedEvents, cacheFile, loadCache } from \"./cache.js\";\nimport { messageTimes } from \"./evidence.js\";\nimport { dataPath, readLocal, storeRoot } from \"./local.js\";\nimport { scan } from \"./scan.js\";\nimport { CURSOR_BUILTIN_TOOLS } from \"./workflow.js\";\n\nexport const CURSOR_HARNESS_NAME = \"cursor\";\nexport { CURSOR_BUILTIN_TOOLS } from \"./workflow.js\";\nexport const cursorAdapter: HarnessAdapter = {\n\tname: CURSOR_HARNESS_NAME,\n\tbuiltinTools: CURSOR_BUILTIN_TOOLS,\n\tasync detect(options) {\n\t\tconst roots = options.roots ?? [dataPath()];\n\t\tfor (const root of roots) {\n\t\t\tconst held = await loadCache(cacheFile(root, storeRoot()));\n\t\t\tif (\n\t\t\t\t!held.complete ||\n\t\t\t\t[\n\t\t\t\t\t...held.value.local,\n\t\t\t\t\t...cachedEvents(held.value, new Set(held.value.sessions)),\n\t\t\t\t].some((row) => row.tsMs >= options.sinceMs)\n\t\t\t)\n\t\t\t\treturn true;\n\t\t\tconst local = await readLocal(root);\n\t\t\t// A discovered unreadable source must reach scanComplete, never silently disappear.\n\t\t\tif (\n\t\t\t\t!local.complete ||\n\t\t\t\tlocal.sessions.some(({ session }) =>\n\t\t\t\t\tmessageTimes(session).some((t) => t !== null && t >= options.sinceMs),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn true;\n\t\t\t// Undated retained history can gain its first usable anchor from dashboard enrichment.\n\t\t\tif (\n\t\t\t\tlocal.sessions.some(\n\t\t\t\t\t({ session }) =>\n\t\t\t\t\t\tsession.messages.length > 0 &&\n\t\t\t\t\t\tmessageTimes(session).every((t) => t === null),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\tscan,\n};\n","import {\n\tapiEquivalentCost,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"grok-build\", workflowLocal),\n\t\tworkflowLocal,\n\t});\n}\n\nexport type UsageContribution = {\n\tsessionId: string;\n\tprojectDir: string;\n\ttsMs: number;\n\tdurationMs?: number;\n\tmodels: Array<{ model: string; counts: TokenCounts }>;\n};\n\ntype GrokEventState = {\n\ttools: Map<string, { name: string; arg?: string; tsMs: number }>;\n\tcompletedTools: Set<string>;\n\tparentSession?: string;\n};\n\nexport const createGrokEventState = (\n\tparentSession?: string,\n): GrokEventState => ({\n\ttools: new Map(),\n\tcompletedTools: new Set(),\n\t...(parentSession ? { parentSession } : {}),\n});\n\nconst bump = (map: Map<string, number>, key: string): void => {\n\tmap.set(key, (map.get(key) ?? 0) + 1);\n};\n\nconst toolMetadata = (update: Record<string, unknown>) => {\n\tconst meta = asObj(update._meta);\n\treturn meta && asObj(meta[\"x.ai/tool\"]);\n};\n\n/** Project one persisted Grok update without retaining prompts or raw arguments. */\nexport function ingestUpdate(\n\tagg: Aggregate,\n\tstate: GrokEventState,\n\tvalue: unknown,\n\tprojectDir: string,\n\tsinceMs?: number,\n): void {\n\tconst root = asObj(value);\n\tconst params = root && asObj(root.params);\n\tconst update = params && asObj(params.update);\n\tconst session = params && asStr(params.sessionId);\n\tconst tsMs = timestampMs(\n\t\tasObj(params?._meta)?.agentTimestampMs ?? root?.timestamp,\n\t);\n\tif (!update || !session || tsMs === null) return;\n\tconst kind = asStr(update.sessionUpdate);\n\tif (kind === \"tool_call\") {\n\t\tconst id = asStr(update.toolCallId);\n\t\tconst metadata = toolMetadata(update);\n\t\tconst name = asName(metadata?.name ?? update.toolName);\n\t\tif (!id || !name || state.tools.has(id)) return;\n\t\tconst raw = asObj(update.input);\n\t\tconst arg = asStr(raw?.command ?? raw?.query ?? raw?.skill ?? raw?.name);\n\t\tstate.tools.set(id, { name, ...(arg ? { arg } : {}), tsMs });\n\t\treturn;\n\t}\n\tif (sinceMs !== undefined && tsMs < sinceMs) return;\n\tif (kind === \"tool_call_update\") {\n\t\tconst id = asStr(update.toolCallId);\n\t\tif (!id || asStr(update.status) !== \"completed\") return;\n\t\tcompleteTool(agg, state, id, session, projectDir, tsMs);\n\t\treturn;\n\t}\n\tif (kind !== \"turn_completed\") return;\n\tconst usage = asObj(update.usage);\n\tif (!usage) return;\n\tconst prompt = asStr(update.prompt_id) ?? `turn:${tsMs}`;\n\tfor (const [model, raw] of Object.entries(asObj(usage.modelUsage) ?? {})) {\n\t\tconst row = asObj(raw);\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"response\",\n\t\t\tsession,\n\t\t\tprojectWorkspace: projectDir,\n\t\t\tparentSession: state.parentSession,\n\t\t\ttsMs,\n\t\t\tresponseId: `${prompt}:${model}`,\n\t\t\tmodel,\n\t\t\tthinkingTokens: asNum(row?.reasoningTokens),\n\t\t\tresponseTokens: asNum(row?.outputTokens),\n\t\t\troutingTokens: asNum(row?.outputTokens),\n\t\t\t...(asNum(row?.apiDurationMs) > 0\n\t\t\t\t? { durationSec: asNum(row?.apiDurationMs) / 1000 }\n\t\t\t\t: asNum(update.elapsed_ms) > 0\n\t\t\t\t\t? { durationSec: asNum(update.elapsed_ms) / 1000 }\n\t\t\t\t\t: {}),\n\t\t});\n\t}\n\tagg.workflow.ingest({\n\t\ttype: \"turn\",\n\t\tsession,\n\t\tprojectWorkspace: projectDir,\n\t\tparentSession: state.parentSession,\n\t\ttsMs,\n\t\tturnId: prompt,\n\t\tquestionBack: asStr(update.stop_reason) === \"question\",\n\t});\n}\n\nfunction completeTool(\n\tagg: Aggregate,\n\tstate: GrokEventState,\n\tid: string,\n\tsession: string,\n\tprojectDir: string,\n\ttsMs: number,\n): void {\n\tif (state.completedTools.has(id)) return;\n\tconst tool = state.tools.get(id);\n\tif (!tool) return;\n\tstate.completedTools.add(id);\n\tbump(agg.toolCalls, tool.name);\n\tif ([\"web_search\", \"websearch\", \"search_web\"].includes(tool.name))\n\t\tagg.webSearchRequests++;\n\tif ([\"skill\", \"use_skill\"].includes(tool.name) && tool.arg)\n\t\tbump(agg.skillCalls, tool.arg);\n\tconst mcp = /^(?:mcp__|mcp:)([^_:]+)[_:](.+)$/.exec(tool.name);\n\tif (mcp) {\n\t\tbump(agg.mcpServerCalls, mcp[1] as string);\n\t\tbump(agg.mcpToolCalls, tool.name);\n\t} else if (tool.name === \"use_tool\" && tool.arg?.includes(\"__\")) {\n\t\tconst [server] = tool.arg.split(\"__\", 1);\n\t\tif (server) {\n\t\t\tbump(agg.mcpServerCalls, server);\n\t\t\tbump(agg.mcpToolCalls, tool.arg);\n\t\t}\n\t}\n\tagg.workflow.ingest({\n\t\ttype: \"event\",\n\t\tsession,\n\t\tprojectWorkspace: projectDir,\n\t\tparentSession: state.parentSession,\n\t\ttsMs: tool.tsMs || tsMs,\n\t\ttool: tool.name,\n\t\t...(tool.arg ? { arg: tool.arg } : {}),\n\t\tbatchId: id,\n\t});\n}\n\n/** Complete a tool from the durable event stream when the ACP update is absent. */\nexport function ingestEvent(\n\tagg: Aggregate,\n\tstate: GrokEventState,\n\tvalue: unknown,\n\tsessionFallback: string,\n\tprojectDir: string,\n\tsinceMs?: number,\n): void {\n\tconst row = asObj(value);\n\tif (!row) return;\n\tconst session = asStr(row.session_id) ?? sessionFallback;\n\tconst tsMs = timestampMs(row.ts);\n\tif (!session || tsMs === null) return;\n\tif (asStr(row.type) === \"tool_started\") {\n\t\tconst id = asStr(row.tool_call_id);\n\t\tconst name = asName(row.tool_name);\n\t\tif (id && name && !state.tools.has(id)) state.tools.set(id, { name, tsMs });\n\t} else if (sinceMs !== undefined && tsMs < sinceMs) {\n\t\treturn;\n\t} else if (asStr(row.type) === \"tool_completed\") {\n\t\tconst id = asStr(row.tool_call_id);\n\t\tif (id) completeTool(agg, state, id, session, projectDir, tsMs);\n\t} else if (asStr(row.type) === \"compaction\") {\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"compaction\",\n\t\t\tsession,\n\t\t\tprojectWorkspace: projectDir,\n\t\t\tparentSession: state.parentSession,\n\t\t\ttsMs,\n\t\t});\n\t}\n}\n\nconst timestampMs = (value: unknown): number | null => {\n\tif (typeof value === \"string\") {\n\t\tconst parsed = Date.parse(value);\n\t\treturn Number.isFinite(parsed) ? parsed : null;\n\t}\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return null;\n\treturn value < 10_000_000_000 ? value * 1000 : value;\n};\n\nfunction counts(value: unknown): TokenCounts | null {\n\tconst row = asObj(value);\n\tif (!row) return null;\n\tconst totalInput = asNum(row.inputTokens);\n\tconst cacheRead = asNum(row.cachedReadTokens);\n\tconst cacheWrite = asNum(row.cacheCreationTokens);\n\tif (totalInput < cacheRead + cacheWrite) return null;\n\tconst result = {\n\t\tinput: totalInput - cacheRead - cacheWrite,\n\t\toutput: asNum(row.outputTokens),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: cacheWrite,\n\t\tcacheRead,\n\t};\n\treturn countsTotal(result) > 0 ? result : null;\n}\n\nexport function sidecarContributions(\n\tvalue: unknown,\n\tprojectDir: string,\n): UsageContribution[] {\n\tconst root = asObj(value);\n\tconst sessionId = root && asStr(root.sessionId);\n\tif (!root || !sessionId) return [];\n\tconst turns = Array.isArray(root.turns) ? root.turns : [];\n\tconst out: UsageContribution[] = [];\n\tfor (const raw of turns) {\n\t\tconst turn = asObj(raw);\n\t\tconst tsMs = turn && timestampMs(turn.endedAt);\n\t\tif (!turn || tsMs === null) continue;\n\t\tconst models: UsageContribution[\"models\"] = [];\n\t\tconst perModel = asObj(turn.modelUsage);\n\t\tfor (const [model, usage] of Object.entries(perModel ?? {})) {\n\t\t\tconst c = counts(usage);\n\t\t\tif (c) models.push({ model, counts: c });\n\t\t}\n\t\tif (models.length === 0) {\n\t\t\tconst c = counts(turn);\n\t\t\tconst model = asStr(turn.primaryModelId);\n\t\t\tif (c && model) models.push({ model, counts: c });\n\t\t}\n\t\tif (models.length > 0)\n\t\t\tout.push({\n\t\t\t\tsessionId,\n\t\t\t\tprojectDir,\n\t\t\t\ttsMs,\n\t\t\t\t...(asNum(turn.apiDurationMs) > 0\n\t\t\t\t\t? { durationMs: asNum(turn.apiDurationMs) }\n\t\t\t\t\t: {}),\n\t\t\t\tmodels,\n\t\t\t});\n\t}\n\treturn out;\n}\n\nexport function terminalContribution(\n\tvalue: unknown,\n\tprojectDir: string,\n): UsageContribution | null {\n\tconst root = asObj(value);\n\tconst params = root && asObj(root.params);\n\tconst update = params && asObj(params.update);\n\tconst meta = params && asObj(params._meta);\n\tif (!update || asStr(update.sessionUpdate) !== \"turn_completed\") return null;\n\tconst usage = asObj(update.usage);\n\tconst sessionId = params && asStr(params.sessionId);\n\tconst tsMs = timestampMs(meta?.agentTimestampMs ?? root?.timestamp);\n\tif (!usage || !sessionId || tsMs === null) return null;\n\tconst models: UsageContribution[\"models\"] = [];\n\tfor (const [model, row] of Object.entries(asObj(usage.modelUsage) ?? {})) {\n\t\tconst c = counts(row);\n\t\tif (c) models.push({ model, counts: c });\n\t}\n\treturn models.length === 0\n\t\t? null\n\t\t: {\n\t\t\t\tsessionId,\n\t\t\t\tprojectDir,\n\t\t\t\ttsMs,\n\t\t\t\t...(asNum(update.elapsed_ms) > 0\n\t\t\t\t\t? { durationMs: asNum(update.elapsed_ms) }\n\t\t\t\t\t: {}),\n\t\t\t\tmodels,\n\t\t\t};\n}\n\nexport function ingestContribution(\n\tagg: Aggregate,\n\trow: UsageContribution,\n): void {\n\tagg.records++;\n\tagg.assistantRecords++;\n\tagg.distinctResponses++;\n\tagg.sessions.add(row.sessionId);\n\tagg.activeDays.add(new Date(row.tsMs).toISOString().slice(0, 10));\n\tagg.projectDirs.add(row.projectDir);\n\tagg.firstTs =\n\t\tagg.firstTs === null ? row.tsMs : Math.min(agg.firstTs, row.tsMs);\n\tagg.lastTs = agg.lastTs === null ? row.tsMs : Math.max(agg.lastTs, row.tsMs);\n\tnoteSessionStart(agg, row.sessionId, row.tsMs);\n\tnoteProjectDay(agg, row.projectDir, row.tsMs);\n\tfor (const { model, counts: tokenCounts } of row.models) {\n\t\tconst key = normalizeModel(model);\n\t\taddModelUsage(\n\t\t\tagg,\n\t\t\tkey,\n\t\t\ttokenCounts,\n\t\t\tapiEquivalentCost(key, tokenCounts, row.tsMs),\n\t\t\t1,\n\t\t\t{\n\t\t\t\ttsMs: row.tsMs,\n\t\t\t},\n\t\t);\n\t}\n}\n","import { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\nimport { parse as parseToml } from \"smol-toml\";\nimport { asObj, asStr, countsTotal } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport type { Aggregate } from \"./analyzer.js\";\nimport {\n\tcreateGrokEventState,\n\tingestContribution,\n\tingestEvent,\n\tingestUpdate,\n\tsidecarContributions,\n\tterminalContribution,\n\ttype UsageContribution,\n} from \"./analyzer.js\";\n\nexport function sessionRoots(): string[] {\n\treturn [\n\t\tpath.join(\n\t\t\tprocess.env.GROK_HOME || path.join(homedir(), \".grok\"),\n\t\t\t\"sessions\",\n\t\t),\n\t];\n}\n\nexport const isGrokEvidenceFile = (name: string): boolean =>\n\tname === \"usage.json\" || name === \"updates.jsonl\" || name === \"events.jsonl\";\n\ntype FileRead = { lines?: unknown[]; json?: unknown; complete: boolean };\nconst delays = [0, 100, 300];\nconst pause = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nasync function stableRead(file: string, jsonl: boolean): Promise<FileRead> {\n\tfor (const delay of delays) {\n\t\tif (delay) await pause(delay);\n\t\ttry {\n\t\t\tconst before = await stat(file);\n\t\t\tif (!before.isFile()) return { complete: false };\n\t\t\tif (jsonl) {\n\t\t\t\tconst lines: unknown[] = [];\n\t\t\t\tconst input = readline.createInterface({\n\t\t\t\t\tinput: createReadStream(file),\n\t\t\t\t\tcrlfDelay: Infinity,\n\t\t\t\t});\n\t\t\t\tfor await (const line of input) {\n\t\t\t\t\tif (!line.trim()) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlines.push(JSON.parse(line));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tlines.push(null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst after = await stat(file);\n\t\t\t\tif (before.size === after.size && before.mtimeMs === after.mtimeMs)\n\t\t\t\t\treturn { lines, complete: true };\n\t\t\t} else {\n\t\t\t\tconst raw = await readFile(file, \"utf8\");\n\t\t\t\tconst after = await stat(file);\n\t\t\t\tif (before.size === after.size && before.mtimeMs === after.mtimeMs)\n\t\t\t\t\treturn { json: JSON.parse(raw), complete: true };\n\t\t\t}\n\t\t} catch {\n\t\t\t/* retry a transient read or parse failure */\n\t\t}\n\t}\n\treturn { complete: false };\n}\n\nasync function directories(root: string): Promise<string[] | null> {\n\ttry {\n\t\tconst workspaces = await readdir(root, { withFileTypes: true });\n\t\tconst out: string[] = [];\n\t\tfor (const workspace of workspaces) {\n\t\t\tif (!workspace.isDirectory() || workspace.isSymbolicLink()) continue;\n\t\t\tconst workspacePath = path.join(root, workspace.name);\n\t\t\tfor (const session of await readdir(workspacePath, {\n\t\t\t\twithFileTypes: true,\n\t\t\t})) {\n\t\t\t\tif (session.isDirectory() && !session.isSymbolicLink())\n\t\t\t\t\tout.push(path.join(workspacePath, session.name));\n\t\t\t}\n\t\t}\n\t\treturn out.sort();\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"ENOENT\" ? [] : null;\n\t}\n}\n\n/**\n * Read only aliases that still use xAI's normal endpoint. Routing overrides\n * remain under their recorded alias because no vendor rate is established.\n */\nexport async function modelAliasesForRoot(\n\troot: string,\n): Promise<ReadonlyMap<string, string>> {\n\tif (process.env.GROK_MODELS_BASE_URL) return new Map();\n\ttry {\n\t\tconst parsed = asObj(\n\t\t\tparseToml(await readFile(path.join(root, \"..\", \"config.toml\"), \"utf8\")),\n\t\t);\n\t\tif (!parsed || asObj(parsed.endpoints)?.models_base_url) return new Map();\n\t\tconst models = asObj(parsed.model);\n\t\tconst aliases = new Map<string, string>();\n\t\tfor (const [alias, raw] of Object.entries(models ?? {})) {\n\t\t\tconst entry = asObj(raw);\n\t\t\tconst model = entry && asStr(entry.model);\n\t\t\tif (!model || entry?.base_url || entry?.model_provider) continue;\n\t\t\taliases.set(alias, model);\n\t\t}\n\t\treturn aliases;\n\t} catch {\n\t\treturn new Map();\n\t}\n}\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: {\n\t\tsinceMs?: number;\n\t\troots?: string[];\n\t\tonProgress?: (files: number) => void;\n\t} = {},\n): Promise<{\n\tstats: ScanStats;\n\tcomplete: boolean;\n\tsessionDates: Map<string, Set<string>>;\n}> {\n\tconst stats = emptyScanStats();\n\tconst sessionDates = new Map<string, Set<string>>();\n\tconst candidates = new Map<\n\t\tstring,\n\t\t{ precedence: number; rows: UsageContribution[] }\n\t>();\n\tlet complete = true;\n\tfor (const root of opts.roots ?? sessionRoots()) {\n\t\tconst aliases = await modelAliasesForRoot(root);\n\t\tconst dirs = await directories(root);\n\t\tif (dirs === null) {\n\t\t\tcomplete = false;\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const dir of dirs) {\n\t\t\tlet entries: Dirent[];\n\t\t\ttry {\n\t\t\t\tentries = await readdir(dir, { withFileTypes: true });\n\t\t\t} catch {\n\t\t\t\tcomplete = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst files = new Map(\n\t\t\t\tentries\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(e) =>\n\t\t\t\t\t\t\te.isFile() &&\n\t\t\t\t\t\t\t(isGrokEvidenceFile(e.name) || e.name === \"summary.json\"),\n\t\t\t\t\t)\n\t\t\t\t\t.map((e) => [e.name, path.join(dir, e.name)]),\n\t\t\t);\n\t\t\tlet projectDir = dir;\n\t\t\tlet sessionFallback = path.basename(dir);\n\t\t\tlet child = false;\n\t\t\tlet parentSession: string | undefined;\n\t\t\tconst summary = files.get(\"summary.json\");\n\t\t\tif (summary) {\n\t\t\t\tconst read = await stableRead(summary, false);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst value = asObj(read.json);\n\t\t\t\tconst info = value && asObj(value.info);\n\t\t\t\tprojectDir = asStr(info?.cwd) ?? asStr(value?.cwd) ?? dir;\n\t\t\t\tsessionFallback = asStr(value?.sessionId) ?? sessionFallback;\n\t\t\t\tparentSession =\n\t\t\t\t\tasStr(value?.parentSessionId) ??\n\t\t\t\t\tasStr(value?.parent_session_id) ??\n\t\t\t\t\tasStr(info?.parentSessionId) ??\n\t\t\t\t\tasStr(info?.parent_session_id) ??\n\t\t\t\t\tundefined;\n\t\t\t\tchild = parentSession !== undefined;\n\t\t\t}\n\t\t\tlet rows = [] as ReturnType<typeof sidecarContributions>;\n\t\t\tlet precedence = 0;\n\t\t\tconst usage = files.get(\"usage.json\");\n\t\t\tif (usage) {\n\t\t\t\tstats.filesFound++;\n\t\t\t\tconst read = await stableRead(usage, false);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstats.filesRead++;\n\t\t\t\trows = sidecarContributions(read.json, projectDir);\n\t\t\t\tif (rows.length > 0) precedence = 2;\n\t\t\t}\n\t\t\tif (child) {\n\t\t\t\trows = [];\n\t\t\t\tprecedence = 0;\n\t\t\t}\n\t\t\tconst eventState = createGrokEventState(parentSession);\n\t\t\tconst updates = files.get(\"updates.jsonl\");\n\t\t\tif (updates) {\n\t\t\t\tconst useTerminalUsage = rows.length === 0 && !child;\n\t\t\t\tstats.filesFound++;\n\t\t\t\tconst read = await stableRead(updates, true);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstats.filesRead++;\n\t\t\t\tfor (const value of read.lines ?? []) {\n\t\t\t\t\tif (value === null) {\n\t\t\t\t\t\tagg.parseErrors++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tingestUpdate(agg, eventState, value, projectDir, opts.sinceMs);\n\t\t\t\t\tif (useTerminalUsage) {\n\t\t\t\t\t\tconst row = terminalContribution(value, projectDir);\n\t\t\t\t\t\tif (row) rows.push(row);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (rows.length > 0) precedence = 1;\n\t\t\t}\n\t\t\tconst events = files.get(\"events.jsonl\");\n\t\t\tif (events) {\n\t\t\t\tstats.filesFound++;\n\t\t\t\tconst read = await stableRead(events, true);\n\t\t\t\tif (!read.complete) {\n\t\t\t\t\tcomplete = false;\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tstats.filesRead++;\n\t\t\t\tfor (const value of read.lines ?? []) {\n\t\t\t\t\tif (value === null) agg.parseErrors++;\n\t\t\t\t\telse\n\t\t\t\t\t\tingestEvent(\n\t\t\t\t\t\t\tagg,\n\t\t\t\t\t\t\teventState,\n\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\tsessionFallback,\n\t\t\t\t\t\t\tprojectDir,\n\t\t\t\t\t\t\topts.sinceMs,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\trows = rows.filter(\n\t\t\t\t(row) => opts.sinceMs === undefined || row.tsMs >= opts.sinceMs,\n\t\t\t);\n\t\t\trows = rows.map((row) => ({\n\t\t\t\t...row,\n\t\t\t\tmodels: row.models.map(({ model, counts }) => ({\n\t\t\t\t\tmodel: aliases.get(model) ?? model,\n\t\t\t\t\tcounts,\n\t\t\t\t})),\n\t\t\t}));\n\t\t\tif (rows.length > 0) {\n\t\t\t\tconst sessionId = rows[0]?.sessionId as string;\n\t\t\t\tconst held = candidates.get(sessionId);\n\t\t\t\tconst total = (values: UsageContribution[]) =>\n\t\t\t\t\tvalues\n\t\t\t\t\t\t.flatMap((value) => value.models)\n\t\t\t\t\t\t.reduce((sum, model) => sum + countsTotal(model.counts), 0);\n\t\t\t\tif (\n\t\t\t\t\t!held ||\n\t\t\t\t\tprecedence > held.precedence ||\n\t\t\t\t\t(precedence === held.precedence && total(rows) > total(held.rows))\n\t\t\t\t)\n\t\t\t\t\tcandidates.set(sessionId, { precedence, rows });\n\t\t\t}\n\t\t\topts.onProgress?.(stats.filesFound);\n\t\t}\n\t}\n\tfor (const { rows } of candidates.values()) {\n\t\tfor (const row of rows) {\n\t\t\tingestContribution(agg, row);\n\t\t\tconst dates = sessionDates.get(row.sessionId) ?? new Set<string>();\n\t\t\tdates.add(new Date(row.tsMs).toISOString().slice(0, 10));\n\t\t\tsessionDates.set(row.sessionId, dates);\n\t\t}\n\t}\n\treturn { stats, complete, sessionDates };\n}\n","import { hasRecentFile } from \"../shared/recency.js\";\nimport type { HarnessAdapter } from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isGrokEvidenceFile, scan, sessionRoots } from \"./scan.js\";\n\nexport const GROK_HARNESS_NAME = \"grok-build\";\nexport const GROK_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"run_terminal_command\",\n\t\"read_file\",\n\t\"write_file\",\n\t\"search\",\n\t\"web_search\",\n]);\n\nexport const grokAdapter: HarnessAdapter = {\n\tname: GROK_HARNESS_NAME,\n\tbuiltinTools: GROK_BUILTIN_TOOLS,\n\tdetect: (opts) =>\n\t\thasRecentFile(\n\t\t\topts.roots ?? sessionRoots(),\n\t\t\tisGrokEvidenceFile,\n\t\t\topts.sinceMs,\n\t\t),\n\tasync scan(opts) {\n\t\tconst aggregate = createAggregate();\n\t\tconst result = await scan(aggregate, opts);\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats: result.stats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t\tscanComplete: result.complete,\n\t\t\tsessionDates: result.sessionDates,\n\t\t};\n\t},\n};\n","// Pure fold over rows projected out of opencode's SQLite store. No I/O, no\n// console - the scanner owns the database and hands this module plain values.\n//\n// Wayfinder ticket #124 (map #121). Field semantics come from\n// docs/research/harness-adapters-2026-08.md (§opencode), keyed by #123's\n// binding rule: every pricing key carries the harness's own provider id\n// (`modelKeyFor(providerID, modelID)`), because one opencode install routes\n// several providers and a gateway re-serving a vendor's model must not price\n// at that vendor's list rate.\n//\n// THE LOAD-BEARING FACTS (research §2):\n// - the four token counters are DISJOINT deltas: input, output, cache.read,\n// cache.write map straight onto TokenCounts with no arithmetic;\n// - `tokens.total` is absent on some rows and wrong on Google rows - never\n// read it; `reasoning` is a subset of `output` for two vendors and\n// additive for one - never add it;\n// - opencode's own `cost` is 0.0 on every record (subscription/OAuth auth);\n// a zero is not a measurement, so cost comes from @aistack/pricing only.\n\nimport {\n\tapiEquivalentCost,\n\tmodelKeyFor,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasNum,\n\tasStr,\n\tbump,\n\tcleanName,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/**\n * opencode's vendor-assigned tool surface, as observed in the probe DB and\n * pinned against the opencode source. Same fail-closed mechanism as the other\n * adapters: a literal set, never a pattern. It doubles as the MCP guard -\n * MCP tool names are `sanitize(server) + \"_\" + sanitize(tool)` and built-in\n * names contain `_` too, so a name must miss this set AND carry a configured\n * server's prefix before any split is attempted.\n */\nexport const OPENCODE_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"apply_patch\",\n\t\"bash\",\n\t\"edit\",\n\t\"glob\",\n\t\"grep\",\n\t\"list\",\n\t\"patch\",\n\t\"question\",\n\t\"read\",\n\t\"skill\",\n\t\"task\",\n\t\"todoread\",\n\t\"todowrite\",\n\t\"webfetch\",\n\t\"websearch\",\n\t\"write\",\n]);\n\n/** Message-id dedup lives in DbFoldState, not the aggregate's `seen`. */\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"opencode\", workflowLocal),\n\t\tworkflowLocal,\n\t});\n}\n\n/** What the scanner knows about one session row, named columns only. */\nexport type SessionInfo = {\n\tid: unknown;\n\tparentId: unknown;\n\tversion: unknown;\n};\n\n/**\n * Per-database fold state. Sessions are loaded up front (a message's session\n * may predate the window); message ids are deduped across the v1 and v2\n * tables because v2 is described in the opencode source as a projection -\n * reading a row from both would double count.\n */\nexport type DbFoldState = {\n\tsessions: Map<string, { parentId: string | null; version: string | null }>;\n\tseenMessageIds: Set<string>;\n\t/** Configured MCP server names, sanitized the way opencode builds tool names. */\n\tmcpServers: string[];\n};\n\nexport function createDbFoldState(): DbFoldState {\n\treturn { sessions: new Map(), seenMessageIds: new Set(), mcpServers: [] };\n}\n\nexport function noteSessions(\n\tstate: DbFoldState,\n\trows: Iterable<SessionInfo>,\n): void {\n\tfor (const row of rows) {\n\t\tconst id = asStr(row.id);\n\t\tif (!id) continue;\n\t\tstate.sessions.set(id, {\n\t\t\tparentId: asStr(row.parentId),\n\t\t\tversion: asStr(row.version),\n\t\t});\n\t}\n}\n\n/**\n * One projected message row. Every value is untrusted - `json_extract` returns\n * whatever the blob holds - so the fold narrows each field itself.\n */\nexport type MessageRow = {\n\tid: unknown;\n\tsessionId: unknown;\n\trole: unknown;\n\tproviderId: unknown;\n\tmodelId: unknown;\n\t/** `data.time.created`, epoch ms. */\n\ttsMs: unknown;\n\tinput: unknown;\n\toutput: unknown;\n\tcacheRead: unknown;\n\tcacheWrite: unknown;\n\tcwd: unknown;\n\treasoning?: unknown;\n\tcompletedTsMs?: unknown;\n};\n\nexport function ingestMessageRow(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\trow: MessageRow,\n): void {\n\tagg.records++;\n\n\tconst id = asStr(row.id);\n\tif (id) {\n\t\tif (state.seenMessageIds.has(id)) return;\n\t\tstate.seenMessageIds.add(id);\n\t}\n\n\tconst tsMs =\n\t\ttypeof row.tsMs === \"number\" && Number.isFinite(row.tsMs) ? row.tsMs : null;\n\tif (tsMs !== null) {\n\t\tagg.activeDays.add(new Date(tsMs).toISOString().slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\n\tconst sessionId = asStr(row.sessionId);\n\tconst session = sessionId ? state.sessions.get(sessionId) : undefined;\n\tif (sessionId) {\n\t\tagg.sessions.add(sessionId);\n\t\tnoteSessionStart(agg, sessionId, tsMs);\n\t\tif (session?.version) agg.ccVersions.add(cleanName(session.version));\n\t}\n\t// Counted, never published - same standing non-goal as Claude project dirs.\n\tconst cwd = asStr(row.cwd);\n\tif (cwd) {\n\t\tagg.projectDirs.add(cwd);\n\t\tnoteProjectDay(agg, cwd, tsMs);\n\t}\n\n\tif (asStr(row.role) !== \"assistant\") return;\n\tagg.assistantRecords++;\n\tagg.distinctResponses++;\n\tif (tsMs === null) agg.untimestampedResponses++;\n\n\tconst counts: TokenCounts = {\n\t\tinput: asNum(row.input),\n\t\toutput: asNum(row.output),\n\t\tcacheWrite5m: 0,\n\t\tcacheWrite1h: 0,\n\t\tcacheWriteUnsplit: asNum(row.cacheWrite),\n\t\tcacheRead: asNum(row.cacheRead),\n\t};\n\n\t// A session with a parent is a subagent's - an honest measurement here,\n\t// unlike Codex's structural 0 (research §inventory: 21.4% on the probe DB).\n\tconst total =\n\t\tcounts.input + counts.output + counts.cacheWriteUnsplit + counts.cacheRead;\n\tif (session?.parentId) agg.sidechainTokens += total;\n\telse agg.mainTokens += total;\n\n\tconst provider = asStr(row.providerId);\n\tconst model = asStr(row.modelId);\n\tconst modelKey =\n\t\tprovider && model\n\t\t\t? normalizeModel(modelKeyFor(provider, cleanName(model)))\n\t\t\t: \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tmodelKey,\n\t\tcounts,\n\t\tapiEquivalentCost(modelKey, counts, tsMs),\n\t\t1,\n\t\t{ tsMs, sidechain: Boolean(session?.parentId) },\n\t);\n\tif (sessionId && tsMs !== null) {\n\t\tconst completed = asNum(row.completedTsMs);\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"response\",\n\t\t\tsession: sessionId,\n\t\t\t...(id ? { responseId: id } : {}),\n\t\t\tprojectWorkspace: cwd ?? undefined,\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\t...(provider && model ? { model: `${provider}:${model}` } : {}),\n\t\t\tthinkingTokens: asNum(row.reasoning),\n\t\t\tresponseTokens: counts.output,\n\t\t\troutingTokens: total,\n\t\t\t...(completed > tsMs ? { durationSec: (completed - tsMs) / 1000 } : {}),\n\t\t});\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession: sessionId,\n\t\t\t...(id ? { turnId: id } : {}),\n\t\t\tprojectWorkspace: cwd ?? undefined,\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\tquestionBack: false,\n\t\t});\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Inventory - `part` rows of type \"tool\"\n// ---------------------------------------------------------------------------\n\n/**\n * opencode's own `sanitize` as applied when composing MCP tool names: the\n * observed names (`chrome-devtools_click`) show `-` surviving, so everything\n * outside the tool-name charset collapses to `_`.\n */\nconst sanitizeMcpName = (name: string): string =>\n\tname.replace(/[^A-Za-z0-9_-]/g, \"_\");\n\n/**\n * One projected `part` row. The scanner extracts these named paths and\n * nothing else - `part.data.state.output` holds full command output and never\n * materializes in JS.\n */\nexport type ToolPartRow = {\n\tid: unknown;\n\tpartType: unknown;\n\ttool: unknown;\n\tcallId: unknown;\n\t/** `$.state.input.name` - the skill tool's skill. */\n\tinputName: unknown;\n\t/** `$.state.input.subagent_type` - the task tool's agent. */\n\tsubagentType: unknown;\n\tsessionId?: unknown;\n\ttsMs?: unknown;\n\tcommand?: unknown;\n\tmessageId?: unknown;\n};\n\nexport function ingestToolPart(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\trow: ToolPartRow,\n): void {\n\t// step-finish parts repeat the message's tokens and text parts carry prose;\n\t// only tool executions count (research §2).\n\tif (asStr(row.partType) !== \"tool\") return;\n\tconst rawName = asStr(row.tool);\n\tif (!rawName) return;\n\tconst name = cleanName(rawName);\n\n\tconst dedupKey = asStr(row.callId) ?? asStr(row.id);\n\tif (dedupKey) {\n\t\tif (agg.toolCallDedup.has(dedupKey)) return;\n\t\tagg.toolCallDedup.add(dedupKey);\n\t}\n\tconst sessionId = asStr(row.sessionId);\n\tconst tsMs = asNum(row.tsMs);\n\tconst session = sessionId ? state.sessions.get(sessionId) : undefined;\n\tif (sessionId && tsMs > 0) {\n\t\tconst messageId = asStr(row.messageId);\n\t\tlet arg = \"\";\n\t\tif (name === \"skill\") arg = asStr(row.inputName) ?? \"\";\n\t\telse if (name === \"task\") arg = asStr(row.subagentType) ?? \"\";\n\t\telse if (name === \"bash\") arg = asStr(row.command) ?? \"\";\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"event\",\n\t\t\tsession: sessionId,\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\ttool: name,\n\t\t\targ,\n\t\t});\n\t\tagg.workflow.ingest({\n\t\t\ttype: \"turn\",\n\t\t\tsession: sessionId,\n\t\t\t...(messageId ? { turnId: messageId } : {}),\n\t\t\tparentSession: session?.parentId ?? undefined,\n\t\t\ttsMs,\n\t\t\tquestionBack: name === \"question\",\n\t\t});\n\t}\n\n\t// Fail-closed order (research §inventory): the literal built-in set first,\n\t// then configured MCP server prefixes, then a plain count the shared\n\t// payload filter withholds - never a split that invents a server name.\n\tif (OPENCODE_BUILTIN_TOOLS.has(name)) {\n\t\tbump(agg.toolCalls, name);\n\t\tif (name === \"skill\") {\n\t\t\tconst skill = asStr(row.inputName);\n\t\t\tif (skill) bump(agg.skillCalls, cleanName(skill));\n\t\t} else if (name === \"task\") {\n\t\t\tconst agent = asStr(row.subagentType);\n\t\t\tif (agent) bump(agg.subagentCalls, cleanName(agent));\n\t\t}\n\t\treturn;\n\t}\n\tfor (const server of state.mcpServers) {\n\t\tif (name.startsWith(`${server}_`)) {\n\t\t\tbump(agg.mcpServerCalls, server);\n\t\t\tbump(agg.mcpToolCalls, name);\n\t\t\treturn;\n\t\t}\n\t}\n\tbump(agg.toolCalls, name);\n}\n\n/**\n * Static MCP inventory from `~/.config/opencode/opencode.json` (JSONC - the\n * scanner owns the tolerant parse). A configured server the window never\n * called still exists: zero-count entries ride into the inventory. The\n * sanitized form is what tool-name prefixes are matched against, longest\n * first so `foo-bar` wins over `foo` for `foo-bar_tool`.\n */\nexport function noteConfiguredMcpServers(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\tserverNames: Iterable<string>,\n): void {\n\tfor (const raw of serverNames) {\n\t\tconst name = cleanName(sanitizeMcpName(raw));\n\t\tif (!agg.mcpServerCalls.has(name)) agg.mcpServerCalls.set(name, 0);\n\t\tif (!state.mcpServers.includes(name)) state.mcpServers.push(name);\n\t}\n\tstate.mcpServers.sort((a, b) => b.length - a.length);\n}\n","// I/O shell around the pure opencode analyzer: find `opencode*.db`, open it\n// read-only with node:sqlite, project NAMED COLUMNS through json_extract, and\n// hand plain values to the fold. Nothing leaves this machine.\n//\n// Wayfinder ticket #124 (map #121), semantics from\n// docs/research/harness-adapters-2026-08.md (§opencode).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths\n// and repo names never leave the machine. The SAME FILE this module opens\n// also holds `account.refresh_token`, `credential.value`,\n// `session_input.prompt` and full file contents in `session.summary_diffs`\n// and `part.data.state.output`. The rule that keeps them out: never\n// `SELECT *` - every query names its columns, and `part.data` reaches JS only\n// as four json_extract'ed scalars. Errors are swallowed, not thrown, because\n// a node:sqlite error message carries the DB path.\n\nimport { readFileSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateDbFoldState,\n\ttype DbFoldState,\n\tingestMessageRow,\n\tingestToolPart,\n\tnoteConfiguredMcpServers,\n\tnoteSessions,\n} from \"./analyzer.js\";\n\n/**\n * Newest migration id this build understands, from the probe DB (research\n * §1). A DB migrated past it may hold the same table names with different\n * semantics, so it counts as UNREADABLE - a visible coverage figure - rather\n * than being read on the guess that nothing moved.\n */\nexport const OPENCODE_MIGRATION_CEILING = 20260622202450;\n\n/** `$XDG_DATA_HOME/opencode` or `~/.local/share/opencode` - opencode's own rule. */\nexport function opencodeDataDirs(): string[] {\n\tconst xdg = process.env.XDG_DATA_HOME;\n\tconst base = xdg || path.join(homedir(), \".local\", \"share\");\n\treturn [path.join(base, \"opencode\")];\n}\n\n/**\n * The store is `opencode.db` on release channels, `opencode-<channel>.db`\n * otherwise, and `$OPENCODE_DB` overrides both. WAL siblings (`-wal`,\n * `-shm`) are opened by SQLite itself, never listed as stores.\n */\nfunction isStoreFile(basename: string): boolean {\n\treturn basename === \"opencode.db\" || /^opencode-[^/]+\\.db$/.test(basename);\n}\n\nasync function dbFilesIn(root: string): Promise<string[]> {\n\tconst override = process.env.OPENCODE_DB;\n\tif (override) return [override];\n\ttry {\n\t\tconst entries = await readdir(root, { withFileTypes: true });\n\t\treturn entries\n\t\t\t.filter((e) => e.isFile() && isStoreFile(e.name))\n\t\t\t.map((e) => path.join(root, e.name))\n\t\t\t.sort();\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// node:sqlite\n// ---------------------------------------------------------------------------\n\ntype SqliteDb = {\n\tprepare(sql: string): {\n\t\tall(...params: unknown[]): Record<string, unknown>[];\n\t\tget(...params: unknown[]): Record<string, unknown> | undefined;\n\t};\n\tclose(): void;\n};\n\n/**\n * `node:sqlite` landed in Node 22.5, which is also this CLI's runtime floor.\n * Keep the import guarded so a damaged or nonstandard runtime reports the DB\n * as unreadable instead of crashing sync.\n */\nasync function loadSqlite(): Promise<((file: string) => SqliteDb) | null> {\n\ttry {\n\t\tconst mod = (await import(\"node:sqlite\")) as {\n\t\t\tDatabaseSync: new (file: string, opts: { readOnly: boolean }) => SqliteDb;\n\t\t};\n\t\tif (typeof mod.DatabaseSync !== \"function\") return null;\n\t\treturn (file) => new mod.DatabaseSync(file, { readOnly: true });\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * A read failure classified WITHOUT the error object's message or stack -\n * both can carry the absolute DB path, which never leaves this module.\n */\nfunction errorClass(e: unknown): string {\n\tconst code = (e as { code?: unknown } | null)?.code;\n\tif (typeof code === \"string\" && code.length > 0) return code;\n\treturn e instanceof Error ? e.constructor.name : \"unknown\";\n}\n\nconst readError = (reason: string): Error =>\n\tObject.assign(new Error(reason), { code: reason });\n\n// ---------------------------------------------------------------------------\n// Scan\n// ---------------------------------------------------------------------------\n\nexport type ScanOptions = {\n\t/** Only count records with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered data dirs. Tests only. */\n\troots?: string[];\n\t/** Override the opencode.json path. Tests only. */\n\tconfigFile?: string;\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst open = await loadSqlite();\n\tconst sinceMs = opts.sinceMs ?? 0;\n\n\tconst state = createDbFoldState();\n\treadConfiguredMcpServers(agg, state, opts.configFile);\n\n\tfor (const root of opts.roots ?? opencodeDataDirs()) {\n\t\tfor (const file of await dbFilesIn(root)) {\n\t\t\tstats.filesFound++;\n\t\t\tagg.files++;\n\t\t\ttry {\n\t\t\t\tif (open === null) throw readError(\"sqlite-unsupported\");\n\t\t\t\treadDb(agg, state, open, file, sinceMs);\n\t\t\t\tstats.filesRead++;\n\t\t\t} catch (e) {\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.basename(file),\n\t\t\t\t\treason: errorClass(e),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (opts.onProgress) opts.onProgress(stats.filesFound);\n\t\t}\n\t}\n\treturn stats;\n}\n\n/** Refuse a DB whose newest migration this build has never seen. */\nfunction checkMigrationCeiling(db: SqliteDb): void {\n\tlet newest: unknown;\n\ttry {\n\t\tnewest = db.prepare(\"select max(id) as id from migration\").get()?.id;\n\t} catch {\n\t\tthrow readError(\"schema-unversioned\");\n\t}\n\tconst prefix = Number.parseInt(String(newest ?? \"\"), 10);\n\tif (!Number.isFinite(prefix)) throw readError(\"schema-unversioned\");\n\tif (prefix > OPENCODE_MIGRATION_CEILING) throw readError(\"schema-too-new\");\n}\n\nfunction readDb(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\topen: (file: string) => SqliteDb,\n\tfile: string,\n\tsinceMs: number,\n): void {\n\tconst db = open(file);\n\ttry {\n\t\tcheckMigrationCeiling(db);\n\n\t\tnoteSessions(\n\t\t\tstate,\n\t\t\tdb\n\t\t\t\t.prepare(\"select id, parent_id, version from session\")\n\t\t\t\t.all()\n\t\t\t\t.map((r) => ({ id: r.id, parentId: r.parent_id, version: r.version })),\n\t\t);\n\n\t\t// v1 messages. `time.created` (epoch ms, in the blob) prices the\n\t\t// response; the indexed integer column runs the window filter and backs\n\t\t// a blob whose clock field is missing or malformed.\n\t\tconst v1 = db.prepare(\n\t\t\t`select id, session_id, time_created,\n\t\t\t\tjson_extract(data, '$.role') as role,\n\t\t\t\tjson_extract(data, '$.providerID') as provider_id,\n\t\t\t\tjson_extract(data, '$.modelID') as model_id,\n\t\t\t\tjson_extract(data, '$.time.created') as ts_ms,\n\t\t\t\tjson_extract(data, '$.tokens.input') as tok_input,\n\t\t\t\tjson_extract(data, '$.tokens.output') as tok_output,\n\t\t\t\tjson_extract(data, '$.tokens.reasoning') as tok_reasoning,\n\t\t\t\tjson_extract(data, '$.tokens.cache.read') as tok_cache_read,\n\t\t\t\tjson_extract(data, '$.tokens.cache.write') as tok_cache_write,\n\t\t\t\tjson_extract(data, '$.time.completed') as completed_ts_ms,\n\t\t\t\tjson_extract(data, '$.path.cwd') as cwd\n\t\t\tfrom message where time_created >= ?`,\n\t\t);\n\t\tfor (const r of v1.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestMessageRow(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\trole: r.role,\n\t\t\t\tproviderId: r.provider_id,\n\t\t\t\tmodelId: r.model_id,\n\t\t\t\ttsMs: pickTs(r.ts_ms, r.time_created),\n\t\t\t\tinput: r.tok_input,\n\t\t\t\toutput: r.tok_output,\n\t\t\t\tcacheRead: r.tok_cache_read,\n\t\t\t\tcacheWrite: r.tok_cache_write,\n\t\t\t\tcwd: r.cwd,\n\t\t\t\treasoning: r.tok_reasoning,\n\t\t\t\tcompletedTsMs: r.completed_ts_ms,\n\t\t\t});\n\t\t}\n\n\t\t// v2 (`session_message`) - the other live generation (research §1: which\n\t\t// one current opencode writes is unproven, so both are read and message\n\t\t// ids dedup across them). The assistant shape differs: `model: {id,\n\t\t// providerID}`, role in the `type` column.\n\t\tconst v2 = db.prepare(\n\t\t\t`select id, session_id, type, time_created,\n\t\t\t\tjson_extract(data, '$.model.providerID') as provider_id,\n\t\t\t\tjson_extract(data, '$.model.id') as model_id,\n\t\t\t\tjson_extract(data, '$.time.created') as ts_ms,\n\t\t\t\tjson_extract(data, '$.tokens.input') as tok_input,\n\t\t\t\tjson_extract(data, '$.tokens.output') as tok_output,\n\t\t\t\tjson_extract(data, '$.tokens.reasoning') as tok_reasoning,\n\t\t\t\tjson_extract(data, '$.tokens.cache.read') as tok_cache_read,\n\t\t\t\tjson_extract(data, '$.tokens.cache.write') as tok_cache_write,\n\t\t\t\tjson_extract(data, '$.time.completed') as completed_ts_ms,\n\t\t\t\tjson_extract(data, '$.path.cwd') as cwd\n\t\t\tfrom session_message where time_created >= ?`,\n\t\t);\n\t\tfor (const r of v2.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestMessageRow(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\trole: r.type,\n\t\t\t\tproviderId: r.provider_id,\n\t\t\t\tmodelId: r.model_id,\n\t\t\t\ttsMs: pickTs(r.ts_ms, r.time_created),\n\t\t\t\tinput: r.tok_input,\n\t\t\t\toutput: r.tok_output,\n\t\t\t\tcacheRead: r.tok_cache_read,\n\t\t\t\tcacheWrite: r.tok_cache_write,\n\t\t\t\tcwd: r.cwd,\n\t\t\t\treasoning: r.tok_reasoning,\n\t\t\t\tcompletedTsMs: r.completed_ts_ms,\n\t\t\t});\n\t\t}\n\n\t\t// v1 tool parts. Only named scalar paths reach JavaScript.\n\t\t// `$.state.output` holds full command output and stays in SQLite.\n\t\tconst parts = db.prepare(\n\t\t\t`select id, message_id, session_id, time_created,\n\t\t\t\tjson_extract(data, '$.type') as part_type,\n\t\t\t\tjson_extract(data, '$.tool') as tool,\n\t\t\t\tjson_extract(data, '$.callID') as call_id,\n\t\t\t\tjson_extract(data, '$.state.input.name') as input_name,\n\t\t\t\tjson_extract(data, '$.state.input.subagent_type') as subagent_type,\n\t\t\t\tjson_extract(data, '$.state.input.command') as command\n\t\t\tfrom part\n\t\t\twhere time_created >= ? and json_extract(data, '$.type') = 'tool'\n\t\t\torder by time_created, message_id, id`,\n\t\t);\n\t\tfor (const r of parts.all(sinceMs)) {\n\t\t\tagg.lines++;\n\t\t\tingestToolPart(agg, state, {\n\t\t\t\tid: r.id,\n\t\t\t\tpartType: r.part_type,\n\t\t\t\ttool: r.tool,\n\t\t\t\tcallId: r.call_id,\n\t\t\t\tinputName: r.input_name,\n\t\t\t\tsubagentType: r.subagent_type,\n\t\t\t\tsessionId: r.session_id,\n\t\t\t\ttsMs: pickTs(null, r.time_created),\n\t\t\t\tcommand: r.command,\n\t\t\t\tmessageId: r.message_id,\n\t\t\t});\n\t\t}\n\n\t\t// v2 inline tool content, same named-scalar rule via json_each. The v2\n\t\t// content shape is unverified on any real machine, so a query error here\n\t\t// is tolerated - the tokens above are the load-bearing read.\n\t\ttry {\n\t\t\tconst v2parts = db.prepare(\n\t\t\t\t`select sm.id || ':' || je.key as id, sm.id as message_id,\n\t\t\t\t\tsm.session_id, sm.time_created,\n\t\t\t\t\tjson_extract(je.value, '$.type') as part_type,\n\t\t\t\t\tjson_extract(je.value, '$.tool') as tool,\n\t\t\t\t\tjson_extract(je.value, '$.callID') as call_id,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.name') as input_name,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.subagent_type') as subagent_type,\n\t\t\t\t\tjson_extract(je.value, '$.state.input.command') as command\n\t\t\t\tfrom session_message sm, json_each(sm.data, '$.content') je\n\t\t\t\twhere sm.time_created >= ? and sm.type = 'assistant'\n\t\t\t\torder by sm.time_created, sm.seq, sm.id, cast(je.key as integer)`,\n\t\t\t);\n\t\t\tfor (const r of v2parts.all(sinceMs)) {\n\t\t\t\tingestToolPart(agg, state, {\n\t\t\t\t\tid: r.id,\n\t\t\t\t\tpartType: r.part_type,\n\t\t\t\t\ttool: r.tool,\n\t\t\t\t\tcallId: r.call_id,\n\t\t\t\t\tinputName: r.input_name,\n\t\t\t\t\tsubagentType: r.subagent_type,\n\t\t\t\t\tsessionId: r.session_id,\n\t\t\t\t\ttsMs: pickTs(null, r.time_created),\n\t\t\t\t\tcommand: r.command,\n\t\t\t\t\tmessageId: r.message_id,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t/* v2 content unreadable - the message tokens already counted */\n\t\t}\n\t} finally {\n\t\ttry {\n\t\t\tdb.close();\n\t\t} catch {\n\t\t\t/* already closed or never opened fully */\n\t\t}\n\t}\n}\n\n/** The blob's own clock when it is a finite number, else the indexed column. */\nfunction pickTs(jsonTs: unknown, columnTs: unknown): number | null {\n\tif (typeof jsonTs === \"number\" && Number.isFinite(jsonTs)) return jsonTs;\n\tif (typeof columnTs === \"number\" && Number.isFinite(columnTs))\n\t\treturn columnTs;\n\t// node:sqlite may hand integers back as bigint depending on flags.\n\tif (typeof columnTs === \"bigint\") return Number(columnTs);\n\tif (typeof jsonTs === \"bigint\") return Number(jsonTs);\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Config - the static MCP inventory\n// ---------------------------------------------------------------------------\n\n/** `$XDG_CONFIG_HOME/opencode/opencode.json` or `~/.config/opencode/opencode.json`. */\nexport function opencodeConfigFile(): string {\n\tconst xdg = process.env.XDG_CONFIG_HOME;\n\tconst base = xdg || path.join(homedir(), \".config\");\n\treturn path.join(base, \"opencode\", \"opencode.json\");\n}\n\n/**\n * The real config is JSONC - comments and trailing commas (research\n * §inventory) - so `JSON.parse` alone throws on it. The strip below is\n * string-aware: a `//` inside a quoted URL survives. Any remaining parse\n * failure is silence, not an error: the observed half of the MCP inventory\n * stands on its own.\n */\nfunction readConfiguredMcpServers(\n\tagg: Aggregate,\n\tstate: DbFoldState,\n\tconfigFile?: string,\n): void {\n\tconst file = configFile ?? opencodeConfigFile();\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(stripJsonc(readFileSync(file, \"utf8\")));\n\t\tconst mcp = (parsed as { mcp?: unknown } | null)?.mcp;\n\t\tif (mcp && typeof mcp === \"object\" && !Array.isArray(mcp)) {\n\t\t\tnoteConfiguredMcpServers(agg, state, Object.keys(mcp));\n\t\t}\n\t} catch {\n\t\treturn;\n\t}\n}\n\nexport function stripJsonc(text: string): string {\n\tlet out = \"\";\n\tlet i = 0;\n\tlet inString = false;\n\twhile (i < text.length) {\n\t\tconst ch = text[i];\n\t\tconst next = text[i + 1];\n\t\tif (inString) {\n\t\t\tout += ch;\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tout += next ?? \"\";\n\t\t\t\ti += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === '\"') inString = false;\n\t\t\ti++;\n\t\t} else if (ch === '\"') {\n\t\t\tinString = true;\n\t\t\tout += ch;\n\t\t\ti++;\n\t\t} else if (ch === \"/\" && next === \"/\") {\n\t\t\twhile (i < text.length && text[i] !== \"\\n\") i++;\n\t\t} else if (ch === \"/\" && next === \"*\") {\n\t\t\ti += 2;\n\t\t\twhile (i < text.length && !(text[i] === \"*\" && text[i + 1] === \"/\")) i++;\n\t\t\ti += 2;\n\t\t} else {\n\t\t\tout += ch;\n\t\t\ti++;\n\t\t}\n\t}\n\t// Trailing commas: `, }` and `, ]` with any whitespace between.\n\treturn out.replace(/,(\\s*[}\\]])/g, \"$1\");\n}\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detection is a QUERY, not a stat walk (#101, research §6): every opencode\n * start - including `opencode --version` - touches the DB file, and the probe\n * machine showed a four-month gap between the file's mtime and the newest\n * real message. The indexed probe costs 0.02 ms.\n */\nexport async function detectOpencode(opts: {\n\tsinceMs: number;\n\troots?: string[];\n}): Promise<boolean> {\n\tconst open = await loadSqlite();\n\tif (open === null) return false;\n\n\tfor (const root of opts.roots ?? opencodeDataDirs()) {\n\t\tfor (const file of await dbFilesIn(root)) {\n\t\t\tif (!(await exists(file))) continue;\n\t\t\tlet db: SqliteDb | null = null;\n\t\t\ttry {\n\t\t\t\tdb = open(file);\n\t\t\t\tcheckMigrationCeiling(db);\n\t\t\t\tconst probe = (table: string) =>\n\t\t\t\t\tdb\n\t\t\t\t\t\t?.prepare(\n\t\t\t\t\t\t\t`select 1 as hit from ${table} where time_created >= ? limit 1`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get(opts.sinceMs) !== undefined;\n\t\t\t\tif (probe(\"message\") || probe(\"session_message\")) return true;\n\t\t\t} catch {\n\t\t\t\t/* unreadable or foreign DB - not detection */\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tdb?.close();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","// The opencode harness behind the seam (#66 decision 6) - wayfinder ticket\n// #124 (map #121).\n\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate, OPENCODE_BUILTIN_TOOLS } from \"./analyzer.js\";\nimport { detectOpencode, scan } from \"./scan.js\";\n\nexport const OPENCODE_HARNESS_NAME = \"opencode\";\n\nexport { OPENCODE_BUILTIN_TOOLS } from \"./analyzer.js\";\n\nexport const opencodeAdapter: HarnessAdapter = {\n\tname: OPENCODE_HARNESS_NAME,\n\tbuiltinTools: OPENCODE_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn detectOpencode({\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.roots ? { roots: opts.roots } : {}),\n\t\t});\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","// Pure fold over parsed pi session-file entries. No I/O, no console.\n//\n// Wayfinder ticket #126 (map #121). Field semantics come from\n// docs/research/harness-adapters-2026-08.md (§pi-mono), read off the vendor\n// doc set shipped in /opt/pi-coding-agent/docs and verified against the real\n// files in ~/.pi/agent/sessions. Every field is untrusted and optional: entries\n// arrive as `unknown` and are narrowed here.\n//\n// THE LOAD-BEARING FACTS (research §2-§3):\n// - `usage.input` already EXCLUDES cache traffic - no subtraction (Codex,\n// inverted); `reasoning` is a subset of `output` - never add it;\n// - `cacheWrite1h` is a SUBSET of `cacheWrite`, so the TTL split maps onto\n// TokenCounts exactly: pi is the only harness that hands the re-pricer\n// the split instead of a lower bound;\n// - /fork and /clone copy entries into a second file KEEPING entry ids, so\n// usage dedup is cross-file, and the 8-hex id alone collides at corpus\n// scale - the key is `${id}:${timestamp}:${totalTokens}`;\n// - `compaction.retainedTail` embeds assistant messages that already appear\n// as their own entries earlier in the same file - never descend into it;\n// - pi's own `usage.cost` is computed against an unpinned network-refreshed\n// table - uncitable, so cost comes from @aistack/pricing only.\n//\n// Pricing keys follow #123's binding rule: every row is keyed\n// `modelKeyFor(provider, model)` with pi's own provider id verbatim. Only\n// `anthropic`/`openai`/`google` reach vendor rates; a router-billed response\n// (`openrouter:anthropic/claude-opus-4.6`) stays unpriced, which is the safe\n// direction - it did not pay vendor list price.\n\nimport {\n\tapiEquivalentCost,\n\tmodelKeyFor,\n\tnormalizeModel,\n\ttype TokenCounts,\n} from \"@aistack/pricing\";\nimport {\n\tcreateHarnessWorkflowReducer,\n\tcreateWorkflowLocalSources,\n\ttype HarnessWorkflowReducer,\n\ttype WorkflowLocalSources,\n} from \"../../workflow/reducer.js\";\nimport {\n\taddModelUsage,\n\tasArr,\n\tasName,\n\tasNum,\n\tasObj,\n\tasStr,\n\tbump,\n\tcountsTotal,\n\tcreateAggregate as createSharedAggregate,\n\tnoteProjectDay,\n\tnoteSessionStart,\n\ttype Aggregate as SharedAggregate,\n} from \"../shared/aggregate.js\";\n\n/** Cross-file dedup lives in FoldState, not the aggregate's `seen`. */\nexport type Aggregate = SharedAggregate<never> & {\n\tworkflow: HarnessWorkflowReducer;\n\tworkflowLocal: WorkflowLocalSources;\n};\n\nexport function createAggregate(): Aggregate {\n\tconst workflowLocal = createWorkflowLocalSources();\n\treturn Object.assign(createSharedAggregate<never>(), {\n\t\tworkflow: createHarnessWorkflowReducer(\"pi-mono\", workflowLocal),\n\t\tworkflowLocal,\n\t});\n}\n\n/**\n * Scan-level fold state, shared across every file in one scan: /fork and\n * /clone duplicate entries into a second file keeping their ids, so the dedup\n * set cannot be per-file.\n */\nexport type FoldState = {\n\tseenUsage: Set<string>;\n};\n\nexport function createFoldState(): FoldState {\n\treturn { seenUsage: new Set() };\n}\n\n/** Per-file fold state. */\nexport type FileState = {\n\tsessionId: string | null;\n\tcwd: string | null;\n\t/** Pricing key of the nearest preceding assistant message or model_change. */\n\tmodelKey: string | null;\n\t/** True once any in-window entry was counted for this file. */\n\tcounted: boolean;\n};\n\nexport function createFileState(): FileState {\n\treturn {\n\t\tsessionId: null,\n\t\tcwd: null,\n\t\tmodelKey: null,\n\t\tcounted: false,\n\t};\n}\n\n/**\n * Fold one parsed session-file entry into the aggregate.\n *\n * `sinceMs` is applied HERE rather than in the scanner because context entries\n * (the header, `model_change`) must update `state` even when they predate the\n * window - a session resumed today bills today's usage to a model named last\n * week. The window filter reads the entry's ISO timestamp first and falls back\n * to the message's Unix-ms timestamp, the same order pricing uses.\n */\nexport function ingestEntry(\n\tagg: Aggregate,\n\traw: unknown,\n\tstate: FileState,\n\tfold: FoldState,\n\tsinceMs?: number,\n): void {\n\tconst rec = asObj(raw);\n\tif (!rec) return;\n\tagg.records++;\n\n\tconst type = asStr(rec.type);\n\tconst message = type === \"message\" ? asObj(rec.message) : null;\n\tconst role = message ? asStr(message.role) : null;\n\n\t// Context updates happen regardless of the window.\n\tif (type === \"session\") {\n\t\tstate.sessionId = asStr(rec.id) ?? state.sessionId;\n\t\tstate.cwd = asStr(rec.cwd) ?? state.cwd;\n\t\treturn;\n\t}\n\tif (type === \"model_change\") {\n\t\tconst provider = asStr(rec.provider);\n\t\tconst model = asStr(rec.modelId);\n\t\tif (provider && model) state.modelKey = toPricingKey(provider, model);\n\t}\n\tif (role === \"assistant\" && message) {\n\t\tconst provider = asStr(message.provider);\n\t\tconst model = asStr(message.model);\n\t\tif (provider && model) state.modelKey = toPricingKey(provider, model);\n\t}\n\n\t// Entry-level ISO timestamp first, message-level Unix ms as the fallback.\n\tconst entryTs = Date.parse(asStr(rec.timestamp) ?? \"\");\n\tconst msgTs = message ? asNum(message.timestamp) : 0;\n\tconst tsMs = !Number.isNaN(entryTs) ? entryTs : msgTs > 0 ? msgTs : null;\n\n\tconst inWindow = sinceMs === undefined || (tsMs !== null && tsMs >= sinceMs);\n\tif (!inWindow) return;\n\n\tif (tsMs !== null) {\n\t\tagg.activeDays.add(new Date(tsMs).toISOString().slice(0, 10));\n\t\tagg.firstTs = agg.firstTs === null ? tsMs : Math.min(agg.firstTs, tsMs);\n\t\tagg.lastTs = agg.lastTs === null ? tsMs : Math.max(agg.lastTs, tsMs);\n\t}\n\tnoteActivity(agg, state, tsMs);\n\tnoteProjectDay(agg, state.cwd ?? \"(unknown)\", tsMs);\n\n\tif (role === \"assistant\" && message) {\n\t\tagg.assistantRecords++;\n\t\t// `model` is what pi asked for, `responseModel` what the API says it\n\t\t// served. Routers make them differ, and then the rate for `model`\n\t\t// cannot be cited - the tokens surface as unpriced instead.\n\t\tconst served = asStr(message.responseModel);\n\t\tconst priceable = served === null || served === asStr(message.model);\n\t\tconst outcome = countUsage(\n\t\t\tagg,\n\t\t\tfold,\n\t\t\trec,\n\t\t\tmsgTs,\n\t\t\tmessage.usage,\n\t\t\tstate.modelKey,\n\t\t\ttsMs,\n\t\t\tpriceable,\n\t\t);\n\t\t// A /fork duplicate repeats the content blocks too; the call-id dedup\n\t\t// already covers tool calls, but the thinking/text tallies have no ids.\n\t\tif (outcome !== \"duplicate\") {\n\t\t\tif (state.sessionId && tsMs !== null) {\n\t\t\t\tconst usage = asObj(message.usage);\n\t\t\t\tconst counts = readCounts(message.usage);\n\t\t\t\tagg.workflow.ingest({\n\t\t\t\t\ttype: \"response\",\n\t\t\t\t\tsession: state.sessionId,\n\t\t\t\t\t...(asStr(rec.id) ? { responseId: asStr(rec.id) as string } : {}),\n\t\t\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\t\t\ttsMs,\n\t\t\t\t\t...(state.modelKey ? { model: state.modelKey } : {}),\n\t\t\t\t\tthinkingTokens: usage ? asNum(usage.reasoning) : 0,\n\t\t\t\t\tresponseTokens: counts?.output ?? 0,\n\t\t\t\t\troutingTokens: counts ? countsTotal(counts) : 0,\n\t\t\t\t});\n\t\t\t\tingestContent(agg, message.content, state.sessionId, state.cwd, tsMs);\n\t\t\t\tagg.workflow.ingest({\n\t\t\t\t\ttype: \"turn\",\n\t\t\t\t\tsession: state.sessionId,\n\t\t\t\t\t...(asStr(rec.id) ? { turnId: asStr(rec.id) as string } : {}),\n\t\t\t\t\tprojectWorkspace: state.cwd ?? undefined,\n\t\t\t\t\ttsMs,\n\t\t\t\t\tquestionBack: false,\n\t\t\t\t});\n\t\t\t} else ingestContent(agg, message.content);\n\t\t}\n\t} else if (role === \"toolResult\" && message) {\n\t\t// \"Nested LLM work performed by the tool\" - real spend, counted by\n\t\t// pi's own footer. No model of its own, so it bills to the model in\n\t\t// effect, the way Codex deltas bill to the nearest turn_context.\n\t\tcountUsage(agg, fold, rec, msgTs, message.usage, state.modelKey, tsMs);\n\t} else if (type === \"compaction\" || type === \"branch_summary\") {\n\t\t// Summary generation is real spend (optional `usage` on the entry). The\n\t\t// materialized `retainedTail` embeds assistant messages that already\n\t\t// appear as their own entries - deliberately never walked.\n\t\tcountUsage(agg, fold, rec, 0, rec.usage, state.modelKey, tsMs);\n\t}\n}\n\n/** Count the file's session and cwd once, on its first in-window entry. */\nfunction noteActivity(\n\tagg: Aggregate,\n\tstate: FileState,\n\ttsMs: number | null,\n): void {\n\tif (state.sessionId) noteSessionStart(agg, state.sessionId, tsMs);\n\tif (state.counted) return;\n\tstate.counted = true;\n\tif (state.sessionId) agg.sessions.add(state.sessionId);\n\t// Counted, never published - same standing non-goal as Claude project dirs.\n\tagg.projectDirs.add(state.cwd ?? \"(unknown)\");\n}\n\n/** Fold one usage block into the totals, behind the cross-file dedup. */\nfunction countUsage(\n\tagg: Aggregate,\n\tfold: FoldState,\n\trec: Record<string, unknown>,\n\tmsgTsMs: number,\n\tusageRaw: unknown,\n\tmodelKey: string | null,\n\ttsMs: number | null,\n\tpriceable = true,\n): \"counted\" | \"duplicate\" | \"none\" {\n\tconst counts = readCounts(usageRaw);\n\tif (!counts) return \"none\";\n\tconst total = countsTotal(counts);\n\tif (total === 0) return \"none\";\n\n\t// /fork and /clone write the same entry into a second file with its id\n\t// intact, so dedup is cross-file. The 8-hex id alone has a real birthday\n\t// collision at corpus scale, so the timestamps and the token total ride\n\t// along - two genuinely different responses sharing an id stay two.\n\tconst id = asStr(rec.id);\n\tif (id) {\n\t\tconst key = `${id}:${asStr(rec.timestamp) ?? \"\"}:${msgTsMs}:${total}`;\n\t\tif (fold.seenUsage.has(key)) {\n\t\t\tagg.continuationsFolded++;\n\t\t\treturn \"duplicate\";\n\t\t}\n\t\tfold.seenUsage.add(key);\n\t} else {\n\t\tagg.unkeyedResponses++;\n\t}\n\n\tif (tsMs === null) agg.untimestampedResponses++;\n\tagg.distinctResponses++;\n\tconst key = modelKey ?? \"(unknown)\";\n\taddModelUsage(\n\t\tagg,\n\t\tkey,\n\t\tcounts,\n\t\tpriceable ? apiEquivalentCost(key, counts, tsMs) : null,\n\t\t1,\n\t\t{ tsMs },\n\t);\n\t// pi has no subagents by vendor design - everything is the main thread,\n\t// which keeps `subagentShare` an honest 0 (same case as Codex).\n\tagg.mainTokens += total;\n\treturn \"counted\";\n}\n\n/**\n * Compose the pricing key from pi's own provider and model ids (#123's\n * binding rule). pi spells fast mode as an id SUFFIX (`claude-opus-5-fast`)\n * and has no `usage.speed` field, so the suffix is translated into the price\n * table's `#fast` marker here. A model genuinely named `-fast` by a provider\n * the table does not cover stays unpriced either way, so the translation\n * cannot invent a rate.\n */\nfunction toPricingKey(provider: string, model: string): string {\n\tconst fast = model.endsWith(\"-fast\");\n\tconst marked = fast ? `${model.slice(0, -\"-fast\".length)}#fast` : model;\n\treturn normalizeModel(modelKeyFor(provider, marked));\n}\n\n/**\n * Assistant content blocks: tool calls plus the thinking/text tallies.\n *\n * `bashExecution` entries are deliberately NOT counted as tool calls - they\n * are user-typed `!` commands, not something the model chose. A name outside\n * PI_BUILTIN_TOOLS is a user extension's tool; it stays a plain count and the\n * shared fail-closed payload filter withholds the name. pi has no MCP, no\n * subagents and no skill tool by vendor design, so those maps stay EMPTY -\n * absent from the payload, never zero (#40).\n */\nfunction ingestContent(\n\tagg: Aggregate,\n\tcontentRaw: unknown,\n\tsession?: string,\n\tprojectWorkspace?: string | null,\n\ttsMs?: number,\n): void {\n\tfor (const blockRaw of asArr(contentRaw)) {\n\t\tconst block = asObj(blockRaw);\n\t\tif (!block) continue;\n\t\tconst type = asStr(block.type);\n\t\tif (type === \"thinking\") {\n\t\t\tagg.thinkingBlocks++;\n\t\t} else if (type === \"text\") {\n\t\t\tagg.textBlocks++;\n\t\t} else if (type === \"toolCall\") {\n\t\t\tconst name = asName(block.name);\n\t\t\tif (!name) continue;\n\t\t\t// /fork duplicates keep call ids, so the shared dedup set makes the\n\t\t\t// copy a repeat rather than a double count.\n\t\t\tconst callId = asStr(block.id);\n\t\t\tif (callId) {\n\t\t\t\tif (agg.toolCallDedup.has(callId)) continue;\n\t\t\t\tagg.toolCallDedup.add(callId);\n\t\t\t} else {\n\t\t\t\tagg.toolBlocksWithoutId++;\n\t\t\t}\n\t\t\tbump(agg.toolCalls, name);\n\t\t\tif (session && tsMs !== undefined) {\n\t\t\t\tconst args = asObj(block.arguments) ?? asObj(block.args) ?? {};\n\t\t\t\tagg.workflow.ingest({\n\t\t\t\t\ttype: \"event\",\n\t\t\t\t\tsession,\n\t\t\t\t\tprojectWorkspace: projectWorkspace ?? undefined,\n\t\t\t\t\ttsMs,\n\t\t\t\t\ttool: name,\n\t\t\t\t\targ: asStr(args.command) ?? \"\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The Usage shape shared by assistant/toolResult messages and summary entries. */\nfunction readCounts(usageRaw: unknown): TokenCounts | null {\n\tconst u = asObj(usageRaw);\n\tif (!u) return null;\n\tconst cacheWrite = asNum(u.cacheWrite);\n\tconst split =\n\t\ttypeof u.cacheWrite1h === \"number\" && Number.isFinite(u.cacheWrite1h);\n\tconst cacheWrite1h = split\n\t\t? Math.min(Math.max(u.cacheWrite1h as number, 0), cacheWrite)\n\t\t: 0;\n\treturn {\n\t\t// Already exclusive of cache traffic - no subtraction (research §2).\n\t\tinput: asNum(u.input),\n\t\t// `reasoning` is a subset of `output` - never added.\n\t\toutput: asNum(u.output),\n\t\tcacheWrite5m: split ? cacheWrite - cacheWrite1h : 0,\n\t\tcacheWrite1h,\n\t\tcacheWriteUnsplit: split ? 0 : cacheWrite,\n\t\tcacheRead: asNum(u.cacheRead),\n\t};\n}\n","// I/O shell around the pure pi analyzer: find session files, stream JSONL,\n// hand each parsed entry to ingestEntry. Nothing leaves this machine.\n//\n// Wayfinder ticket #126 (map #121), semantics from\n// docs/research/harness-adapters-2026-08.md (§pi-mono).\n//\n// STANDING NON-GOAL (locked in #13): raw transcripts, prompts, absolute paths,\n// and repo names never leave the machine. pi's files are MORE sensitive than\n// the other harnesses': there is no separate history file, so raw prompts,\n// bashExecution output, base64 screenshots, provider error bodies and the\n// munged-absolute-path directory names all sit on the same lines the scanner\n// parses. The analyzer reads named fields only; the directory name is never\n// even counted (the header's `cwd` is, as an opaque key). Streaming line by\n// line keeps a pasted screenshot from pulling megabytes into memory.\n// `~/.pi/agent/auth.json` holds credentials - only `sessions/` is ever walked.\n// Read errors are swallowed, not thrown: the error object carries the path.\n\nimport { createReadStream, type Dirent } from \"node:fs\";\nimport { readdir, realpath, stat } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport readline from \"node:readline\";\n\nimport { asNum, asObj, asStr } from \"../shared/aggregate.js\";\nimport { emptyScanStats, type ScanStats } from \"../shared/window.js\";\nimport {\n\ttype Aggregate,\n\tcreateFileState,\n\tcreateFoldState,\n\ttype FoldState,\n\tingestEntry,\n} from \"./analyzer.js\";\n\n/**\n * The newest session-format version this scanner understands. The vendor doc\n * states the ladder (v1 linear, v2 tree, v3 renamed hookMessage to custom);\n * none of the changes touched `usage`, `model` or `timestamp`, so v1-v3 all\n * read with one fold. A file ABOVE the ceiling may have reshaped those fields,\n * so it counts as unreadable - a visible coverage figure - rather than being\n * misread as zeros.\n */\nexport const MAX_SESSION_VERSION = 3;\n\n/** `PI_CODING_AGENT_DIR` honored, `~/.pi/agent` the default - mirrors pi. */\nexport function piAgentDir(): string {\n\treturn (\n\t\tprocess.env.PI_CODING_AGENT_DIR || path.join(homedir(), \".pi\", \"agent\")\n\t);\n}\n\n/**\n * Only `sessions/` is read - `auth.json` (credentials), `settings.json` and\n * the ACP session map live beside it and are out of bounds. A run started\n * with `--session-dir` writes outside every discoverable root and is\n * invisible, which is silence - the direction #40 permits.\n */\nexport function sessionRoots(): string[] {\n\tconst override = process.env.PI_CODING_AGENT_SESSION_DIR;\n\treturn [override || path.join(piAgentDir(), \"sessions\")];\n}\n\n/**\n * pi names every session file `<munged-ISO-start>_<uuid>.jsonl`\n * (`2026-07-23T16-54-00-149Z_019f8fe5-….jsonl`). Shared with `detect` (#101).\n */\nconst SESSION_FILE_RE =\n\t/^\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}-\\d{3}Z_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.jsonl$/;\n\n/** What counts as a pi session file. */\nexport function isSessionFile(basename: string): boolean {\n\treturn SESSION_FILE_RE.test(basename);\n}\n\n/** Recursive walk - the real layout is two levels (one directory per cwd). */\nasync function* walkSessions(dir: string): AsyncGenerator<string> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(dir, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\tfor (const e of entries) {\n\t\tconst full = path.join(dir, e.name);\n\t\tif (e.isDirectory()) yield* walkSessions(full);\n\t\telse if (e.isFile() && isSessionFile(e.name)) yield full;\n\t}\n}\n\nexport type ScanOptions = {\n\t/** Only count entries with a timestamp at or after this epoch ms. */\n\tsinceMs?: number;\n\tonProgress?: (files: number) => void;\n\t/** Override the discovered roots. Tests only. */\n\troots?: string[];\n};\n\nexport async function scan(\n\tagg: Aggregate,\n\topts: ScanOptions = {},\n): Promise<ScanStats> {\n\tconst stats: ScanStats = emptyScanStats();\n\tconst visited = new Set<string>();\n\t// /fork and /clone duplicate entries ACROSS files, so the dedup state is\n\t// one per scan, not one per file.\n\tconst fold = createFoldState();\n\n\tfor (const root of opts.roots ?? sessionRoots()) {\n\t\tif (!(await exists(root))) continue;\n\t\tfor await (const file of walkSessions(root)) {\n\t\t\tstats.filesFound++;\n\n\t\t\tlet resolved: string;\n\t\t\ttry {\n\t\t\t\tresolved = await realpath(file);\n\t\t\t} catch {\n\t\t\t\tresolved = file;\n\t\t\t}\n\t\t\tif (visited.has(resolved)) {\n\t\t\t\tstats.filesSkippedAsDuplicate++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tvisited.add(resolved);\n\n\t\t\t// Session files are append-only, so a file untouched since the window\n\t\t\t// opened cannot hold an in-window entry.\n\t\t\tif (opts.sinceMs !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tconst st = await stat(file);\n\t\t\t\t\tif (st.mtimeMs < opts.sinceMs) {\n\t\t\t\t\t\tstats.filesSkippedByMtime++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* unreadable stat - fall through and try to read it */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tagg.files++;\n\t\t\tstats.filesRead++;\n\t\t\tif (opts.onProgress && agg.files % 200 === 0) opts.onProgress(agg.files);\n\t\t\ttry {\n\t\t\t\tconst verdict = await ingestFile(agg, fold, file, opts.sinceMs);\n\t\t\t\tif (verdict === \"foreign\") {\n\t\t\t\t\t// The first line is not a pi session header: another tool wrote\n\t\t\t\t\t// this file. Its usage stayed out of the aggregate entirely.\n\t\t\t\t\tstats.filesForeign++;\n\t\t\t\t\tstats.filesRead--;\n\t\t\t\t\tconst seen = stats.foreignOriginators.get(\"(no-pi-header)\") ?? 0;\n\t\t\t\t\tstats.foreignOriginators.set(\"(no-pi-header)\", seen + 1);\n\t\t\t\t} else if (verdict === \"version-too-new\") {\n\t\t\t\t\tstats.filesUnreadable++;\n\t\t\t\t\tstats.filesRead--;\n\t\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\t\treason: \"version-too-new\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Swallow deliberately: the error object carries the absolute path.\n\t\t\t\tstats.filesUnreadable++;\n\t\t\t\tstats.filesRead--;\n\t\t\t\tstats.unreadableFiles.push({\n\t\t\t\t\tpath: path.relative(root, file),\n\t\t\t\t\treason: \"read-error\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn stats;\n}\n\nasync function exists(p: string): Promise<boolean> {\n\ttry {\n\t\tawait stat(p);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Stream one session file through the fold. The vendor put the fingerprint on\n * line 1 - `{\"type\":\"session\"}` with a numeric `version` - so the verdict\n * lands before any usage is folded, and a foreign or too-new file leaves the\n * aggregate untouched by construction (no parse-then-fold buffering needed).\n */\nasync function ingestFile(\n\tagg: Aggregate,\n\tfold: FoldState,\n\tfile: string,\n\tsinceMs?: number,\n): Promise<\"ok\" | \"foreign\" | \"version-too-new\"> {\n\tconst rl = readline.createInterface({\n\t\tinput: createReadStream(file, { encoding: \"utf8\" }),\n\t\tcrlfDelay: Number.POSITIVE_INFINITY,\n\t});\n\tconst state = createFileState();\n\tlet first = true;\n\ttry {\n\t\tfor await (const line of rl) {\n\t\t\tif (!line) continue;\n\t\t\tagg.lines++;\n\t\t\tlet entry: unknown;\n\t\t\ttry {\n\t\t\t\tentry = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\tagg.parseErrors++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (first) {\n\t\t\t\tfirst = false;\n\t\t\t\tconst header = asObj(entry);\n\t\t\t\tconst version = header ? asNum(header.version) : 0;\n\t\t\t\tif (!header || asStr(header.type) !== \"session\" || version < 1) {\n\t\t\t\t\tagg.lines--;\n\t\t\t\t\treturn \"foreign\";\n\t\t\t\t}\n\t\t\t\tif (version > MAX_SESSION_VERSION) {\n\t\t\t\t\tagg.lines--;\n\t\t\t\t\treturn \"version-too-new\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tingestEntry(agg, entry, state, fold, sinceMs);\n\t\t}\n\t} finally {\n\t\trl.close();\n\t}\n\treturn \"ok\";\n}\n","// The pi coding agent behind the harness seam (#66 decision 6) - wayfinder\n// ticket #126 (map #121). The payload discriminator is the catalog slug,\n// `pi-mono` (the repo is earendil-works/pi-mono; the binary is `pi`).\n\nimport { hasRecentFile } from \"../shared/recency.js\";\nimport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"../types.js\";\nimport { createAggregate } from \"./analyzer.js\";\nimport { isSessionFile, scan, sessionRoots } from \"./scan.js\";\n\nexport const PI_HARNESS_NAME = \"pi-mono\";\n\n/**\n * pi's vendor-assigned tool surface - seven names, published in the vendor's\n * own docs (usage.md §tools). Same fail-closed mechanism as the other\n * adapters: a literal set, never a pattern. Everything outside it comes from\n * a user extension and publishes only as a per-category count. pi has no MCP,\n * no subagents and no skill tool by explicit vendor design, so those\n * categories stay absent rather than zero (#40).\n */\nexport const PI_BUILTIN_TOOLS: ReadonlySet<string> = new Set([\n\t\"read\",\n\t\"bash\",\n\t\"edit\",\n\t\"write\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n]);\n\nexport const piAdapter: HarnessAdapter = {\n\tname: PI_HARNESS_NAME,\n\tbuiltinTools: PI_BUILTIN_TOOLS,\n\n\tasync detect(opts: HarnessDetectOptions): Promise<boolean> {\n\t\treturn hasRecentFile(\n\t\t\topts.roots ?? sessionRoots(),\n\t\t\tisSessionFile,\n\t\t\topts.sinceMs,\n\t\t);\n\t},\n\n\tasync scan(opts: HarnessScanOptions): Promise<HarnessScan> {\n\t\tconst aggregate = createAggregate();\n\t\tconst stats = await scan(aggregate, {\n\t\t\tsinceMs: opts.sinceMs,\n\t\t\t...(opts.onProgress ? { onProgress: opts.onProgress } : {}),\n\t\t});\n\t\treturn {\n\t\t\taggregate,\n\t\t\tstats,\n\t\t\tworkflow: aggregate.workflow.finish(),\n\t\t\tworkflowLocal: aggregate.workflowLocal,\n\t\t};\n\t},\n};\n","// Local transcript analysis -> the measured-layer wire payload.\n//\n// Wayfinder ticket #37 (map #29), reshaped around the adapter seam by #67\n// (map #60). Everything here runs on the user's machine; only the payloads\n// returned by `buildPayload` are ever candidates to leave it, and only after\n// the approve gate the send channel owns (ticket #41).\n//\n// Typical use:\n//\n// const now = Date.now();\n// const sinceMs = windowStartMs(now, DEFAULT_WINDOW_DAYS);\n// const { config } = await loadSyncConfig({ baseUrl });\n// for (const adapter of await detectedAdapters(sinceMs)) {\n// const { aggregate, stats } = await adapter.scan({ sinceMs });\n// const built = buildPayload({\n// aggregate, stats, syncConfig: config, now,\n// windowDays: DEFAULT_WINDOW_DAYS,\n// harnessName: adapter.name,\n// builtinTools: adapter.builtinTools,\n// projectWorkspaceId: getProjectWorkspaceId,\n// });\n// }\n\nimport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nimport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nimport { CURSOR_HARNESS_NAME, cursorAdapter } from \"./cursor/adapter.js\";\nimport { GROK_HARNESS_NAME, grokAdapter } from \"./grok/adapter.js\";\nimport { OPENCODE_HARNESS_NAME, opencodeAdapter } from \"./opencode/adapter.js\";\nimport { PI_HARNESS_NAME, piAdapter } from \"./pi/adapter.js\";\nimport { DEFAULT_WINDOW_DAYS, windowStartMs } from \"./shared/window.js\";\nimport type { HarnessAdapter } from \"./types.js\";\n\nexport {\n\tapiEquivalentCost,\n\tbaseModelId,\n\tCACHE_READ_MULTIPLIER,\n\tCACHE_WRITE_1H_MULTIPLIER,\n\tCACHE_WRITE_5M_MULTIPLIER,\n\ttype CacheMultipliers,\n\tcacheMultipliersFor,\n\tGOOGLE_PRICING_TABLE_VERSION,\n\tisLocalModel,\n\tisPricedModel,\n\tLOCAL_PRICING_TABLE_VERSION,\n\tmodelKeyFor,\n\tnormalizeModel,\n\tOPENAI_PRICING_TABLE_VERSION,\n\tPRICING_TABLE_VERSION,\n\tPROVIDER_SEPARATOR,\n\ttype PricePeriod,\n\tpriceAt,\n\tSONNET_5_INTRO_ENDS_MS,\n\tsplitModelKey,\n\ttype TokenCounts,\n\tvendorModelId,\n} from \"@aistack/pricing\";\nexport { CLAUDE_HARNESS_NAME, claudeAdapter } from \"./claude/adapter.js\";\nexport {\n\ttype Aggregate as ClaudeAggregate,\n\tcreateAggregate,\n\ttype IngestContext,\n\tingestRecord,\n} from \"./claude/analyzer.js\";\nexport { type ScanOptions, scan, transcriptRoots } from \"./claude/scan.js\";\nexport { CODEX_HARNESS_NAME, codexAdapter } from \"./codex/adapter.js\";\nexport { CURSOR_HARNESS_NAME, cursorAdapter } from \"./cursor/adapter.js\";\nexport { GROK_HARNESS_NAME, grokAdapter } from \"./grok/adapter.js\";\nexport {\n\tOPENCODE_BUILTIN_TOOLS,\n\tOPENCODE_HARNESS_NAME,\n\topencodeAdapter,\n} from \"./opencode/adapter.js\";\nexport {\n\tPI_BUILTIN_TOOLS,\n\tPI_HARNESS_NAME,\n\tpiAdapter,\n} from \"./pi/adapter.js\";\nexport {\n\ttype Aggregate,\n\tcleanName,\n\ttype Finalized,\n\tfinalize,\n\tisDisplaySafeName,\n\ttype ModelRow,\n\ttype ModelUsage,\n\tnewestVersion,\n} from \"./shared/aggregate.js\";\nexport {\n\ttype Atom,\n\ttype AutoSyncPermission,\n\tBUILTIN_TOOLS,\n\tBUNDLED_SYNC_CONFIG,\n\ttype CuratedAllowlist,\n\tEMPTY_OPT_INS,\n\ttype FilteredAtoms,\n\ttype FilterSets,\n\tfilterAtoms,\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\tNAME_CATEGORIES,\n\ttype NameCategory,\n\ttype OptInNames,\n\tpluginGroup,\n\ttype SyncConfig,\n\ttype SyncConfigSource,\n} from \"./shared/allowlist.js\";\nexport { BUNDLED_CURATED_ALLOWLIST } from \"./shared/bundled-allowlist.js\";\nexport {\n\ttype BuildPayloadInput,\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\ttype MeasuredPayload,\n\tmergeKeptPrivate,\n\ttype PayloadAtom,\n\ttype PayloadInventory,\n\ttype PayloadModel,\n\tSCHEMA_VERSION,\n\ttype SyncBody,\n\tsanitizeModelId,\n} from \"./shared/payload.js\";\nexport { hasRecentFile } from \"./shared/recency.js\";\nexport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"./shared/window.js\";\nexport type {\n\tHarnessAdapter,\n\tHarnessDetectOptions,\n\tHarnessScan,\n\tHarnessScanOptions,\n} from \"./types.js\";\n\n/**\n * Every harness this build can read, in the order their payloads publish.\n * Registration order is also display order at the gate, so Claude Code - the\n * documented default - stays first.\n */\nexport const HARNESS_ADAPTERS: readonly HarnessAdapter[] = [\n\tclaudeAdapter,\n\tcodexAdapter,\n\tgrokAdapter,\n\tcursorAdapter,\n\topencodeAdapter,\n\tpiAdapter,\n];\n\n/** Display name for a harness discriminator. One name per harness, defined here. */\nexport function harnessLabel(name: string): string {\n\tif (name === CLAUDE_HARNESS_NAME) return \"Claude Code\";\n\tif (name === CODEX_HARNESS_NAME) return \"Codex\";\n\tif (name === CURSOR_HARNESS_NAME) return \"Cursor\";\n\tif (name === GROK_HARNESS_NAME) return \"Grok Build\";\n\t// opencode spells itself lowercase, so its discriminator IS the label -\n\t// but it is an explicit row, so a rename cannot leak a raw slug.\n\tif (name === OPENCODE_HARNESS_NAME) return \"opencode\";\n\t// The vendor renamed pi-mono to Pi (2026-08). The discriminator stays\n\t// \"pi-mono\" - it is the wire id - only the display label changed.\n\tif (name === PI_HARNESS_NAME) return \"Pi\";\n\treturn name;\n}\n\n/** The detected harnesses as one readable phrase. */\nexport function harnessListLabel(adapters: readonly HarnessAdapter[]): string {\n\treturn adapters.map((a) => harnessLabel(a.name)).join(\" or \");\n}\n\n/**\n * The window every detection uses when the caller has no scan in hand. The\n * scan passes its own window start instead, which is the same number.\n */\nexport function detectionSinceMs(now: number = Date.now()): number {\n\treturn windowStartMs(now, DEFAULT_WINDOW_DAYS);\n}\n\n/**\n * The adapters that wrote a transcript inside the window - the machine's LIVE\n * harnesses (#101). A stale one is skipped everywhere this is read: it does not\n * scan, it does not publish, it earns no upsell and it gets no hook.\n */\nexport async function detectedAdapters(\n\tsinceMs: number = detectionSinceMs(),\n): Promise<HarnessAdapter[]> {\n\tconst out: HarnessAdapter[] = [];\n\tfor (const adapter of HARNESS_ADAPTERS) {\n\t\tif (await adapter.detect({ sinceMs })) out.push(adapter);\n\t}\n\treturn out;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { stackGet } from \"../api.js\";\nimport { getToken } from \"../config.js\";\nimport {\n\tbold,\n\tdim,\n\tdivider,\n\tintro,\n\tlime,\n\tlimeBold,\n\tlines,\n\toutro,\n\toutroCancel,\n\toutroError,\n\toutroSkipped,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\nexport async function createCommand() {\n\tintro(\"create\");\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(\n\t\t\t`Not authenticated. Run ${limeBold(\"npx @use-aistack/cli login\")} first.`,\n\t\t);\n\t\toutroError(\"not authenticated\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst s = p.spinner();\n\ts.start(\"Fetching stack...\");\n\n\tlet stack: Awaited<ReturnType<typeof stackGet>>;\n\ttry {\n\t\tstack = await stackGet(token);\n\t\tif (!stack) {\n\t\t\ts.stop(\"Not found\");\n\t\t\tp.log.error(\"No stack found. Create a stack on aistack.to first.\");\n\t\t\toutroError(\"not found\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ts.stop(bold(stack.name));\n\t} catch (err) {\n\t\ts.stop(\"Failed to fetch stack\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst localFiles: FileToWrite[] = [];\n\n\tfor (const item of stack.resources) {\n\t\tfor (const file of item.files ?? []) {\n\t\t\tlocalFiles.push({ path: file.path ?? file.name, content: file.content });\n\t\t}\n\t}\n\n\t// Linked resources have no files to write - surface them so they aren't\n\t// silently dropped on download (GitHub repos + package refs like MCP servers).\n\tconst linked = stack.resources.filter(\n\t\t(item) => (item.upstream || item.pkg) && !item.files?.length,\n\t);\n\tif (linked.length > 0) {\n\t\tsection(\"linked\", linked.length);\n\t\tlines([dim(\"view only\")]);\n\t\tlines(\n\t\t\tlinked.map((item) =>\n\t\t\t\tdim(\n\t\t\t\t\titem.upstream?.repoUrl ??\n\t\t\t\t\t\t(item.pkg ? `${item.pkg.registry}:${item.pkg.id}` : \"\"),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\t}\n\n\tif (localFiles.length === 0) {\n\t\tp.log.warn(\"No local files to write.\");\n\t\toutroSkipped(\"nothing to create\");\n\t\treturn;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst toWrite: FileToWrite[] = [];\n\tconst skipped: { path: string; differs: boolean }[] = [];\n\n\tfor (const f of localFiles) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconst existing = readFileSync(fullPath, \"utf-8\");\n\t\t\tskipped.push({ path: f.path, differs: existing !== f.content });\n\t\t} else {\n\t\t\ttoWrite.push(f);\n\t\t}\n\t}\n\n\tsection(\"local files\", localFiles.length);\n\tlines(toWrite.map((f) => lime(`+ ${f.path}`)));\n\tlines(\n\t\tskipped.map((f) =>\n\t\t\tf.differs\n\t\t\t\t? `${yellow(`= ${f.path}`)} ${dim(\"(differs)\")}`\n\t\t\t\t: dim(`= ${f.path} (identical)`),\n\t\t),\n\t);\n\n\tif (toWrite.length === 0) {\n\t\tdivider();\n\t\tp.log.info(\"All local files already exist.\");\n\t\toutroSkipped(\"nothing to write\");\n\t\treturn;\n\t}\n\n\tdivider();\n\n\tconst confirm = await p.confirm({\n\t\tmessage: `Write ${lime(String(toWrite.length))} new files? ${dim(`(${skipped.length} skipped)`)}`,\n\t});\n\n\tif (p.isCancel(confirm) || !confirm) {\n\t\toutroCancel();\n\t\tprocess.exit(0);\n\t}\n\n\tfor (const f of toWrite) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tconst dir = dirname(fullPath);\n\t\tmkdirSync(dir, { recursive: true });\n\t\twriteFileSync(fullPath, f.content);\n\t}\n\n\tp.log.success(\n\t\t`${lime(String(toWrite.length))} written, ${dim(String(skipped.length) + \" skipped\")}`,\n\t);\n\toutro(lime(\"done\"));\n}\n\ninterface FileToWrite {\n\tpath: string;\n\tcontent: string;\n}\n","import { hostname } from \"node:os\";\nimport * as p from \"@clack/prompts\";\nimport open from \"open\";\nimport { authPoll, authStart } from \"../api.js\";\nimport { saveToken } from \"../config.js\";\nimport { isDisplaySafeName } from \"../harness/shared/aggregate.js\";\nimport { dim, intro, lime, limeBold, outro, outroError } from \"../theme.js\";\n\n/**\n * What to call this machine on the account's linked-machines list (#49).\n *\n * The hostname is only a proposal - the approval page shows it in an editable\n * field before anything is stored. Trimmed to the server's 64-character bound so\n * a long hostname is dropped by us rather than silently by the server, and\n * `.local` is stripped because mDNS suffixes carry no information for a reader.\n */\nexport function proposedMachineName(\n\tread: () => string = hostname,\n): string | undefined {\n\ttry {\n\t\tconst name = read()\n\t\t\t.trim()\n\t\t\t.replace(/\\.local$/i, \"\");\n\t\tif (!name || name.length > 64) return undefined;\n\t\treturn name;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * The device-auth flow itself, without intro/outro framing or process.exit -\n * so `sync` can run it inline on an unlinked machine (#74) and `login` stays\n * the standalone command. Logs its own progress and errors; returns whether a\n * token was saved.\n */\ntype LoginOptions = {\n\tlabel?: string;\n\t/** Existing bearer replaced atomically when this approval completes. */\n\treplaceToken?: string;\n\t/** A sync cannot finish login until the browser chooses a stack. */\n\tdestinationRequired?: boolean;\n};\n\nexport function requestedMachineLabel(label: string): string {\n\tconst trimmed = label.trim();\n\tif (!isDisplaySafeName(trimmed)) {\n\t\tthrow new Error(\n\t\t\t\"Machine label must be 64 characters or fewer and contain only printable characters.\",\n\t\t);\n\t}\n\treturn trimmed;\n}\n\nexport async function performLogin(\n\toptions: LoginOptions = {},\n): Promise<boolean> {\n\tconst s = p.spinner();\n\ts.start(\"Starting authentication...\");\n\n\tlet session: Awaited<ReturnType<typeof authStart>>;\n\ttry {\n\t\tconst requestedLabel =\n\t\t\toptions.label === undefined\n\t\t\t\t? undefined\n\t\t\t\t: requestedMachineLabel(options.label);\n\t\tsession = await authStart(\n\t\t\trequestedLabel ?? proposedMachineName(),\n\t\t\trequestedLabel !== undefined,\n\t\t\t{\n\t\t\t\t...(options.replaceToken ? { replaceToken: options.replaceToken } : {}),\n\t\t\t\t...(options.destinationRequired ? { destinationRequired: true } : {}),\n\t\t\t},\n\t\t);\n\t\ts.stop(\"Session created\");\n\t} catch (err) {\n\t\ts.stop(\"Failed to start authentication\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\treturn false;\n\t}\n\n\tp.log.info(`${dim(\"CODE\")} ${limeBold(session.userCode)}`);\n\tp.log.info(`${dim(\"OPEN\")} ${dim(session.authUrl)}`);\n\n\ttry {\n\t\tawait open(session.authUrl);\n\t} catch {\n\t\tp.log.warn(\n\t\t\t\"Could not open browser automatically. Please visit the URL above.\",\n\t\t);\n\t}\n\n\ts.start(\"Waiting for approval...\");\n\n\tconst maxAttempts = 36;\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 5000));\n\n\t\ttry {\n\t\t\tconst result = await authPoll(session.secretId);\n\n\t\t\tif (result.status === \"approved\" && result.token) {\n\t\t\t\ts.stop(lime(\"Authenticated\"));\n\t\t\t\tsaveToken(result.token, result.userId);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (result.status === \"expired\") {\n\t\t\t\ts.stop(\"Session expired\");\n\t\t\t\tp.log.error(\"Authentication session expired. Please try again.\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\ts.stop(\"Error polling\");\n\t\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\ts.stop(\"Timed out\");\n\tp.log.error(\"Authentication timed out after 3 minutes. Please try again.\");\n\treturn false;\n}\n\nexport async function loginCommand(options: LoginOptions = {}) {\n\tintro(\"login\");\n\n\tif (!(await performLogin(options))) {\n\t\toutroError(\"error\");\n\t\tprocess.exit(1);\n\t}\n\n\tp.log.success(\n\t\t`Token saved. Run ${limeBold(\"npx @use-aistack/cli sync\")} to publish your usage.`,\n\t);\n\toutro(lime(\"done\"));\n}\n","// The documented default sync surface (#56, built by #55/#57).\n//\n// The MCP-free channel: a human types `aistack sync` in their own terminal,\n// so a real TTY exists and the gate can be a @clack/prompts select. Same\n// staged-bytes property as the MCP server (#41): the summary and the confirm\n// derive from the exact serialized `bodyJson`, and that string goes on the\n// wire byte-identical. One gate policy, two renderings.\n//\n// Fail-closed: ctrl-C, ESC, EOF, and a missing TTY all resolve to \"nothing\n// was sent\" before any network call.\n\nimport * as p from \"@clack/prompts\";\nimport { BASE_URL, syncPublish } from \"../api.js\";\nimport {\n\tCODEX_TRUST_INSTRUCTION,\n\tcodexAutoSyncHookInstalled,\n\tcodexHookTrusted,\n} from \"../autosync/codexHook.js\";\nimport {\n\tdisableAutoSync,\n\tenableAutoSync,\n\tsettleAutoSync,\n} from \"../autosync/optin.js\";\nimport { runAutoSync } from \"../autosync/run.js\";\nimport { DEFAULT_FREQUENCY_HOURS, getSettings, getToken } from \"../config.js\";\nimport { loadSyncConfig } from \"../harness/shared/allowlist.js\";\nimport { stageSync } from \"../sync/stage.js\";\nimport { fmtReceivedAt } from \"../sync/summary.js\";\nimport {\n\tbold,\n\tdim,\n\tintro,\n\tlime,\n\toutro,\n\toutroCancel,\n\toutroError,\n\tyellow,\n} from \"../theme.js\";\nimport { offerConnectUpsell } from \"./connect.js\";\nimport { performLogin } from \"./login.js\";\n\nexport interface SyncOptions {\n\t/** `--auto` → true, `--auto on` → \"on\", `--auto off` → \"off\". */\n\tauto?: boolean | string;\n\t/** `--every <hours>`, applied with `--auto on`. */\n\tevery?: string;\n}\n\nexport async function syncCommand(options: SyncOptions = {}): Promise<void> {\n\t// The silent path (#62): no TTY, no prompts, no upsells. Publishes only\n\t// under the standing opt-in and always exits 0 - the hook command's `||`\n\t// offline fallback must never fire on a mere sync failure.\n\tif (options.auto === true) {\n\t\tawait runAutoSync({ baseUrl: BASE_URL });\n\t\treturn;\n\t}\n\n\tif (options.auto === \"on\" || options.auto === \"off\") {\n\t\tintro(\"sync\");\n\t\tconst result =\n\t\t\toptions.auto === \"on\"\n\t\t\t\t? await enableAutoSync(\n\t\t\t\t\t\toptions.every\n\t\t\t\t\t\t\t? Number.parseInt(options.every, 10) || DEFAULT_FREQUENCY_HOURS\n\t\t\t\t\t\t\t: DEFAULT_FREQUENCY_HOURS,\n\t\t\t\t\t)\n\t\t\t\t: await disableAutoSync();\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t\toutro(\"done\");\n\t\t} else {\n\t\t\toutroError(result.message);\n\t\t\tprocess.exitCode = 1;\n\t\t}\n\t\treturn;\n\t}\n\tif (options.auto !== undefined) {\n\t\tintro(\"sync\");\n\t\toutroError(`unknown --auto value \"${options.auto}\" (use on or off)`);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\tintro(\"sync\");\n\n\t// The interactive surface is where a silent failure becomes visible (#62):\n\t// report the last auto-sync outcome, whatever it was.\n\tconst lastAuto = getSettings().autoSyncState?.lastResult;\n\tif (lastAuto !== undefined) {\n\t\tp.log.message(dim(`auto-sync: ${lastAuto}`));\n\t}\n\n\t// The Codex hook does not run until the user trusts it via /hooks (#65 §6).\n\t// Repeat the one-time instruction while the hook is installed but the trust\n\t// hash is verifiably absent; an unreadable config stays silent.\n\tif (codexAutoSyncHookInstalled() && codexHookTrusted() === false) {\n\t\tp.log.warn(CODEX_TRUST_INSTRUCTION);\n\t}\n\n\t// The whole premise of this channel is a human at a terminal. A pipe or a\n\t// model-launched Bash call has no TTY, and a gate that cannot ask must not\n\t// send (#31) - refuse before scanning anything.\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\n\t\toutroError(\"sync needs an interactive terminal. Nothing was sent.\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// An unlinked machine used to hard-block with \"run login first\" (#74). The\n\t// TTY gate above guarantees a human is present, so the device-auth browser\n\t// hop fits here - `sync` is the whole onboarding command.\n\tlet token = getToken();\n\tif (token === null) {\n\t\tp.log.message(\"This machine needs a destination stack. Linking it now.\");\n\t\tif (!(await performLogin({ destinationRequired: true }))) {\n\t\t\toutroError(\"login failed. Nothing was sent.\");\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\ttoken = getToken();\n\t}\n\tif (token === null) {\n\t\toutroError(\"login completed without a saved credential. Nothing was sent.\");\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Resolve the destination before walking local history. A valid credential\n\t// can outlive its stack choice, especially when login happened before the\n\t// first stack was created. Relinking rotates that credential and returns to\n\t// this same sync, so the user never has to discover a second command.\n\tlet loaded = await loadSyncConfig({ baseUrl: BASE_URL, token });\n\tif (loaded.source === \"bundled\") {\n\t\toutroError(\n\t\t\t\"Could not fetch your settings from aistack, so the destination stack is unknown. Check the network and sync again.\",\n\t\t);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\tif (loaded.config.stack === null) {\n\t\tp.log.message(\n\t\t\t\"This machine is not linked to a destination stack. Opening aistack so you can choose one.\",\n\t\t);\n\t\tif (\n\t\t\t!(await performLogin({\n\t\t\t\tdestinationRequired: true,\n\t\t\t\treplaceToken: token,\n\t\t\t}))\n\t\t) {\n\t\t\toutroError(\"linking failed. Nothing was sent.\");\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\ttoken = getToken();\n\t\tif (token === null) {\n\t\t\toutroError(\n\t\t\t\t\"linking completed without a saved credential. Nothing was sent.\",\n\t\t\t);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tloaded = await loadSyncConfig({ baseUrl: BASE_URL, token });\n\t\tif (loaded.source === \"bundled\" || loaded.config.stack === null) {\n\t\t\toutroError(\n\t\t\t\t\"The destination stack could not be confirmed. Nothing was sent.\",\n\t\t\t);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tp.log.success(`Linked this machine to ${loaded.config.stack.name}`);\n\t}\n\tconst destinationToken = token;\n\tconst destinationConfig = loaded;\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning local agent transcripts\");\n\tlet staged: Awaited<ReturnType<typeof stageSync>>;\n\ttry {\n\t\tstaged = await stageSync({\n\t\t\tbaseUrl: BASE_URL,\n\t\t\tgetTokenImpl: () => destinationToken,\n\t\t\tloadConfigImpl: async () => destinationConfig,\n\t\t\tonProgress: (message) => s.message(message),\n\t\t});\n\t} catch (e) {\n\t\ts.stop(\"Scan failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\ts.stop(\"Scan complete\");\n\n\t// Beat one - the same full summary the MCP preview returns, verbatim,\n\t// printed behind the clack bar so it reads as one flow. The text is the\n\t// bytes' description and stays plain; the color is added here, by line\n\t// shape, so the MCP preview and a pipe get the same characters.\n\tp.log.message(staged.summary.split(\"\\n\").map(styleSummaryLine).join(\"\\n\"));\n\n\tif (staged.blockedReason !== null) {\n\t\toutroError(staged.blockedReason);\n\t\tprocess.exitCode = 1;\n\t\treturn;\n\t}\n\n\t// Beat two - the same short dialog text, as a select. The enum mirrors the\n\t// elicitation's {publish, cancel}; publish is the initial value.\n\tconst decision = await p.select({\n\t\tmessage: staged.dialog.split(\"\\n\").join(dim(\" · \")),\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"publish\",\n\t\t\t\tlabel: \"Publish\",\n\t\t\t\thint: \"no sensitive data is shared\",\n\t\t\t},\n\t\t\t{ value: \"cancel\", label: \"Cancel\", hint: \"nothing leaves this machine\" },\n\t\t],\n\t\tinitialValue: \"publish\",\n\t});\n\n\tif (p.isCancel(decision) || decision !== \"publish\") {\n\t\toutroCancel(\"nothing was sent\");\n\t\treturn;\n\t}\n\n\ts.start(\"Publishing\");\n\ttry {\n\t\tconst res = await syncPublish(staged.token as string, staged.bodyJson);\n\t\tstaged.acknowledgePublish?.();\n\t\ts.stop(\"Published\");\n\t\t// The last thing read is the result, not a receipt (#130): the stamp is\n\t\t// human-form, and the link gets its own line under a sentence that names\n\t\t// the proof. The path stays in the terminal - no browser is opened.\n\t\tconst lines = [\n\t\t\t`Snapshot received ${fmtReceivedAt(res.receivedAt)}`,\n\t\t\t\"\",\n\t\t\t\"Your stack now shows what actually ran:\",\n\t\t\tlime(res.url),\n\t\t];\n\t\tif (res.keptPrivate.refused && staged.body.keptPrivate !== undefined) {\n\t\t\tlines.push(\n\t\t\t\t\"Note: the server refused the kept-private names because its review switch is off. They stayed on this machine.\",\n\t\t\t);\n\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\tlines.push(\n\t\t\t\t`${res.keptPrivate.stored} private review name${res.keptPrivate.stored === 1 ? \"\" : \"s\"} stored at ${res.url}/changes`,\n\t\t\t);\n\t\t}\n\t\tif (res.keptPrivate.machineStored > 0) {\n\t\t\tlines.push(\n\t\t\t\t\"This machine's private label was stored for the same review.\",\n\t\t\t);\n\t\t}\n\t\tp.log.message(lines.join(\"\\n\"));\n\t\t// EVERY interactive sync settles auto-sync against the stack's own\n\t\t// answer (#103): it reconciles the missing triggers when the switch is\n\t\t// on, keeps quiet when it is off, and asks only when nobody has decided.\n\t\t// This is what completes a web-first enable, and what gives a harness\n\t\t// adopted months later its trigger.\n\t\t//\n\t\t// At most one ask per sync (#62): the auto-sync opt-in is the primary\n\t\t// ask; the connect upsell yields and waits for a later sync.\n\t\tconst asked = await settleAutoSync(staged.config.autoSync);\n\t\tif (!asked) await offerConnectUpsell();\n\t\toutro(\"done\");\n\t} catch (e) {\n\t\ts.stop(\"Publish failed\");\n\t\toutroError(e instanceof Error ? e.message : String(e));\n\t\tprocess.exitCode = 1;\n\t}\n}\n\n/**\n * Colors one summary line the way `collect` colors its output: a caps section\n * header in bold with a dim count, the rule dim, the label column dim, dollars\n * lime, and a skipped-files row yellow. Every other line passes through.\n */\nexport function styleSummaryLine(line: string): string {\n\tif (line.startsWith(\"─\")) return dim(line);\n\tconst section = /^([A-Z][A-Z0-9 .-]+?)( \\d+)?$/.exec(line);\n\tif (section) return `${bold(section[1] ?? \"\")}${dim(section[2] ?? \"\")}`;\n\tconst labelled = /^([a-z-]+)( +)(.*)$/.exec(line);\n\tif (labelled) {\n\t\tconst [, label = \"\", gap = \"\", rest = \"\"] = labelled;\n\t\tconst body =\n\t\t\tlabel === \"skipped\"\n\t\t\t\t? yellow(rest)\n\t\t\t\t: rest.replace(/≈\\$[\\d,]+/g, (m) => lime(m));\n\t\treturn `${dim(label)}${gap}${body}`;\n\t}\n\tconst sub = /^( {2}[a-z]+ +)(.*)$/.exec(line);\n\tif (sub) return `${lime(sub[1] ?? \"\")}${dim(sub[2] ?? \"\")}`;\n\tif (/^ {10}\\S/.test(line)) return dim(line);\n\treturn line;\n}\n","// The Codex half of the background trigger (#66 decision 4, built in #67):\n// a `SessionStart` hook in ~/.codex/hooks.json, matcher `startup` only.\n//\n// Two ways this differs from the Claude hook (hook.ts):\n//\n// 1. THE COMMAND SELF-DETACHES. Codex parses `async` but does not honor it -\n// the runner awaits the hook with a timeout and kill_on_drop (#65 §6).\n// So the command backgrounds the real work under `setsid nohup … &` and\n// exits 0 immediately; kill_on_drop kills only the already-exited shell.\n// 2. THE TRUST GATE. Codex pins each hook command's sha256 as a\n// `trusted_hash` in config.toml. An untrusted or CHANGED command silently\n// does not run, and only the user can trust it, via /hooks inside Codex.\n// That is why the command text uses `@latest` - the text (and therefore\n// the hash) stays stable across CLI updates - and why install prints the\n// one-time trust instruction.\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { parse } from \"smol-toml\";\n\nimport type { HookResult } from \"./hook.js\";\n\nfunction codexHome(): string {\n\treturn process.env.CODEX_HOME || join(homedir(), \".codex\");\n}\n\nexport function codexHooksFile(): string {\n\treturn join(codexHome(), \"hooks.json\");\n}\n\n// `codexPresent()` lived here and keyed on $CODEX_HOME existing. #101 replaced\n// it with `codexAdapter.detect()`: a directory proves an install, and an\n// install is not a user. A fresh Codex with no sessions yet gets its hook from\n// the interactive sync that reconciles hooks (#103), one session later.\n\nexport function codexConfigFile(): string {\n\treturn join(codexHome(), \"config.toml\");\n}\n\n/**\n * EXACT quoting, settled here (#66 left it to this ticket): the outer layer is\n * a JSON string in hooks.json; Codex runs it through a shell, and the single\n * `sh -c '…'` wrapper makes the detach group unambiguous regardless of how\n * that outer shell tokenizes. No `||` fallback like the Claude command - the\n * fallback semantics live INSIDE the detached shell so the hook process itself\n * still exits instantly.\n *\n * DO NOT REFORMAT THIS STRING. Its sha256 is the trust hash; any byte change\n * un-trusts the hook on every machine until each user re-runs /hooks.\n */\nexport const CODEX_HOOK_COMMAND =\n\t\"sh -c 'setsid nohup sh -c \\\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\\\" >/dev/null 2>&1 &'\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\ttimeout?: number;\n\tasync?: boolean;\n\tstatusMessage?: string;\n\tadditionalContextLimit?: number;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface CodexHooksJson {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nfunction readHooksJson(\n\tfile: string,\n): { settings: CodexHooksJson } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as CodexHooksJson };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse - it is the user's Codex\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON - fix it, then retry` };\n\t}\n}\n\n/** The instruction install prints; the interactive sync repeats it while untrusted. */\nexport const CODEX_TRUST_INSTRUCTION =\n\t\"Codex hook written - open Codex and run /hooks once to trust it, or it will not run.\";\n\n/**\n * Add the SessionStart auto-sync hook, matcher `startup` only (resume/clear/\n * compact would multiply runs; the freshness gate would drop them anyway).\n * Idempotent: an existing aistack entry is replaced, not duplicated.\n */\nexport function installCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\tmatcher: \"startup\",\n\t\thooks: [{ type: \"command\", command: CODEX_HOOK_COMMAND, timeout: 30 }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: CODEX_TRUST_INSTRUCTION };\n}\n\n/**\n * Remove only our hook. Other hooks and events stay. A missing file or an\n * absent hook is success - the goal state already holds.\n */\nexport function removeCodexAutoSyncHook(\n\tfile: string = codexHooksFile(),\n): HookResult {\n\tif (!existsSync(file))\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no Codex hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the Codex auto-sync hook present? */\nexport function codexAutoSyncHookInstalled(\n\tfile: string = codexHooksFile(),\n): boolean {\n\tconst read = readHooksJson(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n\n/**\n * Codex hashes the normalized full hook definition, then stores that hash under\n * the hook's source/index key in `[hooks.state]`. Match both pieces: another\n * hook's trusted hash says nothing about ours, and a command-only hash does not\n * match current Codex. `null` means the files could not be read or parsed.\n */\nexport function codexHookTrusted(\n\tconfigFile: string = codexConfigFile(),\n\thooksFile: string = codexHooksFile(),\n): boolean | null {\n\ttry {\n\t\tconst config = parse(readFileSync(configFile, \"utf-8\")) as {\n\t\t\thooks?: { state?: Record<string, { trusted_hash?: unknown }> };\n\t\t};\n\t\tconst read = readHooksJson(hooksFile);\n\t\tif (\"error\" in read) return null;\n\t\tconst sessionStart = read.settings.hooks?.SessionStart;\n\t\tif (!Array.isArray(sessionStart)) return false;\n\n\t\tconst matches: boolean[] = [];\n\t\tfor (const [groupIndex, group] of sessionStart.entries()) {\n\t\t\tfor (const [handlerIndex, handler] of (group.hooks ?? []).entries()) {\n\t\t\t\tif (!isOurs(handler) || typeof handler.command !== \"string\") continue;\n\t\t\t\tconst normalizedHandler: Record<string, unknown> = {\n\t\t\t\t\ttype: \"command\",\n\t\t\t\t\tcommand: handler.command,\n\t\t\t\t\ttimeout:\n\t\t\t\t\t\ttypeof handler.timeout === \"number\"\n\t\t\t\t\t\t\t? Math.max(1, handler.timeout)\n\t\t\t\t\t\t\t: 600,\n\t\t\t\t\tasync: handler.async === true,\n\t\t\t\t};\n\t\t\t\tif (typeof handler.statusMessage === \"string\") {\n\t\t\t\t\tnormalizedHandler.statusMessage = handler.statusMessage;\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\ttypeof handler.additionalContextLimit === \"number\" &&\n\t\t\t\t\thandler.additionalContextLimit !== 2500\n\t\t\t\t) {\n\t\t\t\t\tnormalizedHandler.additionalContextLimit =\n\t\t\t\t\t\thandler.additionalContextLimit;\n\t\t\t\t}\n\n\t\t\t\tconst identity: Record<string, unknown> = {\n\t\t\t\t\tevent_name: \"session_start\",\n\t\t\t\t\thooks: [normalizedHandler],\n\t\t\t\t};\n\t\t\t\tif (typeof group.matcher === \"string\") identity.matcher = group.matcher;\n\t\t\t\tconst currentHash = `sha256:${createHash(\"sha256\")\n\t\t\t\t\t.update(JSON.stringify(canonicalJson(identity)))\n\t\t\t\t\t.digest(\"hex\")}`;\n\t\t\t\tconst key = `${hooksFile}:session_start:${groupIndex}:${handlerIndex}`;\n\t\t\t\tmatches.push(config.hooks?.state?.[key]?.trusted_hash === currentHash);\n\t\t\t}\n\t\t}\n\t\treturn matches.length > 0 && matches.every(Boolean);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\ntype JsonValue = null | boolean | number | string | JsonValue[] | JsonObject;\ntype JsonObject = { [key: string]: JsonValue };\n\nfunction canonicalJson(value: unknown): JsonValue {\n\tif (Array.isArray(value)) return value.map(canonicalJson);\n\tif (value && typeof value === \"object\") {\n\t\tconst sorted: JsonObject = {};\n\t\tfor (const [key, child] of Object.entries(value).sort(([a], [b]) =>\n\t\t\ta.localeCompare(b),\n\t\t)) {\n\t\t\tsorted[key] = canonicalJson(child);\n\t\t}\n\t\treturn sorted;\n\t}\n\tif (\n\t\tvalue === null ||\n\t\ttypeof value === \"boolean\" ||\n\t\ttypeof value === \"number\" ||\n\t\ttypeof value === \"string\"\n\t) {\n\t\treturn value;\n\t}\n\tthrow new TypeError(\"hook identity is not JSON-serializable\");\n}\n","// The auto-sync opt-in (#62, map #60), narrowed to active harnesses by #101,\n// and moved onto the stack by #103.\n//\n// WHERE THE PERMISSION LIVES: on the stack, in Convex (#100 decision 2, #102).\n// The local flag and the SessionStart hooks are what this machine holds, and\n// neither of them grants anything - `sync --auto` asks the stack before it\n// publishes. So enable grants on the stack first, revoke revokes here first,\n// and `reconcileAutoSync` brings a machine in line with an answer the owner\n// gave somewhere else.\n//\n// This ask is the PRIMARY post-sync ask: it runs first, and the connect-claude\n// upsell yields to a later sync (at most one ask per sync). \"Maybe later\" and\n// ctrl-C leave no decision, so the question returns on the next manual sync.\n// Only \"Never ask again\" persists a local refusal. A stack that has already\n// decided is never asked at all: `settleAutoSync` reconciles it instead.\n\nimport * as p from \"@clack/prompts\";\nimport { setAutoSync } from \"../api.js\";\nimport {\n\tDEFAULT_FREQUENCY_HOURS,\n\tgetSettings,\n\tgetToken,\n\tnormalizeFrequencyHours,\n\tsaveSettings,\n} from \"../config.js\";\nimport { CLAUDE_HARNESS_NAME } from \"../harness/claude/adapter.js\";\nimport { CODEX_HARNESS_NAME } from \"../harness/codex/adapter.js\";\nimport { CURSOR_HARNESS_NAME } from \"../harness/cursor/adapter.js\";\nimport { GROK_HARNESS_NAME } from \"../harness/grok/adapter.js\";\nimport {\n\ttype AutoSyncPermission,\n\tDEFAULT_WINDOW_DAYS,\n\tdetectedAdapters,\n\tharnessLabel,\n\tharnessListLabel,\n} from \"../harness/index.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport { dim, limeBold } from \"../theme.js\";\nimport {\n\tcodexAutoSyncHookInstalled,\n\tcodexHookTrusted,\n\tinstallCodexAutoSyncHook,\n\tremoveCodexAutoSyncHook,\n} from \"./codexHook.js\";\nimport {\n\tcursorAutoSyncHookInstalled,\n\tinstallCursorAutoSyncHook,\n\tremoveCursorAutoSyncHook,\n} from \"./cursorHook.js\";\nimport {\n\tgrokAutoSyncHookInstalled,\n\tinstallGrokAutoSyncHook,\n\tremoveGrokAutoSyncHook,\n} from \"./grokHook.js\";\nimport {\n\tautoSyncHookInstalled,\n\ttype HookResult,\n\tinstallAutoSyncHook,\n\tremoveAutoSyncHook,\n} from \"./hook.js\";\n\nexport interface EnableDeps {\n\tsettingsFile?: string;\n\tinstallHook?: () => HookResult;\n\tremoveHook?: () => HookResult;\n\tinstallCodexHook?: () => HookResult;\n\tremoveCodexHook?: () => HookResult;\n\tinstallCursorHook?: () => HookResult;\n\tremoveCursorHook?: () => HookResult;\n\tcursorHookInstalledImpl?: () => boolean;\n\tinstallGrokHook?: () => HookResult;\n\tremoveGrokHook?: () => HookResult;\n\t/** Is the Claude Code trigger already on this machine? */\n\thookInstalledImpl?: () => boolean;\n\t/** Is the Codex trigger already on this machine? */\n\tcodexHookInstalledImpl?: () => boolean;\n\t/** Is the installed Codex trigger's exact definition trusted? */\n\tcodexHookTrustedImpl?: () => boolean | null;\n\tgrokHookInstalledImpl?: () => boolean;\n\t/** Override the detected harness set. Tests only. */\n\tdetectedImpl?: () => Promise<HarnessAdapter[]>;\n\tgetTokenImpl?: () => string | null;\n\tsetAutoSyncImpl?: typeof setAutoSync;\n}\n\n/** What an unlinked machine is told when it tries to grant the permission. */\nexport const NOT_LINKED =\n\t\"This machine is not linked to an aistack account, and the auto-sync permission lives on your stack. Run `npx @use-aistack/cli sync` first.\";\n\n/** The message when the machine has no active harness to trigger anything. */\nexport const NOTHING_TO_TRIGGER = `No supported session on this machine in the last ${DEFAULT_WINDOW_DAYS} days, so nothing would trigger an auto-sync. Nothing was changed.`;\n\n/**\n * Turn auto-sync on: grant the permission on the STACK, then write the\n * SessionStart hooks - one per DETECTED harness (#101). A hook for a harness\n * whose last session predates the window is a trigger that will never fire, and\n * its install is the step that made a dead Claude Code install look alive.\n *\n * THE STACK IS ASKED FIRST (#103). The permission is the thing that lets a\n * publish happen, and the hooks are dumb local triggers for it - so a refusal\n * from aistack.to leaves the machine exactly as it was, with no hook running an\n * npx at every session start for a permission nobody granted.\n *\n * When a hook write then fails, the local flag is NOT persisted. The stack\n * shows on-but-never-fired, which is true, and the next interactive sync\n * reconciles the missing trigger.\n */\nexport async function enableAutoSync(\n\tfrequencyHours: number = DEFAULT_FREQUENCY_HOURS,\n\tdeps: EnableDeps = {},\n): Promise<HookResult> {\n\tfrequencyHours = normalizeFrequencyHours(frequencyHours);\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) {\n\t\treturn { ok: false, message: NOTHING_TO_TRIGGER };\n\t}\n\tconst names = new Set(detected.map((a) => a.name));\n\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tif (token === null) return { ok: false, message: NOT_LINKED };\n\ttry {\n\t\tawait (deps.setAutoSyncImpl ?? setAutoSync)(token, {\n\t\t\tenabled: true,\n\t\t\tfrequencyHours,\n\t\t});\n\t} catch (e) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync was not turned on: ${e instanceof Error ? e.message : String(e)}`,\n\t\t};\n\t}\n\n\tif (names.has(CLAUDE_HARNESS_NAME)) {\n\t\tconst result = (deps.installHook ?? installAutoSyncHook)();\n\t\tif (!result.ok) return result;\n\t}\n\n\tlet trustLine: string | null = null;\n\tif (names.has(CODEX_HARNESS_NAME)) {\n\t\tconst codexResult = (deps.installCodexHook ?? installCodexAutoSyncHook)();\n\t\tif (!codexResult.ok) return codexResult;\n\t\t// The one-time /hooks trust step (#65 §6) - repeated by the next\n\t\t// interactive sync while the hook stays untrusted.\n\t\tif ((deps.codexHookTrustedImpl ?? codexHookTrusted)() !== true) {\n\t\t\ttrustLine = codexResult.message;\n\t\t}\n\t}\n\tif (names.has(GROK_HARNESS_NAME)) {\n\t\tconst grokResult = (deps.installGrokHook ?? installGrokAutoSyncHook)();\n\t\tif (!grokResult.ok) return grokResult;\n\t}\n\n\tif (names.has(CURSOR_HARNESS_NAME)) {\n\t\tconst result = (deps.installCursorHook ?? installCursorAutoSyncHook)();\n\t\tif (!result.ok) return result;\n\t}\n\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: { enabled: true, frequencyHours },\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\treturn {\n\t\tok: true,\n\t\tmessage: [\n\t\t\t`Auto-sync is on. It runs about every ${frequencyHours}h when a ${harnessListLabel(detected)} ${names.has(CURSOR_HARNESS_NAME) ? \"session is active\" : \"session starts\"}. Turn it off any time: npx @use-aistack/cli sync --auto off`,\n\t\t\t...(trustLine ? [trustLine] : []),\n\t\t].join(\"\\n\"),\n\t};\n}\n\n/**\n * Revoke: flip the local flag, remove the hooks, then take the permission off\n * the stack. The flag flips even when a hook file cannot be edited, because\n * `sync --auto` gates on it - a stale hook without the flag publishes nothing.\n *\n * THE LOCAL HALF RUNS FIRST, the mirror image of enable (#103). A revoke must\n * never be blocked by an unreachable network: this machine stops publishing the\n * moment the flag flips, whatever aistack.to says next. The server half is\n * still reported when it fails, because it is the half that reaches every OTHER\n * machine, and the owner can also use the switch on their stack page.\n *\n * Removal is unconditional, unlike install: a revoke must reach the hook of a\n * harness that has since gone quiet, and removing an absent hook is success.\n */\nexport async function disableAutoSync(\n\tdeps: EnableDeps = {},\n): Promise<HookResult> {\n\tconst settings = getSettings(deps.settingsFile);\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncAnswered: true,\n\t\t\tautoSync: {\n\t\t\t\tenabled: false,\n\t\t\t\tfrequencyHours: normalizeFrequencyHours(\n\t\t\t\t\tsettings.autoSync?.frequencyHours,\n\t\t\t\t),\n\t\t\t},\n\t\t},\n\t\tdeps.settingsFile,\n\t);\n\tconst result = (deps.removeHook ?? removeAutoSyncHook)();\n\tconst codexResult = (deps.removeCodexHook ?? removeCodexAutoSyncHook)();\n\tconst grokResult = (deps.removeGrokHook ?? removeGrokAutoSyncHook)();\n\tconst cursorResult = (deps.removeCursorHook ?? removeCursorAutoSyncHook)();\n\tconst failures = [result, codexResult, grokResult, cursorResult]\n\t\t.filter((r) => !r.ok)\n\t\t.map((r) => r.message);\n\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tif (token !== null) {\n\t\ttry {\n\t\t\tawait (deps.setAutoSyncImpl ?? setAutoSync)(token, { enabled: false });\n\t\t} catch (e) {\n\t\t\tfailures.push(\n\t\t\t\t`aistack.to was not told (${e instanceof Error ? e.message : String(e)}); your other machines keep the permission`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is off on this machine (nothing will publish), but: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\treturn {\n\t\tok: true,\n\t\tmessage: \"Auto-sync is off. The hooks were removed.\",\n\t};\n}\n\n/**\n * Bring the machine in line with the permission the stack holds (#103).\n *\n * This is not an ask and never prompts. The owner already answered, on the web\n * switch or on another machine, and this is the machine catching up with them:\n *\n * - flag ON - install a trigger for every DETECTED harness that lacks one,\n * and mirror the flag locally so `sync --auto` passes its own\n * gate. That is what makes \"flip the web switch, run one sync\"\n * the whole enable story, and it is also what gives a harness\n * adopted months later its trigger.\n * - flag OFF - disable locally first and remove every owned trigger. A\n * failed removal is retried on the next interactive sync, and\n * the local gate keeps a leftover trigger from publishing.\n * - ABSENT - touch nothing. Nobody has decided, and the post-sync ask still\n * owns that case.\n *\n * Returns the one line to print, or `null` when there was nothing to do.\n */\nexport async function reconcileAutoSync(\n\tpermission: AutoSyncPermission | null,\n\tdeps: EnableDeps = {},\n): Promise<HookResult | null> {\n\tif (permission === null) return null;\n\tif (permission.enabled !== true) {\n\t\tconst settings = getSettings(deps.settingsFile);\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncAnswered: true,\n\t\t\t\tautoSync: {\n\t\t\t\t\tenabled: false,\n\t\t\t\t\tfrequencyHours: normalizeFrequencyHours(\n\t\t\t\t\t\tpermission.frequencyHours ?? settings.autoSync?.frequencyHours,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t},\n\t\t\tdeps.settingsFile,\n\t\t);\n\t\tconst results = [\n\t\t\t(deps.removeHook ?? removeAutoSyncHook)(),\n\t\t\t(deps.removeCodexHook ?? removeCodexAutoSyncHook)(),\n\t\t\t(deps.removeGrokHook ?? removeGrokAutoSyncHook)(),\n\t\t\t(deps.removeCursorHook ?? removeCursorAutoSyncHook)(),\n\t\t];\n\t\tconst failures = results.filter((result) => !result.ok);\n\t\tif (failures.length > 0) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: `Auto-sync is off on this machine, but a trigger could not be removed: ${failures.map((result) => result.message).join(\"; \")}. The next interactive sync will retry.`,\n\t\t\t};\n\t\t}\n\t\tconst changed =\n\t\t\tsettings.autoSync?.enabled !== false ||\n\t\t\tresults.some((result) => !result.message.toLowerCase().startsWith(\"no \"));\n\t\tif (!changed) return null;\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmessage: \"Auto-sync is off on this machine. Removed its local triggers.\",\n\t\t};\n\t}\n\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) return null;\n\tconst names = new Set(detected.map((a) => a.name));\n\n\tconst installed: string[] = [];\n\tconst failures: string[] = [];\n\tconst install = (\n\t\tharnessName: string,\n\t\tisInstalled: () => boolean,\n\t\twrite: () => HookResult,\n\t) => {\n\t\tif (!names.has(harnessName) || isInstalled()) return;\n\t\tconst result = write();\n\t\tif (result.ok) installed.push(harnessLabel(harnessName));\n\t\telse failures.push(result.message);\n\t};\n\n\tinstall(\n\t\tCLAUDE_HARNESS_NAME,\n\t\tdeps.hookInstalledImpl ?? autoSyncHookInstalled,\n\t\tdeps.installHook ?? installAutoSyncHook,\n\t);\n\tinstall(\n\t\tCODEX_HARNESS_NAME,\n\t\tdeps.codexHookInstalledImpl ?? codexAutoSyncHookInstalled,\n\t\tdeps.installCodexHook ?? installCodexAutoSyncHook,\n\t);\n\tinstall(\n\t\tGROK_HARNESS_NAME,\n\t\tdeps.grokHookInstalledImpl ?? grokAutoSyncHookInstalled,\n\t\tdeps.installGrokHook ?? installGrokAutoSyncHook,\n\t);\n\n\tinstall(\n\t\tCURSOR_HARNESS_NAME,\n\t\tdeps.cursorHookInstalledImpl ?? cursorAutoSyncHookInstalled,\n\t\tdeps.installCursorHook ?? installCursorAutoSyncHook,\n\t);\n\n\tif (failures.length > 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Auto-sync is on for this stack, but a trigger could not be written: ${failures.join(\"; \")}`,\n\t\t};\n\t}\n\n\tconst frequencyHours = permission.frequencyHours ?? DEFAULT_FREQUENCY_HOURS;\n\tconst local = getSettings(deps.settingsFile).autoSync;\n\tconst mirrored =\n\t\tlocal?.enabled === true && local.frequencyHours === frequencyHours;\n\tif (!mirrored) {\n\t\tsaveSettings(\n\t\t\t{ autoSyncAnswered: true, autoSync: { enabled: true, frequencyHours } },\n\t\t\tdeps.settingsFile,\n\t\t);\n\t}\n\n\t// Quiet when nothing changed. The steady state is already reported by the\n\t// `auto-sync: <last result>` line the interactive sync prints.\n\tif (installed.length === 0 && mirrored) return null;\n\treturn {\n\t\tok: true,\n\t\tmessage:\n\t\t\tinstalled.length > 0\n\t\t\t\t? `Auto-sync is on for this stack. Installed the ${installed.join(\" and \")} trigger on this machine; it runs about every ${frequencyHours}h during session activity.`\n\t\t\t\t: `Auto-sync is on for this stack. It runs about every ${frequencyHours}h when a ${harnessListLabel(detected)} ${names.has(CURSOR_HARNESS_NAME) ? \"session is active\" : \"session starts\"}.`,\n\t};\n}\n\n/**\n * Everything an interactive sync does about auto-sync, after it publishes.\n *\n * One step with three inputs, because the stack's answer is what decides which\n * of them applies (#103): a stack that has decided is reconciled and never\n * asked again, and only a stack nobody has decided for reaches the ask. The ask\n * asked once and persisted is #62's rule, and the switch now outranks it -\n * re-asking an owner who already answered on the web is asking them twice.\n *\n * Returns true when it ASKED, so the caller knows to hold the connect upsell\n * back to a later sync (at most one ask per sync, #62).\n */\nexport async function settleAutoSync(\n\tpermission: AutoSyncPermission | null,\n\tdeps: EnableDeps = {},\n): Promise<boolean> {\n\tif (permission !== null) {\n\t\tconst result = await reconcileAutoSync(permission, deps);\n\t\tif (result === null) return false;\n\t\tif (result.ok) p.log.success(result.message);\n\t\telse p.log.warn(result.message);\n\t\treturn false;\n\t}\n\treturn offerAutoSyncOptIn(deps);\n}\n\n/**\n * The post-sync ask. Returns true when it asked (so the caller skips the\n * connect upsell this sync), false when it had nothing to ask.\n *\n * The hint names the harnesses this machine actually runs (#101): a Codex-only\n * user is told \"when a Codex session starts\", not a Claude Code sentence that\n * describes nothing they do.\n */\nexport async function offerAutoSyncOptIn(\n\tdeps: EnableDeps = {},\n): Promise<boolean> {\n\tif (getSettings(deps.settingsFile).autoSyncNeverAskAgain === true)\n\t\treturn false;\n\n\tconst detected = await (deps.detectedImpl ?? detectedAdapters)();\n\tif (detected.length === 0) return false;\n\n\tconst names = new Set(detected.map((adapter) => adapter.name));\n\tconst answer = await p.select({\n\t\tmessage: \"Keep this stack fresh automatically every 6 hours?\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"enable\",\n\t\t\t\tlabel: \"Enable\",\n\t\t\t\thint: `a silent sync at most every 6 hours when a ${harnessListLabel(detected)} ${names.has(CURSOR_HARNESS_NAME) ? \"session is active\" : \"session starts\"}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"later\",\n\t\t\t\tlabel: \"Maybe later\",\n\t\t\t\thint: \"ask again after your next manual sync\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"never\",\n\t\t\t\tlabel: \"Never ask again\",\n\t\t\t\thint: \"you can still enable it with sync --auto on\",\n\t\t\t},\n\t\t],\n\t\tinitialValue: \"enable\",\n\t});\n\n\tif (p.isCancel(answer)) return true;\n\n\tif (answer === \"enable\") {\n\t\t// The set is reused, not re-detected: the user answered the hint they saw.\n\t\tconst result = await enableAutoSync(DEFAULT_FREQUENCY_HOURS, {\n\t\t\t...deps,\n\t\t\tdetectedImpl: async () => detected,\n\t\t});\n\t\tif (result.ok) {\n\t\t\tp.log.success(result.message);\n\t\t} else {\n\t\t\tp.log.error(result.message);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (answer === \"never\") {\n\t\tsaveSettings({ autoSyncNeverAskAgain: true }, deps.settingsFile);\n\t}\n\tp.log.message(\n\t\t`If you change your mind: ${limeBold(\"npx @use-aistack/cli sync --auto on\")} ${dim(\n\t\t\t\"(and --auto off to revoke)\",\n\t\t)}`,\n\t);\n\treturn true;\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { AUTO_SYNC_HOOK_COMMAND, type HookResult } from \"./hook.js\";\n\n// The accepted scope is user-wide. Project hooks would also run in cloud agents.\nexport const CURSOR_HOOK_FILE = join(homedir(), \".cursor\", \"hooks.json\");\n\nexport function cursorHookCommand(os: NodeJS.Platform = platform()): string {\n\tif (os === \"win32\") {\n\t\tconst script = `Start-Process -WindowStyle Hidden -FilePath 'cmd.exe' -ArgumentList '/d /s /c \"set AISTACK_HOOK_SOURCE=cursor&& (${AUTO_SYNC_HOOK_COMMAND}) <NUL >NUL 2>&1\"'`;\n\t\treturn `powershell.exe -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script, \"utf16le\").toString(\"base64\")}`;\n\t}\n\tconst detach = os === \"darwin\" ? \"nohup\" : \"setsid nohup\";\n\treturn `${detach} sh -c 'export AISTACK_HOOK_SOURCE=cursor; ${AUTO_SYNC_HOOK_COMMAND}' </dev/null >/dev/null 2>&1 &`;\n}\n\ntype Entry = Record<string, unknown>;\ninterface Config {\n\tversion?: unknown;\n\thooks?: Record<string, Entry[]>;\n\t[key: string]: unknown;\n}\nfunction read(file: string): Config {\n\tif (!existsSync(file)) return {};\n\tconst value: unknown = JSON.parse(readFileSync(file, \"utf8\"));\n\tif (!value || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new Error(\"expected a JSON object\");\n\tconst config = value as Config;\n\tif (config.version !== undefined && config.version !== 1)\n\t\tthrow new Error(\"unsupported hooks version\");\n\tif (\n\t\tconfig.hooks !== undefined &&\n\t\t(!config.hooks ||\n\t\t\ttypeof config.hooks !== \"object\" ||\n\t\t\tArray.isArray(config.hooks) ||\n\t\t\tObject.values(config.hooks).some(\n\t\t\t\t(entries) =>\n\t\t\t\t\t!Array.isArray(entries) ||\n\t\t\t\t\tentries.some(\n\t\t\t\t\t\t(entry) =>\n\t\t\t\t\t\t\t!entry || typeof entry !== \"object\" || Array.isArray(entry),\n\t\t\t\t\t),\n\t\t\t))\n\t)\n\t\tthrow new Error(\"malformed hooks object\");\n\treturn config;\n}\nfunction isOurs(entry: Entry): boolean {\n\t// Exact generated commands avoid claiming another user's command that happens\n\t// to invoke the CLI. Recognize all supported OS definitions when removing.\n\treturn [\"linux\", \"darwin\", \"win32\"].some(\n\t\t(os) => entry.command === cursorHookCommand(os as NodeJS.Platform),\n\t);\n}\nfunction update(\n\tfile: string,\n\tinstall: boolean,\n\tos: NodeJS.Platform,\n): HookResult {\n\ttry {\n\t\tconst config = read(file);\n\t\tconst existing = config.hooks?.stop ?? [];\n\t\tconst kept = existing.filter((entry) => !isOurs(entry));\n\t\tif (!install && kept.length === existing.length)\n\t\t\treturn { ok: true, message: \"no Cursor hook to remove\" };\n\t\tif (install) kept.push({ command: cursorHookCommand(os) });\n\t\tconst hooks = { ...config.hooks };\n\t\tif (kept.length) hooks.stop = kept;\n\t\telse delete hooks.stop;\n\t\tif (Object.keys(hooks).length) config.hooks = hooks;\n\t\telse delete config.hooks;\n\t\tif (install) config.version = 1;\n\t\tmkdirSync(dirname(file), { recursive: true });\n\t\twriteFileSync(file, `${JSON.stringify(config, null, 2)}\\n`);\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmessage: `Cursor stop hook ${install ? \"written to\" : \"removed from\"} ${file}`,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `Could not ${install ? \"install\" : \"remove\"} Cursor hook in ${file}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t};\n\t}\n}\nexport function installCursorAutoSyncHook(\n\tfile = CURSOR_HOOK_FILE,\n\tos = platform(),\n): HookResult {\n\treturn update(file, true, os);\n}\nexport function removeCursorAutoSyncHook(file = CURSOR_HOOK_FILE): HookResult {\n\treturn update(file, false, platform());\n}\nexport function cursorAutoSyncHookInstalled(file = CURSOR_HOOK_FILE): boolean {\n\ttry {\n\t\treturn (read(file).hooks?.stop ?? []).some(isOurs);\n\t} catch {\n\t\treturn false;\n\t}\n}\n","// The background trigger (#62, map #60): a `SessionStart` hook in\n// ~/.claude/settings.json, `async: true`.\n//\n// SessionStart, not SessionEnd - teardown is not guaranteed (crash, SIGKILL,\n// closed terminal), and at start-of-session the previous sessions are fully on\n// disk. The command runs `@latest` through npx, so unattended machines update\n// by construction. The `||` fallback covers the offline case: when the network\n// resolve of `@latest` fails, the second npx runs the cached copy.\n// `sync --auto` always exits 0, so the fallback never fires on a sync failure.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\nexport const CLAUDE_SETTINGS_FILE = join(homedir(), \".claude\", \"settings.json\");\n\nexport const AUTO_SYNC_HOOK_COMMAND =\n\t\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\";\n\ninterface HookEntry {\n\ttype: string;\n\tcommand?: string;\n\tasync?: boolean;\n}\n\ninterface HookMatcher {\n\tmatcher?: string;\n\thooks?: HookEntry[];\n}\n\ninterface ClaudeSettings {\n\thooks?: Record<string, HookMatcher[]>;\n\t[key: string]: unknown;\n}\n\n/** Recognize our hook across versions: package name plus the auto flag. */\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nexport interface HookResult {\n\tok: boolean;\n\tmessage: string;\n}\n\nfunction readClaudeSettings(\n\tfile: string,\n): { settings: ClaudeSettings } | { error: string } {\n\tif (!existsSync(file)) return { settings: {} };\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (raw && typeof raw === \"object\" && !Array.isArray(raw)) {\n\t\t\treturn { settings: raw as ClaudeSettings };\n\t\t}\n\t\treturn { error: `${file} does not hold a JSON object` };\n\t} catch {\n\t\t// Never rewrite a file we cannot parse - it is the user's Claude Code\n\t\t// configuration, and a rewrite would destroy whatever is in it.\n\t\treturn { error: `${file} is not valid JSON - fix it, then retry` };\n\t}\n}\n\n/**\n * Add the SessionStart auto-sync hook. Idempotent: an existing aistack\n * auto-sync entry (any version of the command) is replaced, not duplicated.\n * All other hooks are preserved byte-for-byte in structure.\n */\nexport function installAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst hooks = settings.hooks ?? {};\n\tconst sessionStart = Array.isArray(hooks.SessionStart)\n\t\t? hooks.SessionStart\n\t\t: [];\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tkept.push({\n\t\thooks: [{ type: \"command\", command: AUTO_SYNC_HOOK_COMMAND, async: true }],\n\t});\n\n\tsettings.hooks = { ...hooks, SessionStart: kept };\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `SessionStart hook written to ${file}` };\n}\n\n/**\n * Remove only our hook. Other SessionStart hooks and other events stay. A\n * missing file or an absent hook is success - the goal state already holds.\n */\nexport function removeAutoSyncHook(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): HookResult {\n\tif (!existsSync(file)) return { ok: true, message: \"no hook to remove\" };\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst settings = read.settings;\n\n\tconst sessionStart = settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) {\n\t\treturn { ok: true, message: \"no hook to remove\" };\n\t}\n\n\tconst kept = sessionStart\n\t\t.map((m) => ({\n\t\t\t...m,\n\t\t\thooks: (m.hooks ?? []).filter((h) => !isOurs(h)),\n\t\t}))\n\t\t.filter((m) => (m.hooks?.length ?? 0) > 0);\n\n\tconst hooks = { ...settings.hooks };\n\tif (kept.length > 0) {\n\t\thooks.SessionStart = kept;\n\t} else {\n\t\tdelete hooks.SessionStart;\n\t}\n\tif (Object.keys(hooks).length > 0) {\n\t\tsettings.hooks = hooks;\n\t} else {\n\t\tdelete settings.hooks;\n\t}\n\n\twriteFileSync(file, `${JSON.stringify(settings, null, 2)}\\n`);\n\treturn { ok: true, message: `hook removed from ${file}` };\n}\n\n/** Is the auto-sync hook present? Used by status reporting. */\nexport function autoSyncHookInstalled(\n\tfile: string = CLAUDE_SETTINGS_FILE,\n): boolean {\n\tconst read = readClaudeSettings(file);\n\tif (\"error\" in read) return false;\n\tconst sessionStart = read.settings.hooks?.SessionStart;\n\tif (!Array.isArray(sessionStart)) return false;\n\treturn sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));\n}\n","import {\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir, platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { HookResult } from \"./hook.js\";\n\nexport const GROK_HOOK_FILE = join(\n\tprocess.env.GROK_HOME || join(homedir(), \".grok\"),\n\t\"hooks\",\n\t\"aistack.json\",\n);\n\nconst SYNC_COMMAND =\n\t\"npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto\";\n\nexport function grokHookCommand(os: NodeJS.Platform = platform()): string {\n\tif (os === \"win32\") {\n\t\treturn `Start-Process -WindowStyle Hidden -FilePath \"cmd.exe\" -ArgumentList '/d /s /c \"set AISTACK_HOOK_SOURCE=grok&& (${SYNC_COMMAND}) >NUL 2>&1\"'`;\n\t}\n\tif (os === \"darwin\") {\n\t\treturn `nohup sh -c 'export AISTACK_HOOK_SOURCE=grok; ${SYNC_COMMAND}' </dev/null >/dev/null 2>&1 &`;\n\t}\n\treturn `setsid nohup sh -c 'export AISTACK_HOOK_SOURCE=grok; ${SYNC_COMMAND}' >/dev/null 2>&1 &`;\n}\n\ninterface HookEntry {\n\ttype?: unknown;\n\tcommand?: unknown;\n\ttimeout?: unknown;\n}\n\ninterface GrokHookFile {\n\thooks?: Record<string, Array<{ hooks?: HookEntry[] }>>;\n\t[key: string]: unknown;\n}\n\nfunction readHookFile(\n\tfile: string,\n): { value: GrokHookFile } | { error: string } {\n\tif (!existsSync(file)) return { value: {} };\n\ttry {\n\t\tconst value: unknown = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\tconst candidate = value as GrokHookFile;\n\t\t\tif (\n\t\t\t\tcandidate.hooks !== undefined &&\n\t\t\t\t(!candidate.hooks ||\n\t\t\t\t\ttypeof candidate.hooks !== \"object\" ||\n\t\t\t\t\tArray.isArray(candidate.hooks) ||\n\t\t\t\t\tObject.values(candidate.hooks).some(\n\t\t\t\t\t\t(groups) => !Array.isArray(groups),\n\t\t\t\t\t))\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `${file} has a malformed hooks object. Fix it, then retry.`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { value: candidate };\n\t\t}\n\t} catch {\n\t\treturn { error: `${file} is not valid JSON. Fix it, then retry.` };\n\t}\n\treturn { error: `${file} does not hold a JSON object` };\n}\n\nfunction isOurs(entry: HookEntry): boolean {\n\treturn (\n\t\ttypeof entry.command === \"string\" &&\n\t\tentry.command.includes(\"@use-aistack/cli\") &&\n\t\tentry.command.includes(\"sync --auto\")\n\t);\n}\n\nexport function installGrokAutoSyncHook(\n\tfile: string = GROK_HOOK_FILE,\n\tos: NodeJS.Platform = platform(),\n): HookResult {\n\tconst read = readHookFile(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst foreignKeys = Object.keys(read.value).filter((key) => key !== \"hooks\");\n\tconst foreignEvents = Object.keys(read.value.hooks ?? {}).filter(\n\t\t(key) => key !== \"SessionStart\",\n\t);\n\tconst sessionStart = read.value.hooks?.SessionStart ?? [];\n\tconst foreignHandlers = sessionStart.flatMap((group) =>\n\t\t(group.hooks ?? []).filter((entry) => !isOurs(entry)),\n\t);\n\tif (\n\t\tforeignKeys.length > 0 ||\n\t\tforeignEvents.length > 0 ||\n\t\tforeignHandlers.length > 0\n\t) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${file} contains hooks not owned by AI Stack. Move them to another Grok hook file, then retry.`,\n\t\t};\n\t}\n\tconst value: GrokHookFile = {\n\t\thooks: {\n\t\t\tSessionStart: [\n\t\t\t\t{\n\t\t\t\t\thooks: [\n\t\t\t\t\t\t{ type: \"command\", command: grokHookCommand(os), timeout: 5 },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t};\n\tmkdirSync(dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(value, null, 2)}\\n`);\n\treturn {\n\t\tok: true,\n\t\tmessage: `Grok Build SessionStart hook written to ${file}. Start a new Grok session or reload hooks before expecting it to run.`,\n\t};\n}\n\nexport function removeGrokAutoSyncHook(\n\tfile: string = GROK_HOOK_FILE,\n): HookResult {\n\tif (!existsSync(file))\n\t\treturn { ok: true, message: \"no Grok Build hook to remove\" };\n\tconst read = readHookFile(file);\n\tif (\"error\" in read) return { ok: false, message: read.error };\n\tconst entries = read.value.hooks?.SessionStart ?? [];\n\tconst onlyOurs =\n\t\tObject.keys(read.value).every((key) => key === \"hooks\") &&\n\t\tObject.keys(read.value.hooks ?? {}).every(\n\t\t\t(key) => key === \"SessionStart\",\n\t\t) &&\n\t\tentries.every((group) =>\n\t\t\t(group.hooks ?? []).every((entry) => isOurs(entry)),\n\t\t);\n\tif (!onlyOurs) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: `${file} contains hooks not owned by AI Stack and was not removed.`,\n\t\t};\n\t}\n\tunlinkSync(file);\n\treturn { ok: true, message: `Grok Build hook removed from ${file}` };\n}\n\nexport function grokAutoSyncHookInstalled(\n\tfile: string = GROK_HOOK_FILE,\n): boolean {\n\tconst read = readHookFile(file);\n\tif (\"error\" in read) return false;\n\treturn (read.value.hooks?.SessionStart ?? []).some((group) =>\n\t\t(group.hooks ?? []).some((entry) => isOurs(entry)),\n\t);\n}\n","// `sync --auto` - the silent background run (#62, map #60).\n//\n// The tenets from map #29 hold: passive analysis, never passive publish. TWO\n// gates let this path publish, and either one closes it:\n//\n// 1. The machine's own flag (`autoSync.enabled` in\n// ~/.config/aistack/settings.json). Local, free, and checked first.\n// 2. The STACK's permission, read from `/api/sync-config` (#102, #103). The\n// owner holds this one, from any machine and from the web switch, so a\n// revoke reaches a machine whose hooks are still live.\n//\n// No prompts, no upsells, no email, no dialogs. This path also never installs\n// or removes a hook - the interactive sync owns that (`reconcileAutoSync`).\n// The escalation ladder is: one log line per run → the next interactive sync\n// reports the last result → after 3 consecutive failures, one visible\n// systemMessage line.\n//\n// This function never sets a nonzero exit code. The hook command falls back to\n// the npx cache on `||`, and a nonzero exit from a mere sync failure would\n// fire that fallback and run the whole sync twice.\n\nimport {\n\tappendFileSync,\n\tcloseSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { syncPublish } from \"../api.js\";\nimport {\n\ttype AutoSyncState,\n\tgetSettings,\n\tgetToken,\n\tnormalizeFrequencyHours,\n\tsaveSettings,\n} from \"../config.js\";\nimport { loadSyncConfig } from \"../harness/shared/allowlist.js\";\nimport { stageSync } from \"../sync/stage.js\";\n\nexport const SYNC_LOG_FILE = join(homedir(), \".config\", \"aistack\", \"sync.log\");\n\n/** The log stays small: newest 200 lines, older lines fall off. */\nexport const SYNC_LOG_MAX_LINES = 200;\n\n/** The one-line fix, named in the escalation message and nowhere vaguer. */\nconst FIX_COMMAND = \"npx @use-aistack/cli sync\";\n\nexport type AutoSyncDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tsettingsFile?: string;\n\tlogFile?: string;\n\tstageImpl?: typeof stageSync;\n\tpublishImpl?: typeof syncPublish;\n\tgetTokenImpl?: () => string | null;\n\tloadConfigImpl?: typeof loadSyncConfig;\n\t/** Where the systemMessage JSON goes. Defaults to stdout. */\n\temit?: (line: string) => void;\n\t/** Cursor and Grok hooks keep stdout empty, including failure escalation. */\n\tsuppressOutput?: boolean;\n\treservationFile?: string;\n};\n\n/** What the next interactive sync says when the stack has taken the permission away. */\nexport const REVOKED_RESULT = \"off - auto-sync is switched off for this stack\";\n\nexport function appendLogLine(file: string, line: string): void {\n\tmkdirSync(dirname(file), { recursive: true });\n\tappendFileSync(file, `${line}\\n`);\n\tconst lines = readFileSync(file, \"utf-8\").split(\"\\n\").filter(Boolean);\n\tif (lines.length > SYNC_LOG_MAX_LINES) {\n\t\twriteFileSync(file, `${lines.slice(-SYNC_LOG_MAX_LINES).join(\"\\n\")}\\n`);\n\t}\n}\n\nfunction reserveAttempt(file: string, now: number, windowMs: number): boolean {\n\tmkdirSync(dirname(file), { recursive: true });\n\tfor (;;) {\n\t\ttry {\n\t\t\tconst fd = openSync(file, \"wx\");\n\t\t\twriteFileSync(fd, String(now));\n\t\t\tcloseSync(fd);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\t\tconst held = Number(readFileSync(file, \"utf-8\"));\n\t\t\tif (Number.isFinite(held) && now - held < windowMs) return false;\n\t\t\ttry {\n\t\t\t\tunlinkSync(file);\n\t\t\t} catch (unlinkError) {\n\t\t\t\tif ((unlinkError as NodeJS.ErrnoException).code !== \"ENOENT\")\n\t\t\t\t\tthrow unlinkError;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport async function runAutoSync(deps: AutoSyncDeps): Promise<void> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst settingsFile = deps.settingsFile;\n\tconst logFile = deps.logFile ?? SYNC_LOG_FILE;\n\tconst emit =\n\t\tdeps.emit ?? ((line: string) => process.stdout.write(`${line}\\n`));\n\tconst stamp = new Date(now).toISOString();\n\n\tconst settings = getSettings(settingsFile);\n\tconst config = settings.autoSync;\n\n\t// The hard gate. A hook left behind after a revoke publishes nothing.\n\tif (config?.enabled !== true) {\n\t\tappendLogLine(logFile, `${stamp} skipped - auto-sync is not enabled`);\n\t\treturn;\n\t}\n\n\t// The freshness gate keys on the last ATTEMPT, not the last success. A\n\t// broken setup then retries once per frequency window, not once per\n\t// session start, and still reaches the 3-failure escalation.\n\tconst frequencyHours = normalizeFrequencyHours(config.frequencyHours);\n\tconst windowMs = frequencyHours * 3_600_000;\n\tconst state: AutoSyncState = settings.autoSyncState ?? {};\n\tconst lastRunAt = state.lastRunAt ?? 0;\n\tif (now - lastRunAt < windowMs) return;\n\tconst reservationFile =\n\t\tdeps.reservationFile ??\n\t\t`${settingsFile ?? join(homedir(), \".config\", \"aistack\", \"settings.json\")}.auto-sync-attempt`;\n\tif (!reserveAttempt(reservationFile, now, windowMs)) return;\n\tsaveSettings({ autoSyncState: { ...state, lastRunAt: now } }, settingsFile);\n\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\n\tlet failure: string | null = null;\n\tlet url: string | undefined;\n\tlet revoked = false;\n\ttry {\n\t\t// The permission the STACK holds (#102/#103), read BEFORE the scan. The\n\t\t// run needs the network to publish anyway, so asking first costs nothing\n\t\t// and spares a revoked machine a full walk of its own history.\n\t\tconst loaded = await loadConfig({\n\t\t\tbaseUrl: deps.baseUrl,\n\t\t\t...(token ? { token } : {}),\n\t\t});\n\t\tif (loaded.config.autoSync?.enabled === false) {\n\t\t\t// EXPLICIT OFF ONLY. An absent flag still publishes, because that\n\t\t\t// publish is what seeds the stack from this machine (#102).\n\t\t\trevoked = true;\n\t\t} else {\n\t\t\tconst serverFrequency = loaded.config.autoSync?.frequencyHours;\n\t\t\tif (\n\t\t\t\tloaded.config.autoSync?.enabled === true &&\n\t\t\t\tserverFrequency !== undefined &&\n\t\t\t\tnormalizeFrequencyHours(serverFrequency) !== frequencyHours\n\t\t\t) {\n\t\t\t\tsaveSettings(\n\t\t\t\t\t{\n\t\t\t\t\t\tautoSync: {\n\t\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t\t\tfrequencyHours: normalizeFrequencyHours(serverFrequency),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tsettingsFile,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst staged = await stage({\n\t\t\t\tbaseUrl: deps.baseUrl,\n\t\t\t\tnow: () => now,\n\t\t\t\ttrigger: \"auto\",\n\t\t\t\t// The same body the gate above read, so the permission and the\n\t\t\t\t// destination cannot come from two different fetches.\n\t\t\t\tloadConfigImpl: async () => loaded,\n\t\t\t\tgetTokenImpl: () => token,\n\t\t\t});\n\t\t\tif (staged.blockedReason !== null) {\n\t\t\t\tfailure = staged.blockedReason;\n\t\t\t} else {\n\t\t\t\tconst res = await publish(staged.token as string, staged.bodyJson);\n\t\t\t\turl = res.url;\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tfailure = e instanceof Error ? e.message : String(e);\n\t}\n\n\t// A revoke is not a failure: the streak, the warning and the last success\n\t// all stay as they were. Only the attempt is stamped, so the machine asks\n\t// once per frequency window instead of once per session start.\n\tif (revoked) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: { ...state, lastRunAt: now, lastResult: REVOKED_RESULT },\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} skipped - ${REVOKED_RESULT}`);\n\t\treturn;\n\t}\n\n\tif (failure === null) {\n\t\tsaveSettings(\n\t\t\t{\n\t\t\t\tautoSyncState: {\n\t\t\t\t\tlastRunAt: now,\n\t\t\t\t\tlastSuccessAt: now,\n\t\t\t\t\tlastResult: `ok - published at ${stamp}`,\n\t\t\t\t\tconsecutiveFailures: 0,\n\t\t\t\t\tfailureWarned: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tsettingsFile,\n\t\t);\n\t\tappendLogLine(logFile, `${stamp} ok - published${url ? ` ${url}` : \"\"}`);\n\t\treturn;\n\t}\n\n\tconst consecutiveFailures = (state.consecutiveFailures ?? 0) + 1;\n\tconst shouldWarn = consecutiveFailures >= 3 && state.failureWarned !== true;\n\tsaveSettings(\n\t\t{\n\t\t\tautoSyncState: {\n\t\t\t\t...state,\n\t\t\t\tlastRunAt: now,\n\t\t\t\tlastResult: `failed at ${stamp} - ${failure}`,\n\t\t\t\tconsecutiveFailures,\n\t\t\t\tfailureWarned: state.failureWarned === true || shouldWarn,\n\t\t\t},\n\t\t},\n\t\tsettingsFile,\n\t);\n\tappendLogLine(\n\t\tlogFile,\n\t\t`${stamp} fail (${consecutiveFailures} in a row) - ${failure}`,\n\t);\n\n\t// One visible line, once per failure streak. SessionStart hook JSON:\n\t// Claude Code shows `systemMessage` to the user when the async hook lands.\n\tif (\n\t\tshouldWarn &&\n\t\tdeps.suppressOutput !== true &&\n\t\tprocess.env.AISTACK_HOOK_SOURCE !== \"grok\" &&\n\t\tprocess.env.AISTACK_HOOK_SOURCE !== \"cursor\"\n\t) {\n\t\temit(\n\t\t\tJSON.stringify({\n\t\t\t\tsystemMessage: `aistack auto-sync failed ${consecutiveFailures} times in a row (${failure}). Run \\`${FIX_COMMAND}\\` in a terminal to fix it, or \\`${FIX_COMMAND} --auto off\\` to stop these runs.`,\n\t\t\t}),\n\t\t);\n\t}\n}\n","// Stage one send: scan every ACTIVE harness → build → derive the gate's text\n// from the exact bytes. Active, not installed: a harness with nothing in the\n// window is not scanned and does not publish, so a dead Claude Code install no\n// longer lands a stale snapshot next to a live Codex one (#101).\n//\n// Wayfinder ticket #41 (map #29), widened to the adapter seam by #67 (map\n// #60). The staged `bodyJson` string IS what a publish transmits - the summary\n// and the dialog are derived from it and from nothing else, so the user can\n// never approve a sentence about different bytes (#35's binding constraint).\n// The publish tool takes only the stage id; it can name WHICH staged send to\n// release, never what is in it.\n//\n// One stage covers ALL detected harnesses (#66 decision 4): the payloads ride\n// in one request so the server can land them atomically, and the kept-private\n// union is one list because consent is per name, not per harness.\n\nimport { createHash } from \"node:crypto\";\nimport {\n\tBUNDLED_PRICE_TABLE_ID,\n\tlayeredPricer,\n\ttype PriceTable,\n\tsetActivePricer,\n} from \"@aistack/pricing\";\nimport {\n\tMEASURED_DAYS_V1,\n\ttype MeasuredDay,\n\ttype UsageHarnessDay,\n} from \"@aistack/workflow-rules\";\nimport { fetchDayManifest, fetchPriceTable } from \"../api.js\";\nimport {\n\tgetProjectWorkspaceId,\n\tgetSettings,\n\tgetToken,\n\ttype Settings,\n} from \"../config.js\";\nimport { detectedAdapters } from \"../harness/index.js\";\nimport {\n\ttype KeptPrivateAtom,\n\ttype LoadedSyncConfig,\n\tloadSyncConfig,\n\ttype NameCategory,\n\ttype SyncConfig,\n} from \"../harness/shared/allowlist.js\";\nimport {\n\tapplyDayConsent,\n\ttype BuiltPayload,\n\tbuildPayload,\n\tbuildSyncBody,\n\tmergeKeptPrivate,\n\ttype SyncBody,\n\ttype SyncTrigger,\n} from \"../harness/shared/payload.js\";\nimport {\n\tDEFAULT_WINDOW_DAYS,\n\ttype ScanStats,\n\twindowStartMs,\n} from \"../harness/shared/window.js\";\nimport type { HarnessAdapter } from \"../harness/types.js\";\nimport {\n\tbuildMeasuredDays,\n\tbuildUsageDays,\n\tmergeUsageDays,\n} from \"../usage/days.js\";\nimport {\n\ttype DayManifest,\n\ttype DaySelection,\n\tMAX_DAY_WINDOW,\n\tselectDaysToPublish,\n} from \"../usage/diff.js\";\nimport { CLI_VERSION } from \"../version.js\";\nimport {\n\textractLocalWorkflow,\n\textractLocalWorkflowAsync,\n\ttype GitWorkflowRunner,\n\ttype LocalHarnessWorkflow,\n\tmachineUtcOffsetMinutes,\n\ttype WorkflowExtraction,\n} from \"../workflow/index.js\";\nimport {\n\tgrokCacheScope,\n\tloadGrokDateHints,\n\tmapToHints,\n\tsaveGrokDateHints,\n} from \"./grokDateCache.js\";\nimport { buildGateDialog, buildGateSummary } from \"./summary.js\";\n\nconst utcDate = (ms: number): string => new Date(ms).toISOString().slice(0, 10);\n\nexport type StagedSend = {\n\t/** Content-derived: the sha256 prefix of `bodyJson`. Same bytes, same id. */\n\tid: string;\n\t/** The exact request body a publish sends, already serialized. */\n\tbodyJson: string;\n\tbody: SyncBody;\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tsummary: string;\n\tdialog: string;\n\tconfig: SyncConfig;\n\ttoken: string | null;\n\tstagedAt: number;\n\t/**\n\t * `null` when this stage may not publish, with `blockedReason` saying why.\n\t * A gate that cannot name its destination must not send (#33 decision 7),\n\t * so no token and no resolved stack both block here, before any dialog.\n\t */\n\tblockedReason: string | null;\n\t/**\n\t * How the day rows were chosen (#307): the counts the gate prints and the\n\t * mode, `diff` against a manifest or `full` when there was none.\n\t */\n\tdays?: DaySelection;\n\t/** Which price table priced this stage (#336). Absent only in fixtures. */\n\tprices?: PriceTableUsed;\n\t/** Commit local Grok date hints only after the server accepted these bytes. */\n\tacknowledgePublish?: () => void;\n};\n\n/**\n * The table the adapters priced against. `served` is the server's\n * `modelPrices` table layered over the bundled one; `bundled` means the fetch\n * failed or the server has no such route, and every figure came from the\n * constants shipped with this CLI version.\n */\nexport type PriceTableUsed = {\n\tid: string;\n\torigin: \"served\" | \"bundled\";\n};\n\nexport type StageDeps = {\n\tbaseUrl: string;\n\tnow?: () => number;\n\tgetTokenImpl?: () => string | null;\n\tgetProjectWorkspaceIdImpl?: (directory: string) => string;\n\tloadConfigImpl?: (opts: {\n\t\tbaseUrl: string;\n\t\ttoken?: string;\n\t}) => Promise<LoadedSyncConfig>;\n\t/** Override the adapter set. Tests only. */\n\tadaptersImpl?: (sinceMs: number) => Promise<HarnessAdapter[]>;\n\t/** Override the Git reader the workflow extraction shells out to. Tests only. */\n\tgitRunnerImpl?: GitWorkflowRunner;\n\t/**\n\t * Override the manifest fetch (#307). `null` means the server has none.\n\t * A throw is caught and reads the same: the whole window goes.\n\t */\n\tfetchManifestImpl?: (\n\t\tbaseUrl: string,\n\t\ttoken: string,\n\t) => Promise<DayManifest | null>;\n\t/**\n\t * Override the price table fetch (#336). `null` means the server has none.\n\t * A throw is caught and reads the same: the bundled table prices the stage.\n\t */\n\tfetchPricesImpl?: (baseUrl: string) => Promise<PriceTable | null>;\n\tgetSettingsImpl?: () => Settings;\n\twindowDays?: number;\n\t/**\n\t * How this sync fired (#103). Defaults to `manual`, because every caller but\n\t * the background run has a human at the keyboard.\n\t */\n\ttrigger?: SyncTrigger;\n\t/** Human-facing phase updates for the interactive terminal. */\n\tonProgress?: (message: string) => void;\n};\n\nexport function stageId(bodyJson: string): string {\n\treturn createHash(\"sha256\").update(bodyJson).digest(\"hex\").slice(0, 12);\n}\n\nexport async function stageSync(deps: StageDeps): Promise<StagedSend> {\n\tconst now = (deps.now ?? Date.now)();\n\tconst token = (deps.getTokenImpl ?? getToken)();\n\tconst loadConfig = deps.loadConfigImpl ?? loadSyncConfig;\n\tconst adapters = deps.adaptersImpl ?? detectedAdapters;\n\tconst windowDays = deps.windowDays ?? DEFAULT_WINDOW_DAYS;\n\tconst projectWorkspaceId =\n\t\tdeps.getProjectWorkspaceIdImpl ?? getProjectWorkspaceId;\n\tconst fetchManifest = deps.fetchManifestImpl ?? fetchDayManifest;\n\tconst fetchPrices = deps.fetchPricesImpl ?? fetchPriceTable;\n\tconst progress = deps.onProgress ?? (() => {});\n\n\t// The price table comes from the server BEFORE any adapter prices a\n\t// response (#336): the adapters call the module-level pricing functions,\n\t// which read whichever pricer is active. Served rows win per key; the\n\t// bundled constants fill what the server does not hold. Unreachable reads\n\t// as bundled, and the gate says which one it was.\n\tlet prices: PriceTableUsed = {\n\t\tid: BUNDLED_PRICE_TABLE_ID,\n\t\torigin: \"bundled\",\n\t};\n\tprogress(\"Checking prices and stack settings\");\n\ttry {\n\t\tconst table = await fetchPrices(deps.baseUrl);\n\t\tif (table) {\n\t\t\tsetActivePricer(layeredPricer(table));\n\t\t\tprices = { id: table.id, origin: \"served\" };\n\t\t} else {\n\t\t\tsetActivePricer(null);\n\t\t}\n\t} catch {\n\t\tsetActivePricer(null);\n\t}\n\n\tconst { config, source } = await loadConfig({\n\t\tbaseUrl: deps.baseUrl,\n\t\t...(token ? { token } : {}),\n\t});\n\n\t// The server's day manifest (#307, ADR-0010): which dates it holds and with\n\t// what fingerprint. Missing (an old server) or failing (network) reads as\n\t// \"send the whole window\"; a publish that repeats a held date is correct,\n\t// only wasteful. The manifest also names the retention, which bounds how\n\t// far back the day scan reaches.\n\tlet manifest: DayManifest | null = null;\n\tif (token) {\n\t\ttry {\n\t\t\tmanifest = await fetchManifest(deps.baseUrl, token);\n\t\t} catch {\n\t\t\tmanifest = null;\n\t\t}\n\t}\n\tconst retentionDays = Math.max(\n\t\t1,\n\t\tMath.min(manifest?.retentionDays ?? MAX_DAY_WINDOW, MAX_DAY_WINDOW),\n\t);\n\n\tconst built: BuiltPayload[] = [];\n\tconst scanStats: Record<string, ScanStats> = {};\n\t// Collected per harness, extracted ONCE below (#213): local Git history is a\n\t// property of the machine, not of whichever harness opened the repository,\n\t// and the metric rows are computed across every synced harness at once.\n\tconst workflowScans: LocalHarnessWorkflow[] = [];\n\tconst usageScans: Map<string, UsageHarnessDay>[] = [];\n\t// One window start for detection AND for the snapshot scan (#101), so a\n\t// harness that counts as detected is exactly a harness with something in\n\t// the window.\n\tconst sinceMs = windowStartMs(now, windowDays);\n\t// The day scan reaches the whole retention (#307): the snapshot stays a\n\t// 30-day block until its readers retire, while the day rows cover every\n\t// date the server would keep. Two scans over the same files; the second is\n\t// the one the days and the workflow blocks come from.\n\tconst daysSinceMs = windowStartMs(now, retentionDays);\n\tconst active = await adapters(sinceMs);\n\tconst historical = await adapters(daysSinceMs);\n\tlet dayScansComplete = true;\n\tconst sessionDatesByHarness = new Map<string, Map<string, Set<string>>>();\n\tfor (const adapter of active) {\n\t\tprogress(`Scanning recent ${adapter.name} usage`);\n\t\tconst { aggregate, stats } = await adapter.scan({\n\t\t\tsinceMs,\n\t\t\tpublishWorkflow: false,\n\t\t\tonProgress: (files) =>\n\t\t\t\tprogress(`Scanning recent ${adapter.name} usage · ${files} files`),\n\t\t});\n\t\tscanStats[adapter.name] = stats;\n\t\tbuilt.push(\n\t\t\tbuildPayload({\n\t\t\t\taggregate,\n\t\t\t\tstats,\n\t\t\t\tsyncConfig: config,\n\t\t\t\tnow,\n\t\t\t\twindowDays,\n\t\t\t\tharnessName: adapter.name,\n\t\t\t\tbuiltinTools: adapter.builtinTools,\n\t\t\t\tprojectWorkspaceId,\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const adapter of historical) {\n\t\tprogress(`Reading historical ${adapter.name} days`);\n\t\tconst { aggregate, workflow, workflowLocal, scanComplete, sessionDates } =\n\t\t\tawait adapter.scan({\n\t\t\t\tsinceMs: daysSinceMs,\n\t\t\t\tpublishWorkflow: config.publishWorkflow,\n\t\t\t\tonProgress: (files) =>\n\t\t\t\t\tprogress(`Reading historical ${adapter.name} days · ${files} files`),\n\t\t\t});\n\t\tif (scanComplete === false) dayScansComplete = false;\n\t\tif (adapter.name === \"grok-build\" || adapter.name === \"cursor\")\n\t\t\tsessionDatesByHarness.set(adapter.name, sessionDates ?? new Map());\n\t\tworkflowScans.push({ aggregate: workflow, local: workflowLocal });\n\t\tusageScans.push(\n\t\t\tbuildUsageDays({\n\t\t\t\tharness: adapter.name,\n\t\t\t\taggregate,\n\t\t\t\tpublishCost: config.publishCost,\n\t\t\t\tprojectWorkspaceId,\n\t\t\t}),\n\t\t);\n\t}\n\n\t// The opt-in the machine currently holds, read at stage time so the gate's\n\t// bytes are the bytes sent (#78). Absent from the settings file means this\n\t// machine has never answered, which the backend reads as \"never told us\".\n\tconst settings = (deps.getSettingsImpl ?? getSettings)();\n\n\t// Git runs here and not inside an adapter's scan: the reducers hand back the\n\t// working directories their sessions touched, and reading one repository once\n\t// for all of them is both cheaper and the only way the commit counts stay\n\t// right when two harnesses shared a checkout.\n\t//\n\t// The extraction is skipped entirely when the owner has the switch off. It\n\t// shells out to `git` per repository, and running that work to throw it away\n\t// would be the one visible cost of a preference that is supposed to be free.\n\tlet workflow: WorkflowExtraction | undefined;\n\tif (workflowScans.length > 0 && config.publishWorkflow) {\n\t\tprogress(\"Reading Git history\");\n\t\tworkflow = deps.gitRunnerImpl\n\t\t\t? extractLocalWorkflow({\n\t\t\t\t\tharnesses: workflowScans,\n\t\t\t\t\tfromMs: daysSinceMs,\n\t\t\t\t\ttoMs: now,\n\t\t\t\t\trun: deps.gitRunnerImpl,\n\t\t\t\t})\n\t\t\t: await extractLocalWorkflowAsync({\n\t\t\t\t\tharnesses: workflowScans,\n\t\t\t\t\tfromMs: daysSinceMs,\n\t\t\t\t\ttoMs: now,\n\t\t\t\t});\n\t}\n\n\t// The day rows (#307): usage and workflow joined by date, consent applied\n\t// BEFORE the fingerprint so the hash is over the bytes that go, then diffed\n\t// against the manifest. Today always resends.\n\tconst correctionDates = new Set<string>();\n\tlet acknowledgePublish: (() => void) | undefined;\n\tconst acknowledgements: Array<() => void> = [];\n\tfor (const [harness, currentDates] of sessionDatesByHarness) {\n\t\tif (!token || !config.stack) continue;\n\t\tconst scope = grokCacheScope(\n\t\t\tharness === \"grok-build\" ? deps.baseUrl : `${deps.baseUrl}\\0${harness}`,\n\t\t\tconfig.stack.slug,\n\t\t\ttoken,\n\t\t);\n\t\tconst floor = utcDate(daysSinceMs);\n\t\tconst previous = loadGrokDateHints(scope);\n\t\tconst current = mapToHints(currentDates, floor);\n\t\tfor (const dates of Object.values(previous))\n\t\t\tfor (const date of dates) correctionDates.add(date);\n\t\tfor (const dates of Object.values(current))\n\t\t\tfor (const date of dates) correctionDates.add(date);\n\t\tif (Object.keys(previous).some((id) => !(id in current)))\n\t\t\tdayScansComplete = false;\n\t\tacknowledgements.push(() => saveGrokDateHints(scope, current));\n\t}\n\tif (acknowledgements.length)\n\t\tacknowledgePublish = () => {\n\t\t\tfor (const acknowledge of acknowledgements) acknowledge();\n\t\t};\n\tconst localDays: MeasuredDay[] = applyDayConsent(\n\t\tbuildMeasuredDays({\n\t\t\tusage: mergeUsageDays(usageScans),\n\t\t\t...(workflow ? { workflow: workflow.days } : {}),\n\t\t\tfrom: utcDate(daysSinceMs),\n\t\t\tto: utcDate(now),\n\t\t\tincludeDates: correctionDates,\n\t\t}),\n\t\tconfig,\n\t);\n\tconst days = selectDaysToPublish({\n\t\tlocal: dayScansComplete ? localDays : [],\n\t\tmanifest,\n\t\ttodayUtc: utcDate(now),\n\t});\n\n\tconst body = buildSyncBody(\n\t\tbuilt,\n\t\tconfig,\n\t\tsettings.autoSync,\n\t\tdeps.trigger,\n\t\thistorical.length > 0 && dayScansComplete\n\t\t\t? {\n\t\t\t\t\taggregateVersion: MEASURED_DAYS_V1,\n\t\t\t\t\tutcOffsetMinutes:\n\t\t\t\t\t\tworkflow?.utcOffsetMinutes ?? machineUtcOffsetMinutes(),\n\t\t\t\t\tdays: days.send,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tCLI_VERSION,\n\t);\n\tprogress(\"Preparing review\");\n\tconst bodyJson = JSON.stringify(body);\n\tconst keptPrivate = mergeKeptPrivate(built.map((b) => b.keptPrivate));\n\n\tconst ctx = {\n\t\tbody,\n\t\tkeptPrivate,\n\t\tconfig,\n\t\tsource,\n\t\tbaseUrl: deps.baseUrl,\n\t\tscanStats,\n\t\tdays,\n\t\tprices,\n\t\t// The real terminal, so the inventory rows break where this window ends\n\t\t// (#217). A pipe reports nothing and the preview falls back to 80.\n\t\twidth: process.stdout.columns,\n\t};\n\n\tlet blockedReason: string | null = null;\n\tif (historical.length === 0) {\n\t\tblockedReason = `No supported harness transcript from the last ${retentionDays} days to read.`;\n\t} else if (token === null) {\n\t\tblockedReason =\n\t\t\t\"This machine is not linked. Run `npx @use-aistack/cli login` first.\";\n\t} else if (config.stack === null) {\n\t\tblockedReason =\n\t\t\tsource === \"bundled\"\n\t\t\t\t? \"Could not fetch your settings from aistack, so the destination stack is unknown. Publish needs it. Check the network and preview again.\"\n\t\t\t\t: \"This machine has no destination stack. Run `npx @use-aistack/cli sync` in an interactive terminal to choose one.\";\n\t}\n\n\treturn {\n\t\tid: stageId(bodyJson),\n\t\tbodyJson,\n\t\tbody,\n\t\tkeptPrivate,\n\t\tsummary: buildGateSummary(ctx),\n\t\tdialog: buildGateDialog(ctx),\n\t\tconfig,\n\t\ttoken,\n\t\tstagedAt: now,\n\t\tblockedReason,\n\t\tdays,\n\t\tprices,\n\t\t...(acknowledgePublish ? { acknowledgePublish } : {}),\n\t};\n}\n","// The per-day usage wire, built from the adapters' per-day seam (#307, map\n// #302, ADR-0010). One `UsageHarnessDay` per (harness, UTC date), holding only\n// combinable atoms: session counts, hashed project keys, per-model token sums,\n// exact dollars priced at each response's own timestamp. No share and no mean\n// leaves here; the server folds a window with `foldUsageDays`.\n//\n// The seam is `Aggregate.usageDays` and `Aggregate.sessionStarts` in\n// ../harness/shared/aggregate.ts, filled by the SAME response stream that\n// fills the window totals, so a fold over these days equals the snapshot's\n// figures up to rounding. Everything here is pure.\n\nimport { baseModelId, pricingTableFor } from \"@aistack/pricing\";\nimport type {\n\tMeasuredDay,\n\tUsageDay,\n\tUsageHarnessDay,\n\tUsageModelDay,\n\tUsageTokens,\n\tWorkflowDay,\n} from \"@aistack/workflow-rules\";\nimport {\n\ttype Aggregate,\n\tcountsTotal,\n\tutcDateOf,\n} from \"../harness/shared/aggregate.js\";\nimport { sanitizeModelId } from \"../harness/shared/payload.js\";\n\n/** Dollars keep six places: exact enough for a per-day sum, stable across runs. */\nconst round6 = (n: number): number => Math.round(n * 1_000_000) / 1_000_000;\n\nexport type BuildUsageDaysInput = {\n\t/** The payload discriminator, e.g. `\"claude-code\"`. */\n\tharness: string;\n\taggregate: Aggregate;\n\t/** THE CONSENT GATE for dollars: off leaves `usd` and `pricingTable` out of the bytes. */\n\tpublishCost: boolean;\n\t/** Resolve one local project directory to its persistent opaque id. */\n\tprojectWorkspaceId: (directory: string) => string;\n};\n\ntype ModelAcc = {\n\ttokens: UsageTokens & {\n\t\tcacheWriteTtl: { fiveMinute: number; oneHour: number; unsplit: number };\n\t};\n\tcostUSD: number;\n\tunpricedTokens: number;\n\ttable: string | null;\n};\n\n/**\n * One harness's usage, one row per UTC date it touched. A date with sessions\n * but no response, or a response but no session start, still gets a row: the\n * fold counts a day active when a session started on it.\n */\nexport function buildUsageDays(\n\tinput: BuildUsageDaysInput,\n): Map<string, UsageHarnessDay> {\n\tconst { aggregate: agg, harness, publishCost, projectWorkspaceId } = input;\n\n\tconst sessionsByDay = new Map<string, number>();\n\tfor (const startMs of agg.sessionStarts.values()) {\n\t\tconst date = utcDateOf(startMs);\n\t\tsessionsByDay.set(date, (sessionsByDay.get(date) ?? 0) + 1);\n\t}\n\n\tconst dates = [\n\t\t...new Set([...agg.usageDays.keys(), ...sessionsByDay.keys()]),\n\t].sort();\n\n\tconst out = new Map<string, UsageHarnessDay>();\n\tfor (const date of dates) {\n\t\tconst acc = agg.usageDays.get(date);\n\t\t// The fast-mode key (`#fast`) is ours, not the vendor's: merge rows onto\n\t\t// the base id the way the snapshot's `groupModels` does. Dollars stay\n\t\t// exact because they were priced per response at the fast rate.\n\t\tconst groups = new Map<string, ModelAcc>();\n\t\tlet unpriced = 0;\n\t\tfor (const [modelKey, m] of acc?.models ?? []) {\n\t\t\tconst id = sanitizeModelId(baseModelId(modelKey));\n\t\t\tlet g = groups.get(id);\n\t\t\tif (!g) {\n\t\t\t\tg = {\n\t\t\t\t\ttokens: {\n\t\t\t\t\t\tinput: 0,\n\t\t\t\t\t\toutput: 0,\n\t\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\t\tcacheWriteTtl: { fiveMinute: 0, oneHour: 0, unsplit: 0 },\n\t\t\t\t\t},\n\t\t\t\t\tcostUSD: 0,\n\t\t\t\t\tunpricedTokens: 0,\n\t\t\t\t\ttable: null,\n\t\t\t\t};\n\t\t\t\tgroups.set(id, g);\n\t\t\t}\n\t\t\tg.table ??= pricingTableFor(modelKey);\n\t\t\tg.tokens.input += m.counts.input;\n\t\t\tg.tokens.output += m.counts.output;\n\t\t\tg.tokens.cacheRead += m.counts.cacheRead;\n\t\t\tg.tokens.cacheWrite +=\n\t\t\t\tm.counts.cacheWrite5m +\n\t\t\t\tm.counts.cacheWrite1h +\n\t\t\t\tm.counts.cacheWriteUnsplit;\n\t\t\tg.tokens.cacheWriteTtl.fiveMinute += m.counts.cacheWrite5m;\n\t\t\tg.tokens.cacheWriteTtl.oneHour += m.counts.cacheWrite1h;\n\t\t\tg.tokens.cacheWriteTtl.unsplit += m.counts.cacheWriteUnsplit;\n\t\t\tg.costUSD += m.costUSD;\n\t\t\tg.unpricedTokens += m.unpricedTokens;\n\t\t\tunpriced += m.unpricedTokens;\n\t\t}\n\n\t\tconst models: UsageModelDay[] = [...groups.entries()]\n\t\t\t.map(([model, g]) => {\n\t\t\t\tconst { cacheWriteTtl, ...plain } = g.tokens;\n\t\t\t\tconst tokens: UsageTokens =\n\t\t\t\t\tg.tokens.cacheWrite > 0 ? { ...plain, cacheWriteTtl } : plain;\n\t\t\t\tconst row: UsageModelDay = { model, tokens };\n\t\t\t\t// Absent, not zero (#33 decision 11): a model with an unpriced\n\t\t\t\t// response that day carries no dollars, and its tokens sit in\n\t\t\t\t// `excludedTokens.unpriced`. Dollars and their citation travel\n\t\t\t\t// together (#136).\n\t\t\t\tif (\n\t\t\t\t\tpublishCost &&\n\t\t\t\t\tg.unpricedTokens === 0 &&\n\t\t\t\t\tg.table !== null &&\n\t\t\t\t\tcountsTotalOf(tokens) > 0\n\t\t\t\t) {\n\t\t\t\t\trow.usd = round6(g.costUSD);\n\t\t\t\t\trow.pricingTable = g.table;\n\t\t\t\t}\n\t\t\t\treturn row;\n\t\t\t})\n\t\t\t.filter((row) => countsTotalOf(row.tokens) > 0)\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tcountsTotalOf(b.tokens) - countsTotalOf(a.tokens) ||\n\t\t\t\t\ta.model.localeCompare(b.model),\n\t\t\t);\n\n\t\tout.set(date, {\n\t\t\tharness,\n\t\t\tsessions: sessionsByDay.get(date) ?? 0,\n\t\t\tprojectKeys: [\n\t\t\t\t...new Set([...(acc?.projectDirs ?? [])].map(projectWorkspaceId)),\n\t\t\t].sort(),\n\t\t\tmodels,\n\t\t\tsubagentTokens: acc?.subagentTokens ?? 0,\n\t\t\texcludedTokens: { unpriced, synthetic: acc?.syntheticTokens ?? 0 },\n\t\t});\n\t}\n\treturn out;\n}\n\nconst countsTotalOf = (t: UsageTokens): number =>\n\tt.input + t.output + t.cacheWrite + t.cacheRead;\n\n/** Join several harnesses' day maps into one `UsageDay` per date. */\nexport function mergeUsageDays(\n\tperHarness: readonly Map<string, UsageHarnessDay>[],\n): Map<string, UsageDay> {\n\tconst out = new Map<string, UsageDay>();\n\tconst dates = [\n\t\t...new Set(perHarness.flatMap((days) => [...days.keys()])),\n\t].sort();\n\tfor (const date of dates) {\n\t\tconst harnesses: UsageHarnessDay[] = [];\n\t\tfor (const days of perHarness) {\n\t\t\tconst day = days.get(date);\n\t\t\tif (day) harnesses.push(day);\n\t\t}\n\t\tout.set(date, { harnesses });\n\t}\n\treturn out;\n}\n\n/**\n * Join usage days and workflow days by date into the rows the wire carries,\n * limited to `[from, to]` inclusive (`YYYY-MM-DD`). A date outside the window\n * is a clock-skewed or restored transcript and never becomes a row.\n */\nexport function buildMeasuredDays(input: {\n\tusage: Map<string, UsageDay>;\n\tworkflow?: readonly WorkflowDay[];\n\tfrom: string;\n\tto: string;\n\tincludeDates?: ReadonlySet<string>;\n}): MeasuredDay[] {\n\tconst workflowByDate = new Map<string, WorkflowDay>();\n\tfor (const day of input.workflow ?? []) workflowByDate.set(day.date, day);\n\tconst dates = [\n\t\t...new Set([\n\t\t\t...input.usage.keys(),\n\t\t\t...workflowByDate.keys(),\n\t\t\t...(input.includeDates ?? []),\n\t\t]),\n\t]\n\t\t.filter(\n\t\t\t(d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d) && d >= input.from && d <= input.to,\n\t\t)\n\t\t.sort();\n\treturn dates.map((date) => {\n\t\tconst usage = input.usage.get(date);\n\t\tconst workflow = workflowByDate.get(date);\n\t\treturn {\n\t\t\tdate,\n\t\t\t...(usage ? { usage } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t};\n\t});\n}\n\n/** `input + output + cacheWrite + cacheRead` over a `TokenCounts`, for tests. */\nexport { countsTotal };\n","// Diff-only sync (#307, ADR-0010): which local days go on the wire.\n//\n// The server says what it holds in a day manifest, and the CLI sends only the\n// dates it lacks or holds differently. Pure: the manifest fetch lives in\n// ../api.ts and the decision to fall back lives in ../sync/stage.ts.\n\nimport {\n\tdayFingerprint,\n\tMEASURED_DAYS_V1,\n\ttype MeasuredDay,\n} from \"@aistack/workflow-rules\";\n\n/** The server's answer to `GET /api/cli/sync-manifest`. */\nexport type DayManifest = {\n\tretentionDays: number;\n\taggregateVersion: string;\n\tdays: { date: string; fingerprint: string }[];\n};\n\n/** The most days the CLI ever sends: the page's read cap, and the scan's reach. */\nexport const MAX_DAY_WINDOW = 400;\n\nexport type DaySkipReason = \"unchanged\" | \"expired\";\n\nexport type DaySelection = {\n\t/** Missing from the manifest, changed since, or today. */\n\tsend: MeasuredDay[];\n\t/** Dates the server already holds with the same fingerprint. */\n\tunchanged: number;\n\tskipped: { date: string; reason: DaySkipReason }[];\n\t/** How the selection was made, for the gate's one line about it. */\n\tmode: \"diff\" | \"full\";\n};\n\nconst DAY_MS = 86_400_000;\n\n/** The oldest date still inside a retention of `days` ending on `today`. */\nexport function retentionFloor(todayUtc: string, days: number): string {\n\tconst span = Math.max(1, Math.min(days, MAX_DAY_WINDOW));\n\tconst todayMs = Date.parse(`${todayUtc}T00:00:00.000Z`);\n\treturn new Date(todayMs - (span - 1) * DAY_MS).toISOString().slice(0, 10);\n}\n\n/**\n * Pick the days to publish.\n *\n * - No manifest (old server, network failure): every local day inside the\n * default retention goes, and `mode` says `full`.\n * - A manifest on another aggregate version: its fingerprints mean nothing to\n * this CLI, so every day goes, inside the retention it names.\n * - Otherwise a day goes when its date is missing from the manifest, when its\n * fingerprint differs, or when it is today (still running, always resends).\n * - A date older than the retention is dropped: the server would expire it\n * on arrival.\n */\nexport function selectDaysToPublish(input: {\n\tlocal: readonly MeasuredDay[];\n\tmanifest: DayManifest | null;\n\ttodayUtc: string;\n}): DaySelection {\n\tconst { local, manifest, todayUtc } = input;\n\tconst retention = manifest?.retentionDays ?? MAX_DAY_WINDOW;\n\tconst floor = retentionFloor(todayUtc, retention);\n\tconst comparable =\n\t\tmanifest !== null && manifest.aggregateVersion === MEASURED_DAYS_V1;\n\tconst held = new Map<string, string>();\n\tif (comparable) {\n\t\tfor (const day of manifest.days) held.set(day.date, day.fingerprint);\n\t}\n\n\tconst send: MeasuredDay[] = [];\n\tconst skipped: DaySelection[\"skipped\"] = [];\n\tfor (const day of [...local].sort((a, b) => a.date.localeCompare(b.date))) {\n\t\tif (day.date < floor) {\n\t\t\tskipped.push({ date: day.date, reason: \"expired\" });\n\t\t\tcontinue;\n\t\t}\n\t\tif (comparable && day.date !== todayUtc) {\n\t\t\tconst fingerprint = held.get(day.date);\n\t\t\tif (fingerprint !== undefined && fingerprint === dayFingerprint(day)) {\n\t\t\t\tskipped.push({ date: day.date, reason: \"unchanged\" });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tsend.push(day);\n\t}\n\treturn {\n\t\tsend,\n\t\tunchanged: skipped.filter((s) => s.reason === \"unchanged\").length,\n\t\tskipped,\n\t\tmode: comparable ? \"diff\" : \"full\",\n\t};\n}\n","import { execFile, execFileSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport type { GitDay } from \"@aistack/workflow-rules\";\n\n/**\n * Both rules changed together in #278: a path a machine owns (a dependency\n * tree, build output, a lockfile) no longer reaches either of them, so both the\n * test-file count and the file-type mix can differ from what v1 published for\n * the same repository.\n */\nexport const TEST_FILE_RULE_VERSION = \"test-files/v2\";\nexport const FILE_TYPE_RULE_VERSION = \"file-types/v2\";\n/**\n * Which commits count at all (#279). A merge commit and a commit whose every\n * path is machine-owned leave the reading. Without this id a reading synced\n * before the rule and one synced after are indistinguishable on the wire while\n * disagreeing about which commits exist.\n */\nexport const COMMIT_SET_RULE_VERSION = \"commit-set/v1\";\n\nexport type GitWorkflowRunner = (\n\tcwd: string,\n\targs: readonly string[],\n) => string | null;\n\nexport type AsyncGitWorkflowRunner = (\n\tcwd: string,\n\targs: readonly string[],\n) => Promise<string | null>;\n\n/** One UTC day of Git history, with the day it belongs to. */\nexport type GitDayRow = GitDay & { date: string };\n\n/**\n * Git history for the touched repositories, one row per UTC day that holds a\n * counted commit (#285). A commit belongs to the day of its author time.\n */\nexport type GitWorkflowResult = {\n\tdays: GitDayRow[];\n};\n\nexport type ExtractGitWorkflowOptions = {\n\t/** Local working directories touched by sessions inside the sync window. */\n\tworkingDirectories: Iterable<string>;\n\tfromMs: number;\n\ttoMs: number;\n\t/**\n\t * This machine's offset from UTC, in minutes east. Cells ship in UTC, and the\n\t * late-night count reads those same cells through this offset, so the count\n\t * and the grid always agree. Every commit uses the one offset, not its own.\n\t */\n\tutcOffsetMinutes: number;\n\trun?: GitWorkflowRunner;\n};\n\nconst defaultRunner: GitWorkflowRunner = (cwd, args) => {\n\ttry {\n\t\treturn execFileSync(\"git\", [...args], {\n\t\t\tcwd,\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t\tmaxBuffer: 64 * 1024 * 1024,\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\nconst defaultAsyncRunner: AsyncGitWorkflowRunner = (cwd, args) =>\n\tnew Promise((resolve) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[...args],\n\t\t\t{\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t\tmaxBuffer: 64 * 1024 * 1024,\n\t\t\t},\n\t\t\t(error, stdout) => resolve(error ? null : stdout),\n\t\t);\n\t});\n\n/** A day with no counted commit, carrying the rule ids a fold needs. */\nexport const emptyGitDay = (): GitDay => ({\n\ttestFileRuleVersion: TEST_FILE_RULE_VERSION,\n\tfileTypeRuleVersion: FILE_TYPE_RULE_VERSION,\n\tcommitSetRuleVersion: COMMIT_SET_RULE_VERSION,\n\tcommits: 0,\n\tlateNightCommits: 0,\n\tadditions: 0,\n\tremovals: 0,\n\tchangedLinesPerCommit: [],\n\ttestFileCommits: 0,\n\tchangedLinesByExtension: [],\n\twithheldExtensionLines: 0,\n\tweekdayHourCells: [],\n});\n\ntype MutableGitDay = Omit<\n\tGitDay,\n\t\"changedLinesPerCommit\" | \"changedLinesByExtension\" | \"weekdayHourCells\"\n> & {\n\tchangedLinesPerCommit: number[];\n\textensionLines: Map<string, number>;\n\tcells: Map<string, number>;\n};\n\n/**\n * The names this rule is willing to print. A path with no extension is absent\n * on purpose: `Dockerfile`, `LICENSE` and `.gitignore` are not coding\n * languages, and ranking them as one made the leading language of a TypeScript\n * repository read as `(none)`.\n */\nconst APPROVED_EXTENSIONS: ReadonlySet<string> = new Set([\n\t\".c\",\n\t\".cc\",\n\t\".cjs\",\n\t\".cpp\",\n\t\".cs\",\n\t\".css\",\n\t\".cts\",\n\t\".dart\",\n\t\".ex\",\n\t\".exs\",\n\t\".go\",\n\t\".h\",\n\t\".hpp\",\n\t\".html\",\n\t\".java\",\n\t\".js\",\n\t\".jsx\",\n\t\".json\",\n\t\".kt\",\n\t\".kts\",\n\t\".lua\",\n\t\".md\",\n\t\".mjs\",\n\t\".mts\",\n\t\".php\",\n\t\".py\",\n\t\".r\",\n\t\".rb\",\n\t\".rs\",\n\t\".scala\",\n\t\".scss\",\n\t\".sh\",\n\t\".sql\",\n\t\".svelte\",\n\t\".swift\",\n\t\".toml\",\n\t\".ts\",\n\t\".tsx\",\n\t\".vue\",\n\t\".xml\",\n\t\".yaml\",\n\t\".yml\",\n\t\".zig\",\n]);\n\n/**\n * Directory names a machine owns rather than a person. A dependency tree, a\n * build output directory, or a directory of captured tool output can carry\n * millions of changed lines that nobody wrote, and one accidental commit of one\n * of them is enough to bury every authored line in the reading.\n */\nconst UNAUTHORED_SEGMENTS: ReadonlySet<string> = new Set([\n\t\".bundle\",\n\t\".cache\",\n\t\".cargo\",\n\t\".gradle\",\n\t\".next\",\n\t\".nuxt\",\n\t\".pnpm\",\n\t\".pnpm-store\",\n\t\".svelte-kit\",\n\t\".turbo\",\n\t\".venv\",\n\t\"_generated\",\n\t\"bower_components\",\n\t\"build\",\n\t\"coverage\",\n\t\"dist\",\n\t\"generated\",\n\t\"node_modules\",\n\t\"out\",\n\t\"pods\",\n\t\"site-packages\",\n\t\"target\",\n\t\"third_party\",\n\t\"vendor\",\n\t\"venv\",\n\t\"__pycache__\",\n]);\n\n/** Dependency lockfiles. A resolver writes these, and their extension lies. */\nconst UNAUTHORED_BASENAMES: ReadonlySet<string> = new Set([\n\t\"bun.lock\",\n\t\"bun.lockb\",\n\t\"cargo.lock\",\n\t\"composer.lock\",\n\t\"flake.lock\",\n\t\"gemfile.lock\",\n\t\"go.sum\",\n\t\"mix.lock\",\n\t\"npm-shrinkwrap.json\",\n\t\"package-lock.json\",\n\t\"packages.lock.json\",\n\t\"pipfile.lock\",\n\t\"pnpm-lock.yaml\",\n\t\"podfile.lock\",\n\t\"poetry.lock\",\n\t\"pubspec.lock\",\n\t\"uv.lock\",\n\t\"yarn.lock\",\n]);\n\n/**\n * True when the path is machine-written rather than authored. Those lines leave\n * the reading entirely: they are not withheld, because withholding keeps a line\n * in the denominator, and a line nobody wrote does not belong in either half.\n */\nfunction isUnauthoredPath(file: string): boolean {\n\tconst parts = file.replaceAll(\"\\\\\", \"/\").toLowerCase().split(\"/\");\n\tif (parts.some((part) => UNAUTHORED_SEGMENTS.has(part))) return true;\n\treturn UNAUTHORED_BASENAMES.has(parts.at(-1) ?? \"\");\n}\n\nconst COMMIT_MARKER = \"aistack-commit\";\n\nfunction parseNumstat(\n\tfield: string,\n): { additions: number; removals: number; file: string } | null {\n\tconst normalized = field.replace(/^\\n+(?=(?:\\d+|-)\\t)/, \"\");\n\tconst firstTab = normalized.indexOf(\"\\t\");\n\tconst secondTab = normalized.indexOf(\"\\t\", firstTab + 1);\n\tif (firstTab <= 0 || secondTab <= firstTab) return null;\n\tconst additionsRaw = normalized.slice(0, firstTab);\n\tconst removalsRaw = normalized.slice(firstTab + 1, secondTab);\n\tif (!/^(?:\\d+|-)$/.test(additionsRaw)) return null;\n\tif (!/^(?:\\d+|-)$/.test(removalsRaw)) return null;\n\treturn {\n\t\tadditions: additionsRaw === \"-\" ? 0 : Number(additionsRaw),\n\t\tremovals: removalsRaw === \"-\" ? 0 : Number(removalsRaw),\n\t\tfile: normalized.slice(secondTab + 1),\n\t};\n}\n\nfunction isTestFile(file: string): boolean {\n\tconst normalized = file.replaceAll(\"\\\\\", \"/\").toLowerCase();\n\tconst parts = normalized.split(\"/\");\n\tif (parts.some((part) => [\"test\", \"tests\", \"__tests__\"].includes(part))) {\n\t\treturn true;\n\t}\n\tconst basename = parts.at(-1) ?? \"\";\n\treturn /(?:^|[._-])(test|spec)(?:[._-]|$)/.test(basename);\n}\n\nfunction utcCell(authoredMs: number): { weekdayUtc: number; hourUtc: number } {\n\tconst at = new Date(authoredMs);\n\treturn { weekdayUtc: at.getUTCDay(), hourUtc: at.getUTCHours() };\n}\n\n/** The hour on the machine's clock for a UTC cell. */\nfunction localHour(hourUtc: number, utcOffsetMinutes: number): number {\n\treturn (\n\t\t((((hourUtc * 60 + utcOffsetMinutes) % (24 * 60)) + 24 * 60) % (24 * 60)) /\n\t\t60\n\t);\n}\n\nfunction isLateNight(hour: number): boolean {\n\treturn hour >= 23 || hour < 3;\n}\n\n/**\n * Reduce Git history for the repositories touched by windowed harness sessions.\n * Repository roots and paths exist only during this call and never enter the result.\n */\nexport function extractGitWorkflow(\n\toptions: ExtractGitWorkflowOptions,\n): GitWorkflowResult {\n\tconst run = options.run ?? defaultRunner;\n\tconst roots = new Set<string>();\n\tfor (const directory of options.workingDirectories) {\n\t\tconst root = run(directory, [\"rev-parse\", \"--show-toplevel\"])?.trim();\n\t\tif (root) roots.add(root);\n\t}\n\tconst histories: string[] = [];\n\tfor (const root of roots) {\n\t\tconst history = run(root, gitLogArgs());\n\t\tif (history) histories.push(history);\n\t}\n\treturn reduceGitHistories(histories, options);\n}\n\n/** The non-blocking production path. Tests can keep using the synchronous seam. */\nexport async function extractGitWorkflowAsync(\n\toptions: Omit<ExtractGitWorkflowOptions, \"run\"> & {\n\t\trun?: AsyncGitWorkflowRunner;\n\t},\n): Promise<GitWorkflowResult> {\n\tconst run = options.run ?? defaultAsyncRunner;\n\tconst roots = new Set<string>();\n\tfor (const directory of options.workingDirectories) {\n\t\tconst root = (\n\t\t\tawait run(directory, [\"rev-parse\", \"--show-toplevel\"])\n\t\t)?.trim();\n\t\tif (root) roots.add(root);\n\t}\n\tconst histories = await Promise.all(\n\t\t[...roots].map((root) => run(root, gitLogArgs())),\n\t);\n\treturn reduceGitHistories(\n\t\thistories.filter((history): history is string => history !== null),\n\t\toptions,\n\t);\n}\n\nfunction gitLogArgs(): readonly string[] {\n\treturn [\n\t\t\"log\",\n\t\t\"--all\",\n\t\t\"--no-merges\",\n\t\t`--format=%x00${COMMIT_MARKER}%x00%H%x00%aI%x00`,\n\t\t\"--numstat\",\n\t\t\"-z\",\n\t];\n}\n\nfunction reduceGitHistories(\n\thistories: Iterable<string>,\n\toptions: Pick<\n\t\tExtractGitWorkflowOptions,\n\t\t\"fromMs\" | \"toMs\" | \"utcOffsetMinutes\"\n\t>,\n): GitWorkflowResult {\n\tconst days = new Map<string, MutableGitDay>();\n\tconst dayOf = (date: string): MutableGitDay => {\n\t\tlet day = days.get(date);\n\t\tif (!day) {\n\t\t\tconst {\n\t\t\t\tchangedLinesByExtension: _extensions,\n\t\t\t\tweekdayHourCells: _cells,\n\t\t\t\t...rest\n\t\t\t} = emptyGitDay();\n\t\t\tday = {\n\t\t\t\t...rest,\n\t\t\t\tchangedLinesPerCommit: [],\n\t\t\t\textensionLines: new Map(),\n\t\t\t\tcells: new Map(),\n\t\t\t};\n\t\t\tdays.set(date, day);\n\t\t}\n\t\treturn day;\n\t};\n\tconst seenCommits = new Set<string>();\n\tfor (const history of histories) {\n\t\ttype CurrentCommit = {\n\t\t\tincluded: boolean;\n\t\t\tdate: string;\n\t\t\tcell: { weekdayUtc: number; hourUtc: number };\n\t\t\t/** True once one path a person could have written appears. */\n\t\t\tauthored: boolean;\n\t\t\tadditions: number;\n\t\t\tremovals: number;\n\t\t\tchangedLines: number;\n\t\t\ttouchesTest: boolean;\n\t\t\twithheldLines: number;\n\t\t\textensionLines: Map<string, number>;\n\t\t};\n\t\tlet current: CurrentCommit | undefined;\n\t\t// A commit counts only once its records are read: one with no authored\n\t\t// path leaves the reading entirely, rather than surviving as a commit\n\t\t// that changed nothing (commit-set/v1).\n\t\tconst finishCommit = (): void => {\n\t\t\tif (!current?.included || !current.authored) return;\n\t\t\tconst day = dayOf(current.date);\n\t\t\tday.commits++;\n\t\t\tday.additions += current.additions;\n\t\t\tday.removals += current.removals;\n\t\t\tday.changedLinesPerCommit.push(current.changedLines);\n\t\t\tif (current.touchesTest) day.testFileCommits++;\n\t\t\tconst { weekdayUtc, hourUtc } = current.cell;\n\t\t\tif (isLateNight(localHour(hourUtc, options.utcOffsetMinutes))) {\n\t\t\t\tday.lateNightCommits++;\n\t\t\t}\n\t\t\tconst cellKey = `${weekdayUtc}:${hourUtc}`;\n\t\t\tday.cells.set(cellKey, (day.cells.get(cellKey) ?? 0) + 1);\n\t\t\tday.withheldExtensionLines += current.withheldLines;\n\t\t\tfor (const [extension, lines] of current.extensionLines) {\n\t\t\t\tday.extensionLines.set(\n\t\t\t\t\textension,\n\t\t\t\t\t(day.extensionLines.get(extension) ?? 0) + lines,\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t\tconst fields = history.split(\"\\u0000\");\n\t\tfor (let fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {\n\t\t\tconst field = fields[fieldIndex] ?? \"\";\n\t\t\tif (field.replace(/^\\n+/, \"\") === COMMIT_MARKER) {\n\t\t\t\tfinishCommit();\n\t\t\t\tconst hash = fields[++fieldIndex] ?? \"\";\n\t\t\t\tconst authoredAt = fields[++fieldIndex] ?? \"\";\n\t\t\t\tconst authoredMs = Date.parse(authoredAt);\n\t\t\t\tconst included =\n\t\t\t\t\tNumber.isFinite(authoredMs) &&\n\t\t\t\t\tauthoredMs >= options.fromMs &&\n\t\t\t\t\tauthoredMs <= options.toMs &&\n\t\t\t\t\t!seenCommits.has(hash);\n\t\t\t\tcurrent = {\n\t\t\t\t\tincluded,\n\t\t\t\t\tdate: included ? new Date(authoredMs).toISOString().slice(0, 10) : \"\",\n\t\t\t\t\tcell: utcCell(included ? authoredMs : 0),\n\t\t\t\t\tauthored: false,\n\t\t\t\t\tadditions: 0,\n\t\t\t\t\tremovals: 0,\n\t\t\t\t\tchangedLines: 0,\n\t\t\t\t\ttouchesTest: false,\n\t\t\t\t\twithheldLines: 0,\n\t\t\t\t\textensionLines: new Map(),\n\t\t\t\t};\n\t\t\t\tif (included) seenCommits.add(hash);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst stat = parseNumstat(field);\n\t\t\tif (!stat) continue;\n\t\t\tlet file = stat.file;\n\t\t\tif (file.length === 0) {\n\t\t\t\tfieldIndex += 2;\n\t\t\t\tfile = fields[fieldIndex] ?? fields[fieldIndex - 1] ?? \"\";\n\t\t\t}\n\t\t\tif (!current?.included) continue;\n\t\t\tif (isUnauthoredPath(file)) continue;\n\t\t\tcurrent.authored = true;\n\t\t\tconst fileChangedLines = stat.additions + stat.removals;\n\t\t\tcurrent.additions += stat.additions;\n\t\t\tcurrent.removals += stat.removals;\n\t\t\tcurrent.changedLines += fileChangedLines;\n\t\t\tif (isTestFile(file)) current.touchesTest = true;\n\t\t\tif (fileChangedLines <= 0) continue;\n\t\t\t// An empty extension is not in the approved set, so it withholds.\n\t\t\tconst extension = path.extname(file).toLowerCase();\n\t\t\tif (APPROVED_EXTENSIONS.has(extension)) {\n\t\t\t\tcurrent.extensionLines.set(\n\t\t\t\t\textension,\n\t\t\t\t\t(current.extensionLines.get(extension) ?? 0) + fileChangedLines,\n\t\t\t\t);\n\t\t\t} else current.withheldLines += fileChangedLines;\n\t\t}\n\t\tfinishCommit();\n\t}\n\n\treturn {\n\t\tdays: [...days]\n\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t.map(([date, day]) => {\n\t\t\t\tconst { extensionLines, cells, ...rest } = day;\n\t\t\t\treturn {\n\t\t\t\t\tdate,\n\t\t\t\t\t...rest,\n\t\t\t\t\tchangedLinesByExtension: [...extensionLines]\n\t\t\t\t\t\t.map(([extension, changedLines]) => ({ extension, changedLines }))\n\t\t\t\t\t\t.sort((a, b) => a.extension.localeCompare(b.extension)),\n\t\t\t\t\tweekdayHourCells: [...cells]\n\t\t\t\t\t\t.map(([key, commits]) => {\n\t\t\t\t\t\t\tconst [weekdayUtc, hourUtc] = key.split(\":\").map(Number);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tweekdayUtc: weekdayUtc ?? 0,\n\t\t\t\t\t\t\t\thourUtc: hourUtc ?? 0,\n\t\t\t\t\t\t\t\tcommits,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t(a, b) => a.weekdayUtc - b.weekdayUtc || a.hourUtc - b.hourUtc,\n\t\t\t\t\t\t),\n\t\t\t\t};\n\t\t\t}),\n\t};\n}\n","import {\n\ttype GitDay,\n\ttype HarnessDay,\n\tWORKFLOW_AGGREGATES_V3,\n\ttype WorkflowDay,\n} from \"@aistack/workflow-rules\";\nimport {\n\temptyGitDay,\n\textractGitWorkflow,\n\textractGitWorkflowAsync,\n\ttype GitWorkflowResult,\n\ttype GitWorkflowRunner,\n} from \"./git.js\";\nimport type {\n\tHarnessWorkflowAggregate,\n\tWorkflowLocalSources,\n} from \"./reducer.js\";\n\nexport type LocalHarnessWorkflow = {\n\taggregate: HarnessWorkflowAggregate;\n\tlocal: WorkflowLocalSources;\n};\n\n/**\n * The workflow section as extracted on the machine (#285): one row per UTC\n * day, each holding only combinable atoms. The server folds a window out of\n * these and computes every row there; nothing here computes a share, a median\n * or a rank.\n */\nexport type WorkflowExtraction = {\n\taggregateVersion: typeof WORKFLOW_AGGREGATES_V3;\n\t/**\n\t * This machine's offset from UTC, in minutes east (#218). Session hours ship\n\t * in UTC, and the page renders them in the owner's local time. The machine is\n\t * the only end of the wire that knows which clock the owner reads.\n\t */\n\tutcOffsetMinutes: number;\n\tdays: WorkflowDay[];\n};\n\nexport type ExtractLocalWorkflowOptions = {\n\tharnesses: readonly LocalHarnessWorkflow[];\n\tfromMs: number;\n\ttoMs: number;\n\trun?: GitWorkflowRunner;\n\t/** Tests only: pin the machine clock so a fixture does not move with the runner's zone. */\n\tutcOffsetMinutes?: number;\n};\n\n/** Read only repositories touched by windowed sessions, then return safe daily rows. */\nexport function extractLocalWorkflow(\n\toptions: ExtractLocalWorkflowOptions,\n): WorkflowExtraction {\n\tconst utcOffsetMinutes =\n\t\toptions.utcOffsetMinutes ?? machineUtcOffsetMinutes();\n\tconst git = extractGitWorkflow({\n\t\tworkingDirectories: options.harnesses.flatMap(({ local }) => [\n\t\t\t...local.projectWorkspaces,\n\t\t]),\n\t\tfromMs: options.fromMs,\n\t\ttoMs: options.toMs,\n\t\tutcOffsetMinutes,\n\t\t...(options.run ? { run: options.run } : {}),\n\t});\n\treturn buildWorkflowExtraction(options.harnesses, git, utcOffsetMinutes);\n}\n\n/** Production extraction with Git subprocesses that do not block terminal UI. */\nexport async function extractLocalWorkflowAsync(\n\toptions: Omit<ExtractLocalWorkflowOptions, \"run\">,\n): Promise<WorkflowExtraction> {\n\tconst utcOffsetMinutes =\n\t\toptions.utcOffsetMinutes ?? machineUtcOffsetMinutes();\n\tconst git = await extractGitWorkflowAsync({\n\t\tworkingDirectories: options.harnesses.flatMap(({ local }) => [\n\t\t\t...local.projectWorkspaces,\n\t\t]),\n\t\tfromMs: options.fromMs,\n\t\ttoMs: options.toMs,\n\t\tutcOffsetMinutes,\n\t});\n\treturn buildWorkflowExtraction(options.harnesses, git, utcOffsetMinutes);\n}\n\n/** Minutes EAST of UTC, the sign convention the wire and the page both read. */\nexport function machineUtcOffsetMinutes(now: Date = new Date()): number {\n\treturn -now.getTimezoneOffset();\n}\n\n/**\n * Join the harness days and the Git days by date.\n *\n * A harness that failed its gate over the window ships every day WITHOUT its\n * phase block: the gate is a window judgment (see `HarnessWorkflowAggregate`),\n * and a day that shipped phase atoms anyway could be folded into a playbook\n * the gate refused. The parallel-project count is the union of workspaces\n * across harnesses on that day, counted here because one workspace opened by\n * two harnesses is one project.\n *\n * Local session keys, project paths, event arguments and timestamps do not\n * enter the returned value.\n */\nexport function buildWorkflowExtraction(\n\tharnessWorkflows: readonly LocalHarnessWorkflow[],\n\tgit: GitWorkflowResult,\n\tutcOffsetMinutes: number = machineUtcOffsetMinutes(),\n): WorkflowExtraction {\n\tconst harnessDays = new Map<string, HarnessDay[]>();\n\tconst projectDays = new Map<string, Set<string>>();\n\tfor (const { aggregate, local } of harnessWorkflows) {\n\t\tfor (const { date, ...day } of aggregate.days) {\n\t\t\tconst rows = harnessDays.get(date) ?? [];\n\t\t\tconst { phase, ...safe } = day;\n\t\t\trows.push(\n\t\t\t\taggregate.gate.publishable && phase ? { ...safe, phase } : safe,\n\t\t\t);\n\t\t\tharnessDays.set(date, rows);\n\t\t}\n\t\tfor (const [date, workspaces] of local.activeProjectDays) {\n\t\t\tconst projects = projectDays.get(date) ?? new Set<string>();\n\t\t\tfor (const project of workspaces) projects.add(project);\n\t\t\tprojectDays.set(date, projects);\n\t\t}\n\t}\n\tconst gitDays = new Map<string, GitDay>();\n\tfor (const { date, ...day } of git.days) gitDays.set(date, day);\n\n\tconst dates = [\n\t\t...new Set([\n\t\t\t...harnessDays.keys(),\n\t\t\t...gitDays.keys(),\n\t\t\t...projectDays.keys(),\n\t\t]),\n\t].sort();\n\n\treturn {\n\t\taggregateVersion: WORKFLOW_AGGREGATES_V3,\n\t\tutcOffsetMinutes,\n\t\tdays: dates.map((date) => {\n\t\t\tconst projects = projectDays.get(date)?.size;\n\t\t\treturn {\n\t\t\t\tdate,\n\t\t\t\tharnesses: harnessDays.get(date) ?? [],\n\t\t\t\tgit: gitDays.get(date) ?? emptyGitDay(),\n\t\t\t\t...(projects === undefined ? {} : { parallelProjects: projects }),\n\t\t\t};\n\t\t}),\n\t};\n}\n","import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\n\nexport type GrokDateHints = Record<string, string[]>;\ntype Cache = Record<string, GrokDateHints>;\nconst defaultFile = path.join(\n\thomedir(),\n\t\".config\",\n\t\"aistack\",\n\t\"grok-session-dates.json\",\n);\n\nexport function grokCacheScope(\n\tbaseUrl: string,\n\tstack: string,\n\ttoken: string,\n): string {\n\treturn createHash(\"sha256\")\n\t\t.update(`${baseUrl}\\0${stack}\\0${token}`)\n\t\t.digest(\"hex\");\n}\nfunction read(file: string): Cache {\n\tif (!existsSync(file)) return {};\n\ttry {\n\t\tconst value = JSON.parse(readFileSync(file, \"utf8\"));\n\t\treturn value && typeof value === \"object\" ? (value as Cache) : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\nexport function loadGrokDateHints(\n\tscope: string,\n\tfile = defaultFile,\n): GrokDateHints {\n\treturn read(file)[scope] ?? {};\n}\nexport function saveGrokDateHints(\n\tscope: string,\n\thints: GrokDateHints,\n\tfile = defaultFile,\n): void {\n\tconst cache = read(file);\n\tcache[scope] = hints;\n\tmkdirSync(path.dirname(file), { recursive: true });\n\twriteFileSync(file, JSON.stringify(cache, null, 2));\n}\nexport function mapToHints(\n\tvalue: Map<string, Set<string>>,\n\tfloor: string,\n): GrokDateHints {\n\treturn Object.fromEntries(\n\t\t[...value].map(([id, dates]) => [\n\t\t\tid,\n\t\t\t[...dates].filter((d) => d >= floor).sort(),\n\t\t]),\n\t);\n}\n","// The approve gate's two beats, as text.\n//\n// Wayfinder ticket #41 (map #29), shape fixed by the spike #35 and the copy\n// locked in #48. Beat one is the FULL summary, printed as ordinary scrollable\n// transcript output. Beat two is the SHORT elicitation message - it must stay\n// short, or `Accept` falls below the fold and the gate times out (#35, 1H).\n//\n// Everything here derives from the exact bytes that will be sent (`body`),\n// plus the local-only kept-private list that deliberately never enters them.\n// Nothing in this file is accepted as a caller-supplied argument beside the\n// payload - the spike promoted that from a caution to a demonstrated property.\n\nimport {\n\tfoldWorkflowDays,\n\ttype MeasuredDay,\n\tWORKFLOW_AGGREGATES_V3,\n} from \"@aistack/workflow-rules\";\nimport { HARNESS_ADAPTERS, harnessLabel } from \"../harness/index.js\";\nimport type {\n\tKeptPrivateAtom,\n\tNameCategory,\n\tSyncConfig,\n\tSyncConfigSource,\n} from \"../harness/shared/allowlist.js\";\nimport { NAME_CATEGORIES } from \"../harness/shared/allowlist.js\";\nimport type {\n\tMeasuredPayload,\n\tPayloadMeasuredDays,\n\tSyncBody,\n} from \"../harness/shared/payload.js\";\nimport type { ScanStats } from \"../harness/shared/window.js\";\nimport type { DaySelection } from \"../usage/diff.js\";\n\nexport type GateContext = {\n\t/** The exact request body a publish would send. */\n\tbody: SyncBody;\n\t/** The local-only review list - never inside any payload (#44). */\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>;\n\tconfig: SyncConfig;\n\tsource: SyncConfigSource;\n\t/** Web origin for the URLs the gate prints, e.g. https://aistack.to */\n\tbaseUrl: string;\n\t/**\n\t * Terminal width the preview wraps to. Absent means 80, which is what a\n\t * pipe or a test gets: the gate must render the same way everywhere except\n\t * for where the lines break.\n\t */\n\twidth?: number;\n\t/**\n\t * Per-harness scan stats, keyed by harness name - the LOCAL-ONLY detail\n\t * behind the payload's bare coverage counts (#75): unreadable file names,\n\t * error classes, foreign-file originators. Like `keptPrivate`, it rides\n\t * beside the body and never inside it.\n\t */\n\tscanStats?: Record<string, ScanStats>;\n\t/**\n\t * Which price table priced the dollars in the body (#336). Printed beside\n\t * them, with the sources each figure cites, so a reader can tell a served\n\t * rate from a bundled one.\n\t */\n\tprices?: { id: string; origin: \"served\" | \"bundled\" };\n\t/**\n\t * How the day rows in `body.measuredDays` were chosen (#307). The rows in\n\t * the bytes are the ones going; this says how many the server already held\n\t * unchanged, which the bytes cannot say.\n\t */\n\tdays?: DaySelection;\n};\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\n/** `4.27B`, `40.7M`, `216k`, `950` - three significant digits, like #40. */\nexport function fmtTokens(n: number): string {\n\tconst sig = (v: number): string => {\n\t\tconst s = v.toPrecision(3);\n\t\treturn s.includes(\".\") ? s.replace(/\\.?0+$/, \"\") : s;\n\t};\n\tif (n >= 1e9) return `${sig(n / 1e9)}B`;\n\tif (n >= 1e6) return `${sig(n / 1e6)}M`;\n\tif (n >= 1e3) return `${sig(n / 1e3)}k`;\n\treturn String(n);\n}\n\n/** `≈$5,840` - whole dollars; the ≈ and \"at API prices\" wording are #37's. */\nexport function fmtUSD(n: number): string {\n\treturn `≈$${Math.round(n).toLocaleString(\"en-US\")}`;\n}\n\nconst fmtPct = (share: number): string => `${(share * 100).toFixed(1)}%`;\n\n/**\n * `2026-08-10 21:03 UTC` - the publish receipt's stamp (#130). Milliseconds\n * and the ISO `T`/`Z` machine form dropped: the last thing a person reads\n * should be the result, not a receipt.\n */\nexport function fmtReceivedAt(ms: number): string {\n\treturn `${new Date(ms).toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n}\n\n/**\n * The dollar figure the gate names, or `null` when none may render.\n *\n * Mirrors the public display's rule (#46): a dollar figure never renders\n * without its pricing table. Summing only the models that carry the field\n * matches what actually goes up - an unpriceable model publishes tokens, not\n * dollars.\n */\nexport function totalUSD(payload: MeasuredPayload): number | null {\n\tlet sum = 0;\n\tlet any = false;\n\tfor (const m of payload.models) {\n\t\tif (m.apiEquivalentUSD === undefined) continue;\n\t\t// The citation may sit on the model (#136) or, in the old single-vendor\n\t\t// shape, on the payload. A figure neither cites stays unrendered.\n\t\tif (m.pricingTable === undefined && payload.pricingTable === null) continue;\n\t\tsum += m.apiEquivalentUSD;\n\t\tany = true;\n\t}\n\treturn any ? sum : null;\n}\n\n/** The normalized processed-token split, when model rows cover the headline. */\nexport function tokenBreakdown(\n\tpayload: MeasuredPayload,\n): { fresh: number; cached: number } | null {\n\tlet fresh = 0;\n\tlet cached = 0;\n\tfor (const model of payload.models) {\n\t\tfresh += model.tokens.input + model.tokens.output + model.tokens.cacheWrite;\n\t\tcached += model.tokens.cacheRead;\n\t}\n\treturn fresh + cached === payload.activity.totalTokens\n\t\t? { fresh, cached }\n\t\t: null;\n}\n\n/** DISTINCT kept-private names, from the send bytes (`inventory.withheld`). */\nexport function withheldCount(payload: MeasuredPayload): number {\n\tconst w = payload.inventory.withheld;\n\treturn (\n\t\tw.builtinTools + w.mcpServers + w.skills + w.subagents + w.slashCommands\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Beat two - the elicitation message. Copy locked in #48; keep it SHORT.\n// ---------------------------------------------------------------------------\n\nexport function buildGateDialog(ctx: GateContext): string {\n\tconst { payloads, keptPrivate } = ctx.body;\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tconst lines = [\"Publish to aistack?\"];\n\tif (n > 0) {\n\t\tlines.push(\n\t\t\tkeptPrivate === undefined\n\t\t\t\t? `${n} name${n === 1 ? \"\" : \"s\"} stay${n === 1 ? \"s\" : \"\"} on this machine`\n\t\t\t\t: `${n} private review name${n === 1 ? \"\" : \"s\"} will be stored`,\n\t\t);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Beat one - the full summary, transcript output.\n// ---------------------------------------------------------------------------\n\nconst CATEGORY_LABEL: Record<NameCategory, string> = {\n\tbuiltinTools: \"actions\",\n\tmcpServers: \"mcp\",\n\tskills: \"skills\",\n\tsubagents: \"agents\",\n\tslashCommands: \"commands\",\n};\n\n// ---------------------------------------------------------------------------\n// Wrapping.\n//\n// EVERY PUBLISHED NAME STAYS ON SCREEN. This is the consent surface, so a name\n// that goes up is a name the person reads first - the inventory rows are never\n// truncated to a count the way the kept-private list is, because that list is\n// the opposite case: those names do NOT leave the machine.\n//\n// What changed in #217 is only where the lines break. An unwrapped inventory\n// row ran to several hundred characters and the terminal broke it mid-name,\n// which reads as noise rather than as a list someone can check.\n// ---------------------------------------------------------------------------\n\n/** The label column every harness line shares: `window 30 days · ...`. */\nconst LABEL_WIDTH = 10;\nconst DEFAULT_WIDTH = 80;\n\n/** Wrap to the caller's terminal, clamped to a width a list stays readable at. */\nexport function wrapWidth(width: number | undefined): number {\n\treturn Math.min(110, Math.max(60, width ?? DEFAULT_WIDTH));\n}\n\n/**\n * One labelled row, wrapped with a hanging indent under its own label.\n *\n * Breaks on spaces only, and the callers join names with \", \", so a name is\n * never split across two lines.\n */\nexport function wrapRow(\n\thead: string,\n\tcontinuation: string,\n\ttext: string,\n\twidth: number,\n): string[] {\n\tconst limit = Math.max(24, width - continuation.length);\n\tconst lines: string[] = [];\n\tlet line = \"\";\n\tfor (const word of text.split(\" \")) {\n\t\tif (line === \"\") {\n\t\t\tline = word;\n\t\t\tcontinue;\n\t\t}\n\t\tif (`${line} ${word}`.length > limit) {\n\t\t\tlines.push(line);\n\t\t\tline = word;\n\t\t} else {\n\t\t\tline = `${line} ${word}`;\n\t\t}\n\t}\n\tif (line !== \"\") lines.push(line);\n\treturn lines.map((l, i) => (i === 0 ? head : continuation) + l);\n}\n\n/** Wrap separator-delimited entries without splitting one model from its share. */\nexport function wrapEntries(\n\thead: string,\n\tcontinuation: string,\n\tentries: readonly string[],\n\twidth: number,\n): string[] {\n\tconst limit = Math.max(24, width - continuation.length);\n\tconst lines: string[] = [];\n\tlet line = \"\";\n\tfor (const entry of entries) {\n\t\tconst next = line ? `${line} · ${entry}` : entry;\n\t\tif (line && next.length > limit) {\n\t\t\tlines.push(line);\n\t\t\tline = entry;\n\t\t} else {\n\t\t\tline = next;\n\t\t}\n\t}\n\tif (line) lines.push(line);\n\treturn lines.map(\n\t\t(line, index) => `${index === 0 ? head : continuation}${line}`,\n\t);\n}\n\n/** Kept-private rows for the gate: one row per group, then singles (#48). */\nexport function keptPrivateRows(\n\tkeptPrivate: Record<NameCategory, KeptPrivateAtom[]>,\n): Array<{ label: string; names: number }> {\n\tconst groups = new Map<string, number>();\n\tconst singles: string[] = [];\n\tfor (const category of NAME_CATEGORIES) {\n\t\tfor (const atom of keptPrivate[category]) {\n\t\t\tif (atom.group === null) singles.push(atom.name);\n\t\t\telse groups.set(atom.group, (groups.get(atom.group) ?? 0) + 1);\n\t\t}\n\t}\n\tconst rows = [...groups].map(([label, names]) => ({ label, names }));\n\tfor (const name of singles) rows.push({ label: name, names: 1 });\n\trows.sort((a, b) => b.names - a.names || a.label.localeCompare(b.label));\n\treturn rows;\n}\n\n/**\n * How many kept-private rows the gate names before it counts the rest.\n *\n * Three, not six (#217). These names do NOT leave the machine, which is what\n * makes truncating them safe here and unsafe for the published inventory.\n */\nconst KEPT_PRIVATE_ROWS_SHOWN = 3;\n\n// The harness display names live with the harness names themselves (#101), so\n// one harness has one label everywhere. Re-exported: this module is where the\n// gate's renderers reach for it.\nexport { harnessLabel };\n\n/** How many unreadable files get named before the list truncates. */\nconst UNREADABLE_FILES_SHOWN = 5;\n\n/**\n * The local-only lines behind the bare coverage counts (#75). Everything here\n * stays on this machine: relative paths, error classes, and originator names\n * never enter the payload.\n */\nexport function scanNoteLines(stats: ScanStats, label: string): string[] {\n\tconst out: string[] = [];\n\tconst shown = stats.unreadableFiles.slice(0, UNREADABLE_FILES_SHOWN);\n\tfor (const f of shown) {\n\t\tout.push(` ${f.path} (${f.reason})`);\n\t}\n\tif (stats.unreadableFiles.length > shown.length) {\n\t\tout.push(\n\t\t\t` ...${stats.unreadableFiles.length - shown.length} more`,\n\t\t);\n\t}\n\tif (stats.filesZstdUnsupported > 0) {\n\t\tout.push(\n\t\t\t` ${stats.filesZstdUnsupported} compressed rollout${stats.filesZstdUnsupported === 1 ? \"\" : \"s\"} need Node 22.15 or newer`,\n\t\t);\n\t}\n\tif (stats.filesForeign > 0) {\n\t\tconst origins = [...stats.foreignOriginators]\n\t\t\t.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n\t\t\t.map(([name, n]) => (n > 1 ? `${name} ×${n}` : name))\n\t\t\t.join(\", \");\n\t\tout.push(\n\t\t\t`skipped ${stats.filesForeign} file${stats.filesForeign === 1 ? \"\" : \"s\"} not written by ${label} (originators: ${origins})`,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * One harness's payload block: window, activity, cost, models, inventory.\n *\n * ONE ALIGNED BLOCK, NO EMPTY HEADINGS (#217). Every row hangs off the same\n * label column, and a section with nothing in it is not announced: a bare\n * `models` heading over nothing said only that the code has a models section.\n * A harness that publishes no names says THAT, in one line, because silence\n * there would read as a harness that was never scanned.\n */\nfunction payloadBlock(\n\tpayload: MeasuredPayload,\n\twidth: number,\n\townWindow: boolean,\n\tstats?: ScanStats,\n): string[] {\n\tconst out: string[] = [];\n\t// The header is unconditional (#130): the `searched` line above names four\n\t// harnesses, so an unlabeled block would be unreadable even when only one\n\t// harness was found. It also CARRIES the activity and the cost, which each\n\t// held a line of their own until #217 - three lines saying one harness's\n\t// totals, repeated per harness, was most of a preview nobody read.\n\tconst label = `${harnessLabel(payload.harness.name)}${payload.harness.version ? ` ${payload.harness.version}` : \"\"}`;\n\tconst days = payload.activity.activeDayDates.length;\n\tconst usd = totalUSD(payload);\n\tconst breakdown = tokenBreakdown(payload);\n\tconst totals = [\n\t\t`${payload.activity.sessions} session${payload.activity.sessions === 1 ? \"\" : \"s\"}`,\n\t\t`${days} active day${days === 1 ? \"\" : \"s\"}`,\n\t\t`${fmtTokens(payload.activity.totalTokens)} tokens processed`,\n\t\t...(breakdown\n\t\t\t? [\n\t\t\t\t\t`${fmtTokens(breakdown.fresh)} fresh`,\n\t\t\t\t\t`${fmtTokens(breakdown.cached)} cached`,\n\t\t\t\t]\n\t\t\t: []),\n\t];\n\t// Wrapped, because the merged header is the longest line in the block and a\n\t// narrow terminal would otherwise break it mid-figure.\n\t// The header is a SECTION, the way `collect` prints one: the harness name in\n\t// caps and its session count. The totals hang under it as the `usage` row,\n\t// with the cost at the end of that row. `at API prices` stays: it is the\n\t// qualifier that makes the figure a lower bound rather than a bill (#93).\n\tout.push(`${label.toUpperCase()} ${payload.activity.sessions}`);\n\n\t// A harness that measured nothing says so in its usage row and stops - not\n\t// even a cost, because there is nothing to price. It still gets its header,\n\t// because a scanned harness reading as an absent one is the mistake #130\n\t// fixed.\n\tif (payload.activity.totalTokens === 0) {\n\t\tout.push(`usage ${totals.slice(1).join(\" · \")}`);\n\t\treturn out;\n\t}\n\tout.push(\n\t\t...wrapRow(\n\t\t\t\"usage \",\n\t\t\t\" \".repeat(LABEL_WIDTH),\n\t\t\t`${totals.slice(1).join(\" · \")} · ${usd === null ? \"cost not published\" : `${fmtUSD(usd)} at API prices`}`,\n\t\t\twidth,\n\t\t),\n\t);\n\n\t// Only when this harness read a different window from the rest.\n\tif (ownWindow) {\n\t\tout.push(\n\t\t\t`window ${payload.window.days} days · ${payload.window.from} → ${payload.window.to}`,\n\t\t);\n\t}\n\n\t// Coverage is silent when clean; a degraded scan is named as a floor (#40).\n\tconst cov = payload.coverage;\n\tif (cov.filesUnreadable > 0 || cov.linesFailed > 0) {\n\t\tout.push(\n\t\t\t`coverage ${cov.filesUnreadable} files unreadable · ${cov.linesFailed} lines failed · this reading is a floor`,\n\t\t);\n\t}\n\t// Local-only detail behind those counts (#75): file names, error classes,\n\t// and the foreign-file line. Printed, never sent.\n\tif (stats) {\n\t\tout.push(...scanNoteLines(stats, harnessLabel(payload.harness.name)));\n\t}\n\n\t// The models are one row, wrapped: `id share ≈$` per model, joined with\n\t// dots. A harness that reports no model prints nothing.\n\t//\n\t// A MODEL UNDER ONE PERCENT ROLLS UP. Four entries where two carry 99.9% of\n\t// the tokens is a row that hides its own headline. The rolled figure keeps\n\t// its dollars only when every model in it published one, the same rule a\n\t// single entry follows: a sum missing a term would understate without\n\t// saying so.\n\tconst shown = payload.models.filter((m) => m.tokenShare >= MODEL_ROLLUP);\n\tconst rolled = payload.models.filter((m) => m.tokenShare < MODEL_ROLLUP);\n\tconst entry = (name: string, share: number, dollars: number | undefined) =>\n\t\t`${name} ${fmtPct(share)}${usd !== null && dollars !== undefined ? ` ${fmtUSD(dollars)}` : \"\"}`;\n\tconst entries = shown.map((m) =>\n\t\tentry(m.id, m.tokenShare, m.apiEquivalentUSD),\n\t);\n\tif (rolled.length > 0) {\n\t\tconst priced = rolled.every((m) => m.apiEquivalentUSD !== undefined);\n\t\tentries.push(\n\t\t\tentry(\n\t\t\t\t`+${rolled.length} more`,\n\t\t\t\trolled.reduce((a, m) => a + m.tokenShare, 0),\n\t\t\t\tpriced\n\t\t\t\t\t? rolled.reduce((a, m) => a + (m.apiEquivalentUSD ?? 0), 0)\n\t\t\t\t\t: undefined,\n\t\t\t),\n\t\t);\n\t}\n\tif (entries.length > 0) {\n\t\tout.push(\n\t\t\t...wrapEntries(\"models \", \" \".repeat(LABEL_WIDTH), entries, width),\n\t\t);\n\t}\n\n\t// The inventory. The counts line is the glance, the rows underneath are the\n\t// consent: every name that publishes is printed.\n\tconst filled = NAME_CATEGORIES.filter(\n\t\t(category) => payload.inventory[category].length > 0,\n\t);\n\tif (filled.length === 0) {\n\t\tout.push(\n\t\t\t`${\"sends\".padEnd(LABEL_WIDTH)}no inventory names from this harness`,\n\t\t);\n\t\treturn out;\n\t}\n\tout.push(\n\t\t`${\"sends\".padEnd(LABEL_WIDTH)}${filled\n\t\t\t.map(\n\t\t\t\t(category) =>\n\t\t\t\t\t`${payload.inventory[category].length} ${CATEGORY_LABEL[category]}`,\n\t\t\t)\n\t\t\t.join(\" · \")}`,\n\t);\n\t// The names indent under their own category label, so a wrapped row and the\n\t// row above it start in the same column. `commands` is the longest label and\n\t// still needs a gap after it, which is why the width is its length plus one.\n\tconst subLabel = Math.max(\n\t\t...filled.map((category) => CATEGORY_LABEL[category].length),\n\t);\n\tconst subIndent = \" \".repeat(2 + subLabel + 1);\n\tfor (const category of filled) {\n\t\tconst names = payload.inventory[category].map((a) => a.name).join(\", \");\n\t\tout.push(\n\t\t\t...wrapRow(\n\t\t\t\t` ${CATEGORY_LABEL[category].padEnd(subLabel)} `,\n\t\t\t\tsubIndent,\n\t\t\t\tnames,\n\t\t\t\twidth,\n\t\t\t),\n\t\t);\n\t}\n\treturn out;\n}\n\n/** The rule between sections, the width `collect` draws it at. */\nconst DIVIDER = \"─\".repeat(40);\n\n/** Token share below which a model joins the rolled-up row (#217). */\nconst MODEL_ROLLUP = 0.01;\n\nconst PHASE_ORDER = [\"scout\", \"build\", \"verify\", \"handoff\", \"unknown\"] as const;\n\n/**\n * The workflow section, as the gate describes it (#213).\n *\n * Everything here is read out of `body.workflow` - the exact bytes a publish\n * sends - for the reason the whole file exists: the person approves a sentence\n * about the bytes, not a sentence about what the code meant to send.\n *\n * The last line names the switch, the way the kept-private block does. A\n * default-on opt-out has to be visible before the first upload, or it is not an\n * opt-out.\n */\nfunction workflowBlock(\n\tworkflowDays: NonNullable<MeasuredDay[\"workflow\"]>[],\n\tutcOffsetMinutes: number,\n\thost: string,\n): string[] {\n\tconst out: string[] = [];\n\tconst folded = foldWorkflowDays(workflowDays, {\n\t\taggregateVersion: WORKFLOW_AGGREGATES_V3,\n\t\tutcOffsetMinutes,\n\t});\n\tconst harnesses = folded?.harnesses ?? [];\n\tconst withPlaybook = harnesses.filter((h) => h.phase);\n\tconst sessions = harnesses.reduce((a, h) => a + h.sessions, 0);\n\tconst ruleVersions = [\n\t\t...new Set(withPlaybook.map((h) => h.phase?.ruleVersion ?? \"\")),\n\t].filter(Boolean);\n\n\tout.push(\n\t\t`workflow ${harnesses.length} harness${harnesses.length === 1 ? \"\" : \"es\"} · ${sessions} sessions · ${WORKFLOW_AGGREGATES_V3}`,\n\t);\n\tconst first = folded?.dates[0];\n\tconst last = folded?.dates.at(-1);\n\tout.push(\n\t\t` ${workflowDays.length} day${workflowDays.length === 1 ? \"\" : \"s\"}${first && last ? ` · ${first} to ${last}` : \"\"}`,\n\t);\n\n\tconst seconds = PHASE_ORDER.map((phase) =>\n\t\twithPlaybook.reduce((a, h) => a + (h.phase?.phaseSec[phase] ?? 0), 0),\n\t);\n\tconst total = seconds.reduce((a, b) => a + b, 0);\n\tif (total > 0) {\n\t\tconst mix = PHASE_ORDER.map(\n\t\t\t(phase, i) => `${phase} ${fmtPct((seconds[i] ?? 0) / total)}`,\n\t\t).join(\" · \");\n\t\tout.push(` ${mix} · ${ruleVersions.join(\", \")}`);\n\t}\n\n\tconst git = folded?.git;\n\tout.push(\n\t\t`git ${git?.commits ?? 0} commits · ${fmtTokens((git?.additions ?? 0) + (git?.removals ?? 0))} lines changed`,\n\t);\n\t// The kept-private block points at a control the owner can click, because\n\t// #48 shipped one. This line NAMES the switch and stops there: the owner\n\t// control is #215's, and directions to a control that does not exist yet\n\t// would be the one false sentence in a preview built to be exact. Extend\n\t// this line with the location when #215 lands it.\n\tout.push(` (Publish workflow is on for ${host})`);\n\treturn out;\n}\n\n/**\n * The day counts (#307): \"31 days to publish, 369 unchanged\" against a\n * manifest, \"400 days to publish\" on a fresh machine or an old server.\n */\nexport function daysLine(\n\tmeasuredDays: PayloadMeasuredDays,\n\tselection?: DaySelection,\n): string {\n\tconst n = measuredDays.days.length;\n\tconst head = `${n} day${n === 1 ? \"\" : \"s\"} to publish`;\n\tconst unchanged = selection?.unchanged ?? 0;\n\treturn unchanged > 0 ? `${head}, ${unchanged} unchanged` : head;\n}\n\nfunction daysBlock(\n\tmeasuredDays: PayloadMeasuredDays,\n\tselection?: DaySelection,\n): string[] {\n\tconst out = [`days ${daysLine(measuredDays, selection)}`];\n\tconst first = measuredDays.days[0]?.date;\n\tconst last = measuredDays.days.at(-1)?.date;\n\tconst usageDays = measuredDays.days.filter((d) => d.usage).length;\n\tif (first && last) {\n\t\tout.push(\n\t\t\t` ${first} to ${last} · ${usageDays} with usage · ${measuredDays.aggregateVersion}`,\n\t\t);\n\t}\n\treturn out;\n}\n\nexport function buildGateSummary(ctx: GateContext): string {\n\tconst { body, keptPrivate, config, source, baseUrl } = ctx;\n\tconst { payloads } = body;\n\tconst host = baseUrl.replace(/^https?:\\/\\//, \"\");\n\tconst out: string[] = [];\n\n\tif (config.stack === null) {\n\t\tout.push(\"to (no linked stack; publish is unavailable)\");\n\t} else {\n\t\tout.push(\n\t\t\t`to ${config.stack.name} · ${host}/stacks/${config.stack.slug}`,\n\t\t);\n\t}\n\n\t// What the CLI LOOKED FOR, in search order - a claim about the CLI, never\n\t// about the person's behavior, so it stays inside #40 (#130). Without it, a\n\t// harness the scan misses reads identically to a harness never installed.\n\t// The client version rides here because it travels (#213) and because one\n\t// fact about the CLI does not earn a line of its own.\n\tout.push(\n\t\t`searched ${HARNESS_ADAPTERS.map((a) => harnessLabel(a.name).toLowerCase()).join(\", \")}`,\n\t);\n\n\t// THE WINDOW IS THE SYNC'S, NOT EACH HARNESS'S (#217). Every payload carries\n\t// the same one, so printing it per harness said the same sentence three\n\t// times. A harness that somehow read a different window keeps its own line\n\t// inside its block rather than being silently folded into this one.\n\tconst windows = new Set(\n\t\tpayloads.map(\n\t\t\t(p) => `${p.window.days} days · ${p.window.from} → ${p.window.to}`,\n\t\t),\n\t);\n\tif (windows.size === 1) {\n\t\tout.push(\n\t\t\t`window ${[...windows][0]}${body.cliVersion ? ` · aistack ${body.cliVersion}` : \"\"}`,\n\t\t);\n\t}\n\n\t// THE TABLE BEHIND THE DOLLARS (#336). Printed only when a figure is on its\n\t// way up: a cost-off stage has nothing the id would cite. The sources are\n\t// the per-model citations the payload carries, so what the reader sees is\n\t// what the server will see.\n\tconst cited = [\n\t\t...new Set(\n\t\t\tpayloads.flatMap((p) =>\n\t\t\t\tp.models.flatMap((m) => (m.pricingTable ? [m.pricingTable] : [])),\n\t\t\t),\n\t\t),\n\t];\n\tif (ctx.prices && cited.length > 0) {\n\t\tconst origin =\n\t\t\tctx.prices.origin === \"served\"\n\t\t\t\t? `${ctx.prices.id} from ${host}`\n\t\t\t\t: `${ctx.prices.id} (bundled; the server table was unavailable)`;\n\t\tout.push(`prices ${origin} · cites ${cited.join(\", \")}`);\n\t}\n\n\t// One block per detected harness, each under its own header.\n\tconst width = wrapWidth(ctx.width);\n\tout.push(\n\t\t...wrapRow(\n\t\t\t\"privacy \",\n\t\t\t\" \".repeat(LABEL_WIDTH),\n\t\t\t\"raw conversation text, paths, repo names, and command arguments stay local\",\n\t\t\twidth,\n\t\t),\n\t);\n\tfor (const payload of payloads) {\n\t\tconst stats = ctx.scanStats?.[payload.harness.name];\n\t\tout.push(\"\", DIVIDER, \"\");\n\t\tout.push(...payloadBlock(payload, width, windows.size > 1, stats));\n\t}\n\n\t// Everything that is not a harness: the day rows, the workflow, git, and the\n\t// kept-private count, under one section.\n\tout.push(\"\", DIVIDER, \"\", \"ALSO PUBLISHING\");\n\n\t// The day rows (#307): how many go and how many the server already holds.\n\t// The counts are the sync's one plain sentence about diff-only publishing.\n\tif (body.measuredDays) {\n\t\tout.push(...daysBlock(body.measuredDays, ctx.days));\n\t}\n\n\t// In the bytes, so it is in the preview (#78's rule, applied to #213). Off\n\t// prints as plainly as `cost not published` does, and for the same reason: a\n\t// section the owner declined is a fact about this send, not an absence.\n\tconst workflowDays = (body.measuredDays?.days ?? []).flatMap((d) =>\n\t\td.workflow ? [d.workflow] : [],\n\t);\n\tif (!config.publishWorkflow || !body.measuredDays) {\n\t\tout.push(\"workflow not published\");\n\t} else if (workflowDays.length === 0) {\n\t\tout.push(\"workflow on, no changed day to publish\");\n\t} else {\n\t\tout.push(\n\t\t\t...workflowBlock(workflowDays, body.measuredDays.utcOffsetMinutes, host),\n\t\t);\n\t}\n\n\t// Kept private is ONE ROW: the count, the first few names, and the rest as\n\t// a count. Truncating is safe here and unsafe for the inventory above: these\n\t// names do NOT leave the machine (#217). The switch is still named before\n\t// the first upload (#48), on the row under it.\n\tconst n = payloads.reduce((a, p) => a + withheldCount(p), 0);\n\tif (n > 0) {\n\t\tconst rows = keptPrivateRows(keptPrivate);\n\t\tconst shown = rows.slice(0, KEPT_PRIVATE_ROWS_SHOWN);\n\t\tconst examples = shown\n\t\t\t.map((r) => (r.names > 1 ? `${r.label} ×${r.names}` : r.label))\n\t\t\t.join(\", \");\n\t\tconst more =\n\t\t\trows.length > shown.length\n\t\t\t\t? `, ...${rows.length - shown.length} more`\n\t\t\t\t: \"\";\n\t\tout.push(\n\t\t\t`private ${n} review name${n === 1 ? \"\" : \"s\"} · ${examples}${more}`,\n\t\t);\n\t\tif (body.keptPrivate !== undefined && config.stack !== null) {\n\t\t\tout.push(\n\t\t\t\t` stored privately for your review at ${host}/stacks/${config.stack.slug}/changes`,\n\t\t\t);\n\t\t\tout.push(\n\t\t\t\t\" (turn off: Review kept-private names, on your stack)\",\n\t\t\t);\n\t\t} else {\n\t\t\tout.push(\" they stay on this machine\");\n\t\t}\n\t}\n\n\t// Named at the gate because it is in the bytes (#78). It is not measurement\n\t// and not a name, but the rule is that the preview describes what goes, so a\n\t// field nobody can see in the preview does not get to ride along.\n\tif (body.autoSync !== undefined) {\n\t\tout.push(\n\t\t\t`auto-sync ${body.autoSync.enabled ? `on, about every ${body.autoSync.frequencyHours}h` : \"off\"}`,\n\t\t);\n\t}\n\n\tif (source === \"bundled\") {\n\t\tout.push(\"\");\n\t\tout.push(\n\t\t\t\"! could not fetch your settings from aistack - using the bundled list.\",\n\t\t);\n\t\tout.push(\n\t\t\t\" This publishes less: no cost, no ticked names, nothing staged for review.\",\n\t\t);\n\t}\n\n\treturn out.join(\"\\n\");\n}\n","// The floor is set by `node:sqlite`, which the opencode adapter needs.\n// `node:sqlite` landed in 22.5.0 but stayed behind `--experimental-sqlite`\n// until 22.13.0 (and, on the 23 line, until 23.4.0): on the flagged versions\n// the import throws and opencode silently drops out of detection while the\n// file-based adapters keep working. So the gate refuses every flagged\n// version, not just the ones below 22.5.\nexport const MINIMUM_NODE_VERSION = \"22.13.0\";\n\nfunction versionParts(version: string): [number, number, number] | null {\n\tconst match = /^(?:v)?(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version);\n\tif (!match) return null;\n\treturn [Number(match[1]), Number(match[2]), Number(match[3])];\n}\n\nfunction atLeast(actual: [number, number, number], floor: string): boolean {\n\tconst minimum = versionParts(floor);\n\tif (!minimum) return false;\n\tfor (let i = 0; i < actual.length; i++) {\n\t\tif (actual[i] !== minimum[i])\n\t\t\treturn (actual[i] as number) > (minimum[i] as number);\n\t}\n\treturn true;\n}\n\nexport function supportsNodeVersion(version: string): boolean {\n\tconst actual = versionParts(version);\n\tif (!actual) return false;\n\tif (!atLeast(actual, MINIMUM_NODE_VERSION)) return false;\n\t// 23.0 to 23.3 still flag node:sqlite even though they sort above 22.13.\n\tif (actual[0] === 23) return atLeast(actual, \"23.4.0\");\n\treturn true;\n}\n\nexport function unsupportedNodeMessage(version: string): string {\n\treturn `aistack requires Node.js ${MINIMUM_NODE_VERSION} or newer (23.x needs 23.4.0). You are running ${version}. Upgrade Node.js so sync can read OpenCode's SQLite usage database.`;\n}\n","// The local stdio MCP server - the send channel picked by the spike #35.\n//\n// Wayfinder ticket #41 (map #29). Two tools, two beats:\n//\n// sync_preview - scans locally, stages the exact send bytes, returns the\n// full summary as ordinary transcript output (beat one).\n// sync_publish - takes the stage id, raises a SHORT `elicitation/create`\n// with an ENUM field (beat two), and sends only on\n// `decision: \"publish\"`.\n//\n// Why elicitation and not `requiresUserInteraction`: the spike showed the\n// permission dialog can be silenced forever with one click and writes a grant\n// broader than the sentence shown, while an elicitation is raised INSIDE the\n// call - there is no string a model can spell to route around it, and no\n// \"don't ask again\" exists for it. The enum widget is the working one; the\n// boolean widget is dead in 2.1.220 and must never ship.\n//\n// Fail-closed, by construction: ESC, a timeout, a headless auto-cancel, an\n// error reply, or a client that never declared the elicitation capability all\n// resolve to \"nothing was sent\". The model's arguments count for nothing -\n// the only path to a send runs through the user's own keystrokes.\n//\n// Hand-rolled JSON-RPC over stdio, zero dependencies, structured so tests can\n// drive `handle()` directly and capture every outbound frame.\n\nimport { type SyncPublishResult, syncPublish } from \"../api.js\";\nimport { type StageDeps, type StagedSend, stageSync } from \"./stage.js\";\nimport { fmtReceivedAt } from \"./summary.js\";\n\nconst SERVER_NAME = \"aistack\";\nconst SERVER_VERSION = \"0.3.0\";\n\n/** How long a staged preview stays publishable. Stale bytes must re-preview. */\nexport const STAGE_TTL_MS = 10 * 60 * 1000;\n\n/**\n * How long the gate waits for the human. Deliberately WELL past the harness's\n * own 120 s tool timeout (#35, 1H measured 92 s for a one-line answer): the\n * server must never be the first to give up. On expiry it resolves as cancel.\n */\nexport const ELICIT_TIMEOUT_MS = 10 * 60 * 1000;\n\nconst PREVIEW_TOOL = {\n\tname: \"sync_preview\",\n\tdescription:\n\t\t\"Scan local agent transcripts (Claude Code, Codex) and stage a measured-usage snapshot for aistack. \" +\n\t\t\"Returns the full preview of exactly what would publish. \" +\n\t\t\"Show the returned text to the user VERBATIM - it is the review surface. Nothing is sent.\",\n\tinputSchema: { type: \"object\", properties: {} },\n\tannotations: {\n\t\ttitle: \"aistack - preview sync (sends nothing)\",\n\t\treadOnlyHint: true,\n\t\topenWorldHint: true,\n\t},\n};\n\nconst PUBLISH_TOOL = {\n\tname: \"sync_publish\",\n\tdescription:\n\t\t\"Publish the staged aistack snapshot named by preview_id. \" +\n\t\t\"Asks the user for confirmation during the call; only their explicit choice sends anything. \" +\n\t\t\"Call sync_preview first and show its output.\",\n\tinputSchema: {\n\t\ttype: \"object\",\n\t\tproperties: {\n\t\t\tpreview_id: {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescription: \"The `preview id` line from sync_preview's output.\",\n\t\t\t},\n\t\t},\n\t\trequired: [\"preview_id\"],\n\t},\n\tannotations: {\n\t\ttitle: \"aistack - publish measured usage (asks the user first)\",\n\t\tdestructiveHint: false,\n\t\topenWorldHint: true,\n\t},\n};\n\ntype JsonRpcMessage = {\n\tjsonrpc?: string;\n\tid?: string | number;\n\tmethod?: string;\n\tparams?: Record<string, unknown> | undefined;\n\tresult?: unknown;\n\terror?: unknown;\n};\n\nexport type SyncServerDeps = {\n\tbaseUrl: string;\n\tstageImpl?: (deps: StageDeps) => Promise<StagedSend>;\n\tpublishImpl?: (token: string, bodyJson: string) => Promise<SyncPublishResult>;\n\tnow?: () => number;\n\telicitTimeoutMs?: number;\n\t/** Diagnostics only. NEVER stdout - that would corrupt the protocol. */\n\tlog?: (line: string) => void;\n};\n\nexport type SyncServer = {\n\thandle: (msg: JsonRpcMessage) => void;\n\t/** Test seam: the staged send, if any. */\n\tstaged: () => StagedSend | null;\n};\n\nconst textResult = (text: string, isError = false) => ({\n\tcontent: [{ type: \"text\", text }],\n\t...(isError ? { isError: true } : {}),\n});\n\nexport function createSyncServer(\n\tdeps: SyncServerDeps,\n\tsend: (msg: JsonRpcMessage) => void,\n): SyncServer {\n\tconst now = deps.now ?? Date.now;\n\tconst stage = deps.stageImpl ?? stageSync;\n\tconst publish = deps.publishImpl ?? syncPublish;\n\tconst log = deps.log ?? (() => {});\n\tconst elicitTimeoutMs = deps.elicitTimeoutMs ?? ELICIT_TIMEOUT_MS;\n\n\tlet clientSupportsElicitation = false;\n\tlet staged: StagedSend | null = null;\n\tlet nextRequestId = 1;\n\tconst pending = new Map<string, (reply: JsonRpcMessage | null) => void>();\n\n\tconst ok = (id: string | number | undefined, result: unknown) =>\n\t\tsend({ jsonrpc: \"2.0\", id, result });\n\tconst err = (\n\t\tid: string | number | undefined,\n\t\tcode: number,\n\t\tmessage: string,\n\t) => send({ jsonrpc: \"2.0\", id, error: { code, message } });\n\n\t/** Ask the client something; `null` reply means the gate timed out. */\n\tconst request = (\n\t\tmethod: string,\n\t\tparams: Record<string, unknown>,\n\t\tonReply: (reply: JsonRpcMessage | null) => void,\n\t) => {\n\t\tconst id = `aistack-${nextRequestId++}`;\n\t\tpending.set(id, onReply);\n\t\tsend({ jsonrpc: \"2.0\", id, method, params });\n\t\tconst timer = setTimeout(() => {\n\t\t\tif (pending.delete(id)) onReply(null);\n\t\t}, elicitTimeoutMs);\n\t\t(timer as { unref?: () => void }).unref?.();\n\t};\n\n\tconst runPreview = async (id: string | number | undefined) => {\n\t\ttry {\n\t\t\tstaged = await stage({ baseUrl: deps.baseUrl, now });\n\t\t} catch (e) {\n\t\t\tstaged = null;\n\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\treturn ok(id, textResult(`Preview failed: ${message}`, true));\n\t\t}\n\t\tconst lines = [staged.summary, \"\"];\n\t\tif (staged.blockedReason === null) {\n\t\t\tlines.push(`preview id: ${staged.id}`);\n\t\t\tlines.push(\n\t\t\t\t\"To publish, call sync_publish with this preview id. The user confirms in a dialog during that call.\",\n\t\t\t);\n\t\t} else {\n\t\t\tlines.push(`publish unavailable: ${staged.blockedReason}`);\n\t\t}\n\t\treturn ok(id, textResult(lines.join(\"\\n\")));\n\t};\n\n\tconst runPublish = (\n\t\tid: string | number | undefined,\n\t\targs: Record<string, unknown> | undefined,\n\t) => {\n\t\t// Every refusal below is fail-closed: no dialog was shown, nothing sent.\n\t\tif (!clientSupportsElicitation) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: this Claude Code version did not declare the elicitation capability, \" +\n\t\t\t\t\t\t\"so the approve dialog cannot be shown. The gate never degrades silently - \" +\n\t\t\t\t\t\t\"update Claude Code and try again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tconst previewId = args?.preview_id;\n\t\tif (staged === null) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: nothing is staged. Run sync_preview first and show its output to the user.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (typeof previewId !== \"string\" || previewId !== staged.id) {\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: preview_id does not match the staged preview. Run sync_preview again.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (staged.blockedReason !== null) {\n\t\t\treturn ok(id, textResult(`Not published: ${staged.blockedReason}`, true));\n\t\t}\n\t\tif (now() - staged.stagedAt > STAGE_TTL_MS) {\n\t\t\tstaged = null;\n\t\t\treturn ok(\n\t\t\t\tid,\n\t\t\t\ttextResult(\n\t\t\t\t\t\"Not published: the staged preview is older than 10 minutes. Run sync_preview again so the user reviews current bytes.\",\n\t\t\t\t\ttrue,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tconst approvedStage = staged;\n\t\tlog(`elicitation raised for stage ${approvedStage.id}`);\n\t\trequest(\n\t\t\t\"elicitation/create\",\n\t\t\t{\n\t\t\t\tmessage: approvedStage.dialog,\n\t\t\t\trequestedSchema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tdecision: {\n\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t// The enum widget is the one that works (#35, 1G). Never a boolean.\n\t\t\t\t\t\t\tenum: [\"publish\", \"cancel\"],\n\t\t\t\t\t\t\tdescription: \"Publish the snapshot described above?\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"decision\"],\n\t\t\t\t},\n\t\t\t},\n\t\t\t(reply) => {\n\t\t\t\tconst result = reply?.result as\n\t\t\t\t\t| { action?: string; content?: { decision?: string } }\n\t\t\t\t\t| undefined;\n\t\t\t\tconst approved =\n\t\t\t\t\tresult?.action === \"accept\" &&\n\t\t\t\t\tresult?.content?.decision === \"publish\";\n\t\t\t\tif (!approved) {\n\t\t\t\t\tconst outcome =\n\t\t\t\t\t\treply === null ? \"timed out\" : (result?.action ?? \"error\");\n\t\t\t\t\tlog(`elicitation resolved without consent: ${outcome}`);\n\t\t\t\t\treturn ok(\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttextResult(\n\t\t\t\t\t\t\t`Not published: the confirmation was not accepted (${outcome}). Nothing left this machine.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlog(`consent received, sending stage ${approvedStage.id}`);\n\t\t\t\tpublish(approvedStage.token as string, approvedStage.bodyJson).then(\n\t\t\t\t\t(res) => {\n\t\t\t\t\t\tapprovedStage.acknowledgePublish?.();\n\t\t\t\t\t\tif (staged?.id === approvedStage.id) staged = null;\n\t\t\t\t\t\t// Same ending as the terminal channel (#130): the result last,\n\t\t\t\t\t\t// the stamp in human form.\n\t\t\t\t\t\tconst lines = [\n\t\t\t\t\t\t\t`Published. Snapshot received ${fmtReceivedAt(res.receivedAt)}.`,\n\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\"Your stack now shows what actually ran:\",\n\t\t\t\t\t\t\tres.url,\n\t\t\t\t\t\t];\n\t\t\t\t\t\tconst kp = approvedStage.body.keptPrivate;\n\t\t\t\t\t\tif (res.keptPrivate.refused && kp !== undefined) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\"Note: the server refused the kept-private names because its review switch is off. They stayed on this machine.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (res.keptPrivate.stored > 0) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t`${res.keptPrivate.stored} private review name${res.keptPrivate.stored === 1 ? \"\" : \"s\"} stored at ${res.url}/changes`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (res.keptPrivate.machineStored > 0) {\n\t\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t\t\"This machine's private label was stored for the same review.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tok(id, textResult(lines.join(\"\\n\")));\n\t\t\t\t\t},\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tconst message = e instanceof Error ? e.message : String(e);\n\t\t\t\t\t\tok(\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\ttextResult(`Publish failed after consent: ${message}`, true),\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t};\n\n\tconst handle = (msg: JsonRpcMessage) => {\n\t\tconst { id, method, params } = msg;\n\n\t\t// A reply to something we asked, not a new request.\n\t\tif (method === undefined && id !== undefined && pending.has(String(id))) {\n\t\t\tconst onReply = pending.get(String(id));\n\t\t\tpending.delete(String(id));\n\t\t\tonReply?.(msg);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (method) {\n\t\t\tcase \"initialize\": {\n\t\t\t\tconst capabilities =\n\t\t\t\t\t(params?.capabilities as Record<string, unknown> | undefined) ?? {};\n\t\t\t\tclientSupportsElicitation = \"elicitation\" in capabilities;\n\t\t\t\tlog(\n\t\t\t\t\t`initialize: elicitation ${clientSupportsElicitation ? \"declared\" : \"ABSENT\"}`,\n\t\t\t\t);\n\t\t\t\treturn ok(id, {\n\t\t\t\t\tprotocolVersion:\n\t\t\t\t\t\t(params?.protocolVersion as string | undefined) ?? \"2025-06-18\",\n\t\t\t\t\tcapabilities: { tools: { listChanged: false } },\n\t\t\t\t\tserverInfo: { name: SERVER_NAME, version: SERVER_VERSION },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"ping\":\n\t\t\t\treturn ok(id, {});\n\n\t\t\tcase \"tools/list\":\n\t\t\t\treturn ok(id, { tools: [PREVIEW_TOOL, PUBLISH_TOOL] });\n\n\t\t\tcase \"tools/call\": {\n\t\t\t\tconst name = params?.name;\n\t\t\t\tconst args = params?.arguments as Record<string, unknown> | undefined;\n\t\t\t\tif (name === \"sync_preview\") return void runPreview(id);\n\t\t\t\tif (name === \"sync_publish\") return runPublish(id, args);\n\t\t\t\treturn err(id, -32602, `Unknown tool: ${String(name)}`);\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tif (method?.startsWith(\"notifications/\")) return;\n\t\t\t\tif (method !== undefined)\n\t\t\t\t\treturn err(id, -32601, `Method not found: ${method}`);\n\t\t}\n\t};\n\n\treturn { handle, staged: () => staged };\n}\n\n/** Wire the server to real stdio. Never returns; the harness owns the process. */\nexport function runStdioSyncServer(deps: SyncServerDeps): void {\n\tconst server = createSyncServer(deps, (msg) => {\n\t\tprocess.stdout.write(`${JSON.stringify(msg)}\\n`);\n\t});\n\tlet buffer = \"\";\n\tprocess.stdin.setEncoding(\"utf8\");\n\tprocess.stdin.on(\"data\", (chunk: string) => {\n\t\tbuffer += chunk;\n\t\tlet nl = buffer.indexOf(\"\\n\");\n\t\twhile (nl !== -1) {\n\t\t\tconst line = buffer.slice(0, nl).trim();\n\t\t\tbuffer = buffer.slice(nl + 1);\n\t\t\tif (line) {\n\t\t\t\ttry {\n\t\t\t\t\tserver.handle(JSON.parse(line));\n\t\t\t\t} catch (e) {\n\t\t\t\t\tdeps.log?.(`parse error: ${String(e)}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnl = buffer.indexOf(\"\\n\");\n\t\t}\n\t});\n}\n"],"mappings":"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","names":["p","p","p","path","lines","p","key","dirname","dirname","dirname","key","path","existsSync","readFileSync","homedir","join","path","existsSync","readFileSync","homedir","join","path","parse","readJson","existsSync","readFileSync","homedir","join","path","key","p","readJson","existsSync","readFileSync","join","homedir","existsSync","readdirSync","readFileSync","homedir","join","stat","count","intro","outro","intro","outro","key","existsSync","homedir","dirname","join","p","counts","p","count","key","counts","counts","p","count","key","bump","key","utcDateOf","count","active","createAggregate","timestamp","counts","modelKeyFor","count","readdir","stat","homedir","path","path","homedir","basename","readdir","stat","p","createAggregate","createAggregate","counts","timestamp","readFileSync","readdir","realpath","stat","homedir","path","parseToml","path","homedir","basename","readdir","scan","exists","realpath","stat","p","ingestFile","readFile","readFileSync","parseToml","createAggregate","scan","homedir","path","stat","homedir","path","homedir","platform","path","exists","stat","key","value","path","homedir","key","path","path","path","scan","path","counts","scan","createAggregate","bump","key","update","ingestEvent","createReadStream","readdir","readFile","stat","homedir","path","readline","parseToml","path","homedir","stat","lines","readline","createReadStream","readFile","readdir","parseToml","scan","read","ingestEvent","counts","createAggregate","scan","createAggregate","counts","noteConfiguredMcpServers","readFileSync","readdir","stat","homedir","path","path","homedir","basename","readdir","errorClass","readError","scan","open","readConfiguredMcpServers","readFileSync","noteConfiguredMcpServers","exists","p","stat","createAggregate","scan","createAggregate","createFileState","noteActivity","counts","readCounts","key","createReadStream","readdir","realpath","stat","homedir","path","readline","path","homedir","sessionRoots","basename","readdir","scan","exists","realpath","stat","ingestFile","p","readline","createReadStream","createFileState","sessionRoots","createAggregate","scan","harnessLabel","join","homedir","dirname","existsSync","intro","outro","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join","p","intro","join","existsSync","readFileSync","confirm","dirname","mkdirSync","writeFileSync","outro","p","read","intro","outro","p","createHash","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","codexHome","read","canonicalJson","key","p","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","platform","dirname","join","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","dirname","join","isOurs","read","join","homedir","platform","existsSync","readFileSync","isOurs","mkdirSync","dirname","writeFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","platform","dirname","join","isOurs","read","key","failures","harnessLabel","mkdirSync","readFileSync","unlinkSync","writeFileSync","homedir","dirname","join","createHash","execFileSync","path","basename","lines","stat","key","createHash","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","path","read","p","lines","line","harnessLabel","p","utcDate","createHash","active","workflow","join","homedir","mkdirSync","dirname","lines","readFileSync","writeFileSync","unlinkSync","failure","intro","outro","lines","section","log","request","lines"]}
|