dsh-context-mode 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSING.md +37 -0
  2. package/README.md +39 -13
  3. package/lib/types/cjk.d.ts +54 -0
  4. package/lib/types/cjk.d.ts.map +1 -0
  5. package/lib/types/cjk.js +64 -0
  6. package/lib/types/index.d.ts.map +1 -1
  7. package/lib/types/index.js +65 -21
  8. package/lib/types/output-containment.d.ts +35 -0
  9. package/lib/types/output-containment.d.ts.map +1 -0
  10. package/lib/types/output-containment.js +103 -0
  11. package/lib/types/routing.d.ts +3 -1
  12. package/lib/types/routing.d.ts.map +1 -1
  13. package/lib/types/routing.js +81 -6
  14. package/package.json +9 -5
  15. package/skills/context-mode/SKILL.md +104 -11
  16. package/vendor/context-mode/LICENSE +94 -0
  17. package/vendor/context-mode/server.bundle.mjs +1126 -0
  18. package/vendor/context-mode/src/cli.ts +2040 -0
  19. package/vendor/context-mode/src/db-base.ts +617 -0
  20. package/vendor/context-mode/src/executor.ts +785 -0
  21. package/vendor/context-mode/src/exit-classify.ts +33 -0
  22. package/vendor/context-mode/src/fetch-cache.ts +15 -0
  23. package/vendor/context-mode/src/lifecycle.ts +305 -0
  24. package/vendor/context-mode/src/platform/client-map.ts +45 -0
  25. package/vendor/context-mode/src/platform/detect.ts +645 -0
  26. package/vendor/context-mode/src/platform/dsh.ts +206 -0
  27. package/vendor/context-mode/src/platform/types.ts +503 -0
  28. package/vendor/context-mode/src/runPool.ts +81 -0
  29. package/vendor/context-mode/src/runtime.ts +765 -0
  30. package/vendor/context-mode/src/search/auto-memory.ts +200 -0
  31. package/vendor/context-mode/src/search/ctx-search-schema.ts +143 -0
  32. package/vendor/context-mode/src/search/flood-guard.ts +111 -0
  33. package/vendor/context-mode/src/search/unified.ts +176 -0
  34. package/vendor/context-mode/src/security.ts +889 -0
  35. package/vendor/context-mode/src/server.ts +4991 -0
  36. package/vendor/context-mode/src/session/analytics.ts +3085 -0
  37. package/vendor/context-mode/src/session/db.ts +1726 -0
  38. package/vendor/context-mode/src/session/error-classifier.ts +392 -0
  39. package/vendor/context-mode/src/session/event-emit.ts +132 -0
  40. package/vendor/context-mode/src/session/extract.ts +2958 -0
  41. package/vendor/context-mode/src/session/index.ts +130 -0
  42. package/vendor/context-mode/src/session/model-prices.json +429 -0
  43. package/vendor/context-mode/src/session/persist-tool-calls.ts +128 -0
  44. package/vendor/context-mode/src/session/pricing.ts +191 -0
  45. package/vendor/context-mode/src/session/project-attribution.ts +309 -0
  46. package/vendor/context-mode/src/session/purge.ts +338 -0
  47. package/vendor/context-mode/src/session/retrieval-marker.ts +65 -0
  48. package/vendor/context-mode/src/session/snapshot.ts +577 -0
  49. package/vendor/context-mode/src/store-directory.ts +290 -0
  50. package/vendor/context-mode/src/store.ts +2071 -0
  51. package/vendor/context-mode/src/truncate.ts +154 -0
  52. package/vendor/context-mode/src/types.ts +147 -0
  53. package/vendor/context-mode/src/util/claude-config.ts +95 -0
  54. package/vendor/context-mode/src/util/hook-config.ts +78 -0
  55. package/vendor/context-mode/src/util/jsonc.ts +70 -0
  56. package/vendor/context-mode/src/util/plugin-cache-integrity.ts +167 -0
  57. package/vendor/context-mode/src/util/project-dir.ts +347 -0
  58. package/vendor/context-mode/src/util/sibling-mcp.ts +228 -0
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Public session ingestion API for external host integrations.
3
+ *
4
+ * Host adapters keep their own durable session log and use this recorder to
5
+ * project selected events into context-mode's analytics database. The recorder
6
+ * owns database lifecycle and never exposes the SQLite implementation.
7
+ */
8
+
9
+ import { mkdirSync } from "node:fs";
10
+ import { join, resolve } from "node:path";
11
+ import { EventPriority, type SessionEvent } from "../types.js";
12
+ import { resolveSessionDbPath, SessionDB, type EventBytes } from "./db.js";
13
+ import type { AttributionSource } from "./project-attribution.js";
14
+
15
+ /** One event supplied by an external host adapter. */
16
+ export interface SessionRecorderEvent {
17
+ /** Stable event type used by timeline and resume queries. */
18
+ readonly type: string;
19
+ /** Event category used by analytics and priority filters. */
20
+ readonly category: string;
21
+ /** Serialized event payload. */
22
+ readonly data: string;
23
+ /** Retention priority; defaults to {@link EventPriority.NORMAL}. */
24
+ readonly priority?: number;
25
+ /** Bytes kept out of the model context for this event. */
26
+ readonly bytesAvoided?: number;
27
+ /** Bytes returned to the model for this event. */
28
+ readonly bytesReturned?: number;
29
+ }
30
+
31
+ /** Configuration for one project-scoped recorder. */
32
+ export interface SessionRecorderOptions {
33
+ /** Absolute or relative project directory used for DB sharding and attribution. */
34
+ readonly projectDir: string;
35
+ /** Context-mode storage root; the recorder uses its `sessions` child directory. */
36
+ readonly storageDir: string;
37
+ /** Attribution label stored on projected events. */
38
+ readonly source?: AttributionSource;
39
+ /** Attribution confidence stored on projected events. */
40
+ readonly confidence?: number;
41
+ }
42
+
43
+ /** Stable host-facing session projection API. */
44
+ export interface SessionRecorder {
45
+ /** Ensure a session metadata row exists before tool-side analytics arrive. */
46
+ ensureSession(sessionId: string): void;
47
+ /** Append one projected event to the session database. */
48
+ append(sessionId: string, event: SessionRecorderEvent, sourceHook?: string): void;
49
+ /** Append several projected events in one database transaction. */
50
+ appendBatch(sessionId: string, events: readonly SessionRecorderEvent[], sourceHook?: string): void;
51
+ /** Close the recorder's database connection. */
52
+ close(): void;
53
+ }
54
+
55
+ /**
56
+ * Open a project-scoped recorder for an external host integration.
57
+ *
58
+ * @param options - storage, attribution, and project settings.
59
+ * @returns an owned recorder; callers must invoke `close()` during teardown.
60
+ */
61
+ export function createSessionRecorder(options: SessionRecorderOptions): SessionRecorder {
62
+ const projectDir = resolve(options.projectDir);
63
+ const storageDir = resolve(options.storageDir);
64
+ const sessionsDir = join(storageDir, "sessions");
65
+ mkdirSync(sessionsDir, { recursive: true });
66
+ const source: AttributionSource = options.source ?? "unknown";
67
+ const confidenceValue = options.confidence ?? 1;
68
+ const confidence = Number.isFinite(confidenceValue) ? Math.max(0, Math.min(1, confidenceValue)) : 1;
69
+ const dbPath = resolveSessionDbPath({
70
+ projectDir,
71
+ sessionsDir,
72
+ });
73
+ const db = new SessionDB({ dbPath });
74
+
75
+ const ensureSession = (sessionId: string): void => {
76
+ db.ensureSession(sessionId, projectDir);
77
+ };
78
+
79
+ const toDbEvent = (event: SessionRecorderEvent): Omit<SessionEvent, "data_hash"> & { data_hash?: string } => ({
80
+ type: event.type,
81
+ category: event.category,
82
+ data: event.data,
83
+ priority: event.priority ?? EventPriority.NORMAL,
84
+ project_dir: projectDir,
85
+ attribution_source: source,
86
+ attribution_confidence: confidence,
87
+ });
88
+
89
+ const append = (sessionId: string, event: SessionRecorderEvent, sourceHook = source): void => {
90
+ ensureSession(sessionId);
91
+ const bytes: EventBytes = {
92
+ bytesAvoided: event.bytesAvoided,
93
+ bytesReturned: event.bytesReturned,
94
+ };
95
+ db.insertEvent(sessionId, toDbEvent(event), sourceHook, {
96
+ projectDir,
97
+ source,
98
+ confidence,
99
+ }, bytes);
100
+ };
101
+
102
+ const appendBatch = (
103
+ sessionId: string,
104
+ events: readonly SessionRecorderEvent[],
105
+ sourceHook = source,
106
+ ): void => {
107
+ if (events.length === 0) return;
108
+ ensureSession(sessionId);
109
+ const bytes = events.map(event => ({
110
+ bytesAvoided: event.bytesAvoided,
111
+ bytesReturned: event.bytesReturned,
112
+ }));
113
+ db.bulkInsertEvents(
114
+ sessionId,
115
+ events.map(toDbEvent) as SessionEvent[],
116
+ sourceHook,
117
+ events.map(() => ({ projectDir, source, confidence })),
118
+ bytes,
119
+ );
120
+ };
121
+
122
+ return {
123
+ ensureSession,
124
+ append,
125
+ appendBatch,
126
+ close: () => db.close(),
127
+ };
128
+ }
129
+
130
+ export type { EventBytes } from "./db.js";
@@ -0,0 +1,429 @@
1
+ {
2
+ "claude-opus-4-8": {
3
+ "input_per_mtok": 5,
4
+ "output_per_mtok": 25,
5
+ "cache_read_per_mtok": 0.5,
6
+ "cache_write_per_mtok": 6.25,
7
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
8
+ },
9
+ "claude-opus-4-7": {
10
+ "input_per_mtok": 5,
11
+ "output_per_mtok": 25,
12
+ "cache_read_per_mtok": 0.5,
13
+ "cache_write_per_mtok": 6.25,
14
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
15
+ },
16
+ "claude-opus-4-6": {
17
+ "input_per_mtok": 5,
18
+ "output_per_mtok": 25,
19
+ "cache_read_per_mtok": 0.5,
20
+ "cache_write_per_mtok": 6.25,
21
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
22
+ },
23
+ "claude-opus-4-5": {
24
+ "input_per_mtok": 5,
25
+ "output_per_mtok": 25,
26
+ "cache_read_per_mtok": 0.5,
27
+ "cache_write_per_mtok": 6.25,
28
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
29
+ },
30
+ "claude-sonnet-4-6": {
31
+ "input_per_mtok": 3,
32
+ "output_per_mtok": 15,
33
+ "cache_read_per_mtok": 0.3,
34
+ "cache_write_per_mtok": 3.75,
35
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
36
+ },
37
+ "claude-sonnet-4-5": {
38
+ "input_per_mtok": 3,
39
+ "output_per_mtok": 15,
40
+ "cache_read_per_mtok": 0.3,
41
+ "cache_write_per_mtok": 3.75,
42
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
43
+ },
44
+ "claude-haiku-4-5": {
45
+ "input_per_mtok": 1,
46
+ "output_per_mtok": 5,
47
+ "cache_read_per_mtok": 0.1,
48
+ "cache_write_per_mtok": 1.25,
49
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
50
+ },
51
+ "claude-3-7-sonnet": {
52
+ "input_per_mtok": 3,
53
+ "output_per_mtok": 15,
54
+ "cache_read_per_mtok": 0.3,
55
+ "cache_write_per_mtok": 3.75,
56
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
57
+ },
58
+ "claude-3-5-haiku": {
59
+ "input_per_mtok": 0.8,
60
+ "output_per_mtok": 4,
61
+ "cache_read_per_mtok": 0.08,
62
+ "cache_write_per_mtok": 1,
63
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
64
+ },
65
+ "claude-fable-5": {
66
+ "input_per_mtok": 10,
67
+ "output_per_mtok": 50,
68
+ "cache_read_per_mtok": 1,
69
+ "cache_write_per_mtok": 12.5,
70
+ "source": "https://platform.claude.com/docs/en/about-claude/pricing"
71
+ },
72
+ "gpt-5": {
73
+ "input_per_mtok": 1.25,
74
+ "output_per_mtok": 10,
75
+ "cache_read_per_mtok": 0.125,
76
+ "cache_write_per_mtok": null,
77
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
78
+ },
79
+ "gpt-5-mini": {
80
+ "input_per_mtok": 0.25,
81
+ "output_per_mtok": 2,
82
+ "cache_read_per_mtok": 0.025,
83
+ "cache_write_per_mtok": null,
84
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
85
+ },
86
+ "gpt-5-nano": {
87
+ "input_per_mtok": 0.05,
88
+ "output_per_mtok": 0.4,
89
+ "cache_read_per_mtok": 0.005,
90
+ "cache_write_per_mtok": null,
91
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
92
+ },
93
+ "gpt-5-codex": {
94
+ "input_per_mtok": 1.25,
95
+ "output_per_mtok": 10,
96
+ "cache_read_per_mtok": 0.125,
97
+ "cache_write_per_mtok": null,
98
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
99
+ },
100
+ "gpt-4.1": {
101
+ "input_per_mtok": 2,
102
+ "output_per_mtok": 8,
103
+ "cache_read_per_mtok": 0.5,
104
+ "cache_write_per_mtok": null,
105
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
106
+ },
107
+ "gpt-4.1-mini": {
108
+ "input_per_mtok": 0.4,
109
+ "output_per_mtok": 1.6,
110
+ "cache_read_per_mtok": 0.1,
111
+ "cache_write_per_mtok": null,
112
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
113
+ },
114
+ "gpt-4.1-nano": {
115
+ "input_per_mtok": 0.1,
116
+ "output_per_mtok": 0.4,
117
+ "cache_read_per_mtok": 0.025,
118
+ "cache_write_per_mtok": null,
119
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
120
+ },
121
+ "gpt-4o": {
122
+ "input_per_mtok": 2.5,
123
+ "output_per_mtok": 10,
124
+ "cache_read_per_mtok": 1.25,
125
+ "cache_write_per_mtok": null,
126
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
127
+ },
128
+ "gpt-4o-mini": {
129
+ "input_per_mtok": 0.15,
130
+ "output_per_mtok": 0.6,
131
+ "cache_read_per_mtok": 0.075,
132
+ "cache_write_per_mtok": null,
133
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
134
+ },
135
+ "o3": {
136
+ "input_per_mtok": 2,
137
+ "output_per_mtok": 8,
138
+ "cache_read_per_mtok": 0.5,
139
+ "cache_write_per_mtok": null,
140
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
141
+ },
142
+ "o4-mini": {
143
+ "input_per_mtok": 1.1,
144
+ "output_per_mtok": 4.4,
145
+ "cache_read_per_mtok": 0.275,
146
+ "cache_write_per_mtok": null,
147
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
148
+ },
149
+ "o3-mini": {
150
+ "input_per_mtok": 1.1,
151
+ "output_per_mtok": 4.4,
152
+ "cache_read_per_mtok": 0.55,
153
+ "cache_write_per_mtok": null,
154
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
155
+ },
156
+ "codex-mini-latest": {
157
+ "input_per_mtok": 1.5,
158
+ "output_per_mtok": 6,
159
+ "cache_read_per_mtok": 0.375,
160
+ "cache_write_per_mtok": null,
161
+ "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
162
+ },
163
+ "gemini-2.5-pro": {
164
+ "input_per_mtok": 1.25,
165
+ "output_per_mtok": 10,
166
+ "cache_read_per_mtok": 0.125,
167
+ "cache_write_per_mtok": null,
168
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
169
+ },
170
+ "gemini-2.5-flash": {
171
+ "input_per_mtok": 0.3,
172
+ "output_per_mtok": 2.5,
173
+ "cache_read_per_mtok": 0.03,
174
+ "cache_write_per_mtok": null,
175
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
176
+ },
177
+ "gemini-2.5-flash-lite": {
178
+ "input_per_mtok": 0.1,
179
+ "output_per_mtok": 0.4,
180
+ "cache_read_per_mtok": 0.01,
181
+ "cache_write_per_mtok": null,
182
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
183
+ },
184
+ "gemini-2.0-flash": {
185
+ "input_per_mtok": 0.1,
186
+ "output_per_mtok": 0.4,
187
+ "cache_read_per_mtok": 0.025,
188
+ "cache_write_per_mtok": null,
189
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
190
+ },
191
+ "gemini-2.0-flash-lite": {
192
+ "input_per_mtok": 0.075,
193
+ "output_per_mtok": 0.3,
194
+ "cache_read_per_mtok": null,
195
+ "cache_write_per_mtok": null,
196
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
197
+ },
198
+ "gemini-3-pro-preview": {
199
+ "input_per_mtok": 2,
200
+ "output_per_mtok": 12,
201
+ "cache_read_per_mtok": 0.2,
202
+ "cache_write_per_mtok": null,
203
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
204
+ },
205
+ "gemini-3-flash-preview": {
206
+ "input_per_mtok": 0.5,
207
+ "output_per_mtok": 3,
208
+ "cache_read_per_mtok": 0.05,
209
+ "cache_write_per_mtok": null,
210
+ "source": "https://ai.google.dev/gemini-api/docs/pricing"
211
+ },
212
+ "qwen3-coder": {
213
+ "input_per_mtok": 1,
214
+ "output_per_mtok": 5,
215
+ "cache_read_per_mtok": null,
216
+ "cache_write_per_mtok": null,
217
+ "source": "https://www.alibabacloud.com/help/en/model-studio/models"
218
+ },
219
+ "qwen-max": {
220
+ "input_per_mtok": 1.6,
221
+ "output_per_mtok": 6.4,
222
+ "cache_read_per_mtok": null,
223
+ "cache_write_per_mtok": null,
224
+ "source": "https://www.alibabacloud.com/help/en/model-studio/models"
225
+ },
226
+ "qwen-plus": {
227
+ "input_per_mtok": 0.4,
228
+ "output_per_mtok": 1.2,
229
+ "cache_read_per_mtok": null,
230
+ "cache_write_per_mtok": null,
231
+ "source": "https://www.alibabacloud.com/help/en/model-studio/models"
232
+ },
233
+ "qwen-turbo": {
234
+ "input_per_mtok": 0.05,
235
+ "output_per_mtok": 0.2,
236
+ "cache_read_per_mtok": null,
237
+ "cache_write_per_mtok": null,
238
+ "source": "https://www.alibabacloud.com/help/en/model-studio/models"
239
+ },
240
+ "qwen3-max": {
241
+ "input_per_mtok": 1.2,
242
+ "output_per_mtok": 6,
243
+ "cache_read_per_mtok": null,
244
+ "cache_write_per_mtok": null,
245
+ "source": "https://www.alibabacloud.com/help/en/model-studio/models"
246
+ },
247
+ "kimi-k2": {
248
+ "input_per_mtok": 0.6,
249
+ "output_per_mtok": 2.5,
250
+ "cache_read_per_mtok": 0.15,
251
+ "cache_write_per_mtok": null,
252
+ "source": "https://platform.moonshot.ai/docs/pricing/chat"
253
+ },
254
+ "kimi-k2-turbo": {
255
+ "input_per_mtok": 1.15,
256
+ "output_per_mtok": 8,
257
+ "cache_read_per_mtok": 0.15,
258
+ "cache_write_per_mtok": null,
259
+ "source": "https://platform.moonshot.ai/docs/pricing/chat"
260
+ },
261
+ "moonshot-v1-8k": {
262
+ "input_per_mtok": 0.2,
263
+ "output_per_mtok": 2,
264
+ "cache_read_per_mtok": null,
265
+ "cache_write_per_mtok": null,
266
+ "source": "https://platform.moonshot.ai/docs/pricing"
267
+ },
268
+ "moonshot-v1-32k": {
269
+ "input_per_mtok": 1,
270
+ "output_per_mtok": 3,
271
+ "cache_read_per_mtok": null,
272
+ "cache_write_per_mtok": null,
273
+ "source": "https://platform.moonshot.ai/docs/pricing"
274
+ },
275
+ "moonshot-v1-128k": {
276
+ "input_per_mtok": 2,
277
+ "output_per_mtok": 5,
278
+ "cache_read_per_mtok": null,
279
+ "cache_write_per_mtok": null,
280
+ "source": "https://platform.moonshot.ai/docs/pricing"
281
+ },
282
+ "deepseek-v3": {
283
+ "input_per_mtok": 0.27,
284
+ "output_per_mtok": 1.1,
285
+ "cache_read_per_mtok": 0.07,
286
+ "cache_write_per_mtok": 0,
287
+ "source": "https://api-docs.deepseek.com/quick_start/pricing"
288
+ },
289
+ "deepseek-r1": {
290
+ "input_per_mtok": 0.55,
291
+ "output_per_mtok": 2.19,
292
+ "cache_read_per_mtok": null,
293
+ "cache_write_per_mtok": null,
294
+ "source": "https://api-docs.deepseek.com/quick_start/pricing"
295
+ },
296
+ "deepseek-chat": {
297
+ "input_per_mtok": 0.14,
298
+ "output_per_mtok": 0.28,
299
+ "cache_read_per_mtok": 0.0028,
300
+ "cache_write_per_mtok": null,
301
+ "source": "https://api-docs.deepseek.com/quick_start/pricing"
302
+ },
303
+ "deepseek-reasoner": {
304
+ "input_per_mtok": 0.14,
305
+ "output_per_mtok": 0.28,
306
+ "cache_read_per_mtok": 0.0028,
307
+ "cache_write_per_mtok": null,
308
+ "source": "https://api-docs.deepseek.com/quick_start/pricing"
309
+ },
310
+ "glm-4.6": {
311
+ "input_per_mtok": 0.6,
312
+ "output_per_mtok": 2.2,
313
+ "cache_read_per_mtok": 0.11,
314
+ "cache_write_per_mtok": null,
315
+ "source": "https://docs.z.ai/guides/overview/pricing"
316
+ },
317
+ "glm-4-air": {
318
+ "input_per_mtok": 0.2,
319
+ "output_per_mtok": 1.1,
320
+ "cache_read_per_mtok": 0.03,
321
+ "cache_write_per_mtok": null,
322
+ "source": "https://docs.z.ai/guides/overview/pricing"
323
+ },
324
+ "grok-4": {
325
+ "input_per_mtok": 3,
326
+ "output_per_mtok": 15,
327
+ "cache_read_per_mtok": null,
328
+ "cache_write_per_mtok": null,
329
+ "source": "https://docs.x.ai/docs/pricing"
330
+ },
331
+ "grok-3": {
332
+ "input_per_mtok": 3,
333
+ "output_per_mtok": 15,
334
+ "cache_read_per_mtok": 0.75,
335
+ "cache_write_per_mtok": null,
336
+ "source": "https://docs.x.ai/docs/pricing"
337
+ },
338
+ "grok-code-fast-1": {
339
+ "input_per_mtok": 0.2,
340
+ "output_per_mtok": 1.5,
341
+ "cache_read_per_mtok": 0.02,
342
+ "cache_write_per_mtok": null,
343
+ "source": "https://docs.x.ai/docs/pricing"
344
+ },
345
+ "grok-2": {
346
+ "input_per_mtok": 2,
347
+ "output_per_mtok": 10,
348
+ "cache_read_per_mtok": null,
349
+ "cache_write_per_mtok": null,
350
+ "source": "https://docs.x.ai/docs/pricing"
351
+ },
352
+ "mistral-large-latest": {
353
+ "input_per_mtok": 0.5,
354
+ "output_per_mtok": 1.5,
355
+ "cache_read_per_mtok": null,
356
+ "cache_write_per_mtok": null,
357
+ "source": "https://mistral.ai/pricing"
358
+ },
359
+ "codestral-latest": {
360
+ "input_per_mtok": 0.3,
361
+ "output_per_mtok": 0.9,
362
+ "cache_read_per_mtok": null,
363
+ "cache_write_per_mtok": null,
364
+ "source": "https://mistral.ai/pricing"
365
+ },
366
+ "devstral": {
367
+ "input_per_mtok": 0.4,
368
+ "output_per_mtok": 2,
369
+ "cache_read_per_mtok": null,
370
+ "cache_write_per_mtok": null,
371
+ "source": "https://mistral.ai/pricing"
372
+ },
373
+ "mistral-medium": {
374
+ "input_per_mtok": 0.4,
375
+ "output_per_mtok": 2,
376
+ "cache_read_per_mtok": null,
377
+ "cache_write_per_mtok": null,
378
+ "source": "https://mistral.ai/pricing"
379
+ },
380
+ "llama-4-maverick": {
381
+ "input_per_mtok": 0.27,
382
+ "output_per_mtok": 0.85,
383
+ "cache_read_per_mtok": null,
384
+ "cache_write_per_mtok": null,
385
+ "source": "https://www.together.ai/pricing"
386
+ },
387
+ "llama-4-scout": {
388
+ "input_per_mtok": 0.08,
389
+ "output_per_mtok": 0.3,
390
+ "cache_read_per_mtok": null,
391
+ "cache_write_per_mtok": null,
392
+ "source": "https://www.together.ai/pricing"
393
+ },
394
+ "llama-3.3-70b": {
395
+ "input_per_mtok": 0.88,
396
+ "output_per_mtok": 0.88,
397
+ "cache_read_per_mtok": null,
398
+ "cache_write_per_mtok": null,
399
+ "source": "https://www.together.ai/pricing"
400
+ },
401
+ "command-a": {
402
+ "input_per_mtok": 2.5,
403
+ "output_per_mtok": 10,
404
+ "cache_read_per_mtok": null,
405
+ "cache_write_per_mtok": null,
406
+ "source": "https://cohere.com/pricing"
407
+ },
408
+ "command-r-plus": {
409
+ "input_per_mtok": 2.5,
410
+ "output_per_mtok": 10,
411
+ "cache_read_per_mtok": null,
412
+ "cache_write_per_mtok": null,
413
+ "source": "https://cohere.com/pricing"
414
+ },
415
+ "amazon-nova-pro": {
416
+ "input_per_mtok": 0.8,
417
+ "output_per_mtok": 3.2,
418
+ "cache_read_per_mtok": null,
419
+ "cache_write_per_mtok": null,
420
+ "source": "https://aws.amazon.com/bedrock/pricing/"
421
+ },
422
+ "amazon-nova-lite": {
423
+ "input_per_mtok": 0.06,
424
+ "output_per_mtok": 0.24,
425
+ "cache_read_per_mtok": null,
426
+ "cache_write_per_mtok": null,
427
+ "source": "https://aws.amazon.com/bedrock/pricing/"
428
+ }
429
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * persist-tool-calls — runtime glue between MCP server's in-memory
3
+ * `sessionStats` and the on-disk `tool_calls` SessionDB table.
4
+ *
5
+ * Why this module exists
6
+ * ──────────────────────
7
+ * Commit 4742160 (May 2 16:58) added the SessionDB write path so the
8
+ * statusline counters survived `npm update -g context-mode` and
9
+ * `claude --continue`. Commit b392c2f (May 2 21:43) — the concurrency
10
+ * refactor — silently dropped that wiring as collateral. Same-session
11
+ * `/ctx-upgrade` flips the statusline back to `0 calls / $0.00`
12
+ * because the new PID starts with an empty `sessionStats` and never
13
+ * looks at the table the old PID was writing to.
14
+ *
15
+ * This module re-introduces the write path AND adds the read-side
16
+ * restore that 4742160 never shipped — both pure helpers so the
17
+ * server.ts wiring is a one-liner and the unit tests don't need to
18
+ * boot the MCP server.
19
+ */
20
+
21
+ import { existsSync } from "node:fs";
22
+ import { SessionDB } from "./db.js";
23
+
24
+ /**
25
+ * Shape returned by {@link restoreSessionStats}. Subset of the in-memory
26
+ * `sessionStats` object the MCP server keeps — only the fields that can
27
+ * be recovered from SessionDB.
28
+ */
29
+ export interface RestoredSessionStats {
30
+ /** Per-tool call counts. */
31
+ calls: Record<string, number>;
32
+ /** Per-tool returned bytes. */
33
+ bytesReturned: Record<string, number>;
34
+ /**
35
+ * Epoch-ms for `session_meta.started_at` of the latest session, so the
36
+ * statusline `uptime_ms` reflects the original session start instead of
37
+ * resetting to `Date.now()` on every PID change.
38
+ */
39
+ sessionStart: number;
40
+ }
41
+
42
+ /**
43
+ * Increment the persistent tool-call counter for `toolName` under whatever
44
+ * session_id `session_meta` currently treats as the most recent. This is
45
+ * called from {@link trackResponse} on every tool response and must be
46
+ * cheap, non-throwing, and best-effort — a stats failure must never break
47
+ * the MCP tool call.
48
+ */
49
+ export function persistToolCallCounter(
50
+ sessionDbPath: string,
51
+ toolName: string,
52
+ bytes: number,
53
+ ): void {
54
+ try {
55
+ if (!existsSync(sessionDbPath)) return;
56
+ const sdb = new SessionDB({ dbPath: sessionDbPath });
57
+ try {
58
+ const sid = sdb.getLatestSessionId();
59
+ if (!sid) return;
60
+ sdb.incrementToolCall(sid, toolName, bytes);
61
+ } finally {
62
+ sdb.close();
63
+ }
64
+ } catch {
65
+ // Best-effort: counter must never throw and break the parent tool call.
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Read the latest session's tool-call totals back out of SessionDB so the
71
+ * MCP server can hydrate its in-memory `sessionStats` on startup. Returns
72
+ * `null` when the DB is missing or empty so the caller can keep the
73
+ * default zero-state without branching twice.
74
+ *
75
+ * Used during MCP server boot (BEFORE the heartbeat fires) so the
76
+ * statusline doesn't briefly flash `0 calls / $0.00` after upgrade.
77
+ */
78
+ export function restoreSessionStats(
79
+ sessionDbPath: string,
80
+ ): RestoredSessionStats | null {
81
+ try {
82
+ if (!existsSync(sessionDbPath)) return null;
83
+ const sdb = new SessionDB({ dbPath: sessionDbPath });
84
+ try {
85
+ const sid = sdb.getLatestSessionId();
86
+ if (!sid) return null;
87
+
88
+ const stats = sdb.getToolCallStats(sid);
89
+ const calls: Record<string, number> = {};
90
+ const bytesReturned: Record<string, number> = {};
91
+ for (const [tool, row] of Object.entries(stats.byTool)) {
92
+ calls[tool] = row.calls;
93
+ bytesReturned[tool] = row.bytesReturned;
94
+ }
95
+
96
+ // started_at is "YYYY-MM-DD HH:MM:SS" in UTC (SQLite datetime() default);
97
+ // append "Z" so Date.parse interprets it as UTC, matching how the
98
+ // session was actually persisted.
99
+ let sessionStart = Date.now();
100
+ try {
101
+ const meta = sdb.getSessionStats(sid);
102
+ if (meta?.started_at) {
103
+ const parsed = Date.parse(`${meta.started_at}Z`);
104
+ if (Number.isFinite(parsed) && parsed > 0) sessionStart = parsed;
105
+ }
106
+ } catch {
107
+ // best-effort — keep `Date.now()` fallback
108
+ }
109
+
110
+ // Skip empty restores so callers can `if (restored)` and not stomp
111
+ // their already-zero default with another zero.
112
+ if (
113
+ Object.keys(calls).length === 0 &&
114
+ Object.keys(bytesReturned).length === 0
115
+ ) {
116
+ // Still useful to return sessionStart so uptime_ms doesn't reset
117
+ // even when no tool calls were made — but only if we found a session.
118
+ return { calls, bytesReturned, sessionStart };
119
+ }
120
+
121
+ return { calls, bytesReturned, sessionStart };
122
+ } finally {
123
+ sdb.close();
124
+ }
125
+ } catch {
126
+ return null;
127
+ }
128
+ }