@mbeato/contextscope 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  4. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
  13. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/context/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/items/page.js +3 -2
  21. package/.next/standalone/.next/server/app/items/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/items/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/page.js +3 -2
  24. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  26. package/.next/standalone/.next/server/app/sessions/page.js +3 -2
  27. package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  29. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08l_yt3._.js +3 -0
  30. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0j40w4k._.js +3 -0
  31. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lbywin._.js +3 -0
  32. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p2sxww._.js +3 -0
  33. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0wj0exw._.js +3 -0
  34. package/.next/standalone/.next/server/chunks/ssr/_0j0avc7._.js +1 -1
  35. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  36. package/.next/standalone/.next/server/pages/404.html +1 -1
  37. package/.next/standalone/.next/server/pages/500.html +1 -1
  38. package/.next/standalone/app/page.tsx +8 -2
  39. package/.next/standalone/app/sessions/page.tsx +10 -2
  40. package/.next/standalone/lib/model-prices.json +147 -0
  41. package/.next/standalone/lib/pricing.ts +80 -0
  42. package/.next/standalone/lib/sessions.ts +33 -6
  43. package/.next/standalone/lib/transcripts.ts +218 -54
  44. package/.next/standalone/package.json +2 -1
  45. package/.next/standalone/scripts/refresh-prices.mjs +58 -0
  46. package/.next/standalone/tsconfig.tsbuildinfo +1 -1
  47. package/.next/static/chunks/118uk9v3812u1.css +1 -0
  48. package/package.json +2 -1
  49. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08jw6yr._.js +0 -3
  50. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0duau-w._.js +0 -3
  51. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ubqc9u._.js +0 -3
  52. package/.next/static/chunks/13525lf.7uo9s.css +0 -1
  53. /package/.next/static/{BT6H4g8OlEYi9snYU0PI- → I-W1iV4XdkTRyjis8Ros1}/_buildManifest.js +0 -0
  54. /package/.next/static/{BT6H4g8OlEYi9snYU0PI- → I-W1iV4XdkTRyjis8Ros1}/_clientMiddlewareManifest.js +0 -0
  55. /package/.next/static/{BT6H4g8OlEYi9snYU0PI- → I-W1iV4XdkTRyjis8Ros1}/_ssgManifest.js +0 -0
