@rynfar/meridian 1.34.1 → 1.37.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 (46) hide show
  1. package/README.md +144 -27
  2. package/dist/cli-p9swy5t3.js +67 -0
  3. package/dist/{cli-g9ypdz51.js → cli-pr79d7nw.js} +9 -5
  4. package/dist/{cli-bwchvbfb.js → cli-ygx1djsx.js} +1838 -147
  5. package/dist/cli.js +9 -9
  6. package/dist/{profileCli-5e3p99k0.js → profileCli-5f15dx7k.js} +1 -1
  7. package/dist/{profilePage-9nkbct3w.js → profilePage-g5t5t6av.js} +4 -2
  8. package/dist/{profiles-ntgacztq.js → profiles-edzz1ffd.js} +1 -1
  9. package/dist/proxy/adapter.d.ts +17 -0
  10. package/dist/proxy/adapter.d.ts.map +1 -1
  11. package/dist/proxy/adapters/crush.d.ts.map +1 -1
  12. package/dist/proxy/auth.d.ts +27 -0
  13. package/dist/proxy/auth.d.ts.map +1 -0
  14. package/dist/proxy/errors.d.ts +1 -1
  15. package/dist/proxy/errors.d.ts.map +1 -1
  16. package/dist/proxy/query.d.ts +21 -1
  17. package/dist/proxy/query.d.ts.map +1 -1
  18. package/dist/proxy/sdkFeatures.d.ts +56 -0
  19. package/dist/proxy/sdkFeatures.d.ts.map +1 -0
  20. package/dist/proxy/server.d.ts.map +1 -1
  21. package/dist/proxy/types.d.ts +2 -0
  22. package/dist/proxy/types.d.ts.map +1 -1
  23. package/dist/server.js +3 -3
  24. package/dist/{setup-5x116vbs.js → setup-v5pnqe04.js} +1 -1
  25. package/dist/telemetry/index.d.ts +9 -4
  26. package/dist/telemetry/index.d.ts.map +1 -1
  27. package/dist/telemetry/logStore.d.ts +4 -25
  28. package/dist/telemetry/logStore.d.ts.map +1 -1
  29. package/dist/telemetry/percentiles.d.ts +12 -0
  30. package/dist/telemetry/percentiles.d.ts.map +1 -0
  31. package/dist/telemetry/profileBar.d.ts +1 -1
  32. package/dist/telemetry/profileBar.d.ts.map +1 -1
  33. package/dist/telemetry/prometheus.d.ts +10 -0
  34. package/dist/telemetry/prometheus.d.ts.map +1 -0
  35. package/dist/telemetry/routes.d.ts.map +1 -1
  36. package/dist/telemetry/settingsPage.d.ts +6 -0
  37. package/dist/telemetry/settingsPage.d.ts.map +1 -0
  38. package/dist/telemetry/sqlite.d.ts +7 -0
  39. package/dist/telemetry/sqlite.d.ts.map +1 -0
  40. package/dist/telemetry/store.d.ts +3 -3
  41. package/dist/telemetry/store.d.ts.map +1 -1
  42. package/dist/telemetry/types.d.ts +51 -0
  43. package/dist/telemetry/types.d.ts.map +1 -1
  44. package/dist/{tokenRefresh-ywwpe8k2.js → tokenRefresh-y7d1qvb3.js} +1 -1
  45. package/package.json +4 -3
  46. package/dist/cli-a05ws7rb.js +0 -18
@@ -1,8 +1,9 @@
1
1
  import {
2
+ init_profileBar,
2
3
  profileBarCss,
3
4
  profileBarHtml,
4
5
  profileBarJs
5
- } from "./cli-g9ypdz51.js";
6
+ } from "./cli-pr79d7nw.js";
6
7
  import {
7
8
  checkPluginConfigured
8
9
  } from "./cli-rtab0qa6.js";
@@ -20,9 +21,1519 @@ import {
20
21
  setActiveProfile
21
22
  } from "./cli-vdp9s10c.js";
