@titan-design/session-analytics 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,7 +21,9 @@ priceRequest(
|
|
|
21
21
|
## What it exports
|
|
22
22
|
|
|
23
23
|
- `PRICE_TABLE`, `PRICE_TABLE_VERSION`, `findPrice(model, ts, prices?)` — USD per million
|
|
24
|
-
tokens by longest model prefix, then the latest row effective at `ts`.
|
|
24
|
+
tokens by longest model prefix, then the latest row effective at `ts`. A prefix matches only
|
|
25
|
+
at a model-id boundary (the id, a `-YYYYMMDD` date, or a `[..]` suffix), so `claude-opus-5`
|
|
26
|
+
never prices `claude-opus-5-5`.
|
|
25
27
|
- `priceRequest(tokens, model, ts, prices?)` — the five cost components, `costUsd` and
|
|
26
28
|
`priced`.
|
|
27
29
|
- `classifySession(facts)` — `agent_spawned`, `human_interactive`, `headless_sdk` or
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { EpisodeRow, SessionGraph } from '@titan-design/session-graph';
|
|
|
4
4
|
|
|
5
5
|
/** USD per million tokens. Fitted against 392 `cost-state` rows in the 2026-09-20 audit. */
|
|
6
6
|
interface PriceRow {
|
|
7
|
-
/** Matched
|
|
7
|
+
/** Matched at a model-id boundary: the id itself, or followed by a -YYYYMMDD or [..] suffix. */
|
|
8
8
|
modelPrefix: string;
|
|
9
9
|
/** ISO date. The latest row at or before a request's timestamp wins. */
|
|
10
10
|
effectiveFrom: string;
|
|
@@ -15,10 +15,10 @@ interface PriceRow {
|
|
|
15
15
|
output: number;
|
|
16
16
|
}
|
|
17
17
|
/** Bump on any edit to PRICE_TABLE. The cost report prints it in its footer. */
|
|
18
|
-
declare const PRICE_TABLE_VERSION =
|
|
18
|
+
declare const PRICE_TABLE_VERSION = 2;
|
|
19
19
|
declare const PRICE_TABLE: readonly PriceRow[];
|
|
20
20
|
/**
|
|
21
|
-
* The longest prefix that matches the model, then the latest row effective at `ts`.
|
|
21
|
+
* The longest prefix that matches the model at an id boundary, then the latest row effective at `ts`.
|
|
22
22
|
* Returns null for a model no row covers: an unknown model is unpriced, never defaulted,
|
|
23
23
|
* because a default silently prices a new model at an old model's rate.
|
|
24
24
|
*/
|
|
@@ -322,7 +322,7 @@ interface WrittenEpisodes {
|
|
|
322
322
|
declare function writeEpisodes(graph: SessionGraph, sessionIds: readonly string[]): WrittenEpisodes[];
|
|
323
323
|
|
|
324
324
|
/** worker-v1's role vocabulary, from worker-forensics-report.md section 2. */
|
|
325
|
-
type WorkerRole = "implementer" | "reviewer" | "researcher" | "planner" | "standing_peer" | "unknown";
|
|
325
|
+
type WorkerRole = "implementer" | "reviewer" | "researcher" | "planner" | "coordinator" | "standing_peer" | "unknown";
|
|
326
326
|
/** Spawn profile to role, as the worker report's table (wf_analyze.py:79-91) maps it. */
|
|
327
327
|
declare const PROFILE_ROLES: Readonly<Record<string, WorkerRole>>;
|
|
328
328
|
declare const STANDING_PEER_MIN_HOURS = 12;
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
// src/prices.ts
|
|
2
|
-
var PRICE_TABLE_VERSION =
|
|
2
|
+
var PRICE_TABLE_VERSION = 2;
|
|
3
3
|
var OPUS = { input: 5, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10, output: 25 };
|
|
4
|
+
var OPUS_5_5 = { input: 4, cacheRead: 0.2, cacheWrite5m: 5, cacheWrite1h: 8, output: 20 };
|
|
4
5
|
var SONNET = { input: 2, cacheRead: 0.2, cacheWrite5m: 2.5, cacheWrite1h: 4, output: 10 };
|
|
5
6
|
var FABLE = { input: 10, cacheRead: 0.25, cacheWrite5m: 12.5, cacheWrite1h: 20, output: 50 };
|
|
7
|
+
var SONNET_5_5 = { input: 2, cacheRead: 0.2, cacheWrite5m: 2.5, cacheWrite1h: 4, output: 10 };
|
|
6
8
|
var HAIKU = { input: 1, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2, output: 5 };
|
|
7
9
|
var FREE = { input: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, output: 0 };
|
|
8
10
|
var GENESIS = "2026-01-01";
|
|
9
11
|
var PRICE_TABLE = [
|
|
12
|
+
// 5.5 rows: claude-api skill shared/models.md and prompt-caching.md, bundled copy cached 2026-09-25.
|
|
13
|
+
{ modelPrefix: "claude-opus-5-5", effectiveFrom: GENESIS, ...OPUS_5_5 },
|
|
10
14
|
{ modelPrefix: "claude-opus-5", effectiveFrom: GENESIS, ...OPUS },
|
|
11
15
|
{ modelPrefix: "claude-opus-4-8", effectiveFrom: GENESIS, ...OPUS },
|
|
16
|
+
{ modelPrefix: "claude-sonnet-5-5", effectiveFrom: GENESIS, ...SONNET_5_5 },
|
|
12
17
|
{ modelPrefix: "claude-sonnet-5", effectiveFrom: GENESIS, ...SONNET },
|
|
13
18
|
{ modelPrefix: "claude-fable-5-1", effectiveFrom: GENESIS, ...FABLE },
|
|
14
19
|
{ modelPrefix: "claude-fable-5", effectiveFrom: GENESIS, ...FABLE },
|
|
@@ -18,7 +23,7 @@ var PRICE_TABLE = [
|
|
|
18
23
|
function findPrice(model, ts, prices = PRICE_TABLE) {
|
|
19
24
|
let best = null;
|
|
20
25
|
for (const row of prices) {
|
|
21
|
-
if (!model
|
|
26
|
+
if (!matchesAtBoundary(model, row.modelPrefix)) continue;
|
|
22
27
|
if (row.effectiveFrom > ts) continue;
|
|
23
28
|
if (best && !isBetterMatch(row, best)) continue;
|
|
24
29
|
best = row;
|
|
@@ -29,6 +34,11 @@ function isBetterMatch(row, best) {
|
|
|
29
34
|
if (row.modelPrefix.length !== best.modelPrefix.length) return row.modelPrefix.length > best.modelPrefix.length;
|
|
30
35
|
return row.effectiveFrom > best.effectiveFrom;
|
|
31
36
|
}
|
|
37
|
+
function matchesAtBoundary(model, prefix) {
|
|
38
|
+
if (!model.startsWith(prefix)) return false;
|
|
39
|
+
const rest = model.slice(prefix.length);
|
|
40
|
+
return rest === "" || rest.startsWith("[") || /^-\d{8}(\[.*\])?$/.test(rest);
|
|
41
|
+
}
|
|
32
42
|
|
|
33
43
|
// src/price-request.ts
|
|
34
44
|
var UNPRICED = {
|
|
@@ -440,6 +450,8 @@ var PROFILE_ROLES = {
|
|
|
440
450
|
researcher: "researcher",
|
|
441
451
|
explorer: "researcher",
|
|
442
452
|
"fable-architect": "planner",
|
|
453
|
+
planner: "planner",
|
|
454
|
+
"fable-coordinator": "coordinator",
|
|
443
455
|
peer: "standing_peer"
|
|
444
456
|
};
|
|
445
457
|
var STANDING_PEER_MIN_HOURS = 12;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/prices.ts","../src/price-request.ts","../src/classify-session.ts","../src/bands.ts","../src/cost-report.ts","../src/cost-report-queries.ts","../src/episodes.ts","../src/initiative.ts","../src/roles.ts","../src/render-text.ts"],"sourcesContent":["/** USD per million tokens. Fitted against 392 `cost-state` rows in the 2026-09-20 audit. */\nexport interface PriceRow {\n /** Matched against the model string by longest prefix. */\n modelPrefix: string;\n /** ISO date. The latest row at or before a request's timestamp wins. */\n effectiveFrom: string;\n input: number;\n cacheRead: number;\n cacheWrite5m: number;\n cacheWrite1h: number;\n output: number;\n}\n\n/** Bump on any edit to PRICE_TABLE. The cost report prints it in its footer. */\nexport const PRICE_TABLE_VERSION = 1;\n\nconst OPUS = { input: 5.0, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10.0, output: 25.0 };\nconst SONNET = { input: 2.0, cacheRead: 0.2, cacheWrite5m: 2.5, cacheWrite1h: 4.0, output: 10.0 };\n// Fable's cache read is 0.025 of input, not the 0.1 every other model uses. Reading it as\n// 0.1 overstated the 2026-09-20 audit by about $1,700; `fable-cache-read.test` pins the ratio.\nconst FABLE = { input: 10.0, cacheRead: 0.25, cacheWrite5m: 12.5, cacheWrite1h: 20.0, output: 50.0 };\nconst HAIKU = { input: 1.0, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2.0, output: 5.0 };\nconst FREE = { input: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, output: 0 };\n\nconst GENESIS = \"2026-01-01\";\n\nexport const PRICE_TABLE: readonly PriceRow[] = [\n { modelPrefix: \"claude-opus-5\", effectiveFrom: GENESIS, ...OPUS },\n { modelPrefix: \"claude-opus-4-8\", effectiveFrom: GENESIS, ...OPUS },\n { modelPrefix: \"claude-sonnet-5\", effectiveFrom: GENESIS, ...SONNET },\n { modelPrefix: \"claude-fable-5-1\", effectiveFrom: GENESIS, ...FABLE },\n { modelPrefix: \"claude-fable-5\", effectiveFrom: GENESIS, ...FABLE },\n { modelPrefix: \"claude-haiku-4-5\", effectiveFrom: GENESIS, ...HAIKU },\n { modelPrefix: \"<synthetic>\", effectiveFrom: GENESIS, ...FREE },\n];\n\n/**\n * The longest prefix that matches the model, then the latest row effective at `ts`.\n * Returns null for a model no row covers: an unknown model is unpriced, never defaulted,\n * because a default silently prices a new model at an old model's rate.\n */\nexport function findPrice(model: string, ts: string, prices: readonly PriceRow[] = PRICE_TABLE): PriceRow | null {\n let best: PriceRow | null = null;\n for (const row of prices) {\n if (!model.startsWith(row.modelPrefix)) continue;\n if (row.effectiveFrom > ts) continue;\n if (best && !isBetterMatch(row, best)) continue;\n best = row;\n }\n return best;\n}\n\nfunction isBetterMatch(row: PriceRow, best: PriceRow): boolean {\n if (row.modelPrefix.length !== best.modelPrefix.length) return row.modelPrefix.length > best.modelPrefix.length;\n return row.effectiveFrom > best.effectiveFrom;\n}\n","import type { PriceRow } from \"./prices.js\";\nimport { PRICE_TABLE, findPrice } from \"./prices.js\";\n\n/** Raw token counts for one API request, as the transcript reports them. */\nexport interface RequestTokens {\n inputTokens?: number;\n cacheReadTokens?: number;\n /** Total cache creation. Used only when the 5m/1h split is absent. */\n cacheCreationTokens?: number;\n cacheCreation5mTokens?: number;\n cacheCreation1hTokens?: number;\n outputTokens?: number;\n}\n\nexport interface PricedRequest {\n inputUsd: number;\n cacheReadUsd: number;\n cacheWrite5mUsd: number;\n cacheWrite1hUsd: number;\n outputUsd: number;\n costUsd: number;\n /** False when no price row covers the model. Every component is then zero. */\n priced: boolean;\n}\n\nconst UNPRICED: PricedRequest = {\n inputUsd: 0,\n cacheReadUsd: 0,\n cacheWrite5mUsd: 0,\n cacheWrite1hUsd: 0,\n outputUsd: 0,\n costUsd: 0,\n priced: false,\n};\n\nexport function priceRequest(\n tokens: RequestTokens,\n model: string,\n ts: string,\n prices: readonly PriceRow[] = PRICE_TABLE,\n): PricedRequest {\n const row = findPrice(model, ts, prices);\n if (!row) return { ...UNPRICED };\n\n const { write5m, write1h } = splitCacheCreation(tokens);\n const inputUsd = perMTok(tokens.inputTokens, row.input);\n const cacheReadUsd = perMTok(tokens.cacheReadTokens, row.cacheRead);\n const cacheWrite5mUsd = perMTok(write5m, row.cacheWrite5m);\n const cacheWrite1hUsd = perMTok(write1h, row.cacheWrite1h);\n const outputUsd = perMTok(tokens.outputTokens, row.output);\n const costUsd = inputUsd + cacheReadUsd + cacheWrite5mUsd + cacheWrite1hUsd + outputUsd;\n return { inputUsd, cacheReadUsd, cacheWrite5mUsd, cacheWrite1hUsd, outputUsd, costUsd, priced: true };\n}\n\n/**\n * Older transcripts carry a cache_creation total with no ttl breakdown. Charging the\n * 5m rate there under-reads rather than over-reads, and 5m is the harness default.\n */\nfunction splitCacheCreation(tokens: RequestTokens): { write5m: number; write1h: number } {\n const write5m = tokens.cacheCreation5mTokens ?? 0;\n const write1h = tokens.cacheCreation1hTokens ?? 0;\n if (write5m > 0 || write1h > 0) return { write5m, write1h };\n return { write5m: tokens.cacheCreationTokens ?? 0, write1h: 0 };\n}\n\nfunction perMTok(tokens: number | undefined, ratePerMTok: number): number {\n return ((tokens ?? 0) * ratePerMTok) / 1_000_000;\n}\n","export type SessionClass = \"agent_spawned\" | \"human_interactive\" | \"headless_sdk\" | \"other_headless\";\n\n/** Human sessions only. A coordinator drives other agents; an adhoc session does not. */\nexport type HumanRole = \"coordinator\" | \"adhoc\";\n\n/** The agent-chat spawn record for a session, as the origin resolver returns it. */\nexport interface SessionOrigin {\n depth: number;\n parentName?: string | null;\n /** \"adopted\" and \"inherited\" mark a pane the human took over, not a spawned worker. */\n originKind?: string | null;\n profile?: string | null;\n}\n\nexport interface SessionFacts {\n /** The harness entrypoint. Agent-chat workers and `-p` miners both report \"sdk-cli\". */\n startType?: string | null;\n origin?: SessionOrigin | null;\n firstUserText?: string | null;\n humanTurnCount?: number;\n inboundKinds?: readonly string[];\n signalKinds?: readonly string[];\n}\n\nexport interface SessionClassification {\n sessionClass: SessionClass;\n reason: string;\n humanRole: HumanRole | null;\n}\n\nconst COORDINATOR_SIGNALS = new Set([\"agent_spawn\", \"chat_send\"]);\n\n/**\n * Rule order matters. The origin row wins over `startType` because agent-chat workers\n * run `claude -p` and so report \"sdk-cli\" exactly like a headless miner does.\n */\nexport function classifySession(facts: SessionFacts): SessionClassification {\n const [sessionClass, reason] = classOf(facts);\n const humanRole = sessionClass === \"human_interactive\" ? humanRoleOf(facts) : null;\n return { sessionClass, reason, humanRole };\n}\n\nfunction classOf(facts: SessionFacts): [SessionClass, string] {\n const origin = facts.origin;\n if (origin) {\n if (isAdoptedPane(origin)) return [\"human_interactive\", \"origin depth 0, human or adopted parent\"];\n return [\"agent_spawned\", \"origin depth >= 1\"];\n }\n if (facts.startType === \"sdk-cli\") return [\"headless_sdk\", \"start_type sdk-cli with no origin row\"];\n if (isProgrammaticPrompt(facts)) return [\"other_headless\", \"content-block prompt with <= 2 human turns\"];\n return [\"human_interactive\", \"no origin row, no headless marker\"];\n}\n\nfunction isAdoptedPane(origin: SessionOrigin): boolean {\n if (origin.depth !== 0) return false;\n if (origin.parentName === \"human\") return true;\n if (origin.originKind === \"adopted\" || origin.originKind === \"inherited\") return true;\n return !origin.profile;\n}\n\n/** Cron and RemoteTrigger daemons inject a structured content block, never typed prose. */\nfunction isProgrammaticPrompt(facts: SessionFacts): boolean {\n const text = (facts.firstUserText ?? \"\").trimStart();\n return text.startsWith(\"[{\") && (facts.humanTurnCount ?? 0) <= 2;\n}\n\nfunction humanRoleOf(facts: SessionFacts): HumanRole {\n if (facts.inboundKinds?.includes(\"channel_message\")) return \"coordinator\";\n if (facts.signalKinds?.some((kind) => COORDINATOR_SIGNALS.has(kind))) return \"coordinator\";\n return \"adhoc\";\n}\n","/** Half-open [lo, hi) in the band's own unit. */\nexport interface Band {\n lo: number;\n hi: number;\n name: string;\n}\n\n/** Context size in tokens. From `cf_analyze.py` BANDS. */\nexport const CONTEXT_BANDS: readonly Band[] = [\n { lo: 0, hi: 50_000, name: \"<50k\" },\n { lo: 50_000, hi: 100_000, name: \"50-100k\" },\n { lo: 100_000, hi: 200_000, name: \"100-200k\" },\n { lo: 200_000, hi: Number.POSITIVE_INFINITY, name: \"200k+\" },\n];\n\n/** Idle gap in milliseconds. From `cf_analyze.py` IDLE. */\nexport const GAP_BANDS: readonly Band[] = [\n { lo: 0, hi: 5 * 60_000, name: \"<5m\" },\n { lo: 5 * 60_000, hi: 60 * 60_000, name: \"5-60m\" },\n { lo: 60 * 60_000, hi: Number.POSITIVE_INFINITY, name: \">60m\" },\n];\n\n/** Null for a value no band covers, so a caller reports the gap instead of inventing one. */\nexport function bandOf(value: number, bands: readonly Band[]): string | null {\n for (const band of bands) {\n if (value >= band.lo && value < band.hi) return band.name;\n }\n return null;\n}\n\nexport function contextBand(tokens: number): string | null {\n return bandOf(tokens, CONTEXT_BANDS);\n}\n\nexport function gapBand(millis: number): string | null {\n return bandOf(millis, GAP_BANDS);\n}\n","import type { Db } from \"@titan-design/store-sqlite\";\nimport { z } from \"zod\";\nimport { classifySession, type SessionClass } from \"./classify-session.js\";\nimport {\n readCompactions,\n readCostRows,\n readCoverage,\n readPriceTableVersion,\n readSessionContexts,\n type CompactionRow,\n type CostRow,\n type ReportWindow,\n type SessionContext,\n} from \"./cost-report-queries.js\";\nimport { assignmentCount, heuristicFor } from \"./episodes.js\";\nimport { sessionInitiative } from \"./initiative.js\";\nimport { sessionRole } from \"./roles.js\";\n\nconst count = z.number().int().nonnegative();\nconst bucketSchema = z.object({ key: z.string(), requests: count, sessions: count, costUsd: z.number() });\nconst wakePartSchema = bucketSchema.extend({ midLoopRequests: count, midLoopCostUsd: z.number() });\nconst cellSchema = z.object({ wakeCause: z.string(), gapBand: z.string(), requests: count, costUsd: z.number() });\n\nexport const TOKEN_CLASSES = [\"input\", \"cache_read\", \"cache_write_5m\", \"cache_write_1h\", \"output\"] as const;\nexport type TokenClass = (typeof TOKEN_CLASSES)[number];\n\nexport const costReportSchema = z.object({\n window: z.object({ since: z.string().nullable(), until: z.string().nullable() }),\n priceTableVersion: z.number().int().nullable(),\n totals: z.object({ requests: count, sessions: count, costUsd: z.number(), unpricedRequests: count }),\n byTokenClass: z.array(z.object({ tokenClass: z.enum(TOKEN_CLASSES), tokens: count, costUsd: z.number() })),\n byAccount: z.array(bucketSchema),\n byModel: z.array(bucketSchema),\n byClass: z.array(bucketSchema),\n byRole: z.array(bucketSchema),\n /** Sessions by how many episodes their class's heuristic cut them into; `none` when not yet segmented. */\n byEpisodeCount: z.array(bucketSchema),\n byInitiative: z.array(bucketSchema),\n byContextBand: z.array(bucketSchema),\n byWakeCause: z.array(wakePartSchema.extend({ parts: z.array(wakePartSchema) })),\n wakeCauseByGapBand: z.array(cellSchema),\n coldRebuild: z.object({ requests: count, costUsd: z.number(), byGapBandAndCause: z.array(cellSchema) }),\n compactions: z.object({ total: count, manual: count, auto: count, midLoop: count, droppedTokens: count }),\n topSessions: z.array(\n z.object({\n sessionId: z.string(),\n sessionClass: z.string(),\n role: z.string(),\n initiative: z.string(),\n account: z.string(),\n requests: count,\n costUsd: z.number(),\n }),\n ),\n unpricedModels: z.array(z.object({ model: z.string(), requests: count, tokens: count })),\n coverage: z.object({ transcriptsIndexed: count, transcriptsDiscovered: count.nullable(), facetBacklog: count }),\n});\n\nexport type CostReport = z.infer<typeof costReportSchema>;\nexport type CostBucket = z.infer<typeof bucketSchema>;\nexport type WakeCauseBucket = CostReport[\"byWakeCause\"][number];\nexport type WakeGapCell = z.infer<typeof cellSchema>;\n\nexport interface CostReportOptions {\n /** ISO timestamp or date, inclusive. Overrides `days`. */\n since?: string;\n /** ISO timestamp or date, exclusive. */\n until?: string;\n /** Window length ending at `until`, or at `now` when `until` is absent. */\n days?: number;\n now?: Date;\n /** Sessions listed in `topSessions`. */\n top?: number;\n /** Transcripts the caller discovered on disk; the graph cannot know this. */\n transcriptsDiscovered?: number;\n /** The extraction version an audit facet must reach to count as current. */\n facetVersion?: number;\n}\n\n/** AskUserQuestion answers are the human answering, so they count under `human` (decisions Q4). */\nconst HUMAN_CAUSES: readonly string[] = [\"human_typed\", \"ask_user_answer\"];\nconst NONE = \"none\";\nconst DEFAULT_TOP = 10;\nconst EPISODE_COUNT_CAP = 5;\nconst DAY_MS = 86_400_000;\n\ninterface SessionTags {\n sessionClass: SessionClass;\n role: string;\n episodeCount: string;\n initiative: string;\n account: string;\n}\n\ntype TaggedRow = CostRow & SessionTags;\n\n/** Reads the graph and never writes it, so a read-only connection is enough. */\nexport function costReport(db: Db, options: CostReportOptions = {}): CostReport {\n const window = resolveWindow(options);\n const costRows = readCostRows(db, window);\n const sessionIds = [...new Set(costRows.map((row) => row.sessionId))];\n const tags = tagSessions(readSessionContexts(db, sessionIds));\n const rows = costRows.map((row) => ({ ...row, ...tags.get(row.sessionId)! }));\n return {\n window,\n priceTableVersion: readPriceTableVersion(db),\n totals: { ...sumOf(rows), sessions: sessionIds.length, unpricedRequests: rows.filter((row) => !row.priced).length },\n byTokenClass: tokenClasses(rows),\n ...dimensionBuckets(rows),\n byWakeCause: wakeCauses(rows),\n wakeCauseByGapBand: gapCells(rows),\n coldRebuild: coldRebuild(rows),\n compactions: compactionCounts(readCompactions(db, window)),\n topSessions: topSessions(rows, options.top ?? DEFAULT_TOP),\n unpricedModels: unpricedModels(rows),\n coverage: { ...readCoverage(db, options.facetVersion ?? null), transcriptsDiscovered: options.transcriptsDiscovered ?? null },\n };\n}\n\nexport function resolveWindow(options: CostReportOptions): ReportWindow {\n const until = options.until ?? null;\n if (options.since !== undefined) return { since: options.since, until };\n if (options.days === undefined) return { since: null, until };\n const end = until ? Date.parse(until) : (options.now ?? new Date()).getTime();\n return { since: new Date(end - options.days * DAY_MS).toISOString(), until };\n}\n\nfunction tagSessions(contexts: Map<string, SessionContext>): Map<string, SessionTags> {\n const tags = new Map<string, SessionTags>();\n for (const [sessionId, context] of contexts) {\n const classification = classifySession(context.facts);\n const episodes = context.episodes.filter((episode) => episode.heuristic === heuristicFor(classification.sessionClass));\n const worker = { profile: context.facts.origin?.profile, lifetimeMs: context.lifetimeMs, assignments: assignmentCount(episodes) };\n tags.set(sessionId, {\n sessionClass: classification.sessionClass,\n role: sessionRole(classification, worker),\n episodeCount: episodeCountKey(episodes.length),\n initiative: sessionInitiative(context.tasks, context.cwd),\n account: context.account ?? \"unknown\",\n });\n }\n return tags;\n}\n\n/** The worker report's assignment buckets: 1, 2, 3, 4, 5+. */\nfunction episodeCountKey(episodes: number): string {\n if (episodes === 0) return NONE;\n return episodes >= EPISODE_COUNT_CAP ? `${EPISODE_COUNT_CAP}+` : String(episodes);\n}\n\nfunction sumOf(rows: readonly CostRow[]): { requests: number; costUsd: number } {\n return { requests: rows.length, costUsd: rows.reduce((sum, row) => sum + row.costUsd, 0) };\n}\n\nfunction tokenClasses(rows: readonly CostRow[]): CostReport[\"byTokenClass\"] {\n const pick: Record<TokenClass, (row: CostRow) => [number, number]> = {\n input: (row) => [row.inputTokens, row.inputCostUsd],\n cache_read: (row) => [row.cacheReadTokens, row.cacheReadCostUsd],\n cache_write_5m: (row) => [row.cacheWrite5mTokens, row.cacheWrite5mCostUsd],\n cache_write_1h: (row) => [row.cacheWrite1hTokens, row.cacheWrite1hCostUsd],\n output: (row) => [row.outputTokens, row.outputCostUsd],\n };\n return TOKEN_CLASSES.map((tokenClass) => {\n const pairs = rows.map(pick[tokenClass]);\n return { tokenClass, tokens: pairs.reduce((sum, [tokens]) => sum + tokens, 0), costUsd: pairs.reduce((sum, [, cost]) => sum + cost, 0) };\n });\n}\n\nfunction dimensionBuckets(rows: readonly TaggedRow[]) {\n return {\n byAccount: bucketsBy(rows, (row) => row.account),\n byModel: bucketsBy(rows, (row) => row.model),\n byClass: bucketsBy(rows, (row) => row.sessionClass),\n byRole: bucketsBy(rows, (row) => row.role),\n byEpisodeCount: bucketsBy(rows, (row) => row.episodeCount),\n byInitiative: bucketsBy(rows, (row) => row.initiative),\n byContextBand: bucketsBy(rows, (row) => row.contextBand),\n };\n}\n\n/** Most expensive first; ties by key so the report is stable. */\nfunction bucketsBy<T extends CostRow>(rows: readonly T[], keyOf: (row: T) => string): CostBucket[] {\n const groups = groupRows(rows, keyOf);\n return [...groups]\n .map(([key, members]) => ({ key, ...sumOf(members), sessions: new Set(members.map((row) => row.sessionId)).size }))\n .sort(byCostThenKey);\n}\n\nfunction groupRows<T>(rows: readonly T[], keyOf: (row: T) => string): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n for (const row of rows) {\n const key = keyOf(row);\n const members = groups.get(key);\n if (members) members.push(row);\n else groups.set(key, [row]);\n }\n return groups;\n}\n\nfunction byCostThenKey(a: { key: string; costUsd: number }, b: { key: string; costUsd: number }): number {\n return b.costUsd - a.costUsd || a.key.localeCompare(b.key);\n}\n\nfunction wakeCauses(rows: readonly CostRow[]): WakeCauseBucket[] {\n const causeOf = (row: CostRow) => row.wakeCause ?? NONE;\n const groupOf = (row: CostRow) => (HUMAN_CAUSES.includes(causeOf(row)) ? \"human\" : causeOf(row));\n return [...groupRows(rows, groupOf)]\n .map(([key, members]) => ({\n ...wakePart(key, members),\n parts: key === \"human\" ? [...groupRows(members, causeOf)].map(([cause, part]) => wakePart(cause, part)).sort(byCostThenKey) : [],\n }))\n .sort(byCostThenKey);\n}\n\nfunction wakePart(key: string, rows: readonly CostRow[]) {\n const midLoop = rows.filter((row) => row.wakeDelivery === \"mid_loop\");\n const sessions = new Set(rows.map((row) => row.sessionId)).size;\n return { key, ...sumOf(rows), sessions, midLoopRequests: midLoop.length, midLoopCostUsd: sumOf(midLoop).costUsd };\n}\n\nfunction gapCells(rows: readonly CostRow[]): WakeGapCell[] {\n const keyOf = (row: CostRow) => JSON.stringify([row.wakeCause ?? NONE, row.gapBand ?? NONE]);\n return [...groupRows(rows, keyOf)]\n .map(([key, members]) => {\n const [wakeCause, gapBand] = JSON.parse(key) as [string, string];\n return { wakeCause, gapBand, ...sumOf(members) };\n })\n .sort((a, b) => b.costUsd - a.costUsd || a.wakeCause.localeCompare(b.wakeCause) || a.gapBand.localeCompare(b.gapBand));\n}\n\nfunction coldRebuild(rows: readonly CostRow[]): CostReport[\"coldRebuild\"] {\n const cold = rows.filter((row) => row.isCold);\n return { ...sumOf(cold), byGapBandAndCause: gapCells(cold) };\n}\n\nfunction compactionCounts(rows: readonly CompactionRow[]): CostReport[\"compactions\"] {\n return {\n total: rows.length,\n manual: rows.filter((row) => row.trigger === \"manual\").length,\n auto: rows.filter((row) => row.trigger === \"auto\").length,\n midLoop: rows.filter((row) => row.midLoop).length,\n droppedTokens: rows.reduce((sum, row) => sum + (row.droppedTokens ?? 0), 0),\n };\n}\n\nfunction topSessions(rows: readonly TaggedRow[], top: number): CostReport[\"topSessions\"] {\n return [...groupRows(rows, (row) => row.sessionId)]\n .map(([sessionId, members]) => {\n const { sessionClass, role, initiative, account } = members[0]!;\n return { sessionId, sessionClass, role, initiative, account, ...sumOf(members) };\n })\n .sort((a, b) => b.costUsd - a.costUsd || a.sessionId.localeCompare(b.sessionId))\n .slice(0, top);\n}\n\nfunction unpricedModels(rows: readonly CostRow[]): CostReport[\"unpricedModels\"] {\n const unpriced = rows.filter((row) => !row.priced);\n return [...groupRows(unpriced, (row) => row.model)]\n .map(([model, members]) => ({ model, requests: members.length, tokens: members.reduce((sum, row) => sum + tokensOf(row), 0) }))\n .sort((a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model));\n}\n\nfunction tokensOf(row: CostRow): number {\n return row.inputTokens + row.cacheReadTokens + row.cacheWrite5mTokens + row.cacheWrite1hTokens + row.outputTokens;\n}\n","import { AUDIT_FACET, EPISODE_TABLE, FACET_TABLE, KIT } from \"@titan-design/session-graph\";\nimport type { Db } from \"@titan-design/store-sqlite\";\nimport type { SessionFacts } from \"./classify-session.js\";\nimport type { TaskInitiative } from \"./initiative.js\";\n\n/** ISO bounds on request `ts`: `since` inclusive, `until` exclusive, null for open. */\nexport interface ReportWindow {\n since: string | null;\n until: string | null;\n}\n\n/** One deduplicated, priced request as the `request_cost` view returns it. */\nexport interface CostRow {\n sessionId: string;\n model: string;\n inputTokens: number;\n cacheReadTokens: number;\n cacheWrite5mTokens: number;\n cacheWrite1hTokens: number;\n outputTokens: number;\n inputCostUsd: number;\n cacheReadCostUsd: number;\n cacheWrite5mCostUsd: number;\n cacheWrite1hCostUsd: number;\n outputCostUsd: number;\n costUsd: number;\n priced: boolean;\n isCold: boolean;\n contextBand: string;\n gapBand: string | null;\n wakeCause: string | null;\n wakeDelivery: string | null;\n}\n\n/** What the report needs about one session beyond its requests. */\nexport interface SessionContext {\n facts: SessionFacts;\n cwd: string | null;\n account: string | null;\n tasks: TaskInitiative[];\n /** First to last main-thread request over the whole session, not just the window. */\n lifetimeMs: number;\n /** Stored episode rows of every heuristic, in episode order. */\n episodes: StoredEpisode[];\n}\n\nexport interface StoredEpisode {\n heuristic: string;\n openedBy: string;\n}\n\nexport interface CompactionRow {\n trigger: string | null;\n midLoop: boolean;\n droppedTokens: number | null;\n}\n\nconst IN_WINDOW = \"(@since IS NULL OR ts >= @since) AND (@until IS NULL OR ts < @until)\";\nconst IN_SESSIONS = \"session_id IN (SELECT value FROM json_each(@ids))\";\n\n// The 5m write column mirrors the view: a split-less write is billed at the 5m rate.\nconst COST_ROWS = `\n SELECT session_id AS sessionId, model,\n input_tokens AS inputTokens, cache_read_tokens AS cacheReadTokens,\n CASE WHEN cache_creation_5m + cache_creation_1h = 0 THEN cache_creation_tokens ELSE cache_creation_5m END AS cacheWrite5mTokens,\n cache_creation_1h AS cacheWrite1hTokens, output_tokens AS outputTokens,\n input_cost_usd AS inputCostUsd, cache_read_cost_usd AS cacheReadCostUsd,\n cache_write_5m_cost_usd AS cacheWrite5mCostUsd, cache_write_1h_cost_usd AS cacheWrite1hCostUsd,\n output_cost_usd AS outputCostUsd, cost_usd AS costUsd, priced, is_cold AS isCold,\n context_band AS contextBand, gap_band AS gapBand, wake_cause AS wakeCause, wake_delivery AS wakeDelivery\n FROM request_cost WHERE ${IN_WINDOW}\n`;\n\nexport function readCostRows(db: Db, window: ReportWindow): CostRow[] {\n const rows = db.prepare(COST_ROWS).all(window) as (Omit<CostRow, \"priced\" | \"isCold\"> & { priced: number; isCold: number })[];\n return rows.map((row) => ({ ...row, priced: row.priced === 1, isCold: row.isCold === 1 }));\n}\n\nexport function readCompactions(db: Db, window: ReportWindow): CompactionRow[] {\n const sql = `SELECT trigger, mid_loop AS midLoop, dropped_tokens AS droppedTokens FROM compaction WHERE ${IN_WINDOW}`;\n const rows = db.prepare(sql).all(window) as (Omit<CompactionRow, \"midLoop\"> & { midLoop: number | null })[];\n return rows.map((row) => ({ ...row, midLoop: row.midLoop === 1 }));\n}\n\n/** The graph's own price version, which is what `request_cost` priced with; null before any price row exists. */\nexport function readPriceTableVersion(db: Db): number | null {\n return (db.prepare(\"SELECT MAX(table_version) AS version FROM price\").get() as { version: number | null }).version;\n}\n\n/** Indexed transcripts, and those whose audit facet is missing or older than `facetVersion`. */\nexport function readCoverage(db: Db, facetVersion: number | null): { transcriptsIndexed: number; facetBacklog: number } {\n const indexed = db.prepare(`SELECT COUNT(*) AS n FROM \"${KIT.watermark}\"`).get() as { n: number };\n const backlog = db\n .prepare(\n `SELECT COUNT(*) AS n FROM \"${KIT.watermark}\" t LEFT JOIN \"${FACET_TABLE}\" f\n ON f.transcript_id = t.source_id AND f.facet = @facet AND (@version IS NULL OR f.version >= @version)\n WHERE f.transcript_id IS NULL`,\n )\n .get({ facet: AUDIT_FACET, version: facetVersion }) as { n: number };\n return { transcriptsIndexed: indexed.n, facetBacklog: backlog.n };\n}\n\nexport function readSessionContexts(db: Db, sessionIds: readonly string[]): Map<string, SessionContext> {\n const ids = JSON.stringify(sessionIds);\n const contexts = new Map<string, SessionContext>(sessionIds.map((id) => [id, emptyContext()]));\n addSessionRows(db, ids, contexts);\n addOrigins(db, ids, contexts);\n addInbounds(db, ids, contexts);\n addSignals(db, ids, contexts);\n addTaskInitiatives(db, ids, contexts);\n addLifetimes(db, ids, contexts);\n addEpisodes(db, ids, contexts);\n return contexts;\n}\n\nfunction emptyContext(): SessionContext {\n return { facts: { inboundKinds: [], signalKinds: [], humanTurnCount: 0 }, cwd: null, account: null, tasks: [], lifetimeMs: 0, episodes: [] };\n}\n\nfunction addSessionRows(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, start_type AS startType, cwd, seed_prompt AS seedPrompt, account FROM session WHERE ${IN_SESSIONS}`;\n type Row = { sessionId: string; startType: string | null; cwd: string | null; seedPrompt: string | null; account: string | null };\n for (const row of db.prepare(sql).all({ ids }) as Row[]) {\n const context = contexts.get(row.sessionId)!;\n context.cwd = row.cwd;\n context.account = row.account;\n context.facts.startType = row.startType;\n context.facts.firstUserText = row.seedPrompt;\n }\n}\n\nfunction addOrigins(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, depth, parent_name AS parentName, origin_kind AS originKind, profile FROM session_origin WHERE ${IN_SESSIONS}`;\n type Row = { sessionId: string; depth: number | null; parentName: string | null; originKind: string | null; profile: string | null };\n for (const { sessionId, depth, ...origin } of db.prepare(sql).all({ ids }) as Row[]) {\n contexts.get(sessionId)!.facts.origin = { ...origin, depth: depth ?? 0 };\n }\n}\n\nfunction addInbounds(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, cause, SUM(delivery = 'turn_start') AS turnStarts FROM inbound WHERE ${IN_SESSIONS} GROUP BY session_id, cause`;\n for (const row of db.prepare(sql).all({ ids }) as { sessionId: string; cause: string; turnStarts: number }[]) {\n const facts = contexts.get(row.sessionId)!.facts;\n facts.inboundKinds = [...(facts.inboundKinds ?? []), row.cause];\n if (row.cause === \"human_typed\") facts.humanTurnCount = row.turnStarts;\n }\n}\n\nfunction addSignals(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT DISTINCT session_id AS sessionId, signal FROM session_signal WHERE ${IN_SESSIONS}`;\n for (const row of db.prepare(sql).all({ ids }) as { sessionId: string; signal: string }[]) {\n const facts = contexts.get(row.sessionId)!.facts;\n facts.signalKinds = [...(facts.signalKinds ?? []), row.signal];\n }\n}\n\nfunction addTaskInitiatives(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `\n SELECT substr(e.source_ref, 9) AS sessionId, t.initiative, COUNT(*) AS edges\n FROM \"${KIT.edge}\" e JOIN task t ON t.task_ref = e.target_ref\n WHERE e.relation = 'ran' AND e.t_expired IS NULL AND t.initiative IS NOT NULL\n AND e.source_ref IN (SELECT 'session:' || value FROM json_each(@ids))\n GROUP BY e.source_ref, t.initiative`;\n for (const row of db.prepare(sql).all({ ids }) as ({ sessionId: string } & TaskInitiative)[]) {\n contexts.get(row.sessionId)!.tasks.push({ initiative: row.initiative, edges: row.edges });\n }\n}\n\nfunction addLifetimes(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, MIN(ts) AS first, MAX(ts) AS last FROM request_dedup WHERE ${IN_SESSIONS} AND is_sidechain = 0 GROUP BY session_id`;\n for (const row of db.prepare(sql).all({ ids }) as { sessionId: string; first: string; last: string }[]) {\n contexts.get(row.sessionId)!.lifetimeMs = Date.parse(row.last) - Date.parse(row.first);\n }\n}\n\nfunction addEpisodes(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, heuristic, opened_by AS openedBy FROM \"${EPISODE_TABLE}\" WHERE ${IN_SESSIONS} ORDER BY session_id, heuristic, episode_index`;\n for (const { sessionId, ...episode } of db.prepare(sql).all({ ids }) as ({ sessionId: string } & StoredEpisode)[]) {\n contexts.get(sessionId)!.episodes.push(episode);\n }\n}\n","import { replaceEpisodes, type EpisodeRow, type SessionGraph } from \"@titan-design/session-graph\";\nimport type { Db } from \"@titan-design/store-sqlite\";\nimport { classifySession, type SessionClass } from \"./classify-session.js\";\nimport { readSessionContexts } from \"./cost-report-queries.js\";\n\n/** Two rule sets on purpose (design section 18): workers segment by assignment, coordinators by work phase. */\nexport type Heuristic = \"worker-v1\" | \"coordinator-v1\";\nexport const HEURISTIC_VERSIONS: Readonly<Record<Heuristic, number>> = { \"worker-v1\": 1, \"coordinator-v1\": 1 };\n\nexport interface EpisodeRequest {\n offset: number;\n ts: string;\n transcriptId: number;\n contextTokens: number;\n wakeCause: string | null;\n}\nexport interface EpisodeInbound {\n offset: number;\n ts: string;\n transcriptId: number;\n cause: string;\n}\nexport interface EpisodeSignal {\n offset: number;\n ts: string;\n transcriptId: number;\n signal: string;\n}\nexport interface EpisodeInput {\n requests: readonly EpisodeRequest[];\n inbounds: readonly EpisodeInbound[];\n signals: readonly EpisodeSignal[];\n /** An agent-chat spawn has a brief; anything else opens at `session_start`. */\n spawned: boolean;\n}\n\nconst MINUTE_MS = 60_000;\nexport const IDLE_GAP_MS = 30 * MINUTE_MS;\nexport const CHANNEL_CLUSTER_MS = 10 * MINUTE_MS;\nexport const COORDINATOR_MIN_EPISODE_REQUESTS = 8;\nexport const COORDINATOR_MERGE_DEDUPE_REQUESTS = 15;\nexport const COORDINATOR_WAVE_QUIET_REQUESTS = 15;\nexport const CONTEXT_RESET_TOKENS = 20_000;\nconst DELIVERABLE_SIGNALS = new Set([\"status_report\", \"pr_create\", \"commit\", \"doc_written\", \"task_wrap\"]);\nconst ASSIGNMENT_OPENERS = new Set([\"brief\", \"channel_followup\"]);\n\n/** Pure: the same rows always cut the same way. Rows come back in episode order. */\nexport function buildEpisodes(input: EpisodeInput, heuristic: Heuristic): EpisodeRow[] {\n return heuristic === \"worker-v1\" ? workerEpisodes(input) : coordinatorEpisodes(input);\n}\n\n/** Workers are segmented; human sessions get the coordinator rule; headless runs get none. */\nexport function heuristicFor(sessionClass: SessionClass): Heuristic | null {\n if (sessionClass === \"agent_spawned\") return \"worker-v1\";\n if (sessionClass === \"human_interactive\") return \"coordinator-v1\";\n return null;\n}\n\n/** The worker report's `n_assignments`: episodes an assignment opened, which is what the standing-peer overlay counts. */\nexport function assignmentCount(rows: readonly Pick<EpisodeRow, \"openedBy\">[]): number {\n return rows.filter((row) => ASSIGNMENT_OPENERS.has(row.openedBy)).length;\n}\n\n// ---------------------------------------------------------------- worker-v1\n\ntype Event =\n | { kind: \"request\"; offset: number; ts: string; ms: number; transcriptId: number }\n | { kind: \"inbound\"; offset: number; ts: string; ms: number; transcriptId: number; cause: string }\n | { kind: \"signal\"; offset: number; ts: string; ms: number; transcriptId: number; signal: string };\n\ninterface Draft {\n row: EpisodeRow;\n hasRequest: boolean;\n}\n\ninterface WorkerState {\n drafts: Draft[];\n lastRequestMs: number | null;\n lastChannelMs: number | null;\n}\n\n/** worker-forensics-report.md section 5 and wf_analyze.py:241-251, with the status-report trigger from design section 9. */\nfunction workerEpisodes(input: EpisodeInput): EpisodeRow[] {\n const state: WorkerState = { drafts: [], lastRequestMs: null, lastChannelMs: null };\n for (const event of timeline(input)) {\n const current = state.drafts.at(-1);\n const reason = current ? workerBoundary(event, current, state) : input.spawned ? \"brief\" : \"session_start\";\n if (reason && (!current || current.hasRequest)) state.drafts.push(openDraft(event, reason, state.drafts.length));\n absorb(state.drafts.at(-1)!, event);\n if (event.kind === \"request\") state.lastRequestMs = event.ms;\n if (isChannel(event)) state.lastChannelMs = event.ms;\n }\n return state.drafts.map((draft) => draft.row);\n}\n\nfunction workerBoundary(event: Event, current: Draft, state: WorkerState): string | null {\n if (event.kind === \"request\" && state.lastRequestMs !== null && event.ms - state.lastRequestMs >= IDLE_GAP_MS) return \"idle_gap\";\n if (!isChannel(event)) return null;\n const startsCluster = state.lastChannelMs === null || event.ms - state.lastChannelMs > CHANNEL_CLUSTER_MS;\n return startsCluster && current.row.firstStatusOffset != null ? \"channel_followup\" : null;\n}\n\nfunction isChannel(event: Event): boolean {\n return event.kind === \"inbound\" && event.cause === \"channel_message\";\n}\n\nfunction openDraft(event: Event, openedBy: string, episodeIndex: number): Draft {\n const assignment = ASSIGNMENT_OPENERS.has(openedBy) && event.kind === \"inbound\" ? event.offset : null;\n const row: EpisodeRow = {\n episodeIndex,\n heuristicVersion: HEURISTIC_VERSIONS[\"worker-v1\"],\n startedAt: event.ts,\n endedAt: event.ts,\n startOffset: event.offset,\n endOffset: event.offset,\n openedBy,\n startTranscriptId: event.transcriptId,\n endTranscriptId: event.transcriptId,\n assignmentOffset: assignment,\n firstDeliverableOffset: null,\n firstDeliverableSignal: null,\n firstStatusOffset: null,\n };\n return { row, hasRequest: false };\n}\n\n/** The first commit is recorded as a deliverable but not as a report: commit fires early for implementers (221 of 411). */\nfunction absorb(draft: Draft, event: Event): void {\n draft.row.endedAt = event.ts;\n draft.row.endOffset = event.offset;\n draft.row.endTranscriptId = event.transcriptId;\n if (event.kind === \"request\") draft.hasRequest = true;\n if (event.kind !== \"signal\") return;\n if (draft.row.firstDeliverableOffset == null && DELIVERABLE_SIGNALS.has(event.signal)) {\n draft.row.firstDeliverableOffset = event.offset;\n draft.row.firstDeliverableSignal = event.signal;\n }\n if (draft.row.firstStatusOffset == null && event.signal === \"status_report\") draft.row.firstStatusOffset = event.offset;\n}\n\n/**\n * Offsets only order events inside one transcript, so time orders first, then the transcript a\n * session resumed into, then the offset breaks ties within it.\n */\nfunction timeline(input: EpisodeInput): Event[] {\n const events: Event[] = [\n ...input.requests.map((r) => ({ kind: \"request\" as const, offset: r.offset, ts: r.ts, ms: Date.parse(r.ts), transcriptId: r.transcriptId })),\n ...input.inbounds.map((i) => ({ kind: \"inbound\" as const, offset: i.offset, ts: i.ts, ms: Date.parse(i.ts), transcriptId: i.transcriptId, cause: i.cause })),\n ...input.signals.map((s) => ({ kind: \"signal\" as const, offset: s.offset, ts: s.ts, ms: Date.parse(s.ts), transcriptId: s.transcriptId, signal: s.signal })),\n ];\n return events.sort((a, b) => a.ms - b.ms || a.transcriptId - b.transcriptId || a.offset - b.offset);\n}\n\n// ---------------------------------------------------------------- coordinator-v1\n\ninterface Turn {\n request: EpisodeRequest;\n ms: number;\n signals: Set<string>;\n}\n\ninterface Boundary {\n at: number;\n reason: string;\n}\n\n/** coordinator-forensics-report.md section 1, ported from co_analyze.py `segment` with `merge_lag` 0. */\nfunction coordinatorEpisodes(input: EpisodeInput): EpisodeRow[] {\n const turns = toTurns(input);\n if (turns.length === 0) return [];\n const opens = [{ at: 0, reason: \"session_start\" }, ...dropShort(coordinatorBoundaries(turns), turns.length)];\n return opens.map((open, index) => {\n const first = turns[open.at]!.request;\n const last = turns[(opens[index + 1]?.at ?? turns.length) - 1]!.request;\n return {\n episodeIndex: index,\n heuristicVersion: HEURISTIC_VERSIONS[\"coordinator-v1\"],\n startedAt: first.ts,\n endedAt: last.ts,\n startOffset: first.offset,\n endOffset: last.offset,\n startTranscriptId: first.transcriptId,\n endTranscriptId: last.transcriptId,\n openedBy: open.reason,\n };\n });\n}\n\nfunction coordinatorBoundaries(turns: readonly Turn[]): Boundary[] {\n const bounds: Boundary[] = [];\n let spawnedSince = false;\n let lastMerge = -Infinity;\n for (let i = 1; i < turns.length; i++) {\n const reason = coordinatorTrigger(turns, i, lastMerge) ?? (spawnedSince && waveComplete(turns, i) ? \"spawn_wave_complete\" : null);\n if (turns[i - 1]!.signals.has(\"pr_merge\")) lastMerge = i;\n if (reason) {\n bounds.push({ at: i, reason });\n spawnedSince = false;\n }\n if (turns[i]!.signals.has(\"agent_spawn\")) spawnedSince = true;\n }\n return bounds;\n}\n\n/** Checked in the source's order; the first that fires names the boundary. */\nfunction coordinatorTrigger(turns: readonly Turn[], i: number, lastMerge: number): string | null {\n const [a, b] = [turns[i - 1]!, turns[i]!];\n if (b.ms - a.ms >= IDLE_GAP_MS) return \"idle_gap\";\n if (a.signals.has(\"pr_merge\") && i - lastMerge > COORDINATOR_MERGE_DEDUPE_REQUESTS) return \"pr_merge\";\n if (a.signals.has(\"task_wrap\") || a.signals.has(\"task_done\")) return \"task_wrap\";\n const drop = a.request.contextTokens - b.request.contextTokens;\n if (a.request.contextTokens > 0 && drop > CONTEXT_RESET_TOKENS) return \"context_reset\";\n return null;\n}\n\n/** The last agent-chat traffic before a quiet run; the source also counted any agent-chat tool call, which only reaches the graph as `chat_send` or `agent_spawn`. */\nfunction waveComplete(turns: readonly Turn[], i: number): boolean {\n return hasChatTraffic(turns[i - 1]!) && !turns.slice(i, i + COORDINATOR_WAVE_QUIET_REQUESTS).some(hasChatTraffic);\n}\n\nfunction hasChatTraffic(turn: Turn): boolean {\n return turn.request.wakeCause === \"channel_message\" || turn.signals.has(\"chat_send\") || turn.signals.has(\"agent_spawn\");\n}\n\n/** No episode shorter than the minimum, at either end of the session. */\nfunction dropShort(bounds: readonly Boundary[], total: number): Boundary[] {\n const kept: Boundary[] = [];\n let last = 0;\n for (const bound of bounds) {\n if (bound.at - last < COORDINATOR_MIN_EPISODE_REQUESTS || total - bound.at < COORDINATOR_MIN_EPISODE_REQUESTS) continue;\n kept.push(bound);\n last = bound.at;\n }\n return kept;\n}\n\n/** A signal belongs to the latest request at or before it; one before any request goes to the first. */\nfunction toTurns(input: EpisodeInput): Turn[] {\n const turns = input.requests\n .map((request) => ({ request, ms: Date.parse(request.ts), signals: new Set<string>() }))\n .sort((a, b) => a.ms - b.ms || a.request.transcriptId - b.request.transcriptId || a.request.offset - b.request.offset);\n for (const signal of input.signals) ownerOf(turns, signal)?.signals.add(signal.signal);\n return turns;\n}\n\nfunction ownerOf(turns: readonly Turn[], signal: EpisodeSignal): Turn | undefined {\n const ms = Date.parse(signal.ts);\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]!;\n if (turn.ms < ms || (turn.ms === ms && turn.request.offset <= signal.offset)) return turn;\n }\n return turns[0];\n}\n\n// ---------------------------------------------------------------- graph I/O\n\nconst IN_SESSION = \"session_id = ?\";\n\n/**\n * `request_dedup` for one session without ranking every request: SQLite cannot push the session filter\n * through the view's window. Copies of a request can sit in other sessions, so the rank still covers\n * every copy of each request id the session holds.\n */\nconst SESSION_REQUEST_DEDUP = `\n SELECT * FROM (\n SELECT r.*, ROW_NUMBER() OVER (PARTITION BY r.request_id ORDER BY r.ts, r.transcript_id) AS copy_rank\n FROM request r WHERE r.request_id IN (SELECT request_id FROM request WHERE session_id = @sessionId)\n ) WHERE copy_rank = 1 AND session_id = @sessionId`;\n\n/** Main-thread requests only: a sidechain's requests belong to its subagent, not to the session's episodes. */\nexport const EPISODE_REQUESTS_SQL = `\n SELECT byte_offset AS offset, ts, transcript_id AS transcriptId, context_tokens AS contextTokens, wake_cause AS wakeCause\n FROM (${SESSION_REQUEST_DEDUP}) WHERE is_sidechain = 0`;\n\nexport function readEpisodeInput(db: Db, sessionId: string, spawned: boolean): EpisodeInput {\n const requests = db.prepare(EPISODE_REQUESTS_SQL).all({ sessionId }) as EpisodeRequest[];\n const inbounds = db\n .prepare(`SELECT byte_offset AS offset, ts, transcript_id AS transcriptId, cause FROM inbound WHERE ${IN_SESSION}`)\n .all(sessionId) as EpisodeInbound[];\n const signals = db\n .prepare(`SELECT byte_offset AS offset, ts, transcript_id AS transcriptId, signal FROM session_signal WHERE ${IN_SESSION}`)\n .all(sessionId) as EpisodeSignal[];\n return { requests, inbounds, signals, spawned };\n}\n\nexport interface WrittenEpisodes {\n sessionId: string;\n heuristic: Heuristic;\n episodes: number;\n}\n\n/** Segments each session with its class's heuristic and replaces that heuristic's rows. Headless sessions are skipped. */\nexport function writeEpisodes(graph: SessionGraph, sessionIds: readonly string[]): WrittenEpisodes[] {\n const written: WrittenEpisodes[] = [];\n for (const [sessionId, context] of readSessionContexts(graph.db, sessionIds)) {\n const { sessionClass } = classifySession(context.facts);\n const heuristic = heuristicFor(sessionClass);\n if (!heuristic) continue;\n const rows = buildEpisodes(readEpisodeInput(graph.db, sessionId, sessionClass === \"agent_spawned\"), heuristic);\n written.push({ sessionId, heuristic, episodes: replaceEpisodes(graph, sessionId, heuristic, rows) });\n }\n return written;\n}\n","/** Initiatives a session's task edges name, with how many edges name each. */\nexport interface TaskInitiative {\n initiative: string;\n edges: number;\n}\n\n/**\n * A task edge names the initiative outright, so it wins over the cwd guess. Among several,\n * the most-named initiative wins and a tie goes to the alphabetically first, for a stable report.\n */\nexport function sessionInitiative(tasks: readonly TaskInitiative[], cwd: string | null | undefined): string {\n const best = [...tasks].sort((a, b) => b.edges - a.edges || a.initiative.localeCompare(b.initiative))[0];\n return best ? best.initiative : initiativeFromCwd(cwd);\n}\n\n/** The cwd rule from `cf_analyze.py` `initiative()`. A worktree under a repo counts as that repo. */\nexport function initiativeFromCwd(cwd: string | null | undefined): string {\n if (!cwd) return \"unknown\";\n const repo = /\\/projects\\/([^/]+)/.exec(cwd);\n if (repo) return `repo:${repo[1]}`;\n const activeWork = /active-work\\/([^/]+)/.exec(cwd);\n if (activeWork) return `active-work:${activeWork[1]}`;\n if (/ac-fork-\\w+/.test(cwd)) return \"ac-fork(tmp)\";\n const trimmed = cwd.replace(/\\/+$/, \"\");\n return `other:${trimmed.slice(trimmed.lastIndexOf(\"/\") + 1)}`;\n}\n","import type { SessionClassification } from \"./classify-session.js\";\n\n/** worker-v1's role vocabulary, from worker-forensics-report.md section 2. */\nexport type WorkerRole = \"implementer\" | \"reviewer\" | \"researcher\" | \"planner\" | \"standing_peer\" | \"unknown\";\n\n/** Spawn profile to role, as the worker report's table (wf_analyze.py:79-91) maps it. */\nexport const PROFILE_ROLES: Readonly<Record<string, WorkerRole>> = {\n implementer: \"implementer\",\n \"implementer-lite\": \"implementer\",\n \"relay-implementer\": \"implementer\",\n \"beat-builder\": \"implementer\",\n \"fable-optimizer\": \"implementer\",\n reviewer: \"reviewer\",\n \"relay-reviewer\": \"reviewer\",\n researcher: \"researcher\",\n explorer: \"researcher\",\n \"fable-architect\": \"planner\",\n peer: \"standing_peer\",\n};\n\nexport const STANDING_PEER_MIN_HOURS = 12;\nexport const STANDING_PEER_MIN_ASSIGNMENTS = 2;\nconst HOUR_MS = 3_600_000;\n\nexport interface WorkerFacts {\n profile?: string | null;\n /** First request to last request, over the whole session rather than a report window. */\n lifetimeMs: number;\n /** worker-v1 episodes opened by an assignment (the brief or a channel follow-up), not by an idle gap. */\n assignments: number;\n}\n\nexport function roleFromProfile(profile: string | null | undefined): WorkerRole {\n return PROFILE_ROLES[(profile ?? \"\").toLowerCase()] ?? \"unknown\";\n}\n\n/** A worker that outlives its first assignment is a standing peer whatever it was spawned as (wf_analyze.py:94-103). */\nexport function workerRole(facts: WorkerFacts): WorkerRole {\n const outlived = facts.lifetimeMs >= STANDING_PEER_MIN_HOURS * HOUR_MS && facts.assignments >= STANDING_PEER_MIN_ASSIGNMENTS;\n return outlived ? \"standing_peer\" : roleFromProfile(facts.profile);\n}\n\n/** The report's role key: `coordinator` or `adhoc` for humans, `worker:<role>` for spawned agents, else the class. */\nexport function sessionRole(classification: SessionClassification, worker: WorkerFacts): string {\n if (classification.humanRole) return classification.humanRole;\n if (classification.sessionClass === \"agent_spawned\") return `worker:${workerRole(worker)}`;\n return classification.sessionClass;\n}\n","import type { CostBucket, CostReport, WakeGapCell } from \"./cost-report.js\";\nimport { PRICE_TABLE_VERSION } from \"./prices.js\";\n\nexport const LIST_PRICE_CAVEAT =\n \"List prices, not a bill: the accounts behind these sessions may run on subscription plans. Rankings hold; the absolute figure is not what you paid.\";\n\ntype Cell = string | number;\n\n/** The same report as plain-text tables, one section per report field, caveat and footer last. */\nexport function renderCostReportText(report: CostReport): string {\n const sections = [\n header(report),\n table(\"By token class\", [\"class\", \"tokens\", \"cost\"], report.byTokenClass.map((row) => [row.tokenClass, row.tokens, usd(row.costUsd)])),\n bucketTable(\"By account\", report.byAccount, report.totals.costUsd),\n bucketTable(\"By model\", report.byModel, report.totals.costUsd),\n bucketTable(\"By class\", report.byClass, report.totals.costUsd),\n bucketTable(\"By role\", report.byRole, report.totals.costUsd),\n bucketTable(\"By episode count\", report.byEpisodeCount, report.totals.costUsd),\n bucketTable(\"By initiative\", report.byInitiative, report.totals.costUsd),\n bucketTable(\"By context band\", report.byContextBand, report.totals.costUsd),\n wakeCauseTable(report),\n cellTable(\"Wake cause by gap band\", report.wakeCauseByGapBand),\n cellTable(`Cold rebuilds: ${report.coldRebuild.requests} requests, ${usd(report.coldRebuild.costUsd)}`, report.coldRebuild.byGapBandAndCause),\n compactionTable(report.compactions),\n topSessionTable(report.topSessions),\n unpricedTable(report.unpricedModels),\n [LIST_PRICE_CAVEAT, footer(report)].join(\"\\n\"),\n ];\n return sections.join(\"\\n\\n\") + \"\\n\";\n}\n\nfunction header(report: CostReport): string {\n const { since, until } = report.window;\n const { requests, sessions, costUsd } = report.totals;\n return `Cost report, ${since ?? \"beginning\"} to ${until ?? \"now\"}\\nTotal ${usd(costUsd)} over ${requests} requests in ${sessions} sessions`;\n}\n\nfunction bucketTable(title: string, buckets: readonly CostBucket[], total: number): string {\n const rows = buckets.map((b) => [b.key, b.requests, b.sessions, usd(b.costUsd), share(b.costUsd, total)]);\n return table(title, [\"key\", \"requests\", \"sessions\", \"cost\", \"share\"], rows);\n}\n\n/** Q4: answers sit under `human`, with typed text and answers indented one level down. */\nfunction wakeCauseTable(report: CostReport): string {\n const rows = report.byWakeCause.flatMap((group) => [\n [group.key, group.requests, usd(group.costUsd), group.midLoopRequests, usd(group.midLoopCostUsd)],\n ...group.parts.map((part) => [` ${part.key}`, part.requests, usd(part.costUsd), part.midLoopRequests, usd(part.midLoopCostUsd)]),\n ]);\n return table(\"By wake cause\", [\"cause\", \"requests\", \"cost\", \"mid-loop\", \"mid-loop cost\"], rows);\n}\n\nfunction cellTable(title: string, cells: readonly WakeGapCell[]): string {\n return table(title, [\"cause\", \"gap\", \"requests\", \"cost\"], cells.map((c) => [c.wakeCause, c.gapBand, c.requests, usd(c.costUsd)]));\n}\n\nfunction compactionTable(c: CostReport[\"compactions\"]): string {\n return table(\"Compactions\", [\"total\", \"manual\", \"auto\", \"mid-loop\", \"dropped tokens\"], [[c.total, c.manual, c.auto, c.midLoop, c.droppedTokens]]);\n}\n\nfunction topSessionTable(sessions: CostReport[\"topSessions\"]): string {\n const rows = sessions.map((s) => [s.sessionId, s.sessionClass, s.role, s.initiative, s.requests, usd(s.costUsd)]);\n return table(\"Top sessions\", [\"session\", \"class\", \"role\", \"initiative\", \"requests\", \"cost\"], rows);\n}\n\nfunction unpricedTable(models: CostReport[\"unpricedModels\"]): string {\n if (models.length === 0) return \"Unpriced models: none\";\n return table(\"Unpriced models (counted at zero cost)\", [\"model\", \"requests\", \"tokens\"], models.map((m) => [m.model, m.requests, m.tokens]));\n}\n\nfunction footer(report: CostReport): string {\n const { transcriptsIndexed, transcriptsDiscovered, facetBacklog } = report.coverage;\n const discovered = transcriptsDiscovered === null ? \"\" : ` of ${transcriptsDiscovered} discovered`;\n const version = report.priceTableVersion === null ? \"none\" : `v${report.priceTableVersion}`;\n const stale = report.priceTableVersion !== null && report.priceTableVersion !== PRICE_TABLE_VERSION ? ` (package has v${PRICE_TABLE_VERSION})` : \"\";\n return `Price table ${version}${stale}. Coverage: ${transcriptsIndexed} transcripts indexed${discovered}, facet backlog ${facetBacklog}.`;\n}\n\nfunction table(title: string, head: readonly string[], rows: readonly (readonly Cell[])[]): string {\n if (rows.length === 0) return `${title}: none`;\n const text = [head, ...rows].map((row) => row.map(String));\n const widths = head.map((_, i) => Math.max(...text.map((row) => row[i]!.length)));\n const rightAligned = head.map((_, i) => rows.every((row) => isNumeric(row[i]!)));\n const pad = (cell: string, i: number) => (rightAligned[i] ? cell.padStart(widths[i]!) : cell.padEnd(widths[i]!));\n const lines = text.map((row) => row.map(pad).join(\" \").trimEnd());\n return [title, ...lines].join(\"\\n\");\n}\n\nfunction isNumeric(cell: Cell): boolean {\n return typeof cell === \"number\" || /^\\$|%$/.test(cell);\n}\n\nfunction usd(value: number): string {\n return `$${value.toFixed(2)}`;\n}\n\nfunction share(value: number, total: number): string {\n return total > 0 ? `${((value / total) * 100).toFixed(1)}%` : \"-\";\n}\n"],"mappings":"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","names":["gapBand"]}
|
|
1
|
+
{"version":3,"sources":["../src/prices.ts","../src/price-request.ts","../src/classify-session.ts","../src/bands.ts","../src/cost-report.ts","../src/cost-report-queries.ts","../src/episodes.ts","../src/initiative.ts","../src/roles.ts","../src/render-text.ts"],"sourcesContent":["/** USD per million tokens. Fitted against 392 `cost-state` rows in the 2026-09-20 audit. */\nexport interface PriceRow {\n /** Matched at a model-id boundary: the id itself, or followed by a -YYYYMMDD or [..] suffix. */\n modelPrefix: string;\n /** ISO date. The latest row at or before a request's timestamp wins. */\n effectiveFrom: string;\n input: number;\n cacheRead: number;\n cacheWrite5m: number;\n cacheWrite1h: number;\n output: number;\n}\n\n/** Bump on any edit to PRICE_TABLE. The cost report prints it in its footer. */\nexport const PRICE_TABLE_VERSION = 2;\n\nconst OPUS = { input: 5.0, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10.0, output: 25.0 };\nconst OPUS_5_5 = { input: 4.0, cacheRead: 0.2, cacheWrite5m: 5.0, cacheWrite1h: 8.0, output: 20.0 };\nconst SONNET = { input: 2.0, cacheRead: 0.2, cacheWrite5m: 2.5, cacheWrite1h: 4.0, output: 10.0 };\n// Fable's cache read is 0.025 of input, not the 0.1 every other model uses. Reading it as\n// 0.1 overstated the 2026-09-20 audit by about $1,700; `fable-cache-read.test` pins the ratio.\nconst FABLE = { input: 10.0, cacheRead: 0.25, cacheWrite5m: 12.5, cacheWrite1h: 20.0, output: 50.0 };\nconst SONNET_5_5 = { input: 2.0, cacheRead: 0.2, cacheWrite5m: 2.5, cacheWrite1h: 4.0, output: 10.0 };\nconst HAIKU = { input: 1.0, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2.0, output: 5.0 };\nconst FREE = { input: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0, output: 0 };\n\nconst GENESIS = \"2026-01-01\";\n\nexport const PRICE_TABLE: readonly PriceRow[] = [\n // 5.5 rows: claude-api skill shared/models.md and prompt-caching.md, bundled copy cached 2026-09-25.\n { modelPrefix: \"claude-opus-5-5\", effectiveFrom: GENESIS, ...OPUS_5_5 },\n { modelPrefix: \"claude-opus-5\", effectiveFrom: GENESIS, ...OPUS },\n { modelPrefix: \"claude-opus-4-8\", effectiveFrom: GENESIS, ...OPUS },\n { modelPrefix: \"claude-sonnet-5-5\", effectiveFrom: GENESIS, ...SONNET_5_5 },\n { modelPrefix: \"claude-sonnet-5\", effectiveFrom: GENESIS, ...SONNET },\n { modelPrefix: \"claude-fable-5-1\", effectiveFrom: GENESIS, ...FABLE },\n { modelPrefix: \"claude-fable-5\", effectiveFrom: GENESIS, ...FABLE },\n { modelPrefix: \"claude-haiku-4-5\", effectiveFrom: GENESIS, ...HAIKU },\n { modelPrefix: \"<synthetic>\", effectiveFrom: GENESIS, ...FREE },\n];\n\n/**\n * The longest prefix that matches the model at an id boundary, then the latest row effective at `ts`.\n * Returns null for a model no row covers: an unknown model is unpriced, never defaulted,\n * because a default silently prices a new model at an old model's rate.\n */\nexport function findPrice(model: string, ts: string, prices: readonly PriceRow[] = PRICE_TABLE): PriceRow | null {\n let best: PriceRow | null = null;\n for (const row of prices) {\n if (!matchesAtBoundary(model, row.modelPrefix)) continue;\n if (row.effectiveFrom > ts) continue;\n if (best && !isBetterMatch(row, best)) continue;\n best = row;\n }\n return best;\n}\n\nfunction isBetterMatch(row: PriceRow, best: PriceRow): boolean {\n if (row.modelPrefix.length !== best.modelPrefix.length) return row.modelPrefix.length > best.modelPrefix.length;\n return row.effectiveFrom > best.effectiveFrom;\n}\n\n// A bare startsWith lets claude-opus-5 price claude-opus-5-5 at the older model's rates.\nfunction matchesAtBoundary(model: string, prefix: string): boolean {\n if (!model.startsWith(prefix)) return false;\n const rest = model.slice(prefix.length);\n return rest === \"\" || rest.startsWith(\"[\") || /^-\\d{8}(\\[.*\\])?$/.test(rest);\n}\n","import type { PriceRow } from \"./prices.js\";\nimport { PRICE_TABLE, findPrice } from \"./prices.js\";\n\n/** Raw token counts for one API request, as the transcript reports them. */\nexport interface RequestTokens {\n inputTokens?: number;\n cacheReadTokens?: number;\n /** Total cache creation. Used only when the 5m/1h split is absent. */\n cacheCreationTokens?: number;\n cacheCreation5mTokens?: number;\n cacheCreation1hTokens?: number;\n outputTokens?: number;\n}\n\nexport interface PricedRequest {\n inputUsd: number;\n cacheReadUsd: number;\n cacheWrite5mUsd: number;\n cacheWrite1hUsd: number;\n outputUsd: number;\n costUsd: number;\n /** False when no price row covers the model. Every component is then zero. */\n priced: boolean;\n}\n\nconst UNPRICED: PricedRequest = {\n inputUsd: 0,\n cacheReadUsd: 0,\n cacheWrite5mUsd: 0,\n cacheWrite1hUsd: 0,\n outputUsd: 0,\n costUsd: 0,\n priced: false,\n};\n\nexport function priceRequest(\n tokens: RequestTokens,\n model: string,\n ts: string,\n prices: readonly PriceRow[] = PRICE_TABLE,\n): PricedRequest {\n const row = findPrice(model, ts, prices);\n if (!row) return { ...UNPRICED };\n\n const { write5m, write1h } = splitCacheCreation(tokens);\n const inputUsd = perMTok(tokens.inputTokens, row.input);\n const cacheReadUsd = perMTok(tokens.cacheReadTokens, row.cacheRead);\n const cacheWrite5mUsd = perMTok(write5m, row.cacheWrite5m);\n const cacheWrite1hUsd = perMTok(write1h, row.cacheWrite1h);\n const outputUsd = perMTok(tokens.outputTokens, row.output);\n const costUsd = inputUsd + cacheReadUsd + cacheWrite5mUsd + cacheWrite1hUsd + outputUsd;\n return { inputUsd, cacheReadUsd, cacheWrite5mUsd, cacheWrite1hUsd, outputUsd, costUsd, priced: true };\n}\n\n/**\n * Older transcripts carry a cache_creation total with no ttl breakdown. Charging the\n * 5m rate there under-reads rather than over-reads, and 5m is the harness default.\n */\nfunction splitCacheCreation(tokens: RequestTokens): { write5m: number; write1h: number } {\n const write5m = tokens.cacheCreation5mTokens ?? 0;\n const write1h = tokens.cacheCreation1hTokens ?? 0;\n if (write5m > 0 || write1h > 0) return { write5m, write1h };\n return { write5m: tokens.cacheCreationTokens ?? 0, write1h: 0 };\n}\n\nfunction perMTok(tokens: number | undefined, ratePerMTok: number): number {\n return ((tokens ?? 0) * ratePerMTok) / 1_000_000;\n}\n","export type SessionClass = \"agent_spawned\" | \"human_interactive\" | \"headless_sdk\" | \"other_headless\";\n\n/** Human sessions only. A coordinator drives other agents; an adhoc session does not. */\nexport type HumanRole = \"coordinator\" | \"adhoc\";\n\n/** The agent-chat spawn record for a session, as the origin resolver returns it. */\nexport interface SessionOrigin {\n depth: number;\n parentName?: string | null;\n /** \"adopted\" and \"inherited\" mark a pane the human took over, not a spawned worker. */\n originKind?: string | null;\n profile?: string | null;\n}\n\nexport interface SessionFacts {\n /** The harness entrypoint. Agent-chat workers and `-p` miners both report \"sdk-cli\". */\n startType?: string | null;\n origin?: SessionOrigin | null;\n firstUserText?: string | null;\n humanTurnCount?: number;\n inboundKinds?: readonly string[];\n signalKinds?: readonly string[];\n}\n\nexport interface SessionClassification {\n sessionClass: SessionClass;\n reason: string;\n humanRole: HumanRole | null;\n}\n\nconst COORDINATOR_SIGNALS = new Set([\"agent_spawn\", \"chat_send\"]);\n\n/**\n * Rule order matters. The origin row wins over `startType` because agent-chat workers\n * run `claude -p` and so report \"sdk-cli\" exactly like a headless miner does.\n */\nexport function classifySession(facts: SessionFacts): SessionClassification {\n const [sessionClass, reason] = classOf(facts);\n const humanRole = sessionClass === \"human_interactive\" ? humanRoleOf(facts) : null;\n return { sessionClass, reason, humanRole };\n}\n\nfunction classOf(facts: SessionFacts): [SessionClass, string] {\n const origin = facts.origin;\n if (origin) {\n if (isAdoptedPane(origin)) return [\"human_interactive\", \"origin depth 0, human or adopted parent\"];\n return [\"agent_spawned\", \"origin depth >= 1\"];\n }\n if (facts.startType === \"sdk-cli\") return [\"headless_sdk\", \"start_type sdk-cli with no origin row\"];\n if (isProgrammaticPrompt(facts)) return [\"other_headless\", \"content-block prompt with <= 2 human turns\"];\n return [\"human_interactive\", \"no origin row, no headless marker\"];\n}\n\nfunction isAdoptedPane(origin: SessionOrigin): boolean {\n if (origin.depth !== 0) return false;\n if (origin.parentName === \"human\") return true;\n if (origin.originKind === \"adopted\" || origin.originKind === \"inherited\") return true;\n return !origin.profile;\n}\n\n/** Cron and RemoteTrigger daemons inject a structured content block, never typed prose. */\nfunction isProgrammaticPrompt(facts: SessionFacts): boolean {\n const text = (facts.firstUserText ?? \"\").trimStart();\n return text.startsWith(\"[{\") && (facts.humanTurnCount ?? 0) <= 2;\n}\n\nfunction humanRoleOf(facts: SessionFacts): HumanRole {\n if (facts.inboundKinds?.includes(\"channel_message\")) return \"coordinator\";\n if (facts.signalKinds?.some((kind) => COORDINATOR_SIGNALS.has(kind))) return \"coordinator\";\n return \"adhoc\";\n}\n","/** Half-open [lo, hi) in the band's own unit. */\nexport interface Band {\n lo: number;\n hi: number;\n name: string;\n}\n\n/** Context size in tokens. From `cf_analyze.py` BANDS. */\nexport const CONTEXT_BANDS: readonly Band[] = [\n { lo: 0, hi: 50_000, name: \"<50k\" },\n { lo: 50_000, hi: 100_000, name: \"50-100k\" },\n { lo: 100_000, hi: 200_000, name: \"100-200k\" },\n { lo: 200_000, hi: Number.POSITIVE_INFINITY, name: \"200k+\" },\n];\n\n/** Idle gap in milliseconds. From `cf_analyze.py` IDLE. */\nexport const GAP_BANDS: readonly Band[] = [\n { lo: 0, hi: 5 * 60_000, name: \"<5m\" },\n { lo: 5 * 60_000, hi: 60 * 60_000, name: \"5-60m\" },\n { lo: 60 * 60_000, hi: Number.POSITIVE_INFINITY, name: \">60m\" },\n];\n\n/** Null for a value no band covers, so a caller reports the gap instead of inventing one. */\nexport function bandOf(value: number, bands: readonly Band[]): string | null {\n for (const band of bands) {\n if (value >= band.lo && value < band.hi) return band.name;\n }\n return null;\n}\n\nexport function contextBand(tokens: number): string | null {\n return bandOf(tokens, CONTEXT_BANDS);\n}\n\nexport function gapBand(millis: number): string | null {\n return bandOf(millis, GAP_BANDS);\n}\n","import type { Db } from \"@titan-design/store-sqlite\";\nimport { z } from \"zod\";\nimport { classifySession, type SessionClass } from \"./classify-session.js\";\nimport {\n readCompactions,\n readCostRows,\n readCoverage,\n readPriceTableVersion,\n readSessionContexts,\n type CompactionRow,\n type CostRow,\n type ReportWindow,\n type SessionContext,\n} from \"./cost-report-queries.js\";\nimport { assignmentCount, heuristicFor } from \"./episodes.js\";\nimport { sessionInitiative } from \"./initiative.js\";\nimport { sessionRole } from \"./roles.js\";\n\nconst count = z.number().int().nonnegative();\nconst bucketSchema = z.object({ key: z.string(), requests: count, sessions: count, costUsd: z.number() });\nconst wakePartSchema = bucketSchema.extend({ midLoopRequests: count, midLoopCostUsd: z.number() });\nconst cellSchema = z.object({ wakeCause: z.string(), gapBand: z.string(), requests: count, costUsd: z.number() });\n\nexport const TOKEN_CLASSES = [\"input\", \"cache_read\", \"cache_write_5m\", \"cache_write_1h\", \"output\"] as const;\nexport type TokenClass = (typeof TOKEN_CLASSES)[number];\n\nexport const costReportSchema = z.object({\n window: z.object({ since: z.string().nullable(), until: z.string().nullable() }),\n priceTableVersion: z.number().int().nullable(),\n totals: z.object({ requests: count, sessions: count, costUsd: z.number(), unpricedRequests: count }),\n byTokenClass: z.array(z.object({ tokenClass: z.enum(TOKEN_CLASSES), tokens: count, costUsd: z.number() })),\n byAccount: z.array(bucketSchema),\n byModel: z.array(bucketSchema),\n byClass: z.array(bucketSchema),\n byRole: z.array(bucketSchema),\n /** Sessions by how many episodes their class's heuristic cut them into; `none` when not yet segmented. */\n byEpisodeCount: z.array(bucketSchema),\n byInitiative: z.array(bucketSchema),\n byContextBand: z.array(bucketSchema),\n byWakeCause: z.array(wakePartSchema.extend({ parts: z.array(wakePartSchema) })),\n wakeCauseByGapBand: z.array(cellSchema),\n coldRebuild: z.object({ requests: count, costUsd: z.number(), byGapBandAndCause: z.array(cellSchema) }),\n compactions: z.object({ total: count, manual: count, auto: count, midLoop: count, droppedTokens: count }),\n topSessions: z.array(\n z.object({\n sessionId: z.string(),\n sessionClass: z.string(),\n role: z.string(),\n initiative: z.string(),\n account: z.string(),\n requests: count,\n costUsd: z.number(),\n }),\n ),\n unpricedModels: z.array(z.object({ model: z.string(), requests: count, tokens: count })),\n coverage: z.object({ transcriptsIndexed: count, transcriptsDiscovered: count.nullable(), facetBacklog: count }),\n});\n\nexport type CostReport = z.infer<typeof costReportSchema>;\nexport type CostBucket = z.infer<typeof bucketSchema>;\nexport type WakeCauseBucket = CostReport[\"byWakeCause\"][number];\nexport type WakeGapCell = z.infer<typeof cellSchema>;\n\nexport interface CostReportOptions {\n /** ISO timestamp or date, inclusive. Overrides `days`. */\n since?: string;\n /** ISO timestamp or date, exclusive. */\n until?: string;\n /** Window length ending at `until`, or at `now` when `until` is absent. */\n days?: number;\n now?: Date;\n /** Sessions listed in `topSessions`. */\n top?: number;\n /** Transcripts the caller discovered on disk; the graph cannot know this. */\n transcriptsDiscovered?: number;\n /** The extraction version an audit facet must reach to count as current. */\n facetVersion?: number;\n}\n\n/** AskUserQuestion answers are the human answering, so they count under `human` (decisions Q4). */\nconst HUMAN_CAUSES: readonly string[] = [\"human_typed\", \"ask_user_answer\"];\nconst NONE = \"none\";\nconst DEFAULT_TOP = 10;\nconst EPISODE_COUNT_CAP = 5;\nconst DAY_MS = 86_400_000;\n\ninterface SessionTags {\n sessionClass: SessionClass;\n role: string;\n episodeCount: string;\n initiative: string;\n account: string;\n}\n\ntype TaggedRow = CostRow & SessionTags;\n\n/** Reads the graph and never writes it, so a read-only connection is enough. */\nexport function costReport(db: Db, options: CostReportOptions = {}): CostReport {\n const window = resolveWindow(options);\n const costRows = readCostRows(db, window);\n const sessionIds = [...new Set(costRows.map((row) => row.sessionId))];\n const tags = tagSessions(readSessionContexts(db, sessionIds));\n const rows = costRows.map((row) => ({ ...row, ...tags.get(row.sessionId)! }));\n return {\n window,\n priceTableVersion: readPriceTableVersion(db),\n totals: { ...sumOf(rows), sessions: sessionIds.length, unpricedRequests: rows.filter((row) => !row.priced).length },\n byTokenClass: tokenClasses(rows),\n ...dimensionBuckets(rows),\n byWakeCause: wakeCauses(rows),\n wakeCauseByGapBand: gapCells(rows),\n coldRebuild: coldRebuild(rows),\n compactions: compactionCounts(readCompactions(db, window)),\n topSessions: topSessions(rows, options.top ?? DEFAULT_TOP),\n unpricedModels: unpricedModels(rows),\n coverage: { ...readCoverage(db, options.facetVersion ?? null), transcriptsDiscovered: options.transcriptsDiscovered ?? null },\n };\n}\n\nexport function resolveWindow(options: CostReportOptions): ReportWindow {\n const until = options.until ?? null;\n if (options.since !== undefined) return { since: options.since, until };\n if (options.days === undefined) return { since: null, until };\n const end = until ? Date.parse(until) : (options.now ?? new Date()).getTime();\n return { since: new Date(end - options.days * DAY_MS).toISOString(), until };\n}\n\nfunction tagSessions(contexts: Map<string, SessionContext>): Map<string, SessionTags> {\n const tags = new Map<string, SessionTags>();\n for (const [sessionId, context] of contexts) {\n const classification = classifySession(context.facts);\n const episodes = context.episodes.filter((episode) => episode.heuristic === heuristicFor(classification.sessionClass));\n const worker = { profile: context.facts.origin?.profile, lifetimeMs: context.lifetimeMs, assignments: assignmentCount(episodes) };\n tags.set(sessionId, {\n sessionClass: classification.sessionClass,\n role: sessionRole(classification, worker),\n episodeCount: episodeCountKey(episodes.length),\n initiative: sessionInitiative(context.tasks, context.cwd),\n account: context.account ?? \"unknown\",\n });\n }\n return tags;\n}\n\n/** The worker report's assignment buckets: 1, 2, 3, 4, 5+. */\nfunction episodeCountKey(episodes: number): string {\n if (episodes === 0) return NONE;\n return episodes >= EPISODE_COUNT_CAP ? `${EPISODE_COUNT_CAP}+` : String(episodes);\n}\n\nfunction sumOf(rows: readonly CostRow[]): { requests: number; costUsd: number } {\n return { requests: rows.length, costUsd: rows.reduce((sum, row) => sum + row.costUsd, 0) };\n}\n\nfunction tokenClasses(rows: readonly CostRow[]): CostReport[\"byTokenClass\"] {\n const pick: Record<TokenClass, (row: CostRow) => [number, number]> = {\n input: (row) => [row.inputTokens, row.inputCostUsd],\n cache_read: (row) => [row.cacheReadTokens, row.cacheReadCostUsd],\n cache_write_5m: (row) => [row.cacheWrite5mTokens, row.cacheWrite5mCostUsd],\n cache_write_1h: (row) => [row.cacheWrite1hTokens, row.cacheWrite1hCostUsd],\n output: (row) => [row.outputTokens, row.outputCostUsd],\n };\n return TOKEN_CLASSES.map((tokenClass) => {\n const pairs = rows.map(pick[tokenClass]);\n return { tokenClass, tokens: pairs.reduce((sum, [tokens]) => sum + tokens, 0), costUsd: pairs.reduce((sum, [, cost]) => sum + cost, 0) };\n });\n}\n\nfunction dimensionBuckets(rows: readonly TaggedRow[]) {\n return {\n byAccount: bucketsBy(rows, (row) => row.account),\n byModel: bucketsBy(rows, (row) => row.model),\n byClass: bucketsBy(rows, (row) => row.sessionClass),\n byRole: bucketsBy(rows, (row) => row.role),\n byEpisodeCount: bucketsBy(rows, (row) => row.episodeCount),\n byInitiative: bucketsBy(rows, (row) => row.initiative),\n byContextBand: bucketsBy(rows, (row) => row.contextBand),\n };\n}\n\n/** Most expensive first; ties by key so the report is stable. */\nfunction bucketsBy<T extends CostRow>(rows: readonly T[], keyOf: (row: T) => string): CostBucket[] {\n const groups = groupRows(rows, keyOf);\n return [...groups]\n .map(([key, members]) => ({ key, ...sumOf(members), sessions: new Set(members.map((row) => row.sessionId)).size }))\n .sort(byCostThenKey);\n}\n\nfunction groupRows<T>(rows: readonly T[], keyOf: (row: T) => string): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n for (const row of rows) {\n const key = keyOf(row);\n const members = groups.get(key);\n if (members) members.push(row);\n else groups.set(key, [row]);\n }\n return groups;\n}\n\nfunction byCostThenKey(a: { key: string; costUsd: number }, b: { key: string; costUsd: number }): number {\n return b.costUsd - a.costUsd || a.key.localeCompare(b.key);\n}\n\nfunction wakeCauses(rows: readonly CostRow[]): WakeCauseBucket[] {\n const causeOf = (row: CostRow) => row.wakeCause ?? NONE;\n const groupOf = (row: CostRow) => (HUMAN_CAUSES.includes(causeOf(row)) ? \"human\" : causeOf(row));\n return [...groupRows(rows, groupOf)]\n .map(([key, members]) => ({\n ...wakePart(key, members),\n parts: key === \"human\" ? [...groupRows(members, causeOf)].map(([cause, part]) => wakePart(cause, part)).sort(byCostThenKey) : [],\n }))\n .sort(byCostThenKey);\n}\n\nfunction wakePart(key: string, rows: readonly CostRow[]) {\n const midLoop = rows.filter((row) => row.wakeDelivery === \"mid_loop\");\n const sessions = new Set(rows.map((row) => row.sessionId)).size;\n return { key, ...sumOf(rows), sessions, midLoopRequests: midLoop.length, midLoopCostUsd: sumOf(midLoop).costUsd };\n}\n\nfunction gapCells(rows: readonly CostRow[]): WakeGapCell[] {\n const keyOf = (row: CostRow) => JSON.stringify([row.wakeCause ?? NONE, row.gapBand ?? NONE]);\n return [...groupRows(rows, keyOf)]\n .map(([key, members]) => {\n const [wakeCause, gapBand] = JSON.parse(key) as [string, string];\n return { wakeCause, gapBand, ...sumOf(members) };\n })\n .sort((a, b) => b.costUsd - a.costUsd || a.wakeCause.localeCompare(b.wakeCause) || a.gapBand.localeCompare(b.gapBand));\n}\n\nfunction coldRebuild(rows: readonly CostRow[]): CostReport[\"coldRebuild\"] {\n const cold = rows.filter((row) => row.isCold);\n return { ...sumOf(cold), byGapBandAndCause: gapCells(cold) };\n}\n\nfunction compactionCounts(rows: readonly CompactionRow[]): CostReport[\"compactions\"] {\n return {\n total: rows.length,\n manual: rows.filter((row) => row.trigger === \"manual\").length,\n auto: rows.filter((row) => row.trigger === \"auto\").length,\n midLoop: rows.filter((row) => row.midLoop).length,\n droppedTokens: rows.reduce((sum, row) => sum + (row.droppedTokens ?? 0), 0),\n };\n}\n\nfunction topSessions(rows: readonly TaggedRow[], top: number): CostReport[\"topSessions\"] {\n return [...groupRows(rows, (row) => row.sessionId)]\n .map(([sessionId, members]) => {\n const { sessionClass, role, initiative, account } = members[0]!;\n return { sessionId, sessionClass, role, initiative, account, ...sumOf(members) };\n })\n .sort((a, b) => b.costUsd - a.costUsd || a.sessionId.localeCompare(b.sessionId))\n .slice(0, top);\n}\n\nfunction unpricedModels(rows: readonly CostRow[]): CostReport[\"unpricedModels\"] {\n const unpriced = rows.filter((row) => !row.priced);\n return [...groupRows(unpriced, (row) => row.model)]\n .map(([model, members]) => ({ model, requests: members.length, tokens: members.reduce((sum, row) => sum + tokensOf(row), 0) }))\n .sort((a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model));\n}\n\nfunction tokensOf(row: CostRow): number {\n return row.inputTokens + row.cacheReadTokens + row.cacheWrite5mTokens + row.cacheWrite1hTokens + row.outputTokens;\n}\n","import { AUDIT_FACET, EPISODE_TABLE, FACET_TABLE, KIT } from \"@titan-design/session-graph\";\nimport type { Db } from \"@titan-design/store-sqlite\";\nimport type { SessionFacts } from \"./classify-session.js\";\nimport type { TaskInitiative } from \"./initiative.js\";\n\n/** ISO bounds on request `ts`: `since` inclusive, `until` exclusive, null for open. */\nexport interface ReportWindow {\n since: string | null;\n until: string | null;\n}\n\n/** One deduplicated, priced request as the `request_cost` view returns it. */\nexport interface CostRow {\n sessionId: string;\n model: string;\n inputTokens: number;\n cacheReadTokens: number;\n cacheWrite5mTokens: number;\n cacheWrite1hTokens: number;\n outputTokens: number;\n inputCostUsd: number;\n cacheReadCostUsd: number;\n cacheWrite5mCostUsd: number;\n cacheWrite1hCostUsd: number;\n outputCostUsd: number;\n costUsd: number;\n priced: boolean;\n isCold: boolean;\n contextBand: string;\n gapBand: string | null;\n wakeCause: string | null;\n wakeDelivery: string | null;\n}\n\n/** What the report needs about one session beyond its requests. */\nexport interface SessionContext {\n facts: SessionFacts;\n cwd: string | null;\n account: string | null;\n tasks: TaskInitiative[];\n /** First to last main-thread request over the whole session, not just the window. */\n lifetimeMs: number;\n /** Stored episode rows of every heuristic, in episode order. */\n episodes: StoredEpisode[];\n}\n\nexport interface StoredEpisode {\n heuristic: string;\n openedBy: string;\n}\n\nexport interface CompactionRow {\n trigger: string | null;\n midLoop: boolean;\n droppedTokens: number | null;\n}\n\nconst IN_WINDOW = \"(@since IS NULL OR ts >= @since) AND (@until IS NULL OR ts < @until)\";\nconst IN_SESSIONS = \"session_id IN (SELECT value FROM json_each(@ids))\";\n\n// The 5m write column mirrors the view: a split-less write is billed at the 5m rate.\nconst COST_ROWS = `\n SELECT session_id AS sessionId, model,\n input_tokens AS inputTokens, cache_read_tokens AS cacheReadTokens,\n CASE WHEN cache_creation_5m + cache_creation_1h = 0 THEN cache_creation_tokens ELSE cache_creation_5m END AS cacheWrite5mTokens,\n cache_creation_1h AS cacheWrite1hTokens, output_tokens AS outputTokens,\n input_cost_usd AS inputCostUsd, cache_read_cost_usd AS cacheReadCostUsd,\n cache_write_5m_cost_usd AS cacheWrite5mCostUsd, cache_write_1h_cost_usd AS cacheWrite1hCostUsd,\n output_cost_usd AS outputCostUsd, cost_usd AS costUsd, priced, is_cold AS isCold,\n context_band AS contextBand, gap_band AS gapBand, wake_cause AS wakeCause, wake_delivery AS wakeDelivery\n FROM request_cost WHERE ${IN_WINDOW}\n`;\n\nexport function readCostRows(db: Db, window: ReportWindow): CostRow[] {\n const rows = db.prepare(COST_ROWS).all(window) as (Omit<CostRow, \"priced\" | \"isCold\"> & { priced: number; isCold: number })[];\n return rows.map((row) => ({ ...row, priced: row.priced === 1, isCold: row.isCold === 1 }));\n}\n\nexport function readCompactions(db: Db, window: ReportWindow): CompactionRow[] {\n const sql = `SELECT trigger, mid_loop AS midLoop, dropped_tokens AS droppedTokens FROM compaction WHERE ${IN_WINDOW}`;\n const rows = db.prepare(sql).all(window) as (Omit<CompactionRow, \"midLoop\"> & { midLoop: number | null })[];\n return rows.map((row) => ({ ...row, midLoop: row.midLoop === 1 }));\n}\n\n/** The graph's own price version, which is what `request_cost` priced with; null before any price row exists. */\nexport function readPriceTableVersion(db: Db): number | null {\n return (db.prepare(\"SELECT MAX(table_version) AS version FROM price\").get() as { version: number | null }).version;\n}\n\n/** Indexed transcripts, and those whose audit facet is missing or older than `facetVersion`. */\nexport function readCoverage(db: Db, facetVersion: number | null): { transcriptsIndexed: number; facetBacklog: number } {\n const indexed = db.prepare(`SELECT COUNT(*) AS n FROM \"${KIT.watermark}\"`).get() as { n: number };\n const backlog = db\n .prepare(\n `SELECT COUNT(*) AS n FROM \"${KIT.watermark}\" t LEFT JOIN \"${FACET_TABLE}\" f\n ON f.transcript_id = t.source_id AND f.facet = @facet AND (@version IS NULL OR f.version >= @version)\n WHERE f.transcript_id IS NULL`,\n )\n .get({ facet: AUDIT_FACET, version: facetVersion }) as { n: number };\n return { transcriptsIndexed: indexed.n, facetBacklog: backlog.n };\n}\n\nexport function readSessionContexts(db: Db, sessionIds: readonly string[]): Map<string, SessionContext> {\n const ids = JSON.stringify(sessionIds);\n const contexts = new Map<string, SessionContext>(sessionIds.map((id) => [id, emptyContext()]));\n addSessionRows(db, ids, contexts);\n addOrigins(db, ids, contexts);\n addInbounds(db, ids, contexts);\n addSignals(db, ids, contexts);\n addTaskInitiatives(db, ids, contexts);\n addLifetimes(db, ids, contexts);\n addEpisodes(db, ids, contexts);\n return contexts;\n}\n\nfunction emptyContext(): SessionContext {\n return { facts: { inboundKinds: [], signalKinds: [], humanTurnCount: 0 }, cwd: null, account: null, tasks: [], lifetimeMs: 0, episodes: [] };\n}\n\nfunction addSessionRows(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, start_type AS startType, cwd, seed_prompt AS seedPrompt, account FROM session WHERE ${IN_SESSIONS}`;\n type Row = { sessionId: string; startType: string | null; cwd: string | null; seedPrompt: string | null; account: string | null };\n for (const row of db.prepare(sql).all({ ids }) as Row[]) {\n const context = contexts.get(row.sessionId)!;\n context.cwd = row.cwd;\n context.account = row.account;\n context.facts.startType = row.startType;\n context.facts.firstUserText = row.seedPrompt;\n }\n}\n\nfunction addOrigins(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, depth, parent_name AS parentName, origin_kind AS originKind, profile FROM session_origin WHERE ${IN_SESSIONS}`;\n type Row = { sessionId: string; depth: number | null; parentName: string | null; originKind: string | null; profile: string | null };\n for (const { sessionId, depth, ...origin } of db.prepare(sql).all({ ids }) as Row[]) {\n contexts.get(sessionId)!.facts.origin = { ...origin, depth: depth ?? 0 };\n }\n}\n\nfunction addInbounds(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, cause, SUM(delivery = 'turn_start') AS turnStarts FROM inbound WHERE ${IN_SESSIONS} GROUP BY session_id, cause`;\n for (const row of db.prepare(sql).all({ ids }) as { sessionId: string; cause: string; turnStarts: number }[]) {\n const facts = contexts.get(row.sessionId)!.facts;\n facts.inboundKinds = [...(facts.inboundKinds ?? []), row.cause];\n if (row.cause === \"human_typed\") facts.humanTurnCount = row.turnStarts;\n }\n}\n\nfunction addSignals(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT DISTINCT session_id AS sessionId, signal FROM session_signal WHERE ${IN_SESSIONS}`;\n for (const row of db.prepare(sql).all({ ids }) as { sessionId: string; signal: string }[]) {\n const facts = contexts.get(row.sessionId)!.facts;\n facts.signalKinds = [...(facts.signalKinds ?? []), row.signal];\n }\n}\n\nfunction addTaskInitiatives(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `\n SELECT substr(e.source_ref, 9) AS sessionId, t.initiative, COUNT(*) AS edges\n FROM \"${KIT.edge}\" e JOIN task t ON t.task_ref = e.target_ref\n WHERE e.relation = 'ran' AND e.t_expired IS NULL AND t.initiative IS NOT NULL\n AND e.source_ref IN (SELECT 'session:' || value FROM json_each(@ids))\n GROUP BY e.source_ref, t.initiative`;\n for (const row of db.prepare(sql).all({ ids }) as ({ sessionId: string } & TaskInitiative)[]) {\n contexts.get(row.sessionId)!.tasks.push({ initiative: row.initiative, edges: row.edges });\n }\n}\n\nfunction addLifetimes(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, MIN(ts) AS first, MAX(ts) AS last FROM request_dedup WHERE ${IN_SESSIONS} AND is_sidechain = 0 GROUP BY session_id`;\n for (const row of db.prepare(sql).all({ ids }) as { sessionId: string; first: string; last: string }[]) {\n contexts.get(row.sessionId)!.lifetimeMs = Date.parse(row.last) - Date.parse(row.first);\n }\n}\n\nfunction addEpisodes(db: Db, ids: string, contexts: Map<string, SessionContext>): void {\n const sql = `SELECT session_id AS sessionId, heuristic, opened_by AS openedBy FROM \"${EPISODE_TABLE}\" WHERE ${IN_SESSIONS} ORDER BY session_id, heuristic, episode_index`;\n for (const { sessionId, ...episode } of db.prepare(sql).all({ ids }) as ({ sessionId: string } & StoredEpisode)[]) {\n contexts.get(sessionId)!.episodes.push(episode);\n }\n}\n","import { replaceEpisodes, type EpisodeRow, type SessionGraph } from \"@titan-design/session-graph\";\nimport type { Db } from \"@titan-design/store-sqlite\";\nimport { classifySession, type SessionClass } from \"./classify-session.js\";\nimport { readSessionContexts } from \"./cost-report-queries.js\";\n\n/** Two rule sets on purpose (design section 18): workers segment by assignment, coordinators by work phase. */\nexport type Heuristic = \"worker-v1\" | \"coordinator-v1\";\nexport const HEURISTIC_VERSIONS: Readonly<Record<Heuristic, number>> = { \"worker-v1\": 1, \"coordinator-v1\": 1 };\n\nexport interface EpisodeRequest {\n offset: number;\n ts: string;\n transcriptId: number;\n contextTokens: number;\n wakeCause: string | null;\n}\nexport interface EpisodeInbound {\n offset: number;\n ts: string;\n transcriptId: number;\n cause: string;\n}\nexport interface EpisodeSignal {\n offset: number;\n ts: string;\n transcriptId: number;\n signal: string;\n}\nexport interface EpisodeInput {\n requests: readonly EpisodeRequest[];\n inbounds: readonly EpisodeInbound[];\n signals: readonly EpisodeSignal[];\n /** An agent-chat spawn has a brief; anything else opens at `session_start`. */\n spawned: boolean;\n}\n\nconst MINUTE_MS = 60_000;\nexport const IDLE_GAP_MS = 30 * MINUTE_MS;\nexport const CHANNEL_CLUSTER_MS = 10 * MINUTE_MS;\nexport const COORDINATOR_MIN_EPISODE_REQUESTS = 8;\nexport const COORDINATOR_MERGE_DEDUPE_REQUESTS = 15;\nexport const COORDINATOR_WAVE_QUIET_REQUESTS = 15;\nexport const CONTEXT_RESET_TOKENS = 20_000;\nconst DELIVERABLE_SIGNALS = new Set([\"status_report\", \"pr_create\", \"commit\", \"doc_written\", \"task_wrap\"]);\nconst ASSIGNMENT_OPENERS = new Set([\"brief\", \"channel_followup\"]);\n\n/** Pure: the same rows always cut the same way. Rows come back in episode order. */\nexport function buildEpisodes(input: EpisodeInput, heuristic: Heuristic): EpisodeRow[] {\n return heuristic === \"worker-v1\" ? workerEpisodes(input) : coordinatorEpisodes(input);\n}\n\n/** Workers are segmented; human sessions get the coordinator rule; headless runs get none. */\nexport function heuristicFor(sessionClass: SessionClass): Heuristic | null {\n if (sessionClass === \"agent_spawned\") return \"worker-v1\";\n if (sessionClass === \"human_interactive\") return \"coordinator-v1\";\n return null;\n}\n\n/** The worker report's `n_assignments`: episodes an assignment opened, which is what the standing-peer overlay counts. */\nexport function assignmentCount(rows: readonly Pick<EpisodeRow, \"openedBy\">[]): number {\n return rows.filter((row) => ASSIGNMENT_OPENERS.has(row.openedBy)).length;\n}\n\n// ---------------------------------------------------------------- worker-v1\n\ntype Event =\n | { kind: \"request\"; offset: number; ts: string; ms: number; transcriptId: number }\n | { kind: \"inbound\"; offset: number; ts: string; ms: number; transcriptId: number; cause: string }\n | { kind: \"signal\"; offset: number; ts: string; ms: number; transcriptId: number; signal: string };\n\ninterface Draft {\n row: EpisodeRow;\n hasRequest: boolean;\n}\n\ninterface WorkerState {\n drafts: Draft[];\n lastRequestMs: number | null;\n lastChannelMs: number | null;\n}\n\n/** worker-forensics-report.md section 5 and wf_analyze.py:241-251, with the status-report trigger from design section 9. */\nfunction workerEpisodes(input: EpisodeInput): EpisodeRow[] {\n const state: WorkerState = { drafts: [], lastRequestMs: null, lastChannelMs: null };\n for (const event of timeline(input)) {\n const current = state.drafts.at(-1);\n const reason = current ? workerBoundary(event, current, state) : input.spawned ? \"brief\" : \"session_start\";\n if (reason && (!current || current.hasRequest)) state.drafts.push(openDraft(event, reason, state.drafts.length));\n absorb(state.drafts.at(-1)!, event);\n if (event.kind === \"request\") state.lastRequestMs = event.ms;\n if (isChannel(event)) state.lastChannelMs = event.ms;\n }\n return state.drafts.map((draft) => draft.row);\n}\n\nfunction workerBoundary(event: Event, current: Draft, state: WorkerState): string | null {\n if (event.kind === \"request\" && state.lastRequestMs !== null && event.ms - state.lastRequestMs >= IDLE_GAP_MS) return \"idle_gap\";\n if (!isChannel(event)) return null;\n const startsCluster = state.lastChannelMs === null || event.ms - state.lastChannelMs > CHANNEL_CLUSTER_MS;\n return startsCluster && current.row.firstStatusOffset != null ? \"channel_followup\" : null;\n}\n\nfunction isChannel(event: Event): boolean {\n return event.kind === \"inbound\" && event.cause === \"channel_message\";\n}\n\nfunction openDraft(event: Event, openedBy: string, episodeIndex: number): Draft {\n const assignment = ASSIGNMENT_OPENERS.has(openedBy) && event.kind === \"inbound\" ? event.offset : null;\n const row: EpisodeRow = {\n episodeIndex,\n heuristicVersion: HEURISTIC_VERSIONS[\"worker-v1\"],\n startedAt: event.ts,\n endedAt: event.ts,\n startOffset: event.offset,\n endOffset: event.offset,\n openedBy,\n startTranscriptId: event.transcriptId,\n endTranscriptId: event.transcriptId,\n assignmentOffset: assignment,\n firstDeliverableOffset: null,\n firstDeliverableSignal: null,\n firstStatusOffset: null,\n };\n return { row, hasRequest: false };\n}\n\n/** The first commit is recorded as a deliverable but not as a report: commit fires early for implementers (221 of 411). */\nfunction absorb(draft: Draft, event: Event): void {\n draft.row.endedAt = event.ts;\n draft.row.endOffset = event.offset;\n draft.row.endTranscriptId = event.transcriptId;\n if (event.kind === \"request\") draft.hasRequest = true;\n if (event.kind !== \"signal\") return;\n if (draft.row.firstDeliverableOffset == null && DELIVERABLE_SIGNALS.has(event.signal)) {\n draft.row.firstDeliverableOffset = event.offset;\n draft.row.firstDeliverableSignal = event.signal;\n }\n if (draft.row.firstStatusOffset == null && event.signal === \"status_report\") draft.row.firstStatusOffset = event.offset;\n}\n\n/**\n * Offsets only order events inside one transcript, so time orders first, then the transcript a\n * session resumed into, then the offset breaks ties within it.\n */\nfunction timeline(input: EpisodeInput): Event[] {\n const events: Event[] = [\n ...input.requests.map((r) => ({ kind: \"request\" as const, offset: r.offset, ts: r.ts, ms: Date.parse(r.ts), transcriptId: r.transcriptId })),\n ...input.inbounds.map((i) => ({ kind: \"inbound\" as const, offset: i.offset, ts: i.ts, ms: Date.parse(i.ts), transcriptId: i.transcriptId, cause: i.cause })),\n ...input.signals.map((s) => ({ kind: \"signal\" as const, offset: s.offset, ts: s.ts, ms: Date.parse(s.ts), transcriptId: s.transcriptId, signal: s.signal })),\n ];\n return events.sort((a, b) => a.ms - b.ms || a.transcriptId - b.transcriptId || a.offset - b.offset);\n}\n\n// ---------------------------------------------------------------- coordinator-v1\n\ninterface Turn {\n request: EpisodeRequest;\n ms: number;\n signals: Set<string>;\n}\n\ninterface Boundary {\n at: number;\n reason: string;\n}\n\n/** coordinator-forensics-report.md section 1, ported from co_analyze.py `segment` with `merge_lag` 0. */\nfunction coordinatorEpisodes(input: EpisodeInput): EpisodeRow[] {\n const turns = toTurns(input);\n if (turns.length === 0) return [];\n const opens = [{ at: 0, reason: \"session_start\" }, ...dropShort(coordinatorBoundaries(turns), turns.length)];\n return opens.map((open, index) => {\n const first = turns[open.at]!.request;\n const last = turns[(opens[index + 1]?.at ?? turns.length) - 1]!.request;\n return {\n episodeIndex: index,\n heuristicVersion: HEURISTIC_VERSIONS[\"coordinator-v1\"],\n startedAt: first.ts,\n endedAt: last.ts,\n startOffset: first.offset,\n endOffset: last.offset,\n startTranscriptId: first.transcriptId,\n endTranscriptId: last.transcriptId,\n openedBy: open.reason,\n };\n });\n}\n\nfunction coordinatorBoundaries(turns: readonly Turn[]): Boundary[] {\n const bounds: Boundary[] = [];\n let spawnedSince = false;\n let lastMerge = -Infinity;\n for (let i = 1; i < turns.length; i++) {\n const reason = coordinatorTrigger(turns, i, lastMerge) ?? (spawnedSince && waveComplete(turns, i) ? \"spawn_wave_complete\" : null);\n if (turns[i - 1]!.signals.has(\"pr_merge\")) lastMerge = i;\n if (reason) {\n bounds.push({ at: i, reason });\n spawnedSince = false;\n }\n if (turns[i]!.signals.has(\"agent_spawn\")) spawnedSince = true;\n }\n return bounds;\n}\n\n/** Checked in the source's order; the first that fires names the boundary. */\nfunction coordinatorTrigger(turns: readonly Turn[], i: number, lastMerge: number): string | null {\n const [a, b] = [turns[i - 1]!, turns[i]!];\n if (b.ms - a.ms >= IDLE_GAP_MS) return \"idle_gap\";\n if (a.signals.has(\"pr_merge\") && i - lastMerge > COORDINATOR_MERGE_DEDUPE_REQUESTS) return \"pr_merge\";\n if (a.signals.has(\"task_wrap\") || a.signals.has(\"task_done\")) return \"task_wrap\";\n const drop = a.request.contextTokens - b.request.contextTokens;\n if (a.request.contextTokens > 0 && drop > CONTEXT_RESET_TOKENS) return \"context_reset\";\n return null;\n}\n\n/** The last agent-chat traffic before a quiet run; the source also counted any agent-chat tool call, which only reaches the graph as `chat_send` or `agent_spawn`. */\nfunction waveComplete(turns: readonly Turn[], i: number): boolean {\n return hasChatTraffic(turns[i - 1]!) && !turns.slice(i, i + COORDINATOR_WAVE_QUIET_REQUESTS).some(hasChatTraffic);\n}\n\nfunction hasChatTraffic(turn: Turn): boolean {\n return turn.request.wakeCause === \"channel_message\" || turn.signals.has(\"chat_send\") || turn.signals.has(\"agent_spawn\");\n}\n\n/** No episode shorter than the minimum, at either end of the session. */\nfunction dropShort(bounds: readonly Boundary[], total: number): Boundary[] {\n const kept: Boundary[] = [];\n let last = 0;\n for (const bound of bounds) {\n if (bound.at - last < COORDINATOR_MIN_EPISODE_REQUESTS || total - bound.at < COORDINATOR_MIN_EPISODE_REQUESTS) continue;\n kept.push(bound);\n last = bound.at;\n }\n return kept;\n}\n\n/** A signal belongs to the latest request at or before it; one before any request goes to the first. */\nfunction toTurns(input: EpisodeInput): Turn[] {\n const turns = input.requests\n .map((request) => ({ request, ms: Date.parse(request.ts), signals: new Set<string>() }))\n .sort((a, b) => a.ms - b.ms || a.request.transcriptId - b.request.transcriptId || a.request.offset - b.request.offset);\n for (const signal of input.signals) ownerOf(turns, signal)?.signals.add(signal.signal);\n return turns;\n}\n\nfunction ownerOf(turns: readonly Turn[], signal: EpisodeSignal): Turn | undefined {\n const ms = Date.parse(signal.ts);\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]!;\n if (turn.ms < ms || (turn.ms === ms && turn.request.offset <= signal.offset)) return turn;\n }\n return turns[0];\n}\n\n// ---------------------------------------------------------------- graph I/O\n\nconst IN_SESSION = \"session_id = ?\";\n\n/**\n * `request_dedup` for one session without ranking every request: SQLite cannot push the session filter\n * through the view's window. Copies of a request can sit in other sessions, so the rank still covers\n * every copy of each request id the session holds.\n */\nconst SESSION_REQUEST_DEDUP = `\n SELECT * FROM (\n SELECT r.*, ROW_NUMBER() OVER (PARTITION BY r.request_id ORDER BY r.ts, r.transcript_id) AS copy_rank\n FROM request r WHERE r.request_id IN (SELECT request_id FROM request WHERE session_id = @sessionId)\n ) WHERE copy_rank = 1 AND session_id = @sessionId`;\n\n/** Main-thread requests only: a sidechain's requests belong to its subagent, not to the session's episodes. */\nexport const EPISODE_REQUESTS_SQL = `\n SELECT byte_offset AS offset, ts, transcript_id AS transcriptId, context_tokens AS contextTokens, wake_cause AS wakeCause\n FROM (${SESSION_REQUEST_DEDUP}) WHERE is_sidechain = 0`;\n\nexport function readEpisodeInput(db: Db, sessionId: string, spawned: boolean): EpisodeInput {\n const requests = db.prepare(EPISODE_REQUESTS_SQL).all({ sessionId }) as EpisodeRequest[];\n const inbounds = db\n .prepare(`SELECT byte_offset AS offset, ts, transcript_id AS transcriptId, cause FROM inbound WHERE ${IN_SESSION}`)\n .all(sessionId) as EpisodeInbound[];\n const signals = db\n .prepare(`SELECT byte_offset AS offset, ts, transcript_id AS transcriptId, signal FROM session_signal WHERE ${IN_SESSION}`)\n .all(sessionId) as EpisodeSignal[];\n return { requests, inbounds, signals, spawned };\n}\n\nexport interface WrittenEpisodes {\n sessionId: string;\n heuristic: Heuristic;\n episodes: number;\n}\n\n/** Segments each session with its class's heuristic and replaces that heuristic's rows. Headless sessions are skipped. */\nexport function writeEpisodes(graph: SessionGraph, sessionIds: readonly string[]): WrittenEpisodes[] {\n const written: WrittenEpisodes[] = [];\n for (const [sessionId, context] of readSessionContexts(graph.db, sessionIds)) {\n const { sessionClass } = classifySession(context.facts);\n const heuristic = heuristicFor(sessionClass);\n if (!heuristic) continue;\n const rows = buildEpisodes(readEpisodeInput(graph.db, sessionId, sessionClass === \"agent_spawned\"), heuristic);\n written.push({ sessionId, heuristic, episodes: replaceEpisodes(graph, sessionId, heuristic, rows) });\n }\n return written;\n}\n","/** Initiatives a session's task edges name, with how many edges name each. */\nexport interface TaskInitiative {\n initiative: string;\n edges: number;\n}\n\n/**\n * A task edge names the initiative outright, so it wins over the cwd guess. Among several,\n * the most-named initiative wins and a tie goes to the alphabetically first, for a stable report.\n */\nexport function sessionInitiative(tasks: readonly TaskInitiative[], cwd: string | null | undefined): string {\n const best = [...tasks].sort((a, b) => b.edges - a.edges || a.initiative.localeCompare(b.initiative))[0];\n return best ? best.initiative : initiativeFromCwd(cwd);\n}\n\n/** The cwd rule from `cf_analyze.py` `initiative()`. A worktree under a repo counts as that repo. */\nexport function initiativeFromCwd(cwd: string | null | undefined): string {\n if (!cwd) return \"unknown\";\n const repo = /\\/projects\\/([^/]+)/.exec(cwd);\n if (repo) return `repo:${repo[1]}`;\n const activeWork = /active-work\\/([^/]+)/.exec(cwd);\n if (activeWork) return `active-work:${activeWork[1]}`;\n if (/ac-fork-\\w+/.test(cwd)) return \"ac-fork(tmp)\";\n const trimmed = cwd.replace(/\\/+$/, \"\");\n return `other:${trimmed.slice(trimmed.lastIndexOf(\"/\") + 1)}`;\n}\n","import type { SessionClassification } from \"./classify-session.js\";\n\n/** worker-v1's role vocabulary, from worker-forensics-report.md section 2. */\nexport type WorkerRole = \"implementer\" | \"reviewer\" | \"researcher\" | \"planner\" | \"coordinator\" | \"standing_peer\" | \"unknown\";\n\n/** Spawn profile to role, as the worker report's table (wf_analyze.py:79-91) maps it. */\nexport const PROFILE_ROLES: Readonly<Record<string, WorkerRole>> = {\n implementer: \"implementer\",\n \"implementer-lite\": \"implementer\",\n \"relay-implementer\": \"implementer\",\n \"beat-builder\": \"implementer\",\n \"fable-optimizer\": \"implementer\",\n reviewer: \"reviewer\",\n \"relay-reviewer\": \"reviewer\",\n researcher: \"researcher\",\n explorer: \"researcher\",\n \"fable-architect\": \"planner\",\n planner: \"planner\",\n \"fable-coordinator\": \"coordinator\",\n peer: \"standing_peer\",\n};\n\nexport const STANDING_PEER_MIN_HOURS = 12;\nexport const STANDING_PEER_MIN_ASSIGNMENTS = 2;\nconst HOUR_MS = 3_600_000;\n\nexport interface WorkerFacts {\n profile?: string | null;\n /** First request to last request, over the whole session rather than a report window. */\n lifetimeMs: number;\n /** worker-v1 episodes opened by an assignment (the brief or a channel follow-up), not by an idle gap. */\n assignments: number;\n}\n\nexport function roleFromProfile(profile: string | null | undefined): WorkerRole {\n return PROFILE_ROLES[(profile ?? \"\").toLowerCase()] ?? \"unknown\";\n}\n\n/** A worker that outlives its first assignment is a standing peer whatever it was spawned as (wf_analyze.py:94-103). */\nexport function workerRole(facts: WorkerFacts): WorkerRole {\n const outlived = facts.lifetimeMs >= STANDING_PEER_MIN_HOURS * HOUR_MS && facts.assignments >= STANDING_PEER_MIN_ASSIGNMENTS;\n return outlived ? \"standing_peer\" : roleFromProfile(facts.profile);\n}\n\n/** The report's role key: `coordinator` or `adhoc` for humans, `worker:<role>` for spawned agents, else the class. */\nexport function sessionRole(classification: SessionClassification, worker: WorkerFacts): string {\n if (classification.humanRole) return classification.humanRole;\n if (classification.sessionClass === \"agent_spawned\") return `worker:${workerRole(worker)}`;\n return classification.sessionClass;\n}\n","import type { CostBucket, CostReport, WakeGapCell } from \"./cost-report.js\";\nimport { PRICE_TABLE_VERSION } from \"./prices.js\";\n\nexport const LIST_PRICE_CAVEAT =\n \"List prices, not a bill: the accounts behind these sessions may run on subscription plans. Rankings hold; the absolute figure is not what you paid.\";\n\ntype Cell = string | number;\n\n/** The same report as plain-text tables, one section per report field, caveat and footer last. */\nexport function renderCostReportText(report: CostReport): string {\n const sections = [\n header(report),\n table(\"By token class\", [\"class\", \"tokens\", \"cost\"], report.byTokenClass.map((row) => [row.tokenClass, row.tokens, usd(row.costUsd)])),\n bucketTable(\"By account\", report.byAccount, report.totals.costUsd),\n bucketTable(\"By model\", report.byModel, report.totals.costUsd),\n bucketTable(\"By class\", report.byClass, report.totals.costUsd),\n bucketTable(\"By role\", report.byRole, report.totals.costUsd),\n bucketTable(\"By episode count\", report.byEpisodeCount, report.totals.costUsd),\n bucketTable(\"By initiative\", report.byInitiative, report.totals.costUsd),\n bucketTable(\"By context band\", report.byContextBand, report.totals.costUsd),\n wakeCauseTable(report),\n cellTable(\"Wake cause by gap band\", report.wakeCauseByGapBand),\n cellTable(`Cold rebuilds: ${report.coldRebuild.requests} requests, ${usd(report.coldRebuild.costUsd)}`, report.coldRebuild.byGapBandAndCause),\n compactionTable(report.compactions),\n topSessionTable(report.topSessions),\n unpricedTable(report.unpricedModels),\n [LIST_PRICE_CAVEAT, footer(report)].join(\"\\n\"),\n ];\n return sections.join(\"\\n\\n\") + \"\\n\";\n}\n\nfunction header(report: CostReport): string {\n const { since, until } = report.window;\n const { requests, sessions, costUsd } = report.totals;\n return `Cost report, ${since ?? \"beginning\"} to ${until ?? \"now\"}\\nTotal ${usd(costUsd)} over ${requests} requests in ${sessions} sessions`;\n}\n\nfunction bucketTable(title: string, buckets: readonly CostBucket[], total: number): string {\n const rows = buckets.map((b) => [b.key, b.requests, b.sessions, usd(b.costUsd), share(b.costUsd, total)]);\n return table(title, [\"key\", \"requests\", \"sessions\", \"cost\", \"share\"], rows);\n}\n\n/** Q4: answers sit under `human`, with typed text and answers indented one level down. */\nfunction wakeCauseTable(report: CostReport): string {\n const rows = report.byWakeCause.flatMap((group) => [\n [group.key, group.requests, usd(group.costUsd), group.midLoopRequests, usd(group.midLoopCostUsd)],\n ...group.parts.map((part) => [` ${part.key}`, part.requests, usd(part.costUsd), part.midLoopRequests, usd(part.midLoopCostUsd)]),\n ]);\n return table(\"By wake cause\", [\"cause\", \"requests\", \"cost\", \"mid-loop\", \"mid-loop cost\"], rows);\n}\n\nfunction cellTable(title: string, cells: readonly WakeGapCell[]): string {\n return table(title, [\"cause\", \"gap\", \"requests\", \"cost\"], cells.map((c) => [c.wakeCause, c.gapBand, c.requests, usd(c.costUsd)]));\n}\n\nfunction compactionTable(c: CostReport[\"compactions\"]): string {\n return table(\"Compactions\", [\"total\", \"manual\", \"auto\", \"mid-loop\", \"dropped tokens\"], [[c.total, c.manual, c.auto, c.midLoop, c.droppedTokens]]);\n}\n\nfunction topSessionTable(sessions: CostReport[\"topSessions\"]): string {\n const rows = sessions.map((s) => [s.sessionId, s.sessionClass, s.role, s.initiative, s.requests, usd(s.costUsd)]);\n return table(\"Top sessions\", [\"session\", \"class\", \"role\", \"initiative\", \"requests\", \"cost\"], rows);\n}\n\nfunction unpricedTable(models: CostReport[\"unpricedModels\"]): string {\n if (models.length === 0) return \"Unpriced models: none\";\n return table(\"Unpriced models (counted at zero cost)\", [\"model\", \"requests\", \"tokens\"], models.map((m) => [m.model, m.requests, m.tokens]));\n}\n\nfunction footer(report: CostReport): string {\n const { transcriptsIndexed, transcriptsDiscovered, facetBacklog } = report.coverage;\n const discovered = transcriptsDiscovered === null ? \"\" : ` of ${transcriptsDiscovered} discovered`;\n const version = report.priceTableVersion === null ? \"none\" : `v${report.priceTableVersion}`;\n const stale = report.priceTableVersion !== null && report.priceTableVersion !== PRICE_TABLE_VERSION ? ` (package has v${PRICE_TABLE_VERSION})` : \"\";\n return `Price table ${version}${stale}. Coverage: ${transcriptsIndexed} transcripts indexed${discovered}, facet backlog ${facetBacklog}.`;\n}\n\nfunction table(title: string, head: readonly string[], rows: readonly (readonly Cell[])[]): string {\n if (rows.length === 0) return `${title}: none`;\n const text = [head, ...rows].map((row) => row.map(String));\n const widths = head.map((_, i) => Math.max(...text.map((row) => row[i]!.length)));\n const rightAligned = head.map((_, i) => rows.every((row) => isNumeric(row[i]!)));\n const pad = (cell: string, i: number) => (rightAligned[i] ? cell.padStart(widths[i]!) : cell.padEnd(widths[i]!));\n const lines = text.map((row) => row.map(pad).join(\" \").trimEnd());\n return [title, ...lines].join(\"\\n\");\n}\n\nfunction isNumeric(cell: Cell): boolean {\n return typeof cell === \"number\" || /^\\$|%$/.test(cell);\n}\n\nfunction usd(value: number): string {\n return `$${value.toFixed(2)}`;\n}\n\nfunction share(value: number, total: number): string {\n return total > 0 ? `${((value / total) * 100).toFixed(1)}%` : \"-\";\n}\n"],"mappings":"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","names":["gapBand"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@titan-design/session-analytics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Pricing, session classification, banding and the cost report over mined session data",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@titan-design/session-graph": "^0.
|
|
34
|
+
"@titan-design/session-graph": "^0.11.0",
|
|
35
35
|
"@titan-design/store-sqlite": "^0.3.1"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|