@@ -0,0 +1,147 @@
1
+ {
2
+ "_source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
3
+ "_refreshedAt": "2026-05-27T17:24:45.345Z",
4
+ "_description": "Anthropic model prices in USD per token (input, output, cache_read, cache_creation 5min default). Refresh with `npm run refresh-prices`.",
5
+ "models": {
6
+ "claude-3-7-sonnet-20250219": {
7
+ "input": 0.000003,
8
+ "output": 0.000015,
9
+ "cache_read": 3e-7,
10
+ "cache_creation_5m": 0.00000375,
11
+ "cache_creation_1h": 0.000006
12
+ },
13
+ "claude-3-haiku-20240307": {
14
+ "input": 2.5e-7,
15
+ "output": 0.00000125,
16
+ "cache_read": 3e-8,
17
+ "cache_creation_5m": 3e-7,
18
+ "cache_creation_1h": 0.000006
19
+ },
20
+ "claude-3-opus-20240229": {
21
+ "input": 0.000015,
22
+ "output": 0.000075,
23
+ "cache_read": 0.0000015,
24
+ "cache_creation_5m": 0.00001875,
25
+ "cache_creation_1h": 0.000006
26
+ },
27
+ "claude-4-opus-20250514": {
28
+ "input": 0.000015,
29
+ "output": 0.000075,
30
+ "cache_read": 0.0000015,
31
+ "cache_creation_5m": 0.00001875,
32
+ "cache_creation_1h": 0.0000375
33
+ },
34
+ "claude-4-sonnet-20250514": {
35
+ "input": 0.000003,
36
+ "output": 0.000015,
37
+ "cache_read": 3e-7,
38
+ "cache_creation_5m": 0.00000375,
39
+ "cache_creation_1h": 0.0000075
40
+ },
41
+ "claude-haiku-4-5": {
42
+ "input": 0.000001,
43
+ "output": 0.000005,
44
+ "cache_read": 1e-7,
45
+ "cache_creation_5m": 0.00000125,
46
+ "cache_creation_1h": 0.000002
47
+ },
48
+ "claude-haiku-4-5-20251001": {
49
+ "input": 0.000001,
50
+ "output": 0.000005,
51
+ "cache_read": 1e-7,
52
+ "cache_creation_5m": 0.00000125,
53
+ "cache_creation_1h": 0.000002
54
+ },
55
+ "claude-opus-4-1": {
56
+ "input": 0.000015,
57
+ "output": 0.000075,
58
+ "cache_read": 0.0000015,
59
+ "cache_creation_5m": 0.00001875,
60
+ "cache_creation_1h": 0.00003
61
+ },
62
+ "claude-opus-4-1-20250805": {
63
+ "input": 0.000015,
64
+ "output": 0.000075,
65
+ "cache_read": 0.0000015,
66
+ "cache_creation_5m": 0.00001875,
67
+ "cache_creation_1h": 0.00003
68
+ },
69
+ "claude-opus-4-20250514": {
70
+ "input": 0.000015,
71
+ "output": 0.000075,
72
+ "cache_read": 0.0000015,
73
+ "cache_creation_5m": 0.00001875,
74
+ "cache_creation_1h": 0.00003
75
+ },
76
+ "claude-opus-4-5": {
77
+ "input": 0.000005,
78
+ "output": 0.000025,
79
+ "cache_read": 5e-7,
80
+ "cache_creation_5m": 0.00000625,
81
+ "cache_creation_1h": 0.00001
82
+ },
83
+ "claude-opus-4-5-20251101": {
84
+ "input": 0.000005,
85
+ "output": 0.000025,
86
+ "cache_read": 5e-7,
87
+ "cache_creation_5m": 0.00000625,
88
+ "cache_creation_1h": 0.00001
89
+ },
90
+ "claude-opus-4-6": {
91
+ "input": 0.000005,
92
+ "output": 0.000025,
93
+ "cache_read": 5e-7,
94
+ "cache_creation_5m": 0.00000625,
95
+ "cache_creation_1h": 0.00001
96
+ },
97
+ "claude-opus-4-6-20260205": {
98
+ "input": 0.000005,
99
+ "output": 0.000025,
100
+ "cache_read": 5e-7,
101
+ "cache_creation_5m": 0.00000625,
102
+ "cache_creation_1h": 0.00001
103
+ },
104
+ "claude-opus-4-7": {
105
+ "input": 0.000005,
106
+ "output": 0.000025,
107
+ "cache_read": 5e-7,
108
+ "cache_creation_5m": 0.00000625,
109
+ "cache_creation_1h": 0.00001
110
+ },
111
+ "claude-opus-4-7-20260416": {
112
+ "input": 0.000005,
113
+ "output": 0.000025,
114
+ "cache_read": 5e-7,
115
+ "cache_creation_5m": 0.00000625,
116
+ "cache_creation_1h": 0.00001
117
+ },
118
+ "claude-sonnet-4-20250514": {
119
+ "input": 0.000003,
120
+ "output": 0.000015,
121
+ "cache_read": 3e-7,
122
+ "cache_creation_5m": 0.00000375,
123
+ "cache_creation_1h": 0.000006
124
+ },
125
+ "claude-sonnet-4-5": {
126
+ "input": 0.000003,
127
+ "output": 0.000015,
128
+ "cache_read": 3e-7,
129
+ "cache_creation_5m": 0.00000375,
130
+ "cache_creation_1h": 0.0000075
131
+ },
132
+ "claude-sonnet-4-5-20250929": {
133
+ "input": 0.000003,
134
+ "output": 0.000015,
135
+ "cache_read": 3e-7,
136
+ "cache_creation_5m": 0.00000375,
137
+ "cache_creation_1h": 0.0000075
138
+ },
139
+ "claude-sonnet-4-6": {
140
+ "input": 0.000003,
141
+ "output": 0.000015,
142
+ "cache_read": 3e-7,
143
+ "cache_creation_5m": 0.00000375,
144
+ "cache_creation_1h": 0.0000075
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Per-message API cost calculator.
3
+ *
4
+ * Prices come from lib/model-prices.json (refreshed from LiteLLM — see
5
+ * scripts/refresh-prices.mjs). They're Anthropic's PUBLIC API rates — the
6
+ * "you'd pay this on the API" number, not what CC subscribers actually pay.
7
+ *
8
+ * Cache-creation cost uses the 5-minute (default) tier. CC sometimes uses the
9
+ * 1-hour tier; transcripts don't tell us which, so we use the more conservative
10
+ * lower price (5min). Real cost may be marginally higher.
11
+ *
12
+ * Unknown models log once and return 0 — never silently fudge.
13
+ */
14
+ import prices from "./model-prices.json";
15
+
16
+ type ModelPrice = {
17
+ input: number;
18
+ output: number;
19
+ cache_read: number;
20
+ cache_creation_5m: number;
21
+ cache_creation_1h: number;
22
+ };
23
+
24
+ const MODELS = prices.models as Record<string, ModelPrice>;
25
+
26
+ // Aliases for short model names that appear in some CC transcripts.
27
+ // Map each to the latest released model of that tier.
28
+ const ALIASES: Record<string, string> = {
29
+ opus: "claude-opus-4-7",
30
+ sonnet: "claude-sonnet-4-6",
31
+ haiku: "claude-haiku-4-5",
32
+ };
33
+
34
+ const warned = new Set<string>();
35
+
36
+ export type Usage = {
37
+ input: number;
38
+ output: number;
39
+ cacheRead: number;
40
+ cacheCreation5m: number;
41
+ cacheCreation1h: number;
42
+ };
43
+
44
+ function resolveModel(model: string): ModelPrice | null {
45
+ if (!model || model === "<synthetic>") return null;
46
+ if (MODELS[model]) return MODELS[model];
47
+ if (ALIASES[model] && MODELS[ALIASES[model]]) return MODELS[ALIASES[model]];
48
+ // Strip [variant] suffix (e.g. claude-opus-4-7[1m] → claude-opus-4-7)
49
+ const noSuffix = model.replace(/\[[^\]]+\]$/, "");
50
+ if (noSuffix !== model && MODELS[noSuffix]) return MODELS[noSuffix];
51
+ // Strip date suffix (-YYYYMMDD) and retry
52
+ const noDate = noSuffix.replace(/-\d{8}$/, "");
53
+ if (MODELS[noDate]) return MODELS[noDate];
54
+ if (!warned.has(model)) {
55
+ warned.add(model);
56
+ console.warn(`[contextscope] no price found for model "${model}" — treating as $0. Add to lib/model-prices.json or alias.`);
57
+ }
58
+ return null;
59
+ }
60
+
61
+ export function costForUsage(model: string, u: Usage): number {
62
+ const p = resolveModel(model);
63
+ if (!p) return 0;
64
+ return (
65
+ u.input * p.input +
66
+ u.output * p.output +
67
+ u.cacheRead * p.cache_read +
68
+ u.cacheCreation5m * p.cache_creation_5m +
69
+ u.cacheCreation1h * p.cache_creation_1h
70
+ );
71
+ }
72
+
73
+ export function formatUsd(n: number): string {
74
+ if (n >= 100) return `$${n.toFixed(0)}`;
75
+ if (n >= 10) return `$${n.toFixed(1)}`;
76
+ if (n >= 1) return `$${n.toFixed(2)}`;
77
+ if (n >= 0.01) return `$${n.toFixed(2)}`;
78
+ if (n > 0) return `<$0.01`;
79
+ return `$0`;
80
+ }
@@ -1,4 +1,5 @@
1
1
  import { getAllTranscripts, type TranscriptResult } from "./transcripts";
2
+ import { costForUsage } from "./pricing";
2
3
 
3
4
  export type Session = {
4
5
  sessionId: string;
@@ -14,11 +15,26 @@ export type Session = {
14
15
  cacheCreationTokens: number;
15
16
  outputTokens: number;
16
17
  totalTokens: number;
18
+ costUsd: number;
17
19
  toolCalls: Record<string, number>;
18
20
  toolErrors: number;
19
21
  sidechainTurns: number;
20
22
  };
21
23
 
24
+ function computeCost(t: TranscriptResult): number {
25
+ let cost = 0;
26
+ for (const [model, u] of Object.entries(t.byModel)) {
27
+ cost += costForUsage(model, {
28
+ input: u.inputTokens,
29
+ output: u.outputTokens,
30
+ cacheRead: u.cacheReadTokens,
31
+ cacheCreation5m: u.cacheCreation5mTokens,
32
+ cacheCreation1h: u.cacheCreation1hTokens,
33
+ });
34
+ }
35
+ return cost;
36
+ }
37
+
22
38
  function toSession(t: TranscriptResult): Session {
23
39
  const totalTokens =
24
40
  t.inputTokens + t.cacheReadTokens + t.cacheCreationTokens + t.outputTokens;
@@ -36,6 +52,7 @@ function toSession(t: TranscriptResult): Session {
36
52
  cacheCreationTokens: t.cacheCreationTokens,
37
53
  outputTokens: t.outputTokens,
38
54
  totalTokens,
55
+ costUsd: computeCost(t),
39
56
  toolCalls: t.toolCalls,
40
57
  toolErrors: t.toolErrors,
41
58
  sidechainTurns: t.sidechainTurns,
@@ -55,14 +72,15 @@ export type SessionsSummary = {
55
72
  totalTokens: number;
56
73
  totalOutputTokens: number;
57
74
  totalInputPlusCache: number;
75
+ totalCostUsd: number;
58
76
  cacheHitRatio: number;
59
77
  outputInputRatio: number;
60
78
  averageSessionTokens: number;
61
79
  medianSessionTokens: number;
62
80
  p95SessionTokens: number;
63
81
  longSessions: Session[];
64
- dailyBurn: { date: string; tokens: number }[];
65
- byProject: { project: string; projectPath: string; count: number; tokens: number; turns: number }[];
82
+ dailyBurn: { date: string; tokens: number; cost: number }[];
83
+ byProject: { project: string; projectPath: string; count: number; tokens: number; cost: number; turns: number }[];
66
84
  totalToolCalls: Record<string, number>;
67
85
  totalToolErrors: number;
68
86
  totalSidechainTurns: number;
@@ -78,6 +96,7 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
78
96
  totalTokens: 0,
79
97
  totalOutputTokens: 0,
80
98
  totalInputPlusCache: 0,
99
+ totalCostUsd: 0,
81
100
  cacheHitRatio: 0,
82
101
  outputInputRatio: 0,
83
102
  averageSessionTokens: 0,
@@ -110,7 +129,7 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
110
129
 
111
130
  const longSessions = sessions.filter((s) => s.totalTokens >= LONG_SESSION_THRESHOLD);
112
131
 
113
- const byDay = new Map<string, number>();
132
+ const byDay = new Map<string, { tokens: number; cost: number }>();
114
133
  for (const s of sessions) {
115
134
  const ts = s.endTime || s.startTime;
116
135
  if (!ts) continue;
@@ -118,27 +137,34 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
118
137
  const key = `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(
119
138
  d.getUTCDate()
120
139
  ).padStart(2, "0")}`;
121
- byDay.set(key, (byDay.get(key) ?? 0) + s.totalTokens);
140
+ const cur = byDay.get(key) ?? { tokens: 0, cost: 0 };
141
+ cur.tokens += s.totalTokens;
142
+ cur.cost += s.costUsd;
143
+ byDay.set(key, cur);
122
144
  }
123
145
  const dailyBurn = [...byDay.entries()]
124
- .map(([date, tokens]) => ({ date, tokens }))
146
+ .map(([date, v]) => ({ date, tokens: v.tokens, cost: v.cost }))
125
147
  .sort((a, b) => a.date.localeCompare(b.date));
126
148
 
127
149
  // By project
128
150
  const projectMap = new Map<
129
151
  string,
130
- { project: string; projectPath: string; count: number; tokens: number; turns: number }
152
+ { project: string; projectPath: string; count: number; tokens: number; cost: number; turns: number }
131
153
  >();
154
+ let totalCostUsd = 0;
132
155
  for (const s of sessions) {
156
+ totalCostUsd += s.costUsd;
133
157
  const cur = projectMap.get(s.project) ?? {
134
158
  project: s.project,
135
159
  projectPath: s.projectPath,
136
160
  count: 0,
137
161
  tokens: 0,
162
+ cost: 0,
138
163
  turns: 0,
139
164
  };
140
165
  cur.count += 1;
141
166
  cur.tokens += s.totalTokens;
167
+ cur.cost += s.costUsd;
142
168
  cur.turns += s.turnCount;
143
169
  projectMap.set(s.project, cur);
144
170
  }
@@ -163,6 +189,7 @@ export function summarizeSessions(sessions: Session[]): SessionsSummary {
163
189
  totalTokens: total,
164
190
  totalOutputTokens: outSum,
165
191
  totalInputPlusCache: inputPlusCache,
192
+ totalCostUsd,
166
193
  cacheHitRatio,
167
194
  outputInputRatio,
168
195
  averageSessionTokens: Math.round(total / sessions.length),