22
23
  import {
24
+ __commonJS,
25
+ __esm,
23
26
  __export,
24
- __require
25
- } from "./cli-a05ws7rb.js";
27
+ __require,
28
+ __toCommonJS,
29
+ __toESM
30
+ } from "./cli-p9swy5t3.js";
31
+
32
+ // src/telemetry/percentiles.ts
33
+ function computePercentiles(values) {
34
+ if (values.length === 0)
35
+ return { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
36
+ const sorted = [...values].sort((a, b) => a - b);
37
+ const sum = sorted.reduce((a, b) => a + b, 0);
38
+ return {
39
+ p50: sorted[Math.floor(sorted.length * 0.5)],
40
+ p95: sorted[Math.floor(sorted.length * 0.95)],
41
+ p99: sorted[Math.floor(sorted.length * 0.99)],
42
+ min: sorted[0],
43
+ max: sorted[sorted.length - 1],
44
+ avg: Math.round(sum / sorted.length)
45
+ };
46
+ }
47
+ function computeSummary(metrics, windowMs) {
48
+ if (metrics.length === 0) {
49
+ const emptyPhase = { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
50
+ return {
51
+ windowMs,
52
+ totalRequests: 0,
53
+ errorCount: 0,
54
+ requestsPerMinute: 0,
55
+ queueWait: emptyPhase,
56
+ proxyOverhead: emptyPhase,
57
+ ttfb: emptyPhase,
58
+ upstreamDuration: emptyPhase,
59
+ totalDuration: emptyPhase,
60
+ byModel: {},
61
+ byMode: {},
62
+ tokenUsage: {
63
+ totalInputTokens: 0,
64
+ totalOutputTokens: 0,
65
+ totalCacheReadTokens: 0,
66
+ totalCacheCreationTokens: 0,
67
+ avgCacheHitRate: 0,
68
+ cacheMissOnResumeCount: 0
69
+ }
70
+ };
71
+ }
72
+ const errorCount = metrics.filter((m) => m.error !== null).length;
73
+ const oldest = metrics[metrics.length - 1].timestamp;
74
+ const newest = metrics[0].timestamp;
75
+ const spanMs = Math.max(newest - oldest, 1);
76
+ const requestsPerMinute = metrics.length / spanMs * 60000;
77
+ const queueWaits = metrics.map((m) => m.queueWaitMs);
78
+ const overheads = metrics.map((m) => m.proxyOverheadMs);
79
+ const ttfbs = metrics.filter((m) => m.ttfbMs !== null).map((m) => m.ttfbMs);
80
+ const upstreams = metrics.map((m) => m.upstreamDurationMs);
81
+ const totals = metrics.map((m) => m.totalDurationMs);
82
+ const byModel = {};
83
+ for (const m of metrics) {
84
+ const modelKey = m.requestModel || m.model;
85
+ const entry = byModel[modelKey] ??= { count: 0, totalMs: 0 };
86
+ entry.count++;
87
+ entry.totalMs += m.totalDurationMs;
88
+ }
89
+ const byMode = {};
90
+ for (const m of metrics) {
91
+ const entry = byMode[m.mode] ??= { count: 0, totalMs: 0 };
92
+ entry.count++;
93
+ entry.totalMs += m.totalDurationMs;
94
+ }
95
+ let totalInputTokens = 0;
96
+ let totalOutputTokens = 0;
97
+ let totalCacheReadTokens = 0;
98
+ let totalCacheCreationTokens = 0;
99
+ let cacheHitRateSum = 0;
100
+ let cacheHitRateCount = 0;
101
+ let cacheMissOnResumeCount = 0;
102
+ for (const m of metrics) {
103
+ totalInputTokens += m.inputTokens ?? 0;
104
+ totalOutputTokens += m.outputTokens ?? 0;
105
+ totalCacheReadTokens += m.cacheReadInputTokens ?? 0;
106
+ totalCacheCreationTokens += m.cacheCreationInputTokens ?? 0;
107
+ if (m.cacheHitRate !== undefined) {
108
+ cacheHitRateSum += m.cacheHitRate;
109
+ cacheHitRateCount++;
110
+ }
111
+ if (m.isResume && m.cacheHitRate !== undefined && m.cacheHitRate === 0) {
112
+ cacheMissOnResumeCount++;
113
+ }
114
+ }
115
+ return {
116
+ windowMs,
117
+ totalRequests: metrics.length,
118
+ errorCount,
119
+ requestsPerMinute: Math.round(requestsPerMinute * 100) / 100,
120
+ queueWait: computePercentiles(queueWaits),
121
+ proxyOverhead: computePercentiles(overheads),
122
+ ttfb: ttfbs.length > 0 ? computePercentiles(ttfbs) : { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 },
123
+ upstreamDuration: computePercentiles(upstreams),
124
+ totalDuration: computePercentiles(totals),
125
+ byModel: Object.fromEntries(Object.entries(byModel).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
126
+ byMode: Object.fromEntries(Object.entries(byMode).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
127
+ tokenUsage: {
128
+ totalInputTokens,
129
+ totalOutputTokens,
130
+ totalCacheReadTokens,
131
+ totalCacheCreationTokens,
132
+ avgCacheHitRate: cacheHitRateCount > 0 ? Math.round(cacheHitRateSum / cacheHitRateCount * 100) / 100 : 0,
133
+ cacheMissOnResumeCount
134
+ }
135
+ };
136
+ }
137
+ var init_percentiles = () => {};
138
+
139
+ // node_modules/@neon-rs/load/dist/index.js
140
+ var require_dist = __commonJS((exports) => {
141
+ var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
142
+ if (k2 === undefined)
143
+ k2 = k;
144
+ var desc = Object.getOwnPropertyDescriptor(m, k);
145
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
146
+ desc = { enumerable: true, get: function() {
147
+ return m[k];
148
+ } };
149
+ }
150
+ Object.defineProperty(o, k2, desc);
151
+ } : function(o, m, k, k2) {
152
+ if (k2 === undefined)
153
+ k2 = k;
154
+ o[k2] = m[k];
155
+ });
156
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
157
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
158
+ } : function(o, v) {
159
+ o["default"] = v;
160
+ });
161
+ var __importStar = exports && exports.__importStar || function(mod) {
162
+ if (mod && mod.__esModule)
163
+ return mod;
164
+ var result = {};
165
+ if (mod != null) {
166
+ for (var k in mod)
167
+ if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
168
+ __createBinding(result, mod, k);
169
+ }
170
+ __setModuleDefault(result, mod);
171
+ return result;
172
+ };
173
+ Object.defineProperty(exports, "__esModule", { value: true });
174
+ exports.load = exports.currentTarget = undefined;
175
+ var path = __importStar(__require("path"));
176
+ var fs = __importStar(__require("fs"));
177
+ function currentTarget() {
178
+ let os = null;
179
+ switch (process.platform) {
180
+ case "android":
181
+ switch (process.arch) {
182
+ case "arm":
183
+ return "android-arm-eabi";
184
+ case "arm64":
185
+ return "android-arm64";
186
+ }
187
+ os = "Android";
188
+ break;
189
+ case "win32":
190
+ switch (process.arch) {
191
+ case "x64":
192
+ return "win32-x64-msvc";
193
+ case "arm64":
194
+ return "win32-arm64-msvc";
195
+ case "ia32":
196
+ return "win32-ia32-msvc";
197
+ }
198
+ os = "Windows";
199
+ break;
200
+ case "darwin":
201
+ switch (process.arch) {
202
+ case "x64":
203
+ return "darwin-x64";
204
+ case "arm64":
205
+ return "darwin-arm64";
206
+ }
207
+ os = "macOS";
208
+ break;
209
+ case "linux":
210
+ switch (process.arch) {
211
+ case "x64":
212
+ case "arm64":
213
+ return isGlibc() ? `linux-${process.arch}-gnu` : `linux-${process.arch}-musl`;
214
+ case "arm":
215
+ return "linux-arm-gnueabihf";
216
+ }
217
+ os = "Linux";
218
+ break;
219
+ case "freebsd":
220
+ if (process.arch === "x64") {
221
+ return "freebsd-x64";
222
+ }
223
+ os = "FreeBSD";
224
+ break;
225
+ }
226
+ if (os) {
227
+ throw new Error(`Neon: unsupported ${os} architecture: ${process.arch}`);
228
+ }
229
+ throw new Error(`Neon: unsupported system: ${process.platform}`);
230
+ }
231
+ exports.currentTarget = currentTarget;
232
+ function isGlibc() {
233
+ const report = process.report?.getReport();
234
+ if (typeof report !== "object" || !report || !("header" in report)) {
235
+ return false;
236
+ }
237
+ const header = report.header;
238
+ return typeof header === "object" && !!header && "glibcVersionRuntime" in header;
239
+ }
240
+ function load(dirname2) {
241
+ const m = path.join(dirname2, "index.node");
242
+ return fs.existsSync(m) ? __require(m) : null;
243
+ }
244
+ exports.load = load;
245
+ });
246
+
247
+ // node_modules/detect-libc/lib/process.js
248
+ var require_process = __commonJS((exports, module) => {
249
+ var isLinux = () => process.platform === "linux";
250
+ var report = null;
251
+ var getReport = () => {
252
+ if (!report) {
253
+ report = isLinux() && process.report ? process.report.getReport() : {};
254
+ }
255
+ return report;
256
+ };
257
+ module.exports = { isLinux, getReport };
258
+ });
259
+
260
+ // node_modules/detect-libc/lib/filesystem.js
261
+ var require_filesystem = __commonJS((exports, module) => {
262
+ var fs = __require("fs");
263
+ var LDD_PATH = "/usr/bin/ldd";
264
+ var readFileSync2 = (path) => fs.readFileSync(path, "utf-8");
265
+ var readFile = (path) => new Promise((resolve2, reject) => {
266
+ fs.readFile(path, "utf-8", (err, data) => {
267
+ if (err) {
268
+ reject(err);
269
+ } else {
270
+ resolve2(data);
271
+ }
272
+ });
273
+ });
274
+ module.exports = {
275
+ LDD_PATH,
276
+ readFileSync: readFileSync2,
277
+ readFile
278
+ };
279
+ });
280
+
281
+ // node_modules/detect-libc/lib/detect-libc.js
282
+ var require_detect_libc = __commonJS((exports, module) => {
283
+ var childProcess = __require("child_process");
284
+ var { isLinux, getReport } = require_process();
285
+ var { LDD_PATH, readFile, readFileSync: readFileSync2 } = require_filesystem();
286
+ var cachedFamilyFilesystem;
287
+ var cachedVersionFilesystem;
288
+ var command = "getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true";
289
+ var commandOut = "";
290
+ var safeCommand = () => {
291
+ if (!commandOut) {
292
+ return new Promise((resolve2) => {
293
+ childProcess.exec(command, (err, out) => {
294
+ commandOut = err ? " " : out;
295
+ resolve2(commandOut);
296
+ });
297
+ });
298
+ }
299
+ return commandOut;
300
+ };
301
+ var safeCommandSync = () => {
302
+ if (!commandOut) {
303
+ try {
304
+ commandOut = childProcess.execSync(command, { encoding: "utf8" });
305
+ } catch (_err) {
306
+ commandOut = " ";
307
+ }
308
+ }
309
+ return commandOut;
310
+ };
311
+ var GLIBC = "glibc";
312
+ var RE_GLIBC_VERSION = /GLIBC\s(\d+\.\d+)/;
313
+ var MUSL = "musl";
314
+ var GLIBC_ON_LDD = GLIBC.toUpperCase();
315
+ var MUSL_ON_LDD = MUSL.toLowerCase();
316
+ var isFileMusl = (f) => f.includes("libc.musl-") || f.includes("ld-musl-");
317
+ var familyFromReport = () => {
318
+ const report = getReport();
319
+ if (report.header && report.header.glibcVersionRuntime) {
320
+ return GLIBC;
321
+ }
322
+ if (Array.isArray(report.sharedObjects)) {
323
+ if (report.sharedObjects.some(isFileMusl)) {
324
+ return MUSL;
325
+ }
326
+ }
327
+ return null;
328
+ };
329
+ var familyFromCommand = (out) => {
330
+ const [getconf, ldd1] = out.split(/[\r\n]+/);
331
+ if (getconf && getconf.includes(GLIBC)) {
332
+ return GLIBC;
333
+ }
334
+ if (ldd1 && ldd1.includes(MUSL)) {
335
+ return MUSL;
336
+ }
337
+ return null;
338
+ };
339
+ var getFamilyFromLddContent = (content) => {
340
+ if (content.includes(MUSL_ON_LDD)) {
341
+ return MUSL;
342
+ }
343
+ if (content.includes(GLIBC_ON_LDD)) {
344
+ return GLIBC;
345
+ }
346
+ return null;
347
+ };
348
+ var familyFromFilesystem = async () => {
349
+ if (cachedFamilyFilesystem !== undefined) {
350
+ return cachedFamilyFilesystem;
351
+ }
352
+ cachedFamilyFilesystem = null;
353
+ try {
354
+ const lddContent = await readFile(LDD_PATH);
355
+ cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
356
+ } catch (e) {}
357
+ return cachedFamilyFilesystem;
358
+ };
359
+ var familyFromFilesystemSync = () => {
360
+ if (cachedFamilyFilesystem !== undefined) {
361
+ return cachedFamilyFilesystem;
362
+ }
363
+ cachedFamilyFilesystem = null;
364
+ try {
365
+ const lddContent = readFileSync2(LDD_PATH);
366
+ cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
367
+ } catch (e) {}
368
+ return cachedFamilyFilesystem;
369
+ };
370
+ var family = async () => {
371
+ let family2 = null;
372
+ if (isLinux()) {
373
+ family2 = await familyFromFilesystem();
374
+ if (!family2) {
375
+ family2 = familyFromReport();
376
+ }
377
+ if (!family2) {
378
+ const out = await safeCommand();
379
+ family2 = familyFromCommand(out);
380
+ }
381
+ }
382
+ return family2;
383
+ };
384
+ var familySync = () => {
385
+ let family2 = null;
386
+ if (isLinux()) {
387
+ family2 = familyFromFilesystemSync();
388
+ if (!family2) {
389
+ family2 = familyFromReport();
390
+ }
391
+ if (!family2) {
392
+ const out = safeCommandSync();
393
+ family2 = familyFromCommand(out);
394
+ }
395
+ }
396
+ return family2;
397
+ };
398
+ var isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
399
+ var isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
400
+ var versionFromFilesystem = async () => {
401
+ if (cachedVersionFilesystem !== undefined) {
402
+ return cachedVersionFilesystem;
403
+ }
404
+ cachedVersionFilesystem = null;
405
+ try {
406
+ const lddContent = await readFile(LDD_PATH);
407
+ const versionMatch = lddContent.match(RE_GLIBC_VERSION);
408
+ if (versionMatch) {
409
+ cachedVersionFilesystem = versionMatch[1];
410
+ }
411
+ } catch (e) {}
412
+ return cachedVersionFilesystem;
413
+ };
414
+ var versionFromFilesystemSync = () => {
415
+ if (cachedVersionFilesystem !== undefined) {
416
+ return cachedVersionFilesystem;
417
+ }
418
+ cachedVersionFilesystem = null;
419
+ try {
420
+ const lddContent = readFileSync2(LDD_PATH);
421
+ const versionMatch = lddContent.match(RE_GLIBC_VERSION);
422
+ if (versionMatch) {
423
+ cachedVersionFilesystem = versionMatch[1];
424
+ }
425
+ } catch (e) {}
426
+ return cachedVersionFilesystem;
427
+ };
428
+ var versionFromReport = () => {
429
+ const report = getReport();
430
+ if (report.header && report.header.glibcVersionRuntime) {
431
+ return report.header.glibcVersionRuntime;
432
+ }
433
+ return null;
434
+ };
435
+ var versionSuffix = (s) => s.trim().split(/\s+/)[1];
436
+ var versionFromCommand = (out) => {
437
+ const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/);
438
+ if (getconf && getconf.includes(GLIBC)) {
439
+ return versionSuffix(getconf);
440
+ }
441
+ if (ldd1 && ldd2 && ldd1.includes(MUSL)) {
442
+ return versionSuffix(ldd2);
443
+ }
444
+ return null;
445
+ };
446
+ var version = async () => {
447
+ let version2 = null;
448
+ if (isLinux()) {
449
+ version2 = await versionFromFilesystem();
450
+ if (!version2) {
451
+ version2 = versionFromReport();
452
+ }
453
+ if (!version2) {
454
+ const out = await safeCommand();
455
+ version2 = versionFromCommand(out);
456
+ }
457
+ }
458
+ return version2;
459
+ };
460
+ var versionSync = () => {
461
+ let version2 = null;
462
+ if (isLinux()) {
463
+ version2 = versionFromFilesystemSync();
464
+ if (!version2) {
465
+ version2 = versionFromReport();
466
+ }
467
+ if (!version2) {
468
+ const out = safeCommandSync();
469
+ version2 = versionFromCommand(out);
470
+ }
471
+ }
472
+ return version2;
473
+ };
474
+ module.exports = {
475
+ GLIBC,
476
+ MUSL,
477
+ family,
478
+ familySync,
479
+ isNonGlibcLinux,
480
+ isNonGlibcLinuxSync,
481
+ version,
482
+ versionSync
483
+ };
484
+ });
485
+
486
+ // node_modules/libsql/auth.js
487
+ var require_auth = __commonJS((exports, module) => {
488
+ var Authorization = {
489
+ ALLOW: 0,
490
+ DENY: 1
491
+ };
492
+ module.exports = Authorization;
493
+ });
494
+
495
+ // node_modules/libsql/sqlite-error.js
496
+ var require_sqlite_error = __commonJS((exports, module) => {
497
+ var descriptor = { value: "SqliteError", writable: true, enumerable: false, configurable: true };
498
+ function SqliteError(message, code, rawCode) {
499
+ if (new.target !== SqliteError) {
500
+ return new SqliteError(message, code);
501
+ }
502
+ if (typeof code !== "string") {
503
+ throw new TypeError("Expected second argument to be a string");
504
+ }
505
+ Error.call(this, message);
506
+ descriptor.value = "" + message;
507
+ Object.defineProperty(this, "message", descriptor);
508
+ Error.captureStackTrace(this, SqliteError);
509
+ this.code = code;
510
+ this.rawCode = rawCode;
511
+ }
512
+ Object.setPrototypeOf(SqliteError, Error);
513
+ Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
514
+ Object.defineProperty(SqliteError.prototype, "name", descriptor);
515
+ module.exports = SqliteError;
516
+ });
517
+
518
+ // node_modules/libsql/index.js
519
+ var require_libsql = __commonJS((exports, module) => {
520
+ var __dirname = "/home/runner/work/meridian/meridian/node_modules/libsql";
521
+ var { load, currentTarget } = require_dist();
522
+ var { familySync, GLIBC, MUSL } = require_detect_libc();
523
+ function requireNative() {
524
+ if (process.env.LIBSQL_JS_DEV) {
525
+ return load(__dirname);
526
+ }
527
+ let target = currentTarget();
528
+ if (familySync() == GLIBC) {
529
+ switch (target) {
530
+ case "linux-x64-musl":
531
+ target = "linux-x64-gnu";
532
+ break;
533
+ case "linux-arm64-musl":
534
+ target = "linux-arm64-gnu";
535
+ break;
536
+ }
537
+ }
538
+ if (target === "linux-arm-gnueabihf" && familySync() == MUSL) {
539
+ target = "linux-arm-musleabihf";
540
+ }
541
+ return __require(`@libsql/${target}`);
542
+ }
543
+ var {
544
+ databaseOpen,
545
+ databaseOpenWithSync,
546
+ databaseInTransaction,
547
+ databaseInterrupt,
548
+ databaseClose,
549
+ databaseSyncSync,
550
+ databaseSyncUntilSync,
551
+ databaseExecSync,
552
+ databasePrepareSync,
553
+ databaseDefaultSafeIntegers,
554
+ databaseAuthorizer,
555
+ databaseLoadExtension,
556
+ databaseMaxWriteReplicationIndex,
557
+ statementRaw,
558
+ statementIsReader,
559
+ statementGet,
560
+ statementRun,
561
+ statementInterrupt,
562
+ statementRowsSync,
563
+ statementColumns,
564
+ statementSafeIntegers,
565
+ rowsNext
566
+ } = requireNative();
567
+ var Authorization = require_auth();
568
+ var SqliteError = require_sqlite_error();
569
+ function convertError(err) {
570
+ if (err.libsqlError) {
571
+ return new SqliteError(err.message, err.code, err.rawCode);
572
+ }
573
+ return err;
574
+ }
575
+
576
+ class Database {
577
+ constructor(path, opts) {
578
+ const encryptionCipher = opts?.encryptionCipher ?? "aes256cbc";
579
+ if (opts && opts.syncUrl) {
580
+ var authToken = "";
581
+ if (opts.syncAuth) {
582
+ console.warn("Warning: The `syncAuth` option is deprecated, please use `authToken` option instead.");
583
+ authToken = opts.syncAuth;
584
+ } else if (opts.authToken) {
585
+ authToken = opts.authToken;
586
+ }
587
+ const encryptionKey = opts?.encryptionKey ?? "";
588
+ const syncPeriod = opts?.syncPeriod ?? 0;
589
+ const readYourWrites = opts?.readYourWrites ?? true;
590
+ const offline = opts?.offline ?? false;
591
+ const remoteEncryptionKey = opts?.remoteEncryptionKey ?? "";
592
+ this.db = databaseOpenWithSync(path, opts.syncUrl, authToken, encryptionCipher, encryptionKey, syncPeriod, readYourWrites, offline, remoteEncryptionKey);
593
+ } else {
594
+ const authToken2 = opts?.authToken ?? "";
595
+ const encryptionKey = opts?.encryptionKey ?? "";
596
+ const timeout = opts?.timeout ?? 0;
597
+ const remoteEncryptionKey = opts?.remoteEncryptionKey ?? "";
598
+ this.db = databaseOpen(path, authToken2, encryptionCipher, encryptionKey, timeout, remoteEncryptionKey);
599
+ }
600
+ this.memory = path === ":memory:";
601
+ this.readonly = false;
602
+ this.name = "";
603
+ this.open = true;
604
+ const db = this.db;
605
+ Object.defineProperties(this, {
606
+ inTransaction: {
607
+ get() {
608
+ return databaseInTransaction(db);
609
+ }
610
+ }
611
+ });
612
+ }
613
+ sync() {
614
+ return databaseSyncSync.call(this.db);
615
+ }
616
+ syncUntil(replicationIndex) {
617
+ return databaseSyncUntilSync.call(this.db, replicationIndex);
618
+ }
619
+ prepare(sql) {
620
+ try {
621
+ const stmt = databasePrepareSync.call(this.db, sql);
622
+ return new Statement(stmt);
623
+ } catch (err) {
624
+ throw convertError(err);
625
+ }
626
+ }
627
+ transaction(fn) {
628
+ if (typeof fn !== "function")
629
+ throw new TypeError("Expected first argument to be a function");
630
+ const db = this;
631
+ const wrapTxn = (mode) => {
632
+ return (...bindParameters) => {
633
+ db.exec("BEGIN " + mode);
634
+ try {
635
+ const result = fn(...bindParameters);
636
+ db.exec("COMMIT");
637
+ return result;
638
+ } catch (err) {
639
+ db.exec("ROLLBACK");
640
+ throw err;
641
+ }
642
+ };
643
+ };
644
+ const properties = {
645
+ default: { value: wrapTxn("") },
646
+ deferred: { value: wrapTxn("DEFERRED") },
647
+ immediate: { value: wrapTxn("IMMEDIATE") },
648
+ exclusive: { value: wrapTxn("EXCLUSIVE") },
649
+ database: { value: this, enumerable: true }
650
+ };
651
+ Object.defineProperties(properties.default.value, properties);
652
+ Object.defineProperties(properties.deferred.value, properties);
653
+ Object.defineProperties(properties.immediate.value, properties);
654
+ Object.defineProperties(properties.exclusive.value, properties);
655
+ return properties.default.value;
656
+ }
657
+ pragma(source, options) {
658
+ if (options == null)
659
+ options = {};
660
+ if (typeof source !== "string")
661
+ throw new TypeError("Expected first argument to be a string");
662
+ if (typeof options !== "object")
663
+ throw new TypeError("Expected second argument to be an options object");
664
+ const simple = options["simple"];
665
+ const stmt = this.prepare(`PRAGMA ${source}`, this, true);
666
+ return simple ? stmt.pluck().get() : stmt.all();
667
+ }
668
+ backup(filename, options) {
669
+ throw new Error("not implemented");
670
+ }
671
+ serialize(options) {
672
+ throw new Error("not implemented");
673
+ }
674
+ function(name, options, fn) {
675
+ if (options == null)
676
+ options = {};
677
+ if (typeof options === "function") {
678
+ fn = options;
679
+ options = {};
680
+ }
681
+ if (typeof name !== "string")
682
+ throw new TypeError("Expected first argument to be a string");
683
+ if (typeof fn !== "function")
684
+ throw new TypeError("Expected last argument to be a function");
685
+ if (typeof options !== "object")
686
+ throw new TypeError("Expected second argument to be an options object");
687
+ if (!name)
688
+ throw new TypeError("User-defined function name cannot be an empty string");
689
+ throw new Error("not implemented");
690
+ }
691
+ aggregate(name, options) {
692
+ if (typeof name !== "string")
693
+ throw new TypeError("Expected first argument to be a string");
694
+ if (typeof options !== "object" || options === null)
695
+ throw new TypeError("Expected second argument to be an options object");
696
+ if (!name)
697
+ throw new TypeError("User-defined function name cannot be an empty string");
698
+ throw new Error("not implemented");
699
+ }
700
+ table(name, factory) {
701
+ if (typeof name !== "string")
702
+ throw new TypeError("Expected first argument to be a string");
703
+ if (!name)
704
+ throw new TypeError("Virtual table module name cannot be an empty string");
705
+ throw new Error("not implemented");
706
+ }
707
+ authorizer(rules) {
708
+ databaseAuthorizer.call(this.db, rules);
709
+ }
710
+ loadExtension(...args) {
711
+ databaseLoadExtension.call(this.db, ...args);
712
+ }
713
+ maxWriteReplicationIndex() {
714
+ return databaseMaxWriteReplicationIndex.call(this.db);
715
+ }
716
+ exec(sql) {
717
+ try {
718
+ databaseExecSync.call(this.db, sql);
719
+ } catch (err) {
720
+ throw convertError(err);
721
+ }
722
+ }
723
+ interrupt() {
724
+ databaseInterrupt.call(this.db);
725
+ }
726
+ close() {
727
+ databaseClose.call(this.db);
728
+ this.open = false;
729
+ }
730
+ defaultSafeIntegers(toggle) {
731
+ databaseDefaultSafeIntegers.call(this.db, toggle ?? true);
732
+ return this;
733
+ }
734
+ unsafeMode(...args) {
735
+ throw new Error("not implemented");
736
+ }
737
+ }
738
+
739
+ class Statement {
740
+ constructor(stmt) {
741
+ this.stmt = stmt;
742
+ this.pluckMode = false;
743
+ }
744
+ raw(raw2) {
745
+ statementRaw.call(this.stmt, raw2 ?? true);
746
+ return this;
747
+ }
748
+ pluck(pluckMode) {
749
+ this.pluckMode = pluckMode ?? true;
750
+ return this;
751
+ }
752
+ get reader() {
753
+ return statementIsReader.call(this.stmt);
754
+ }
755
+ run(...bindParameters) {
756
+ try {
757
+ if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
758
+ return statementRun.call(this.stmt, bindParameters[0]);
759
+ } else {
760
+ return statementRun.call(this.stmt, bindParameters.flat());
761
+ }
762
+ } catch (err) {
763
+ throw convertError(err);
764
+ }
765
+ }
766
+ get(...bindParameters) {
767
+ try {
768
+ if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
769
+ return statementGet.call(this.stmt, bindParameters[0]);
770
+ } else {
771
+ return statementGet.call(this.stmt, bindParameters.flat());
772
+ }
773
+ } catch (err) {
774
+ throw convertError(err);
775
+ }
776
+ }
777
+ iterate(...bindParameters) {
778
+ var rows = undefined;
779
+ if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
780
+ rows = statementRowsSync.call(this.stmt, bindParameters[0]);
781
+ } else {
782
+ rows = statementRowsSync.call(this.stmt, bindParameters.flat());
783
+ }
784
+ const iter = {
785
+ nextRows: Array(100),
786
+ nextRowIndex: 100,
787
+ next() {
788
+ try {
789
+ if (this.nextRowIndex === 100) {
790
+ rowsNext.call(rows, this.nextRows);
791
+ this.nextRowIndex = 0;
792
+ }
793
+ const row = this.nextRows[this.nextRowIndex];
794
+ this.nextRows[this.nextRowIndex] = undefined;
795
+ if (!row) {
796
+ return { done: true };
797
+ }
798
+ this.nextRowIndex++;
799
+ return { value: row, done: false };
800
+ } catch (err) {
801
+ throw convertError(err);
802
+ }
803
+ },
804
+ [Symbol.iterator]() {
805
+ return this;
806
+ }
807
+ };
808
+ return iter;
809
+ }
810
+ all(...bindParameters) {
811
+ try {
812
+ const result = [];
813
+ for (const row of this.iterate(...bindParameters)) {
814
+ if (this.pluckMode) {
815
+ result.push(row[Object.keys(row)[0]]);
816
+ } else {
817
+ result.push(row);
818
+ }
819
+ }
820
+ return result;
821
+ } catch (err) {
822
+ throw convertError(err);
823
+ }
824
+ }
825
+ interrupt() {
826
+ statementInterrupt.call(this.stmt);
827
+ }
828
+ columns() {
829
+ return statementColumns.call(this.stmt);
830
+ }
831
+ safeIntegers(toggle) {
832
+ statementSafeIntegers.call(this.stmt, toggle ?? true);
833
+ return this;
834
+ }
835
+ }
836
+ module.exports = Database;
837
+ module.exports.Authorization = Authorization;
838
+ module.exports.SqliteError = SqliteError;
839
+ });
840
+
841
+ // src/telemetry/sqlite.ts
842
+ var exports_sqlite = {};
843
+ __export(exports_sqlite, {
844
+ createSqliteStores: () => createSqliteStores
845
+ });
846
+ function openDatabase(dbPath) {
847
+ const db = new import_libsql.default(dbPath);
848
+ db.pragma("journal_mode = WAL");
849
+ db.pragma("synchronous = NORMAL");
850
+ db.exec(METRICS_SCHEMA);
851
+ db.exec(LOGS_SCHEMA);
852
+ return db;
853
+ }
854
+
855
+ class SqliteTelemetryStore {
856
+ db;
857
+ retentionMs;
858
+ insertCount = 0;
859
+ insertStmt;
860
+ countStmt;
861
+ constructor(db, retentionDays) {
862
+ this.db = db;
863
+ this.retentionMs = retentionDays * 24 * 60 * 60 * 1000;
864
+ this.insertStmt = db.prepare(`
865
+ INSERT INTO metrics (
866
+ request_id, timestamp, adapter, model, request_model, mode,
867
+ is_resume, is_passthrough, lineage_type,
868
+ has_deferred_tools, deferred_tool_count, tool_count, discovered_tools, session_discovered_count,
869
+ message_count, sdk_session_id,
870
+ status, queue_wait_ms, proxy_overhead_ms, ttfb_ms,
871
+ upstream_duration_ms, total_duration_ms, content_blocks, text_events, error,
872
+ input_tokens, output_tokens, cache_read_input_tokens,
873
+ cache_creation_input_tokens, cache_hit_rate
874
+ ) VALUES (
875
+ @requestId, @timestamp, @adapter, @model, @requestModel, @mode,
876
+ @isResume, @isPassthrough, @lineageType,
877
+ @hasDeferredTools, @deferredToolCount, @toolCount, @discoveredTools, @sessionDiscoveredCount,
878
+ @messageCount, @sdkSessionId,
879
+ @status, @queueWaitMs, @proxyOverheadMs, @ttfbMs,
880
+ @upstreamDurationMs, @totalDurationMs, @contentBlocks, @textEvents, @error,
881
+ @inputTokens, @outputTokens, @cacheReadInputTokens,
882
+ @cacheCreationInputTokens, @cacheHitRate
883
+ )
884
+ `);
885
+ this.countStmt = db.prepare("SELECT COUNT(*) as cnt FROM metrics");
886
+ }
887
+ record(metric) {
888
+ try {
889
+ this.insertStmt.run({
890
+ requestId: metric.requestId,
891
+ timestamp: metric.timestamp,
892
+ adapter: metric.adapter ?? null,
893
+ model: metric.model,
894
+ requestModel: metric.requestModel ?? null,
895
+ mode: metric.mode,
896
+ isResume: metric.isResume ? 1 : 0,
897
+ isPassthrough: metric.isPassthrough ? 1 : 0,
898
+ lineageType: metric.lineageType ?? null,
899
+ hasDeferredTools: metric.hasDeferredTools ? 1 : metric.hasDeferredTools === false ? 0 : null,
900
+ deferredToolCount: metric.deferredToolCount ?? null,
901
+ toolCount: metric.toolCount ?? null,
902
+ discoveredTools: metric.discoveredTools ? JSON.stringify(metric.discoveredTools) : null,
903
+ sessionDiscoveredCount: metric.sessionDiscoveredCount ?? null,
904
+ messageCount: metric.messageCount ?? null,
905
+ sdkSessionId: metric.sdkSessionId ?? null,
906
+ status: metric.status,
907
+ queueWaitMs: metric.queueWaitMs,
908
+ proxyOverheadMs: metric.proxyOverheadMs,
909
+ ttfbMs: metric.ttfbMs ?? null,
910
+ upstreamDurationMs: metric.upstreamDurationMs,
911
+ totalDurationMs: metric.totalDurationMs,
912
+ contentBlocks: metric.contentBlocks,
913
+ textEvents: metric.textEvents,
914
+ error: metric.error ?? null,
915
+ inputTokens: metric.inputTokens ?? null,
916
+ outputTokens: metric.outputTokens ?? null,
917
+ cacheReadInputTokens: metric.cacheReadInputTokens ?? null,
918
+ cacheCreationInputTokens: metric.cacheCreationInputTokens ?? null,
919
+ cacheHitRate: metric.cacheHitRate ?? null
920
+ });
921
+ } catch (err) {
922
+ console.error("[telemetry] SQLite write failed, skipping:", err);
923
+ return;
924
+ }
925
+ if (++this.insertCount % CLEANUP_INTERVAL === 0) {
926
+ this.cleanup();
927
+ }
928
+ }
929
+ get size() {
930
+ try {
931
+ return this.countStmt.get().cnt;
932
+ } catch {
933
+ return 0;
934
+ }
935
+ }
936
+ getRecent(options = {}) {
937
+ const { limit = 50, since, model } = options;
938
+ const conditions = [];
939
+ const params = { limit };
940
+ if (since !== undefined) {
941
+ conditions.push("timestamp >= @since");
942
+ params.since = since;
943
+ }
944
+ if (model !== undefined) {
945
+ conditions.push("model = @model");
946
+ params.model = model;
947
+ }
948
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
949
+ const sql = `SELECT * FROM metrics ${where} ORDER BY timestamp DESC, id DESC LIMIT @limit`;
950
+ try {
951
+ const rows = this.db.prepare(sql).all(params);
952
+ return rows.map(rowToMetric);
953
+ } catch {
954
+ return [];
955
+ }
956
+ }
957
+ getLastForSession(sdkSessionId) {
958
+ try {
959
+ const row = this.db.prepare(`SELECT * FROM metrics WHERE sdk_session_id = ? AND error IS NULL ORDER BY timestamp DESC, id DESC LIMIT 1`).get(sdkSessionId);
960
+ return row ? rowToMetric(row) : undefined;
961
+ } catch {
962
+ return;
963
+ }
964
+ }
965
+ summarize(windowMs = 60 * 60 * 1000) {
966
+ const since = Date.now() - windowMs;
967
+ const metrics = this.getRecent({ limit: 1e5, since });
968
+ return computeSummary(metrics, windowMs);
969
+ }
970
+ clear() {
971
+ try {
972
+ this.db.exec("DELETE FROM metrics");
973
+ } catch {}
974
+ }
975
+ cleanup() {
976
+ try {
977
+ const cutoff = Date.now() - this.retentionMs;
978
+ this.db.prepare("DELETE FROM metrics WHERE timestamp < ?").run(cutoff);
979
+ this.db.prepare("DELETE FROM diagnostic_logs WHERE timestamp < ?").run(cutoff);
980
+ this.db.pragma("wal_checkpoint(TRUNCATE)");
981
+ } catch (err) {
982
+ console.error("[telemetry] SQLite cleanup failed:", err);
983
+ }
984
+ }
985
+ }
986
+
987
+ class SqliteDiagnosticLogStore {
988
+ db;
989
+ insertStmt;
990
+ constructor(db) {
991
+ this.db = db;
992
+ this.insertStmt = db.prepare(`
993
+ INSERT INTO diagnostic_logs (timestamp, level, category, request_id, message)
994
+ VALUES (@timestamp, @level, @category, @requestId, @message)
995
+ `);
996
+ }
997
+ log(entry) {
998
+ try {
999
+ this.insertStmt.run({
1000
+ timestamp: Date.now(),
1001
+ level: entry.level,
1002
+ category: entry.category,
1003
+ requestId: entry.requestId ?? null,
1004
+ message: entry.message
1005
+ });
1006
+ } catch (err) {
1007
+ console.error("[telemetry] SQLite log write failed:", err);
1008
+ }
1009
+ }
1010
+ session(message, requestId) {
1011
+ this.log({ level: "info", category: "session", message, requestId });
1012
+ }
1013
+ lineage(message, requestId) {
1014
+ this.log({ level: "warn", category: "lineage", message, requestId });
1015
+ }
1016
+ error(message, requestId) {
1017
+ this.log({ level: "error", category: "error", message, requestId });
1018
+ }
1019
+ getRecent(options = {}) {
1020
+ const { limit = 100, since, category } = options;
1021
+ const conditions = [];
1022
+ const params = { limit };
1023
+ if (since !== undefined) {
1024
+ conditions.push("timestamp >= @since");
1025
+ params.since = since;
1026
+ }
1027
+ if (category !== undefined) {
1028
+ conditions.push("category = @category");
1029
+ params.category = category;
1030
+ }
1031
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1032
+ const sql = `SELECT * FROM diagnostic_logs ${where} ORDER BY timestamp DESC, id DESC LIMIT @limit`;
1033
+ try {
1034
+ const rows = this.db.prepare(sql).all(params);
1035
+ return rows.map((r) => ({
1036
+ timestamp: r.timestamp,
1037
+ level: r.level,
1038
+ category: r.category,
1039
+ requestId: r.request_id ?? undefined,
1040
+ message: r.message
1041
+ }));
1042
+ } catch {
1043
+ return [];
1044
+ }
1045
+ }
1046
+ clear() {
1047
+ try {
1048
+ this.db.exec("DELETE FROM diagnostic_logs");
1049
+ } catch {}
1050
+ }
1051
+ }
1052
+ function rowToMetric(r) {
1053
+ return {
1054
+ requestId: r.request_id,
1055
+ timestamp: r.timestamp,
1056
+ adapter: r.adapter ?? undefined,
1057
+ model: r.model,
1058
+ requestModel: r.request_model ?? undefined,
1059
+ mode: r.mode,
1060
+ isResume: r.is_resume === 1,
1061
+ isPassthrough: r.is_passthrough === 1,
1062
+ lineageType: r.lineage_type ?? undefined,
1063
+ hasDeferredTools: r.has_deferred_tools === 1 ? true : r.has_deferred_tools === 0 ? false : undefined,
1064
+ deferredToolCount: r.deferred_tool_count ?? undefined,
1065
+ toolCount: r.tool_count ?? undefined,
1066
+ discoveredTools: r.discovered_tools ? JSON.parse(r.discovered_tools) : undefined,
1067
+ sessionDiscoveredCount: r.session_discovered_count ?? undefined,
1068
+ messageCount: r.message_count ?? undefined,
1069
+ sdkSessionId: r.sdk_session_id ?? undefined,
1070
+ status: r.status,
1071
+ queueWaitMs: r.queue_wait_ms,
1072
+ proxyOverheadMs: r.proxy_overhead_ms,
1073
+ ttfbMs: r.ttfb_ms ?? null,
1074
+ upstreamDurationMs: r.upstream_duration_ms,
1075
+ totalDurationMs: r.total_duration_ms,
1076
+ contentBlocks: r.content_blocks,
1077
+ textEvents: r.text_events,
1078
+ error: r.error ?? null,
1079
+ inputTokens: r.input_tokens ?? undefined,
1080
+ outputTokens: r.output_tokens ?? undefined,
1081
+ cacheReadInputTokens: r.cache_read_input_tokens ?? undefined,
1082
+ cacheCreationInputTokens: r.cache_creation_input_tokens ?? undefined,
1083
+ cacheHitRate: r.cache_hit_rate ?? undefined
1084
+ };
1085
+ }
1086
+ function createSqliteStores(dbPath, retentionDays) {
1087
+ const db = openDatabase(dbPath);
1088
+ return {
1089
+ telemetry: new SqliteTelemetryStore(db, retentionDays),
1090
+ diagnostics: new SqliteDiagnosticLogStore(db),
1091
+ close: () => {
1092
+ try {
1093
+ db.close();
1094
+ } catch {}
1095
+ }
1096
+ };
1097
+ }
1098
+ var import_libsql, METRICS_SCHEMA = `
1099
+ CREATE TABLE IF NOT EXISTS metrics (
1100
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1101
+ request_id TEXT NOT NULL,
1102
+ timestamp INTEGER NOT NULL,
1103
+ adapter TEXT,
1104
+ model TEXT NOT NULL,
1105
+ request_model TEXT,
1106
+ mode TEXT NOT NULL,
1107
+ is_resume INTEGER NOT NULL,
1108
+ is_passthrough INTEGER NOT NULL,
1109
+ lineage_type TEXT,
1110
+ has_deferred_tools INTEGER,
1111
+ deferred_tool_count INTEGER,
1112
+ tool_count INTEGER,
1113
+ discovered_tools TEXT,
1114
+ session_discovered_count INTEGER,
1115
+ message_count INTEGER,
1116
+ sdk_session_id TEXT,
1117
+ status INTEGER NOT NULL,
1118
+ queue_wait_ms REAL NOT NULL,
1119
+ proxy_overhead_ms REAL NOT NULL,
1120
+ ttfb_ms REAL,
1121
+ upstream_duration_ms REAL NOT NULL,
1122
+ total_duration_ms REAL NOT NULL,
1123
+ content_blocks INTEGER NOT NULL,
1124
+ text_events INTEGER NOT NULL,
1125
+ error TEXT,
1126
+ input_tokens INTEGER,
1127
+ output_tokens INTEGER,
1128
+ cache_read_input_tokens INTEGER,
1129
+ cache_creation_input_tokens INTEGER,
1130
+ cache_hit_rate REAL
1131
+ );
1132
+ CREATE INDEX IF NOT EXISTS idx_metrics_ts ON metrics(timestamp);
1133
+ CREATE INDEX IF NOT EXISTS idx_metrics_model ON metrics(model);
1134
+ CREATE INDEX IF NOT EXISTS idx_metrics_session_success ON metrics(sdk_session_id, timestamp DESC, id DESC);
1135
+ `, LOGS_SCHEMA = `
1136
+ CREATE TABLE IF NOT EXISTS diagnostic_logs (
1137
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1138
+ timestamp INTEGER NOT NULL,
1139
+ level TEXT NOT NULL,
1140
+ category TEXT NOT NULL,
1141
+ request_id TEXT,
1142
+ message TEXT NOT NULL
1143
+ );
1144
+ CREATE INDEX IF NOT EXISTS idx_logs_ts ON diagnostic_logs(timestamp);
1145
+ CREATE INDEX IF NOT EXISTS idx_logs_cat ON diagnostic_logs(category);
1146
+ `, CLEANUP_INTERVAL = 1000;
1147
+ var init_sqlite = __esm(() => {
1148
+ init_percentiles();
1149
+ import_libsql = __toESM(require_libsql(), 1);
1150
+ });
1151
+
1152
+ // src/proxy/sdkFeatures.ts
1153
+ var exports_sdkFeatures = {};
1154
+ __export(exports_sdkFeatures, {
1155
+ validateFeatureUpdate: () => validateFeatureUpdate,
1156
+ updateAdapterFeatures: () => updateAdapterFeatures,
1157
+ resetAdapterFeatures: () => resetAdapterFeatures,
1158
+ getFeaturesForAdapter: () => getFeaturesForAdapter,
1159
+ getAllFeatureConfigs: () => getAllFeatureConfigs
1160
+ });
1161
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
1162
+ import { join as join5 } from "node:path";
1163
+ import { homedir as homedir4 } from "node:os";
1164
+ function getConfigPath() {
1165
+ const dir = join5(homedir4(), ".config", "meridian");
1166
+ if (!existsSync4(dir))
1167
+ mkdirSync2(dir, { recursive: true });
1168
+ return join5(dir, "sdk-features.json");
1169
+ }
1170
+ function readConfig() {
1171
+ const now = Date.now();
1172
+ if (cachedConfig && now - lastReadTime < CACHE_TTL_MS)
1173
+ return cachedConfig;
1174
+ const path3 = getConfigPath();
1175
+ try {
1176
+ if (existsSync4(path3)) {
1177
+ cachedConfig = JSON.parse(readFileSync3(path3, "utf-8"));
1178
+ } else {
1179
+ cachedConfig = {};
1180
+ }
1181
+ } catch {
1182
+ cachedConfig = {};
1183
+ }
1184
+ lastReadTime = now;
1185
+ return cachedConfig;
1186
+ }
1187
+ function writeConfig(config) {
1188
+ const path3 = getConfigPath();
1189
+ const tmp = `${path3}.tmp`;
1190
+ try {
1191
+ writeFileSync2(tmp, JSON.stringify(config, null, 2));
1192
+ renameSync2(tmp, path3);
1193
+ cachedConfig = config;
1194
+ lastReadTime = Date.now();
1195
+ } catch (e) {
1196
+ console.error("[sdk-features] write failed:", e.message);
1197
+ }
1198
+ }
1199
+ function getFeaturesForAdapter(adapterName) {
1200
+ const config = readConfig();
1201
+ const userOverrides = config[adapterName] ?? {};
1202
+ const adapterDefaults = ADAPTER_DEFAULTS[adapterName] ?? {};
1203
+ return {
1204
+ ...DEFAULT_FEATURES,
1205
+ ...adapterDefaults,
1206
+ ...userOverrides
1207
+ };
1208
+ }
1209
+ function getAllFeatureConfigs() {
1210
+ const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough"];
1211
+ const result = {};
1212
+ for (const name of adapters) {
1213
+ result[name] = getFeaturesForAdapter(name);
1214
+ }
1215
+ return result;
1216
+ }
1217
+ function validateFeatureUpdate(raw2) {
1218
+ if (raw2 === null || typeof raw2 !== "object" || Array.isArray(raw2)) {
1219
+ throw new Error("body must be a JSON object");
1220
+ }
1221
+ const input = raw2;
1222
+ const result = {};
1223
+ for (const [key, value] of Object.entries(input)) {
1224
+ if (!(key in DEFAULT_FEATURES))
1225
+ continue;
1226
+ const expected = typeof DEFAULT_FEATURES[key];
1227
+ if (key === "claudeMd") {
1228
+ if (typeof value !== "string" || !VALID_CLAUDE_MD_VALUES.has(value)) {
1229
+ throw new Error(`claudeMd must be one of: ${[...VALID_CLAUDE_MD_VALUES].join(", ")}`);
1230
+ }
1231
+ result[key] = value;
1232
+ } else if (key === "thinking") {
1233
+ if (typeof value !== "string" || !VALID_THINKING_VALUES.has(value)) {
1234
+ throw new Error(`thinking must be one of: ${[...VALID_THINKING_VALUES].join(", ")}`);
1235
+ }
1236
+ result[key] = value;
1237
+ } else if (expected === "boolean") {
1238
+ if (typeof value !== "boolean")
1239
+ throw new Error(`${key} must be a boolean`);
1240
+ result[key] = value;
1241
+ } else if (expected === "number") {
1242
+ if (typeof value !== "number" || !isFinite(value))
1243
+ throw new Error(`${key} must be a finite number`);
1244
+ result[key] = value;
1245
+ } else if (expected === "string") {
1246
+ if (typeof value !== "string")
1247
+ throw new Error(`${key} must be a string`);
1248
+ result[key] = value;
1249
+ }
1250
+ }
1251
+ return result;
1252
+ }
1253
+ function updateAdapterFeatures(adapterName, features) {
1254
+ const config = readConfig();
1255
+ config[adapterName] = { ...config[adapterName] ?? {}, ...features };
1256
+ writeConfig(config);
1257
+ }
1258
+ function resetAdapterFeatures(adapterName) {
1259
+ const config = readConfig();
1260
+ delete config[adapterName];
1261
+ writeConfig(config);
1262
+ }
1263
+ var DEFAULT_FEATURES, ADAPTER_DEFAULTS, cachedConfig = null, lastReadTime = 0, CACHE_TTL_MS = 5000, VALID_CLAUDE_MD_VALUES, VALID_THINKING_VALUES;
1264
+ var init_sdkFeatures = __esm(() => {
1265
+ DEFAULT_FEATURES = {
1266
+ codeSystemPrompt: false,
1267
+ clientSystemPrompt: true,
1268
+ claudeMd: "off",
1269
+ memory: false,
1270
+ dreaming: false,
1271
+ thinking: "disabled",
1272
+ thinkingPassthrough: false,
1273
+ sharedMemory: false,
1274
+ maxBudgetUsd: 0,
1275
+ fallbackModel: "",
1276
+ sdkDebug: false,
1277
+ additionalDirectories: ""
1278
+ };
1279
+ ADAPTER_DEFAULTS = {};
1280
+ VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
1281
+ VALID_THINKING_VALUES = new Set(["adaptive", "enabled", "disabled"]);
1282
+ });
1283
+
1284
+ // src/telemetry/settingsPage.ts
1285
+ var exports_settingsPage = {};
1286
+ __export(exports_settingsPage, {
1287
+ settingsPageHtml: () => settingsPageHtml
1288
+ });
1289
+ var settingsPageHtml;
1290
+ var init_settingsPage = __esm(() => {
1291
+ init_profileBar();
1292
+ settingsPageHtml = `<!DOCTYPE html>
1293
+ <html lang="en">
1294
+ <head>
1295
+ <meta charset="utf-8">
1296
+ <meta name="viewport" content="width=device-width, initial-scale=1">
1297
+ <title>Meridian — SDK Features</title>
1298
+ <link rel="icon" type="image/svg+xml" href="/telemetry/icon.svg">
1299
+ <style>
1300
+ :root {
1301
+ --bg: #0d1117; --surface: #161b22; --border: #30363d;
1302
+ --text: #e6edf3; --muted: #8b949e; --accent: #58a6ff;
1303
+ --green: #3fb950; --yellow: #d29922; --red: #f85149;
1304
+ --purple: #bc8cff;
1305
+ }
1306
+ * { box-sizing: border-box; margin: 0; padding: 0; }
1307
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
1308
+ background: var(--bg); color: var(--text); padding: 0; line-height: 1.5; }
1309
+ ${profileBarCss}
1310
+ .content { max-width: 900px; margin: 0 auto; padding: 24px; }
1311
+ h1 { font-size: 20px; font-weight: 600; margin-bottom: 4px; }
1312
+ .subtitle { color: var(--muted); font-size: 13px; margin-bottom: 24px; }
1313
+ .nav { display: flex; gap: 16px; margin-bottom: 24px; font-size: 13px; }
1314
+ .nav a { color: var(--muted); text-decoration: none; }
1315
+ .nav a:hover { color: var(--accent); }
1316
+ .nav a.active { color: var(--accent); }
1317
+
1318
+ .adapter-card {
1319
+ background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
1320
+ padding: 20px; margin-bottom: 16px;
1321
+ }
1322
+ .adapter-header {
1323
+ display: flex; align-items: center; justify-content: space-between;
1324
+ margin-bottom: 16px;
1325
+ }
1326
+ .adapter-name { font-size: 16px; font-weight: 600; }
1327
+ .adapter-badge {
1328
+ font-size: 10px; padding: 2px 8px; border-radius: 10px;
1329
+ text-transform: uppercase; letter-spacing: 0.5px;
1330
+ }
1331
+ .badge-active { background: rgba(63, 185, 80, 0.15); color: var(--green); }
1332
+ .badge-inactive { background: rgba(139, 148, 158, 0.15); color: var(--muted); }
1333
+
1334
+ .feature-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
1335
+ @media (max-width: 600px) { .feature-grid { grid-template-columns: 1fr; } }
1336
+
1337
+ .feature-row {
1338
+ display: flex; align-items: center; justify-content: space-between;
1339
+ padding: 10px 14px; border-radius: 6px;
1340
+ background: var(--bg); border: 1px solid var(--border);
1341
+ }
1342
+ .feature-info { display: flex; flex-direction: column; }
1343
+ .feature-label { font-size: 13px; font-weight: 500; }
1344
+ .feature-desc { font-size: 11px; color: var(--muted); margin-top: 2px; }
1345
+
1346
+ /* Toggle switch */
1347
+ .toggle { position: relative; width: 36px; height: 20px; flex-shrink: 0; }
1348
+ .toggle input { opacity: 0; width: 0; height: 0; }
1349
+ .toggle-track {
1350
+ position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0;
1351
+ background: var(--border); border-radius: 10px; transition: background 0.2s;
1352
+ }
1353
+ .toggle-track::after {
1354
+ content: ""; position: absolute; height: 14px; width: 14px;
1355
+ left: 3px; bottom: 3px; background: var(--muted); border-radius: 50%;
1356
+ transition: transform 0.2s, background 0.2s;
1357
+ }
1358
+ .toggle input:checked + .toggle-track { background: var(--accent); }
1359
+ .toggle input:checked + .toggle-track::after {
1360
+ transform: translateX(16px); background: var(--text);
1361
+ }
1362
+
1363
+ /* Select dropdown */
1364
+ .feature-select {
1365
+ background: var(--surface); color: var(--text); border: 1px solid var(--border);
1366
+ border-radius: 6px; padding: 4px 8px; font-size: 12px; cursor: pointer;
1367
+ }
1368
+
1369
+ .save-indicator {
1370
+ position: fixed; bottom: 24px; right: 24px;
1371
+ background: var(--green); color: #000; padding: 8px 16px;
1372
+ border-radius: 6px; font-size: 13px; font-weight: 500;
1373
+ opacity: 0; transition: opacity 0.3s; pointer-events: none;
1374
+ }
1375
+ .save-indicator.visible { opacity: 1; }
1376
+
1377
+ .reset-btn {
1378
+ background: none; border: 1px solid var(--border); color: var(--muted);
1379
+ border-radius: 6px; padding: 4px 12px; font-size: 11px; cursor: pointer;
1380
+ }
1381
+ .reset-btn:hover { border-color: var(--red); color: var(--red); }
1382
+ </style>
1383
+ </head>
1384
+ <body>
1385
+ ${profileBarHtml}
1386
+ <div class="content">
1387
+ <h1>SDK Features <span style="font-size:11px;padding:2px 8px;border-radius:10px;background:rgba(210,153,34,0.15);color:var(--yellow);vertical-align:middle;margin-left:8px">Experimental</span></h1>
1388
+ <p class="subtitle" style="max-width:720px;line-height:1.6">
1389
+ Unlock Claude Code features for any connected agent. Capabilities like auto-memory, dreaming, and CLAUDE.md — normally
1390
+ exclusive to Claude Code — become available to OpenCode, Crush, Droid, and any other harness routed through Meridian.
1391
+ Each agent keeps its own toolchain while gaining access to these additional features.<br><br>
1392
+ <strong style="color:var(--text)">System prompts:</strong> For these features to work correctly, both the Claude Code prompt and your client prompt
1393
+ should be enabled. When both are active, they are appended together — Claude Code's base instructions come first,
1394
+ followed by your agent's specific instructions.
1395
+ </p>
1396
+
1397
+ <div id="adapters"></div>
1398
+ </div>
1399
+
1400
+ <div class="save-indicator" id="saveIndicator">Saved</div>
1401
+
1402
+ <script>
1403
+ const FEATURES = [
1404
+ { key: 'codeSystemPrompt', label: 'Claude Code Prompt', desc: 'Include the built-in Claude Code system prompt (tool usage rules, safety guidelines, coding best practices)', type: 'toggle' },
1405
+ { key: 'clientSystemPrompt', label: 'Client Prompt', desc: 'Include the system prompt sent by the connecting agent (e.g. OpenCode or Crush instructions)', type: 'toggle' },
1406
+ { key: 'claudeMd', label: 'CLAUDE.md', desc: 'Load CLAUDE.md instruction files — Off: none, Project: ./CLAUDE.md only, Full: ~/.claude/CLAUDE.md + ./CLAUDE.md', type: 'select', options: ['off', 'project', 'full'] },
1407
+ { key: 'memory', label: 'Memory', desc: 'Read and write memories across sessions', type: 'toggle' },
1408
+ { key: 'dreaming', label: 'Auto-Dream', desc: 'Background memory consolidation', type: 'toggle' },
1409
+ { key: 'thinking', label: 'Thinking', desc: 'Extended thinking mode', type: 'select', options: ['disabled', 'adaptive', 'enabled'] },
1410
+ { key: 'thinkingPassthrough', label: 'Thinking Passthrough', desc: 'Forward thinking blocks to the client', type: 'toggle' },
1411
+ { key: 'sharedMemory', label: 'Shared Memory', desc: 'Share memory with Claude Code (~/.claude) instead of isolated storage', type: 'toggle' },
1412
+ { key: 'maxBudgetUsd', label: 'Max Budget (USD)', desc: 'Per-request cost cap — query aborts if exceeded (0 = disabled)', type: 'number' },
1413
+ { key: 'fallbackModel', label: 'Fallback Model', desc: 'Auto-fallback model if primary fails', type: 'select', options: ['', 'sonnet', 'opus', 'haiku', 'sonnet[1m]', 'opus[1m]'] },
1414
+ { key: 'sdkDebug', label: 'SDK Debug Logging', desc: 'Enable verbose SDK debug output to proxy stderr', type: 'toggle' },
1415
+ { key: 'additionalDirectories', label: 'Additional Directories', desc: 'Comma-separated extra paths Claude can access (monorepo libs, etc.)', type: 'text' },
1416
+ ];
1417
+
1418
+ const ADAPTER_LABELS = {
1419
+ opencode: 'OpenCode',
1420
+ crush: 'Crush',
1421
+ forgecode: 'ForgeCode',
1422
+ pi: 'Pi',
1423
+ droid: 'Droid',
1424
+ passthrough: 'LiteLLM / Passthrough',
1425
+ };
1426
+
1427
+ let currentConfig = {};
1428
+
1429
+ async function loadConfig() {
1430
+ const res = await fetch('/settings/api/features');
1431
+ currentConfig = await res.json();
1432
+ render();
1433
+ }
1434
+
1435
+ async function saveFeature(adapter, key, value) {
1436
+ const patch = {};
1437
+ patch[key] = value;
1438
+ await fetch('/settings/api/features/' + adapter, {
1439
+ method: 'PATCH',
1440
+ headers: { 'Content-Type': 'application/json' },
1441
+ body: JSON.stringify(patch),
1442
+ });
1443
+ currentConfig[adapter][key] = value;
1444
+ showSaved();
1445
+ }
1446
+
1447
+ async function resetAdapter(adapter) {
1448
+ await fetch('/settings/api/features/' + adapter, { method: 'DELETE' });
1449
+ await loadConfig();
1450
+ showSaved();
1451
+ }
1452
+
1453
+ function showSaved() {
1454
+ const el = document.getElementById('saveIndicator');
1455
+ el.classList.add('visible');
1456
+ setTimeout(() => el.classList.remove('visible'), 1500);
1457
+ }
1458
+
1459
+ function hasAnyEnabled(features) {
1460
+ return features.codeSystemPrompt || !features.clientSystemPrompt || features.claudeMd !== 'off' || features.memory || features.dreaming ||
1461
+ features.thinking !== 'disabled' || features.thinkingPassthrough ||
1462
+ features.sharedMemory || features.maxBudgetUsd > 0 ||
1463
+ features.fallbackModel || features.sdkDebug ||
1464
+ features.additionalDirectories;
1465
+ }
1466
+
1467
+ function render() {
1468
+ const container = document.getElementById('adapters');
1469
+ container.innerHTML = '';
1470
+
1471
+ for (const [adapter, label] of Object.entries(ADAPTER_LABELS)) {
1472
+ const features = currentConfig[adapter] || {};
1473
+ const active = hasAnyEnabled(features);
1474
+
1475
+ const card = document.createElement('div');
1476
+ card.className = 'adapter-card';
1477
+ card.innerHTML = '<div class="adapter-header">' +
1478
+ '<span class="adapter-name">' + label + '</span>' +
1479
+ '<div style="display:flex;gap:8px;align-items:center">' +
1480
+ '<span class="adapter-badge ' + (active ? 'badge-active' : 'badge-inactive') + '">' +
1481
+ (active ? 'Active' : 'Default') +
1482
+ '</span>' +
1483
+ '<button class="reset-btn" onclick="resetAdapter(\\''+adapter+'\\')">Reset</button>' +
1484
+ '</div>' +
1485
+ '</div>';
1486
+
1487
+ const grid = document.createElement('div');
1488
+ grid.className = 'feature-grid';
1489
+
1490
+ for (const feat of FEATURES) {
1491
+ const row = document.createElement('div');
1492
+ row.className = 'feature-row';
1493
+
1494
+ const info = '<div class="feature-info"><span class="feature-label">' +
1495
+ feat.label + '</span><span class="feature-desc">' + feat.desc + '</span></div>';
1496
+
1497
+ if (feat.type === 'toggle') {
1498
+ const checked = features[feat.key] ? 'checked' : '';
1499
+ row.innerHTML = info +
1500
+ '<label class="toggle"><input type="checkbox" ' + checked +
1501
+ ' onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', this.checked)">' +
1502
+ '<span class="toggle-track"></span></label>';
1503
+ } else if (feat.type === 'select') {
1504
+ const options = feat.options.map(o => {
1505
+ const label = o === '' ? '(None)' : o.charAt(0).toUpperCase()+o.slice(1);
1506
+ return '<option value="'+o+'"'+(features[feat.key]===o?' selected':'')+'>'+label+'</option>';
1507
+ }).join('');
1508
+ row.innerHTML = info +
1509
+ '<select class="feature-select" onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', this.value)">' +
1510
+ options + '</select>';
1511
+ } else if (feat.type === 'number') {
1512
+ const value = features[feat.key] ?? 0;
1513
+ row.innerHTML = info +
1514
+ '<input type="number" class="feature-select" style="width:80px;text-align:right" min="0" step="0.01" value="'+value+'"' +
1515
+ ' onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', parseFloat(this.value)||0)">';
1516
+ } else if (feat.type === 'text') {
1517
+ const value = (features[feat.key] ?? '').toString().replace(/"/g, '&quot;');
1518
+ row.innerHTML = info +
1519
+ '<input type="text" class="feature-select" style="width:180px" value="'+value+'"' +
1520
+ ' onchange="saveFeature(\\''+adapter+'\\', \\''+feat.key+'\\', this.value)">';
1521
+ }
1522
+
1523
+ grid.appendChild(row);
1524
+ }
1525
+
1526
+ card.appendChild(grid);
1527
+ container.appendChild(card);
1528
+ }
1529
+ }
1530
+
1531
+ loadConfig();
1532
+ ${profileBarJs}
1533
+ </script>
1534
+ </body>
1535
+ </html>`;
1536
+ });
26
1537
 
