@rynfar/meridian 1.35.0 → 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.
@@ -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";
@@ -1148,6 +1149,392 @@ var init_sqlite = __esm(() => {
1148
1149
  import_libsql = __toESM(require_libsql(), 1);
1149
1150
  });
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
+ });
1537
+
1151
1538
  // node_modules/hono/dist/compose.js
1152
1539
  var compose = (middleware, onError, onNotFound) => {
1153
1540
  return (context, next) => {
@@ -7532,6 +7919,7 @@ import { resolve, dirname } from "node:path";
7532
7919
  import { fileURLToPath } from "node:url";
7533
7920
 
7534
7921
  // src/telemetry/dashboard.ts
7922
+ init_profileBar();
7535
7923
  var dashboardHtml = `<!DOCTYPE html>
7536
7924
  <html lang="en">
7537
7925
  <head>
@@ -7929,6 +8317,7 @@ function createTelemetryRoutes() {
7929
8317
  return routes;
7930
8318
  }
7931
8319
  // src/telemetry/landing.ts
8320
+ init_profileBar();
7932
8321
  var landingHtml = `<!DOCTYPE html>
7933
8322
  <html lang="en">
7934
8323
  <head>
@@ -8041,7 +8430,7 @@ function render(h,s){
8041
8430
  o+='</div>';
8042
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>'}
8043
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>';
8044
- 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>';
8045
8434
  o+='<div class="footer">Meridian · Built on the <a href="https://github.com/anthropics/claude-code-sdk-js">Claude Code SDK</a></div>';
8046
8435
  document.getElementById('content').innerHTML=o;
8047
8436
  }
@@ -8232,7 +8621,8 @@ function isExpiredTokenError(errMsg) {
8232
8621
  function isStaleSessionError(error) {
8233
8622
  if (!(error instanceof Error))
8234
8623
  return false;
8235
- 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");
8236
8626
  }
8237
8627
  function isRateLimitError(errMsg) {
8238
8628
  const lower = errMsg.toLowerCase();
@@ -8605,6 +8995,42 @@ function getLastUserMessage(messages) {
8605
8995
  return messages.slice(-1);
8606
8996
  }
8607
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
+
8608
9034
  // src/proxy/fileChanges.ts
8609
9035
  function extractFileChange(toolName, toolInput, mcpPrefix) {
8610
9036
  if (!toolName.startsWith(mcpPrefix))
@@ -9091,6 +9517,9 @@ var crushAdapter = {
9091
9517
  buildSystemContextAddendum(_body, _sdkAgents) {
9092
9518
  return "";
9093
9519
  },
9520
+ supportsThinking() {
9521
+ return true;
9522
+ },
9094
9523
  extractFileChangesFromToolUse(toolName, toolInput) {
9095
9524
  const input = toolInput;
9096
9525
  const filePath = input?.file_path ?? input?.path;
@@ -9375,6 +9804,10 @@ function detectAdapter(c) {
9375
9804
  return defaultAdapter;
9376
9805
  }
9377
9806
 
9807
+ // src/proxy/query.ts
9808
+ import { join as join3 } from "node:path";
9809
+ import { homedir as homedir2 } from "node:os";
9810
+
9378
9811
  // src/mcpTools.ts
9379
9812
  import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/claude-agent-sdk";
9380
9813
  import * as fs from "node:fs/promises";
@@ -15067,6 +15500,18 @@ function createOpencodeMcpServer() {
15067
15500
  }
15068
15501
 
15069
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
+ }
15070
15515
  function buildQueryOptions(ctx) {
15071
15516
  const {
15072
15517
  prompt,
@@ -15089,7 +15534,17 @@ function buildQueryOptions(ctx) {
15089
15534
  effort,
15090
15535
  thinking,
15091
15536
  taskBudget,
15092
- betas
15537
+ betas,
15538
+ settingSources,
15539
+ codeSystemPrompt,
15540
+ clientSystemPrompt,
15541
+ memory,
15542
+ dreaming,
15543
+ sharedMemory,
15544
+ maxBudgetUsd,
15545
+ fallbackModel,
15546
+ sdkDebug,
15547
+ additionalDirectories
15093
15548
  } = ctx;
15094
15549
  const blockedTools = [...adapter.getBlockedBuiltinTools(), ...adapter.getAgentIncompatibleTools()];
15095
15550
  const mcpServerName = adapter.getMcpServerName();
@@ -15105,9 +15560,7 @@ function buildQueryOptions(ctx) {
15105
15560
  ...stream2 ? { includePartialMessages: true } : {},
15106
15561
  permissionMode: "bypassPermissions",
15107
15562
  allowDangerouslySkipPermissions: true,
15108
- ...systemContext ? {
15109
- systemPrompt: passthrough ? systemContext : { type: "preset", preset: "claude_code", append: systemContext }
15110
- } : {},
15563
+ ...resolveSystemPrompt(systemContext, passthrough, settingSources, codeSystemPrompt, clientSystemPrompt),
15111
15564
  ...passthrough ? {
15112
15565
  disallowedTools: blockedTools,
15113
15566
  ...passthroughMcp ? {
@@ -15120,11 +15573,19 @@ function buildQueryOptions(ctx) {
15120
15573
  mcpServers: { [mcpServerName]: createOpencodeMcpServer() }
15121
15574
  },
15122
15575
  plugins: [],
15576
+ ...settingSources && settingSources.length > 0 ? {
15577
+ settingSources,
15578
+ settings: {
15579
+ autoMemoryEnabled: ctx.memory ?? true,
15580
+ autoDreamEnabled: ctx.dreaming ?? false
15581
+ }
15582
+ } : {},
15123
15583
  ...onStderr ? { stderr: onStderr } : {},
15124
15584
  env: {
15125
15585
  ...cleanEnv,
15126
15586
  ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
15127
15587
  ...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
15588
+ ...sharedMemory ? { CLAUDE_CONFIG_DIR: join3(homedir2(), ".claude") } : {},
15128
15589
  ...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {}
15129
15590
  },
15130
15591
  ...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
@@ -15134,7 +15595,11 @@ function buildQueryOptions(ctx) {
15134
15595
  ...effort ? { effort } : {},
15135
15596
  ...thinking ? { thinking } : {},
15136
15597
  ...taskBudget ? { taskBudget } : {},
15137
- ...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 } : {}
15138
15603
  }
15139
15604
  };
15140
15605
  }
@@ -15505,8 +15970,8 @@ import {
15505
15970
  unlinkSync,
15506
15971
  writeFileSync
15507
15972
  } from "node:fs";
15508
- import { homedir as homedir2 } from "node:os";
15509
- import { join as join3 } from "node:path";
15973
+ import { homedir as homedir3 } from "node:os";
15974
+ import { join as join4 } from "node:path";
15510
15975
  var DEFAULT_MAX_STORED_SESSIONS = 1e4;
15511
15976
  var STALE_LOCK_THRESHOLD_MS = 30000;
15512
15977
  function getMaxStoredSessions() {
@@ -15557,11 +16022,11 @@ function getStorePath() {
15557
16022
  if (!existsSync3(dir)) {
15558
16023
  mkdirSync(dir, { recursive: true });
15559
16024
  }
15560
- return join3(dir, "sessions.json");
16025
+ return join4(dir, "sessions.json");
15561
16026
  }
15562
16027
  function getDefaultCacheDir() {
15563
- const newDir = join3(homedir2(), ".cache", "meridian");
15564
- const oldDir = join3(homedir2(), ".cache", "opencode-claude-max-proxy");
16028
+ const newDir = join4(homedir3(), ".cache", "meridian");
16029
+ const oldDir = join4(homedir3(), ".cache", "opencode-claude-max-proxy");
15565
16030
  if (existsSync3(newDir))
15566
16031
  return newDir;
15567
16032
  if (existsSync3(oldDir)) {
@@ -16028,6 +16493,14 @@ function createProxyServer(config = {}) {
16028
16493
  const sessionDiscoveredTools = new Map;
16029
16494
  const app = new Hono2;
16030
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);
16031
16504
  app.get("/", (c) => {
16032
16505
  const accept = c.req.header("accept") || "";
16033
16506
  if (accept.includes("application/json") && !accept.includes("text/html")) {
@@ -16130,6 +16603,14 @@ function createProxyServer(config = {}) {
16130
16603
  console.error(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
16131
16604
  }
16132
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
+ }
16133
16614
  const thinkingBetaStripped = betaFilter.stripped.some((b) => b.startsWith("interleaved-thinking"));
16134
16615
  if (thinkingBetaStripped) {
16135
16616
  thinking = { type: "disabled" };
@@ -16282,6 +16763,7 @@ function createProxyServer(config = {}) {
16282
16763
  }
16283
16764
  const adapterPassthrough = adapter.usesPassthrough?.();
16284
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?.() ?? [];
16285
16767
  const capturedToolUses = [];
16286
16768
  const fileChanges = [];
16287
16769
  let passthroughMcp;
@@ -16373,7 +16855,17 @@ function createProxyServer(config = {}) {
16373
16855
  effort,
16374
16856
  thinking,
16375
16857
  taskBudget,
16376
- 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
16377
16869
  }))) {
16378
16870
  if (event.type === "assistant" && !event.error) {
16379
16871
  didYieldContent = true;
@@ -16417,7 +16909,17 @@ function createProxyServer(config = {}) {
16417
16909
  effort,
16418
16910
  thinking,
16419
16911
  taskBudget,
16420
- 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
16421
16923
  }));
16422
16924
  return;
16423
16925
  }
@@ -16502,7 +17004,7 @@ function createProxyServer(config = {}) {
16502
17004
  claudeLog("passthrough.toolsearch_filtered", { mode: "non_stream" });
16503
17005
  continue;
16504
17006
  }
16505
- 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")) {
16506
17008
  claudeLog("passthrough.thinking_stripped", { mode: "non_stream", type: b.type });
16507
17009
  continue;
16508
17010
  }
@@ -16641,7 +17143,12 @@ Subprocess stderr: ${stderrOutput}`;
16641
17143
  content: contentBlocks,
16642
17144
  model: body.model,
16643
17145
  stop_reason: stopReason,
16644
- 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
+ }
16645
17152
  }), {
16646
17153
  headers: {
16647
17154
  "Content-Type": "application/json",
@@ -16717,7 +17224,17 @@ Subprocess stderr: ${stderrOutput}`;
16717
17224
  effort,
16718
17225
  thinking,
16719
17226
  taskBudget,
16720
- 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
16721
17238
  }))) {
16722
17239
  if (event.type === "stream_event") {
16723
17240
  didYieldClientEvent = true;
@@ -16761,7 +17278,17 @@ Subprocess stderr: ${stderrOutput}`;
16761
17278
  effort,
16762
17279
  thinking,
16763
17280
  taskBudget,
16764
- 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
16765
17292
  }));
16766
17293
  return;
16767
17294
  }
@@ -16898,7 +17425,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
16898
17425
  }
16899
17426
  if (eventType === "content_block_start") {
16900
17427
  const block = event.content_block;
16901
- 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")) {
16902
17429
  if (eventIndex !== undefined)
16903
17430
  skipBlockIndices.add(eventIndex);
16904
17431
  claudeLog("passthrough.thinking_stripped", { mode: "stream", type: block.type, index: eventIndex });
@@ -17259,6 +17786,33 @@ data: ${JSON.stringify({
17259
17786
  app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
17260
17787
  app.post("/messages", (c) => handleWithQueue(c, "/messages"));
17261
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
+ });
17262
17816
  app.get("/metrics", (c) => {
17263
17817
  const body = renderPrometheusMetrics(telemetryStore2);
17264
17818
  return c.body(body, 200, {
@@ -17328,7 +17882,7 @@ data: ${JSON.stringify({
17328
17882
  });
17329
17883
  });
17330
17884
  app.get("/profiles", async (c) => {
17331
- const { profilePageHtml } = await import("./profilePage-e90fq8ye.js");
17885
+ const { profilePageHtml } = await import("./profilePage-g5t5t6av.js");
17332
17886
  return c.html(profilePageHtml);
17333
17887
  });
17334
17888
  app.post("/profiles/active", async (c) => {