27
1538
  // node_modules/hono/dist/compose.js
28
1539
  var compose = (middleware, onError, onNotFound) => {
@@ -2187,7 +3698,8 @@ var DEFAULT_PROXY_CONFIG = {
2187
3698
  idleTimeoutSeconds: 120,
2188
3699
  silent: false,
2189
3700
  profiles: undefined,
2190
- defaultProfile: undefined
3701
+ defaultProfile: undefined,
3702
+ version: undefined
2191
3703
  };
2192
3704
 
2193
3705
  // src/env.ts
@@ -2198,6 +3710,13 @@ function envBool(suffix) {
2198
3710
  const val = env(suffix);
2199
3711
  return val === "1" || val === "true" || val === "yes";
2200
3712
  }
3713
+ function envInt(suffix, defaultValue) {
3714
+ const val = env(suffix);
3715
+ if (!val)
3716
+ return defaultValue;
3717
+ const parsed = parseInt(val, 10);
3718
+ return Number.isFinite(parsed) ? parsed : defaultValue;
3719
+ }
2201
3720
 
2202
3721
  // src/proxy/server.ts
2203
3722
  import { exec as execCallback2 } from "child_process";
@@ -6270,7 +7789,12 @@ function stripMcpPrefix(toolName) {
6270
7789
  return toolName;
6271
7790
  }
6272
7791
 
7792
+ // src/telemetry/index.ts
7793
+ import { join } from "node:path";
7794
+ import { homedir } from "node:os";
7795
+
6273
7796
  // src/telemetry/store.ts
7797
+ init_percentiles();
6274
7798
  var DEFAULT_CAPACITY = 1000;
6275
7799
  function getCapacity() {
6276
7800
  const raw2 = process.env.MERIDIAN_TELEMETRY_SIZE ?? process.env.CLAUDE_PROXY_TELEMETRY_SIZE;
@@ -6282,7 +7806,7 @@ function getCapacity() {
6282
7806
  return parsed;
6283
7807
  }
6284
7808
 
6285
- class TelemetryStore {
7809
+ class MemoryTelemetryStore {
6286
7810
  buffer;
6287
7811
  head = 0;
6288
7812
  count = 0;
@@ -6329,94 +7853,7 @@ class TelemetryStore {
6329
7853
  summarize(windowMs = 60 * 60 * 1000) {
6330
7854
  const since = Date.now() - windowMs;
6331
7855
  const metrics = this.getRecent({ limit: this.capacity, since });
6332
- if (metrics.length === 0) {
6333
- const emptyPhase = { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
6334
- return {
6335
- windowMs,
6336
- totalRequests: 0,
6337
- errorCount: 0,
6338
- requestsPerMinute: 0,
6339
- queueWait: emptyPhase,
6340
- proxyOverhead: emptyPhase,
6341
- ttfb: emptyPhase,
6342
- upstreamDuration: emptyPhase,
6343
- totalDuration: emptyPhase,
6344
- byModel: {},
6345
- byMode: {},
6346
- tokenUsage: {
6347
- totalInputTokens: 0,
6348
- totalOutputTokens: 0,
6349
- totalCacheReadTokens: 0,
6350
- totalCacheCreationTokens: 0,
6351
- avgCacheHitRate: 0,
6352
- cacheMissOnResumeCount: 0
6353
- }
6354
- };
6355
- }
6356
- const errorCount = metrics.filter((m) => m.error !== null).length;
6357
- const oldest = metrics[metrics.length - 1].timestamp;
6358
- const newest = metrics[0].timestamp;
6359
- const spanMs = Math.max(newest - oldest, 1);
6360
- const requestsPerMinute = metrics.length / spanMs * 60000;
6361
- const queueWaits = metrics.map((m) => m.queueWaitMs);
6362
- const overheads = metrics.map((m) => m.proxyOverheadMs);
6363
- const ttfbs = metrics.filter((m) => m.ttfbMs !== null).map((m) => m.ttfbMs);
6364
- const upstreams = metrics.map((m) => m.upstreamDurationMs);
6365
- const totals = metrics.map((m) => m.totalDurationMs);
6366
- const byModel = {};
6367
- for (const m of metrics) {
6368
- const modelKey = m.requestModel || m.model;
6369
- const entry = byModel[modelKey] ??= { count: 0, totalMs: 0 };
6370
- entry.count++;
6371
- entry.totalMs += m.totalDurationMs;
6372
- }
6373
- const byMode = {};
6374
- for (const m of metrics) {
6375
- const entry = byMode[m.mode] ??= { count: 0, totalMs: 0 };
6376
- entry.count++;
6377
- entry.totalMs += m.totalDurationMs;
6378
- }
6379
- let totalInputTokens = 0;
6380
- let totalOutputTokens = 0;
6381
- let totalCacheReadTokens = 0;
6382
- let totalCacheCreationTokens = 0;
6383
- let cacheHitRateSum = 0;
6384
- let cacheHitRateCount = 0;
6385
- let cacheMissOnResumeCount = 0;
6386
- for (const m of metrics) {
6387
- totalInputTokens += m.inputTokens ?? 0;
6388
- totalOutputTokens += m.outputTokens ?? 0;
6389
- totalCacheReadTokens += m.cacheReadInputTokens ?? 0;
6390
- totalCacheCreationTokens += m.cacheCreationInputTokens ?? 0;
6391
- if (m.cacheHitRate !== undefined) {
6392
- cacheHitRateSum += m.cacheHitRate;
6393
- cacheHitRateCount++;
6394
- }
6395
- if (m.isResume && m.cacheHitRate !== undefined && m.cacheHitRate === 0) {
6396
- cacheMissOnResumeCount++;
6397
- }
6398
- }
6399
- return {
6400
- windowMs,
6401
- totalRequests: metrics.length,
6402
- errorCount,
6403
- requestsPerMinute: Math.round(requestsPerMinute * 100) / 100,
6404
- queueWait: computePercentiles(queueWaits),
6405
- proxyOverhead: computePercentiles(overheads),
6406
- ttfb: ttfbs.length > 0 ? computePercentiles(ttfbs) : { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 },
6407
- upstreamDuration: computePercentiles(upstreams),
6408
- totalDuration: computePercentiles(totals),
6409
- byModel: Object.fromEntries(Object.entries(byModel).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
6410
- byMode: Object.fromEntries(Object.entries(byMode).map(([k, v]) => [k, { count: v.count, avgTotalMs: Math.round(v.totalMs / v.count) }])),
6411
- tokenUsage: {
6412
- totalInputTokens,
6413
- totalOutputTokens,
6414
- totalCacheReadTokens,
6415
- totalCacheCreationTokens,
6416
- avgCacheHitRate: cacheHitRateCount > 0 ? Math.round(cacheHitRateSum / cacheHitRateCount * 100) / 100 : 0,
6417
- cacheMissOnResumeCount
6418
- }
6419
- };
7856
+ return computeSummary(metrics, windowMs);
6420
7857
  }
6421
7858
  clear() {
6422
7859
  this.buffer = new Array(this.capacity).fill(null);
@@ -6424,25 +7861,12 @@ class TelemetryStore {
6424
7861
  this.count = 0;
6425
7862
  }
6426
7863
  }
6427
- function computePercentiles(values) {
6428
- if (values.length === 0)
6429
- return { p50: 0, p95: 0, p99: 0, min: 0, max: 0, avg: 0 };
6430
- const sorted = [...values].sort((a, b) => a - b);
6431
- const sum = sorted.reduce((a, b) => a + b, 0);
6432
- return {
6433
- p50: sorted[Math.floor(sorted.length * 0.5)],
6434
- p95: sorted[Math.floor(sorted.length * 0.95)],
6435
- p99: sorted[Math.floor(sorted.length * 0.99)],
6436
- min: sorted[0],
6437
- max: sorted[sorted.length - 1],
6438
- avg: Math.round(sum / sorted.length)
6439
- };
6440
- }
6441
- var telemetryStore = new TelemetryStore;
7864
+ var telemetryStore = new MemoryTelemetryStore;
7865
+
6442
7866
  // src/telemetry/logStore.ts
6443
7867
  var DEFAULT_CAPACITY2 = 500;
6444
7868
 
6445
- class DiagnosticLogStore {
7869
+ class MemoryDiagnosticLogStore {
6446
7870
  buffer;
6447
7871
  head = 0;
6448
7872
  count = 0;
@@ -6488,13 +7912,14 @@ class DiagnosticLogStore {
6488
7912
  this.count = 0;
6489
7913
  }
6490
7914
  }
6491
- var diagnosticLog = new DiagnosticLogStore;
7915
+ var diagnosticLog = new MemoryDiagnosticLogStore;
6492
7916
  // src/telemetry/routes.ts
6493
7917
  import { existsSync, readFileSync } from "node:fs";
6494
7918
  import { resolve, dirname } from "node:path";
6495
7919
  import { fileURLToPath } from "node:url";
6496
7920
 
6497
7921
  // src/telemetry/dashboard.ts
7922
+ init_profileBar();
6498
7923
  var dashboardHtml = `<!DOCTYPE html>
6499
7924
  <html lang="en">
6500
7925
  <head>
@@ -6866,7 +8291,7 @@ function createTelemetryRoutes() {
6866
8291
  const limit = Number.parseInt(c.req.query("limit") || "50", 10);
6867
8292
  const since = c.req.query("since") ? Number.parseInt(c.req.query("since"), 10) : undefined;
6868
8293
  const model = c.req.query("model") || undefined;
6869
- const requests = telemetryStore.getRecent({
8294
+ const requests = telemetryStore2.getRecent({
6870
8295
  limit: Math.min(limit, 500),
6871
8296
  since,
6872
8297
  model
@@ -6875,14 +8300,14 @@ function createTelemetryRoutes() {
6875
8300
  });
6876
8301
  routes.get("/summary", (c) => {
6877
8302
  const windowMs = Number.parseInt(c.req.query("window") || "3600000", 10);
6878
- const summary = telemetryStore.summarize(windowMs);
8303
+ const summary = telemetryStore2.summarize(windowMs);
6879
8304
  return c.json(summary);
6880
8305
  });
6881
8306
  routes.get("/logs", (c) => {
6882
8307
  const limit = Number.parseInt(c.req.query("limit") || "100", 10);
6883
8308
  const since = c.req.query("since") ? Number.parseInt(c.req.query("since"), 10) : undefined;
6884
8309
  const category = c.req.query("category") || undefined;
6885
- const logs = diagnosticLog.getRecent({
8310
+ const logs = diagnosticLog2.getRecent({
6886
8311
  limit: Math.min(limit, 500),
6887
8312
  since,
6888
8313
  category
@@ -6892,6 +8317,7 @@ function createTelemetryRoutes() {
6892
8317
  return routes;
6893
8318
  }
6894
8319
  // src/telemetry/landing.ts
8320
+ init_profileBar();
6895
8321
  var landingHtml = `<!DOCTYPE html>
6896
8322
  <html lang="en">
6897
8323
  <head>
@@ -7004,7 +8430,7 @@ function render(h,s){
7004
8430
  o+='</div>';
7005
8431
  if(s.byModel&&Object.keys(s.byModel).length>0){o+='<div class="section"><div class="section-title">Models (24h)</div><div class="grid">';for(const[n,d]of Object.entries(s.byModel))o+=card(n,d.count,'avg '+ms(d.avgTotalMs),'');o+='</div></div>'}
7006
8432
  o+='<div class="section"><div class="section-title">Connect an Agent</div><div class="snippet"><div class="snippet-tabs"><div class="snippet-tab active" onclick="showTab(this,&apos;opencode&apos;)">OpenCode</div><div class="snippet-tab" onclick="showTab(this,&apos;crush&apos;)">Crush</div><div class="snippet-tab" onclick="showTab(this,&apos;generic&apos;)">Any Tool</div></div><div id="tab-opencode"><code>ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://'+location.host+' opencode</code></div><div id="tab-crush" style="display:none"><code>'+JSON.stringify({providers:{meridian:{type:"anthropic",base_url:"http://"+location.host,api_key:"x",models:[{id:"claude-sonnet-4-5-20250514",name:"Sonnet 4.5"}]}}},null,2)+'</code></div><div id="tab-generic" style="display:none"><code>export ANTHROPIC_API_KEY=x\\nexport ANTHROPIC_BASE_URL=http://'+location.host+'</code></div></div></div>';
7007
- o+='<div class="links"><a href="/telemetry" class="link">\uD83D\uDCCA Telemetry</a><a href="/profiles" class="link">\uD83D\uDC64 Profiles</a><a href="/health" class="link">\uD83E\uDE7A Health</a><a href="/telemetry/summary" class="link">\uD83D\uDCC8 Stats API</a><a href="https://github.com/rynfar/meridian" class="link">⚙️ GitHub</a></div>';
8433
+ o+='<div class="links"><a href="/telemetry" class="link">\uD83D\uDCCA Telemetry</a><a href="/settings" class="link">\uD83D\uDD27 Settings</a><a href="/profiles" class="link">\uD83D\uDC64 Profiles</a><a href="/health" class="link">\uD83E\uDE7A Health</a><a href="/telemetry/summary" class="link">\uD83D\uDCC8 Stats API</a><a href="https://github.com/rynfar/meridian" class="link">⚙️ GitHub</a></div>';
7008
8434
  o+='<div class="footer">Meridian · Built on the <a href="https://github.com/anthropics/claude-code-sdk-js">Claude Code SDK</a></div>';
7009
8435
  document.getElementById('content').innerHTML=o;
7010
8436
  }
@@ -7014,6 +8440,95 @@ refresh();setInterval(refresh,10000);
7014
8440
  </script>
7015
8441
  </body>
7016
8442
  </html>`;
8443
+
8444
+ // src/telemetry/index.ts
8445
+ init_percentiles();
8446
+
8447
+ // src/telemetry/prometheus.ts
8448
+ var DURATION_BUCKETS = [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 1e4, 30000];
8449
+ var PHASES = [
8450
+ { key: "queue_wait", extract: (m) => m.queueWaitMs },
8451
+ { key: "proxy_overhead", extract: (m) => m.proxyOverheadMs },
8452
+ { key: "ttfb", extract: (m) => m.ttfbMs },
8453
+ { key: "upstream", extract: (m) => m.upstreamDurationMs },
8454
+ { key: "total", extract: (m) => m.totalDurationMs }
8455
+ ];
8456
+ function escapeLabelValue(v) {
8457
+ return v.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
8458
+ }
8459
+ function formatLabels(labels) {
8460
+ return Object.entries(labels).map(([k, v]) => `${k}="${escapeLabelValue(v)}"`).join(",");
8461
+ }
8462
+ function renderPrometheusMetrics(store) {
8463
+ const metrics = store.getRecent({ limit: 1e4 });
8464
+ const lines = [];
8465
+ lines.push("# HELP meridian_requests_total Total proxy requests");
8466
+ lines.push("# TYPE meridian_requests_total counter");
8467
+ const counters = new Map;
8468
+ for (const m of metrics) {
8469
+ const key = `${m.model}\x00${m.mode}\x00${m.status}`;
8470
+ counters.set(key, (counters.get(key) ?? 0) + 1);
8471
+ }
8472
+ for (const [key, count] of counters) {
8473
+ const [model, mode, status] = key.split("\x00");
8474
+ lines.push(`meridian_requests_total{${formatLabels({ model, mode, status })}} ${count}`);
8475
+ }
8476
+ lines.push("");
8477
+ lines.push("# HELP meridian_request_duration_ms Request duration by phase in milliseconds");
8478
+ lines.push("# TYPE meridian_request_duration_ms histogram");
8479
+ for (const phase of PHASES) {
8480
+ const values = [];
8481
+ for (const m of metrics) {
8482
+ const v = phase.extract(m);
8483
+ if (v !== null)
8484
+ values.push(v);
8485
+ }
8486
+ const phaseLabel = `phase="${escapeLabelValue(phase.key)}"`;
8487
+ for (const le of DURATION_BUCKETS) {
8488
+ const count = values.filter((v) => v <= le).length;
8489
+ lines.push(`meridian_request_duration_ms_bucket{${phaseLabel},le="${le}"} ${count}`);
8490
+ }
8491
+ lines.push(`meridian_request_duration_ms_bucket{${phaseLabel},le="+Inf"} ${values.length}`);
8492
+ const sum = values.reduce((a, b) => a + b, 0);
8493
+ lines.push(`meridian_request_duration_ms_sum{${phaseLabel}} ${sum}`);
8494
+ lines.push(`meridian_request_duration_ms_count{${phaseLabel}} ${values.length}`);
8495
+ }
8496
+ lines.push("");
8497
+ return lines.join(`
8498
+ `);
8499
+ }
8500
+
8501
+ // src/telemetry/index.ts
8502
+ init_sqlite();
8503
+ function getDefaultDbPath() {
8504
+ return join(homedir(), ".config", "meridian", "telemetry.db");
8505
+ }
8506
+ function createStores() {
8507
+ if (!envBool("TELEMETRY_PERSIST")) {
8508
+ return {
8509
+ telemetry: new MemoryTelemetryStore,
8510
+ diagnostics: new MemoryDiagnosticLogStore
8511
+ };
8512
+ }
8513
+ try {
8514
+ const { createSqliteStores: createSqliteStores2 } = (init_sqlite(), __toCommonJS(exports_sqlite));
8515
+ const dbPath = env("TELEMETRY_DB") ?? getDefaultDbPath();
8516
+ const retention = envInt("TELEMETRY_RETENTION_DAYS", 7);
8517
+ const stores = createSqliteStores2(dbPath, retention);
8518
+ console.error(`[telemetry] SQLite persistence enabled: ${dbPath} (${retention}d retention)`);
8519
+ return { telemetry: stores.telemetry, diagnostics: stores.diagnostics };
8520
+ } catch {
8521
+ console.warn("[telemetry] MERIDIAN_TELEMETRY_PERSIST is set but libsql is not installed. Run: npm install libsql");
8522
+ return {
8523
+ telemetry: new MemoryTelemetryStore,
8524
+ diagnostics: new MemoryDiagnosticLogStore
8525
+ };
8526
+ }
8527
+ }
8528
+ var stores = createStores();
8529
+ var telemetryStore2 = stores.telemetry;
8530
+ var diagnosticLog2 = stores.diagnostics;
8531
+
7017
8532
  // src/proxy/errors.ts
7018
8533
  function classifyError(errMsg) {
7019
8534
  const lower = errMsg.toLowerCase();
@@ -7106,7 +8621,8 @@ function isExpiredTokenError(errMsg) {
7106
8621
  function isStaleSessionError(error) {
7107
8622
  if (!(error instanceof Error))
7108
8623
  return false;
7109
- return error.message.includes("No message found with message.uuid");
8624
+ const msg = error.message;
8625
+ return msg.includes("No message found with message.uuid") || msg.includes("No conversation found with session ID") || msg.includes("No conversation found to continue") || msg.includes("No conversations found to resume");
7110
8626
  }
7111
8627
  function isRateLimitError(errMsg) {
7112
8628
  const lower = errMsg.toLowerCase();
@@ -7121,7 +8637,7 @@ function isExtraUsageRequiredError(errMsg) {
7121
8637
  import { exec as execCallback } from "child_process";
7122
8638
  import { existsSync as existsSync2 } from "fs";
7123
8639
  import { fileURLToPath as fileURLToPath2 } from "url";
7124
- import { join, dirname as dirname2 } from "path";
8640
+ import { join as join2, dirname as dirname2 } from "path";
7125
8641
  import { promisify } from "util";
7126
8642
  var exec = promisify(execCallback);
7127
8643
  var AUTH_STATUS_CACHE_TTL_MS = 60000;
@@ -7263,7 +8779,7 @@ async function resolveClaudeExecutableAsync() {
7263
8779
  if (runningUnderBun) {
7264
8780
  try {
7265
8781
  const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
7266
- const sdkCliJs = join(dirname2(sdkPath), "cli.js");
8782
+ const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
7267
8783
  if (existsSync2(sdkCliJs)) {
7268
8784
  cachedClaudePath = sdkCliJs;
7269
8785
  return sdkCliJs;
@@ -7281,7 +8797,7 @@ async function resolveClaudeExecutableAsync() {
7281
8797
  if (!runningUnderBun) {
7282
8798
  try {
7283
8799
  const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
7284
- const sdkCliJs = join(dirname2(sdkPath), "cli.js");
8800
+ const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
7285
8801
  if (existsSync2(sdkCliJs)) {
7286
8802
  cachedClaudePath = sdkCliJs;
7287
8803
  return sdkCliJs;
@@ -7479,6 +8995,42 @@ function getLastUserMessage(messages) {
7479
8995
  return messages.slice(-1);
7480
8996
  }
7481
8997
 
8998
+ // src/proxy/auth.ts
8999
+ import { createHmac, timingSafeEqual } from "node:crypto";
9000
+ function getConfiguredKey() {
9001
+ return process.env.MERIDIAN_API_KEY || undefined;
9002
+ }
9003
+ function safeCompare(a, b) {
9004
+ const hashA = createHmac("sha256", "meridian").update(a).digest();
9005
+ const hashB = createHmac("sha256", "meridian").update(b).digest();
9006
+ return timingSafeEqual(hashA, hashB);
9007
+ }
9008
+ function extractKey(c) {
9009
+ const apiKey = c.req.header("x-api-key");
9010
+ if (apiKey)
9011
+ return apiKey;
9012
+ const auth = c.req.header("authorization");
9013
+ if (auth?.startsWith("Bearer "))
9014
+ return auth.slice(7);
9015
+ return;
9016
+ }
9017
+ async function requireAuth(c, next) {
9018
+ const key = getConfiguredKey();
9019
+ if (!key)
9020
+ return next();
9021
+ const provided = extractKey(c);
9022
+ if (!provided || !safeCompare(provided, key)) {
9023
+ return c.json({
9024
+ type: "error",
9025
+ error: {
9026
+ type: "authentication_error",
9027
+ message: "Invalid or missing API key"
9028
+ }
9029
+ }, 401);
9030
+ }
9031
+ return next();
9032
+ }
9033
+
7482
9034
  // src/proxy/fileChanges.ts
7483
9035
  function extractFileChange(toolName, toolInput, mcpPrefix) {
7484
9036
  if (!toolName.startsWith(mcpPrefix))
@@ -7965,6 +9517,9 @@ var crushAdapter = {
7965
9517
  buildSystemContextAddendum(_body, _sdkAgents) {
7966
9518
  return "";
7967
9519
  },
9520
+ supportsThinking() {
9521
+ return true;
9522
+ },
7968
9523
  extractFileChangesFromToolUse(toolName, toolInput) {
7969
9524
  const input = toolInput;
7970
9525
  const filePath = input?.file_path ?? input?.path;
@@ -8249,6 +9804,10 @@ function detectAdapter(c) {
8249
9804
  return defaultAdapter;
8250
9805
  }
8251
9806
 
9807
+ // src/proxy/query.ts
9808
+ import { join as join3 } from "node:path";
9809
+ import { homedir as homedir2 } from "node:os";
9810
+
8252
9811
  // src/mcpTools.ts
8253
9812
  import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/claude-agent-sdk";
8254
9813
  import * as fs from "node:fs/promises";
@@ -13941,6 +15500,18 @@ function createOpencodeMcpServer() {
13941
15500
  }
13942
15501
 
13943
15502
  // src/proxy/query.ts
15503
+ function resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt) {
15504
+ const hasSettings = settingSources != null && settingSources.length > 0;
15505
+ const usePreset = codeSystemPrompt ?? (hasSettings || !passthrough && !!systemContext);
15506
+ const includeClient = clientSystemPrompt ?? true;
15507
+ const clientContext = includeClient ? systemContext : undefined;
15508
+ if (usePreset) {
15509
+ return clientContext ? { systemPrompt: { type: "preset", preset: "claude_code", append: clientContext } } : { systemPrompt: { type: "preset", preset: "claude_code" } };
15510
+ }
15511
+ if (clientContext)
15512
+ return { systemPrompt: clientContext };
15513
+ return {};
15514
+ }
13944
15515
  function buildQueryOptions(ctx) {
13945
15516
  const {
13946
15517
  prompt,
@@ -13963,7 +15534,17 @@ function buildQueryOptions(ctx) {
13963
15534
  effort,
13964
15535
  thinking,
13965
15536
  taskBudget,
13966
- betas
15537
+ betas,
15538
+ settingSources,
15539
+ codeSystemPrompt,
15540
+ clientSystemPrompt,
15541
+ memory,
15542
+ dreaming,
15543
+ sharedMemory,
15544
+ maxBudgetUsd,
15545
+ fallbackModel,
15546
+ sdkDebug,
15547
+ additionalDirectories
13967
15548
  } = ctx;
13968
15549
  const blockedTools = [...adapter.getBlockedBuiltinTools(), ...adapter.getAgentIncompatibleTools()];
13969
15550
  const mcpServerName = adapter.getMcpServerName();
@@ -13979,9 +15560,7 @@ function buildQueryOptions(ctx) {
13979
15560
  ...stream2 ? { includePartialMessages: true } : {},
13980
15561
  permissionMode: "bypassPermissions",
13981
15562
  allowDangerouslySkipPermissions: true,
13982
- ...systemContext ? {
13983
- systemPrompt: passthrough ? systemContext : { type: "preset", preset: "claude_code", append: systemContext }
13984
- } : {},
15563
+ ...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt),
13985
15564
  ...passthrough ? {
13986
15565
  disallowedTools: blockedTools,
13987
15566
  ...passthroughMcp ? {
@@ -13994,11 +15573,19 @@ function buildQueryOptions(ctx) {
13994
15573
  mcpServers: { [mcpServerName]: createOpencodeMcpServer() }
13995
15574
  },
13996
15575
  plugins: [],
15576
+ ...settingSources && settingSources.length > 0 ? {
15577
+ settingSources,
15578
+ settings: {
15579
+ autoMemoryEnabled: ctx.memory ?? true,
15580
+ autoDreamEnabled: ctx.dreaming ?? false
15581
+ }
15582
+ } : {},
13997
15583
  ...onStderr ? { stderr: onStderr } : {},
13998
15584
  env: {
13999
15585
  ...cleanEnv,
14000
15586
  ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
14001
15587
  ...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
15588
+ ...sharedMemory ? { CLAUDE_CONFIG_DIR: join3(homedir2(), ".claude") } : {},
14002
15589
  ...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {}
14003
15590
  },
14004
15591
  ...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
@@ -14008,7 +15595,11 @@ function buildQueryOptions(ctx) {
14008
15595
  ...effort ? { effort } : {},
14009
15596
  ...thinking ? { thinking } : {},
14010
15597
  ...taskBudget ? { taskBudget } : {},
14011
- ...betas && betas.length > 0 ? { betas } : {}
15598
+ ...betas && betas.length > 0 ? { betas } : {},
15599
+ ...maxBudgetUsd && maxBudgetUsd > 0 ? { maxBudgetUsd } : {},
15600
+ ...fallbackModel ? { fallbackModel } : {},
15601
+ ...sdkDebug ? { debug: true } : {},
15602
+ ...additionalDirectories && additionalDirectories.length > 0 ? { additionalDirectories } : {}
14012
15603
  }
14013
15604
  };
14014
15605
  }
@@ -14268,7 +15859,7 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
14268
15859
  if (suffixOverlap >= MIN_SUFFIX_FOR_COMPACTION && cached.messageHashes.length >= MIN_STORED_FOR_COMPACTION && suffixStartInIncoming > 0) {
14269
15860
  const compactionMsg = `Compaction detected (key=${cacheKey2.slice(0, 8)}…): suffix overlap ${suffixOverlap}/${cached.messageHashes.length}. Allowing resume.`;
14270
15861
  console.error(`[PROXY] ${compactionMsg}`);
14271
- diagnosticLog.lineage(compactionMsg);
15862
+ diagnosticLog2.lineage(compactionMsg);
14272
15863
  cached.lineageHash = computeLineageHash(messages);
14273
15864
  cached.messageHashes = incomingHashes;
14274
15865
  cached.messageCount = messages.length;
@@ -14286,13 +15877,13 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
14286
15877
  }
14287
15878
  const undoMsg = `Undo detected (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${prefixOverlap}/${cached.messageHashes.length}, rollback UUID: ${rollbackUuid || "none (legacy session)"}.`;
14288
15879
  console.error(`[PROXY] ${undoMsg}`);
14289
- diagnosticLog.lineage(undoMsg);
15880
+ diagnosticLog2.lineage(undoMsg);
14290
15881
  return { type: "undo", session: cached, prefixOverlap, rollbackUuid };
14291
15882
  }
14292
15883
  if (prefixOverlap > 0 && messages.length > cached.messageCount) {
14293
15884
  const modifiedMsg = `Modified continuation (key=${cacheKey2.slice(0, 8)}…): prefix overlap ${prefixOverlap}/${cached.messageHashes.length}, incoming ${messages.length} msgs. Allowing resume.`;
14294
15885
  console.error(`[PROXY] ${modifiedMsg}`);
14295
- diagnosticLog.lineage(modifiedMsg);
15886
+ diagnosticLog2.lineage(modifiedMsg);
14296
15887
  cached.lineageHash = computeLineageHash(messages.slice(0, messages.length));
14297
15888
  cached.messageHashes = incomingHashes;
14298
15889
  cached.messageCount = messages.length;
@@ -14379,8 +15970,8 @@ import {
14379
15970
  unlinkSync,
14380
15971
  writeFileSync
14381
15972
  } from "node:fs";
14382
- import { homedir } from "node:os";
14383
- import { join as join2 } from "node:path";
15973
+ import { homedir as homedir3 } from "node:os";
15974
+ import { join as join4 } from "node:path";
14384
15975
  var DEFAULT_MAX_STORED_SESSIONS = 1e4;
14385
15976
  var STALE_LOCK_THRESHOLD_MS = 30000;
14386
15977
  function getMaxStoredSessions() {
@@ -14431,11 +16022,11 @@ function getStorePath() {
14431
16022
  if (!existsSync3(dir)) {
14432
16023
  mkdirSync(dir, { recursive: true });
14433
16024
  }
14434
- return join2(dir, "sessions.json");
16025
+ return join4(dir, "sessions.json");
14435
16026
  }
14436
16027
  function getDefaultCacheDir() {
14437
- const newDir = join2(homedir(), ".cache", "meridian");
14438
- const oldDir = join2(homedir(), ".cache", "opencode-claude-max-proxy");
16028
+ const newDir = join4(homedir3(), ".cache", "meridian");
16029
+ const oldDir = join4(homedir3(), ".cache", "opencode-claude-max-proxy");
14439
16030
  if (existsSync3(newDir))
14440
16031
  return newDir;
14441
16032
  if (existsSync3(oldDir)) {
@@ -14867,7 +16458,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
14867
16458
  isResume,
14868
16459
  isPassthrough
14869
16460
  };
14870
- const prevMetric = telemetryStore.getLastForSession(sdkSessionId);
16461
+ const prevMetric = telemetryStore2.getLastForSession(sdkSessionId);
14871
16462
  const previous = prevMetric ? {
14872
16463
  requestId: prevMetric.requestId,
14873
16464
  turnNumber: turnNumber - 1,
@@ -14886,7 +16477,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
14886
16477
  console.error(line);
14887
16478
  }
14888
16479
  for (const a of anomalies) {
14889
- diagnosticLog.log({
16480
+ diagnosticLog2.log({
14890
16481
  level: a.severity === "critical" ? "error" : "warn",
14891
16482
  category: "token",
14892
16483
  message: `${requestId} ${a.type}: ${a.detail}`,
@@ -14897,10 +16488,19 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
14897
16488
  }
14898
16489
  function createProxyServer(config = {}) {
14899
16490
  const finalConfig = { ...DEFAULT_PROXY_CONFIG, ...config };
16491
+ const serverVersion = finalConfig.version ?? "unknown";
14900
16492
  restoreActiveProfile(finalConfig.profiles);
14901
16493
  const sessionDiscoveredTools = new Map;
14902
16494
  const app = new Hono2;
14903
16495
  app.use("*", cors());
16496
+ app.use("/v1/*", requireAuth);
16497
+ app.use("/messages", requireAuth);
16498
+ app.use("/telemetry/*", requireAuth);
16499
+ app.use("/telemetry", requireAuth);
16500
+ app.use("/metrics", requireAuth);
16501
+ app.use("/profiles/*", requireAuth);
16502
+ app.use("/profiles", requireAuth);
16503
+ app.use("/auth/*", requireAuth);
14904
16504
  app.get("/", (c) => {
14905
16505
  const accept = c.req.header("accept") || "";
14906
16506
  if (accept.includes("application/json") && !accept.includes("text/html")) {
@@ -14908,7 +16508,7 @@ function createProxyServer(config = {}) {
14908
16508
  status: "ok",
14909
16509
  service: "meridian",
14910
16510
  format: "anthropic",
14911
- endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/health"]
16511
+ endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/metrics", "/health"]
14912
16512
  });
14913
16513
  }
14914
16514
  return c.html(landingHtml);
@@ -15003,6 +16603,14 @@ function createProxyServer(config = {}) {
15003
16603
  console.error(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
15004
16604
  }
15005
16605
  }
16606
+ const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
16607
+ const sdkFeatures = getFeaturesForAdapter2(adapter.name);
16608
+ if (!thinking) {
16609
+ if (sdkFeatures.thinking === "adaptive")
16610
+ thinking = { type: "adaptive" };
16611
+ else if (sdkFeatures.thinking === "enabled")
16612
+ thinking = { type: "enabled" };
16613
+ }
15006
16614
  const thinkingBetaStripped = betaFilter.stripped.some((b) => b.startsWith("interleaved-thinking"));
15007
16615
  if (thinkingBetaStripped) {
15008
16616
  thinking = { type: "disabled" };
@@ -15031,14 +16639,14 @@ function createProxyServer(config = {}) {
15031
16639
  const toolCount = body.tools?.length ?? 0;
15032
16640
  const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name} model=${model} stream=${stream2} tools=${toolCount} lineage=${lineageType} session=${resumeSessionId?.slice(0, 8) || "new"}${isUndo && undoRollbackUuid ? ` rollback=${undoRollbackUuid.slice(0, 8)}` : ""}${agentMode ? ` agent=${agentMode}` : ""} active=${activeSessions}/${MAX_CONCURRENT_SESSIONS} msgCount=${msgCount}`;
15033
16641
  console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
15034
- diagnosticLog.session(`${requestLogLine}`, requestMeta.requestId);
16642
+ diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
15035
16643
  if (lineageResult.type === "diverged" && profileSessionId) {
15036
16644
  const recovery = lookupSessionRecovery(profileSessionId);
15037
16645
  if (recovery) {
15038
16646
  const prevId = recovery.previousClaudeSessionId || recovery.claudeSessionId;
15039
16647
  const recoveryMsg = `${requestMeta.requestId} SESSION RECOVERY: previous conversation available. Run: claude --resume ${prevId}`;
15040
16648
  console.error(`[PROXY] ${recoveryMsg}`);
15041
- diagnosticLog.session(recoveryMsg, requestMeta.requestId);
16649
+ diagnosticLog2.session(recoveryMsg, requestMeta.requestId);
15042
16650
  }
15043
16651
  }
15044
16652
  claudeLog("request.received", {
@@ -15155,6 +16763,7 @@ function createProxyServer(config = {}) {
15155
16763
  }
15156
16764
  const adapterPassthrough = adapter.usesPassthrough?.();
15157
16765
  const passthrough = adapterPassthrough !== undefined ? adapterPassthrough : envBool("PASSTHROUGH");
16766
+ const settingSources = envBool("LOAD_CONTEXT") || sdkFeatures.claudeMd === "full" ? ["user", "project"] : sdkFeatures.claudeMd === "project" ? ["project"] : adapter.getSettingSources?.() ?? [];
15158
16767
  const capturedToolUses = [];
15159
16768
  const fileChanges = [];
15160
16769
  let passthroughMcp;
@@ -15246,7 +16855,17 @@ function createProxyServer(config = {}) {
15246
16855
  effort,
15247
16856
  thinking,
15248
16857
  taskBudget,
15249
- betas
16858
+ betas,
16859
+ settingSources,
16860
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
16861
+ clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
16862
+ memory: sdkFeatures.memory,
16863
+ dreaming: sdkFeatures.dreaming,
16864
+ sharedMemory: sdkFeatures.sharedMemory,
16865
+ maxBudgetUsd: sdkFeatures.maxBudgetUsd,
16866
+ fallbackModel: sdkFeatures.fallbackModel,
16867
+ sdkDebug: sdkFeatures.sdkDebug,
16868
+ additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
15250
16869
  }))) {
15251
16870
  if (event.type === "assistant" && !event.error) {
15252
16871
  didYieldContent = true;
@@ -15290,7 +16909,17 @@ function createProxyServer(config = {}) {
15290
16909
  effort,
15291
16910
  thinking,
15292
16911
  taskBudget,
15293
- betas
16912
+ betas,
16913
+ settingSources,
16914
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
16915
+ clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
16916
+ memory: sdkFeatures.memory,
16917
+ dreaming: sdkFeatures.dreaming,
16918
+ sharedMemory: sdkFeatures.sharedMemory,
16919
+ maxBudgetUsd: sdkFeatures.maxBudgetUsd,
16920
+ fallbackModel: sdkFeatures.fallbackModel,
16921
+ sdkDebug: sdkFeatures.sdkDebug,
16922
+ additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
15294
16923
  }));
15295
16924
  return;
15296
16925
  }
@@ -15375,7 +17004,7 @@ function createProxyServer(config = {}) {
15375
17004
  claudeLog("passthrough.toolsearch_filtered", { mode: "non_stream" });
15376
17005
  continue;
15377
17006
  }
15378
- if (passthrough && !adapter.supportsThinking?.() && (b.type === "thinking" || b.type === "redacted_thinking")) {
17007
+ if (passthrough && !adapter.supportsThinking?.() && !sdkFeatures.thinkingPassthrough && (b.type === "thinking" || b.type === "redacted_thinking")) {
15379
17008
  claudeLog("passthrough.thinking_stripped", { mode: "non_stream", type: b.type });
15380
17009
  continue;
15381
17010
  }
@@ -15471,7 +17100,7 @@ Subprocess stderr: ${stderrOutput}`;
15471
17100
  });
15472
17101
  const nonStreamQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
15473
17102
  checkTokenHealth(requestMeta.requestId, currentSessionId || resumeSessionId, lastUsage, allMessages.length, isResume, passthrough);
15474
- telemetryStore.record({
17103
+ telemetryStore2.record({
15475
17104
  requestId: requestMeta.requestId,
15476
17105
  timestamp: Date.now(),
15477
17106
  adapter: adapter.name,
@@ -15514,7 +17143,12 @@ Subprocess stderr: ${stderrOutput}`;
15514
17143
  content: contentBlocks,
15515
17144
  model: body.model,
15516
17145
  stop_reason: stopReason,
15517
- usage: { input_tokens: 0, output_tokens: 0 }
17146
+ usage: {
17147
+ input_tokens: lastUsage?.input_tokens ?? 0,
17148
+ output_tokens: lastUsage?.output_tokens ?? 0,
17149
+ cache_read_input_tokens: lastUsage?.cache_read_input_tokens,
17150
+ cache_creation_input_tokens: lastUsage?.cache_creation_input_tokens
17151
+ }
15518
17152
  }), {
15519
17153
  headers: {
15520
17154
  "Content-Type": "application/json",
@@ -15590,7 +17224,17 @@ Subprocess stderr: ${stderrOutput}`;
15590
17224
  effort,
15591
17225
  thinking,
15592
17226
  taskBudget,
15593
- betas
17227
+ betas,
17228
+ settingSources,
17229
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
17230
+ clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
17231
+ memory: sdkFeatures.memory,
17232
+ dreaming: sdkFeatures.dreaming,
17233
+ sharedMemory: sdkFeatures.sharedMemory,
17234
+ maxBudgetUsd: sdkFeatures.maxBudgetUsd,
17235
+ fallbackModel: sdkFeatures.fallbackModel,
17236
+ sdkDebug: sdkFeatures.sdkDebug,
17237
+ additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
15594
17238
  }))) {
15595
17239
  if (event.type === "stream_event") {
15596
17240
  didYieldClientEvent = true;
@@ -15634,7 +17278,17 @@ Subprocess stderr: ${stderrOutput}`;
15634
17278
  effort,
15635
17279
  thinking,
15636
17280
  taskBudget,
15637
- betas
17281
+ betas,
17282
+ settingSources,
17283
+ codeSystemPrompt: sdkFeatures.codeSystemPrompt ? true : undefined,
17284
+ clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
17285
+ memory: sdkFeatures.memory,
17286
+ dreaming: sdkFeatures.dreaming,
17287
+ sharedMemory: sdkFeatures.sharedMemory,
17288
+ maxBudgetUsd: sdkFeatures.maxBudgetUsd,
17289
+ fallbackModel: sdkFeatures.fallbackModel,
17290
+ sdkDebug: sdkFeatures.sdkDebug,
17291
+ additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined
15638
17292
  }));
15639
17293
  return;
15640
17294
  }
@@ -15771,7 +17425,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
15771
17425
  }
15772
17426
  if (eventType === "content_block_start") {
15773
17427
  const block = event.content_block;
15774
- if (passthrough && !adapter.supportsThinking?.() && (block?.type === "thinking" || block?.type === "redacted_thinking")) {
17428
+ if (passthrough && !adapter.supportsThinking?.() && !sdkFeatures.thinkingPassthrough && (block?.type === "thinking" || block?.type === "redacted_thinking")) {
15775
17429
  if (eventIndex !== undefined)
15776
17430
  skipBlockIndices.add(eventIndex);
15777
17431
  claudeLog("passthrough.thinking_stripped", { mode: "stream", type: block.type, index: eventIndex });
@@ -15969,7 +17623,7 @@ data: {"type":"message_stop"}
15969
17623
  });
15970
17624
  const streamQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
15971
17625
  checkTokenHealth(requestMeta.requestId, currentSessionId || resumeSessionId, lastUsage, allMessages.length, isResume, passthrough);
15972
- telemetryStore.record({
17626
+ telemetryStore2.record({
15973
17627
  requestId: requestMeta.requestId,
15974
17628
  timestamp: Date.now(),
15975
17629
  adapter: adapter.name,
@@ -16088,7 +17742,7 @@ data: ${JSON.stringify({
16088
17742
  const classified = classifyError(errMsg);
16089
17743
  claudeLog("proxy.error", { error: errMsg, classified: classified.type });
16090
17744
  const errorQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
16091
- telemetryStore.record({
17745
+ telemetryStore2.record({
16092
17746
  requestId: requestMeta.requestId,
16093
17747
  timestamp: Date.now(),
16094
17748
  adapter: adapter.name,
@@ -16132,6 +17786,39 @@ data: ${JSON.stringify({
16132
17786
  app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
16133
17787
  app.post("/messages", (c) => handleWithQueue(c, "/messages"));
16134
17788
  app.route("/telemetry", createTelemetryRoutes());
17789
+ app.get("/settings", (c) => {
17790
+ const { settingsPageHtml: settingsPageHtml2 } = (init_settingsPage(), __toCommonJS(exports_settingsPage));
17791
+ return c.html(settingsPageHtml2);
17792
+ });
17793
+ app.get("/settings/api/features", (c) => {
17794
+ const { getAllFeatureConfigs: getAllFeatureConfigs2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
17795
+ return c.json(getAllFeatureConfigs2());
17796
+ });
17797
+ app.patch("/settings/api/features/:adapter", async (c) => {
17798
+ const { validateFeatureUpdate: validateFeatureUpdate2, updateAdapterFeatures: updateAdapterFeatures2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
17799
+ const adapter = c.req.param("adapter");
17800
+ const body = await c.req.json();
17801
+ let validated;
17802
+ try {
17803
+ validated = validateFeatureUpdate2(body);
17804
+ } catch (e) {
17805
+ return c.json({ error: e.message }, 400);
17806
+ }
17807
+ updateAdapterFeatures2(adapter, validated);
17808
+ return c.json({ ok: true });
17809
+ });
17810
+ app.delete("/settings/api/features/:adapter", (c) => {
17811
+ const { resetAdapterFeatures: resetAdapterFeatures2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
17812
+ const adapter = c.req.param("adapter");
17813
+ resetAdapterFeatures2(adapter);
17814
+ return c.json({ ok: true });
17815
+ });
17816
+ app.get("/metrics", (c) => {
17817
+ const body = renderPrometheusMetrics(telemetryStore2);
17818
+ return c.body(body, 200, {
17819
+ "Content-Type": "text/plain; version=0.0.4; charset=utf-8"
17820
+ });
17821
+ });
16135
17822
  app.get("/health", async (c) => {
16136
17823
  try {
16137
17824
  const healthProfile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile);
@@ -16140,6 +17827,7 @@ data: ${JSON.stringify({
16140
17827
  if (!auth) {
16141
17828
  return c.json({
16142
17829
  status: "degraded",
17830
+ version: serverVersion,
16143
17831
  error: "Could not verify auth status",
16144
17832
  mode: envBool("PASSTHROUGH") ? "passthrough" : "internal"
16145
17833
  });
@@ -16147,12 +17835,14 @@ data: ${JSON.stringify({
16147
17835
  if (!auth.loggedIn) {
16148
17836
  return c.json({
16149
17837
  status: "unhealthy",
17838
+ version: serverVersion,
16150
17839
  error: "Not logged in. Run: claude login",
16151
17840
  auth: { loggedIn: false }
16152
17841
  }, 503);
16153
17842
  }
16154
17843
  return c.json({
16155
17844
  status: "healthy",
17845
+ version: serverVersion,
16156
17846
  auth: {
16157
17847
  loggedIn: true,
16158
17848
  email: auth.email,
@@ -16164,6 +17854,7 @@ data: ${JSON.stringify({
16164
17854
  } catch {
16165
17855
  return c.json({
16166
17856
  status: "degraded",
17857
+ version: serverVersion,
16167
17858
  error: "Could not verify auth status",
16168
17859
  mode: envBool("PASSTHROUGH") ? "passthrough" : "internal"
16169
17860
  });
@@ -16191,7 +17882,7 @@ data: ${JSON.stringify({
16191
17882
  });
16192
17883
  });
16193
17884
  app.get("/profiles", async (c) => {
16194
- const { profilePageHtml } = await import("./profilePage-9nkbct3w.js");
17885
+ const { profilePageHtml } = await import("./profilePage-g5t5t6av.js");
16195
17886
  return c.html(profilePageHtml);
16196
17887
  });
16197
17888
  app.post("/profiles/active", async (c) => {