@rynfar/meridian 1.46.0 → 1.48.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.
package/README.md CHANGED
@@ -222,11 +222,14 @@ MERIDIAN_PASSTHROUGH=0 meridian # force internal
222
222
 
223
223
  For large tool sets (>15 tools), non-core tools are automatically deferred via the SDK's ToolSearch mechanism. Core tools (read, write, edit, bash, glob, grep) are always loaded eagerly. The deferral threshold is configurable with `MERIDIAN_DEFER_TOOL_THRESHOLD`.
224
224
 
225
+ **Digest-turn elimination** — after a tool call is captured, the SDK would normally invoke the model one more time to "digest" the denial before ending the turn. That extra invocation is discarded by the proxy but fully billed — measured at ~400+ wasted output tokens and 2–3× extra latency per tool step (and on always-thinking models like Fable, a full thinking pass each time). Meridian now aborts the SDK query the moment every tool call's denial is persisted, so the digest turn never generates. Sessions remain resumable and tool-result attribution is unaffected. Kill switch: `MERIDIAN_PASSTHROUGH_EARLY_STOP=0` restores the old behavior.
226
+
225
227
  ### Known limitations
226
228
 
227
229
  - **Single tool round-trip per request** — in passthrough mode, the SDK is configured with `maxTurns=2` (or 3 for deferred tools). Multi-step agentic loops where Claude needs several consecutive tool calls require the client to re-send after each round.
228
230
  - **Blocked tools** — 13 built-in SDK tools (Read, Write, Bash, etc.) are blocked to prevent conflicts with the client's own tools. 15 additional Claude Code-only tools (CronCreate, EnterWorktree, Agent, etc.) are blocked because they require capabilities that external clients don't support.
229
231
  - **Subagent extraction** — Meridian parses the client's Task tool description to extract subagent names and build SDK AgentDefinitions. If the client's agent framework uses a non-standard format, subagent routing may not work automatically.
232
+ - **Anthropic server tools not supported** — native server-side tools (`web_search_*`, `web_fetch_*`) are a raw Anthropic API feature (billed to an API key) that emits `server_tool_use` / `web_search_tool_result` blocks the Claude Max / Agent SDK path cannot produce. A request carrying one is rejected with a `400` explaining the fix. If a plugin needs server-side web search (e.g. [`opencode-websearch`](https://github.com/emilsvennesson/opencode-websearch)), give it its **own** provider pointed at `https://api.anthropic.com` with your `ANTHROPIC_API_KEY` — don't route that call through Meridian.
230
233
 
231
234
  ## Multi-Profile Support
232
235
 
@@ -473,6 +476,20 @@ Point any OpenAI-compatible tool at `http://127.0.0.1:3456` with any API key val
473
476
 
474
477
  > **Note:** Multi-turn conversations work by packing prior turns into the system prompt. Each request is a fresh SDK session — OpenAI clients replay full history themselves and don't use Meridian's session resumption.
475
478
 
479
+ ### Cherry Studio
480
+
481
+ [Cherry Studio](https://github.com/CherryHQ/cherry-studio) is a desktop chat client. Point it at Meridian by setting the Anthropic API base URL to `http://127.0.0.1:3456` (any API key value works).
482
+
483
+ Because Cherry Studio is a chat client rather than a coding agent, select the `cherry` adapter so Claude's **built-in web search** is available (coding-agent adapters block it in favour of their own):
484
+
485
+ ```bash
486
+ MERIDIAN_DEFAULT_AGENT=cherry meridian
487
+ ```
488
+
489
+ The `cherry` adapter runs in internal mode: Claude executes `WebSearch`/`WebFetch` itself and Meridian returns the grounded answer — the internal tool calls are hidden from the client. This resolves the "no WebSearch/WebFetch tool exposed" error (#481).
490
+
491
+ > Cherry Studio doesn't send a Meridian-specific header, so set `MERIDIAN_DEFAULT_AGENT=cherry` on a Meridian dedicated to it, or send `x-meridian-agent: cherry` if your setup allows custom headers.
492
+
476
493
  ### ForgeCode
477
494
 
478
495
  Add a custom provider to `~/forge/.forge.toml`:
@@ -689,6 +706,7 @@ ANTHROPIC_API_KEY=your-secret-key ANTHROPIC_BASE_URL=http://meridian-host:3456 o
689
706
  | `MERIDIAN_TELEMETRY_SIZE` | `CLAUDE_PROXY_TELEMETRY_SIZE` | `1000` | Telemetry ring buffer size |
690
707
  | `MERIDIAN_NO_FILE_CHANGES` | `CLAUDE_PROXY_NO_FILE_CHANGES` | unset | Disable "Files changed" summary in responses |
691
708
  | `MERIDIAN_SONNET_MODEL` | `CLAUDE_PROXY_SONNET_MODEL` | `sonnet` | Sonnet context tier: `sonnet` (200k, default) or `sonnet[1m]` (1M, requires Extra Usage†) |
709
+ | `MERIDIAN_1M_CONTEXT_SUPPORT` | `CLAUDE_PROXY_1M_CONTEXT_SUPPORT` | unset | Set to `0`/`false`/`no` to disable 1M context entirely — every model resolves to its 200k base variant, so Meridian never requests the extended window (avoids Extra Usage on 1M). |
692
710
  | `MERIDIAN_DEFAULT_AGENT` | — | `opencode` | Default adapter for unrecognized agents: `opencode`, `forgecode`, `pi`, `crush`, `droid`, `passthrough`. Requires restart. |
693
711
  | `MERIDIAN_PROFILES` | — | unset | JSON array of profile configs (overrides disk discovery). See [Multi-Profile Support](#multi-profile-support). |
694
712
  | `MERIDIAN_DEFER_TOOL_THRESHOLD` | — | `15` | Number of tools before non-core tools are deferred via ToolSearch. Set to `0` to disable. |
@@ -914,6 +932,8 @@ This keeps the SDK's preset prompt and tool bridge, but removes the external cli
914
932
  **I'm hitting rate limits on 1M context. What do I do?**
915
933
  Meridian defaults Sonnet to 200k context because Sonnet 1M is always billed as Extra Usage on Max plans — even when regular usage isn't exhausted. This is [Anthropic's intended billing model](https://code.claude.com/docs/en/model-config#extended-context), not a bug. Set `MERIDIAN_SONNET_MODEL=sonnet[1m]` to opt in if you have Extra Usage enabled and understand the billing implications. Opus defaults to 1M context, which is included with Max/Team/Enterprise subscriptions at no extra cost. Note: there is a [known upstream bug](https://github.com/anthropics/claude-code/issues/39841) where Claude Code incorrectly gates Opus 1M behind Extra Usage on Max — this is Anthropic's to fix.
916
934
 
935
+ To turn off 1M context entirely for **every** model (so Meridian never requests the extended window), set `MERIDIAN_1M_CONTEXT_SUPPORT=0`. Meridian also auto-detects the "out of extra usage" error, falls back to the 200k model, and skips 1M for an hour — so it self-heals after the first occurrence even without the env var.
936
+
917
937
  **Why does the health endpoint show `"plugin": "not-configured"`?**
918
938
  You haven't run `meridian setup`. Without the plugin, OpenCode requests won't have session tracking or subagent model selection. Run `meridian setup` and restart OpenCode.
919
939
 
@@ -1326,6 +1326,9 @@ var init_sdkFeatures = __esm(() => {
1326
1326
  },
1327
1327
  openai: {
1328
1328
  codeSystemPrompt: false
1329
+ },
1330
+ cherry: {
1331
+ codeSystemPrompt: false
1329
1332
  }
1330
1333
  };
1331
1334
  VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
@@ -8181,6 +8184,119 @@ function normalizeToolInput(input, clientSchema) {
8181
8184
  return normalized;
8182
8185
  }
8183
8186
 
8187
+ // src/proxy/tools.ts
8188
+ var BLOCKED_BUILTIN_TOOLS = [
8189
+ "Read",
8190
+ "Write",
8191
+ "Edit",
8192
+ "Bash",
8193
+ "Glob",
8194
+ "Grep",
8195
+ "NotebookEdit",
8196
+ "WebFetch",
8197
+ "WebSearch",
8198
+ "TodoWrite"
8199
+ ];
8200
+ var CLAUDE_CODE_ONLY_TOOLS = [
8201
+ "CronCreate",
8202
+ "CronDelete",
8203
+ "CronList",
8204
+ "EnterPlanMode",
8205
+ "ExitPlanMode",
8206
+ "EnterWorktree",
8207
+ "ExitWorktree",
8208
+ "Monitor",
8209
+ "NotebookEdit",
8210
+ "PushNotification",
8211
+ "RemoteTrigger",
8212
+ "ScheduleWakeup",
8213
+ "TodoWrite",
8214
+ "AskUserQuestion",
8215
+ "Skill",
8216
+ "Agent",
8217
+ "TaskOutput",
8218
+ "TaskStop",
8219
+ "WebSearch"
8220
+ ];
8221
+ var SERVER_TOOL_TYPE = /^web_(search|fetch)_\d+$/;
8222
+ function detectServerTools(tools) {
8223
+ if (!Array.isArray(tools))
8224
+ return [];
8225
+ const found = [];
8226
+ for (const tool of tools) {
8227
+ const type = tool?.type;
8228
+ if (typeof type === "string" && SERVER_TOOL_TYPE.test(type) && !found.includes(type)) {
8229
+ found.push(type);
8230
+ }
8231
+ }
8232
+ return found;
8233
+ }
8234
+ function serverToolErrorMessage(types2) {
8235
+ const list = types2.join(", ");
8236
+ return `Anthropic server tools (${list}) can't run through Meridian. ` + `Server-side web search/fetch is a raw Anthropic API feature (billed to an API key) ` + `that produces server_tool_use / web_search_tool_result blocks the Claude Max / Agent SDK path cannot emit. ` + `Point the plugin making this call at the real API instead — configure it with a separate provider using ` + `baseURL "https://api.anthropic.com" and your own ANTHROPIC_API_KEY, rather than routing it through Meridian.`;
8237
+ }
8238
+ var MCP_SERVER_NAME = "opencode";
8239
+ var ALLOWED_MCP_TOOLS = [
8240
+ `mcp__${MCP_SERVER_NAME}__read`,
8241
+ `mcp__${MCP_SERVER_NAME}__write`,
8242
+ `mcp__${MCP_SERVER_NAME}__edit`,
8243
+ `mcp__${MCP_SERVER_NAME}__bash`,
8244
+ `mcp__${MCP_SERVER_NAME}__glob`,
8245
+ `mcp__${MCP_SERVER_NAME}__grep`
8246
+ ];
8247
+
8248
+ // src/proxy/passthroughEarlyStop.ts
8249
+ var CLIENT_TOOL_PREFIX = "mcp__oc__";
8250
+ var INTERNAL_TOOLS = new Set(["ToolSearch"]);
8251
+ function createEarlyStopTracker() {
8252
+ return { expected: new Set, resolved: new Set, fired: false };
8253
+ }
8254
+ function isClientForwardedToolUse(block) {
8255
+ const b = block;
8256
+ if (!b || b.type !== "tool_use")
8257
+ return false;
8258
+ if (typeof b.id !== "string" || b.id.length === 0)
8259
+ return false;
8260
+ if (typeof b.name !== "string")
8261
+ return false;
8262
+ if (INTERNAL_TOOLS.has(b.name))
8263
+ return false;
8264
+ if (b.name.startsWith("mcp__") && !b.name.startsWith(CLIENT_TOOL_PREFIX))
8265
+ return false;
8266
+ return true;
8267
+ }
8268
+ function noteAssistantContent(tracker, content) {
8269
+ if (!Array.isArray(content))
8270
+ return;
8271
+ for (const block of content) {
8272
+ if (isClientForwardedToolUse(block)) {
8273
+ tracker.expected.add(block.id);
8274
+ }
8275
+ }
8276
+ }
8277
+ function noteUserContent(tracker, content) {
8278
+ if (!Array.isArray(content))
8279
+ return;
8280
+ for (const block of content) {
8281
+ const b = block;
8282
+ if (b?.type === "tool_result" && typeof b.tool_use_id === "string" && tracker.expected.has(b.tool_use_id)) {
8283
+ tracker.resolved.add(b.tool_use_id);
8284
+ }
8285
+ }
8286
+ }
8287
+ function shouldEarlyStop(tracker) {
8288
+ if (tracker.fired)
8289
+ return false;
8290
+ if (tracker.expected.size === 0)
8291
+ return false;
8292
+ for (const id of tracker.expected) {
8293
+ if (!tracker.resolved.has(id))
8294
+ return false;
8295
+ }
8296
+ tracker.fired = true;
8297
+ return true;
8298
+ }
8299
+
8184
8300
  // src/proxy/agentDefs.ts
8185
8301
  var FALLBACK_AGENT_NAME = "general";
8186
8302
  var DEFAULT_AGENT_TYPES = {
@@ -8641,6 +8757,27 @@ var dashboardHtml = `<!DOCTYPE html>
8641
8757
  transition: all 0.15s; }
8642
8758
  .log-filter:hover { border-color: var(--accent); color: var(--text); }
8643
8759
  .log-filter.active { background: rgba(88,166,255,0.1); border-color: var(--accent); color: var(--accent); }
8760
+
8761
+ /* Usage tab */
8762
+ .usage-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-bottom: 16px; }
8763
+ .ucard { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 16px 18px; }
8764
+ .ucard-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
8765
+ .ucard-title { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
8766
+ .ucard-reset { font-size: 11px; color: var(--muted); white-space: nowrap; }
8767
+ .ucard-pct { font-size: 32px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; margin-top: 8px; color: var(--green); }
8768
+ .ucard.warn .ucard-pct { color: var(--yellow); }
8769
+ .ucard.high .ucard-pct { color: var(--red); }
8770
+ .ucard-sub { font-size: 12px; color: var(--muted); margin-top: 8px; min-height: 16px; }
8771
+ .ubar { position: relative; height: 8px; border-radius: 4px; background: var(--border); overflow: visible; margin-top: 12px; }
8772
+ .ubar-fill { height: 100%; border-radius: 4px; background: var(--green); transition: width 0.4s ease; max-width: 100%; }
8773
+ .ucard.warn .ubar-fill { background: var(--yellow); }
8774
+ .ucard.high .ubar-fill { background: var(--red); }
8775
+ .ubar-marker { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--text); opacity: 0.55; border-radius: 1px; }
8776
+ .pace-pill { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 10px; }
8777
+ .pace-pill.on, .pace-pill.under { background: rgba(63,185,80,0.15); color: var(--green); }
8778
+ .pace-pill.ahead { background: rgba(210,153,34,0.18); color: var(--yellow); }
8779
+ .pace-pill.over { background: rgba(248,81,73,0.15); color: var(--red); }
8780
+ .usage-note { font-size: 11px; color: var(--muted); }
8644
8781
  ` + profileBarCss + `
8645
8782
  </style>
8646
8783
  </head>
@@ -8713,20 +8850,22 @@ function setLogFilter(filter) {
8713
8850
  async function refresh() {
8714
8851
  const w = $('#window').value;
8715
8852
  try {
8716
- const [summary, reqs, logs] = await Promise.all([
8853
+ const [summary, reqs, logs, quota] = await Promise.all([
8717
8854
  fetch('/telemetry/summary?window=' + w).then(r => r.json()),
8718
8855
  fetch('/telemetry/requests?limit=50&since=' + (Date.now() - Number(w))).then(r => r.json()),
8719
8856
  fetch('/telemetry/logs?limit=200&since=' + (Date.now() - Number(w))).then(r => r.json()),
8857
+ fetch('/v1/usage/quota').then(r => r.json()).catch(() => null),
8720
8858
  ]);
8721
- render(summary, reqs, logs);
8859
+ render(summary, reqs, logs, quota);
8722
8860
  $('#lastUpdate').textContent = 'Updated ' + new Date().toLocaleTimeString();
8723
8861
  } catch (e) {
8724
8862
  $('#content').innerHTML = '<div class="empty">Failed to load telemetry</div>';
8725
8863
  }
8726
8864
  }
8727
8865
 
8728
- function render(s, reqs, logs) {
8729
- if (s.totalRequests === 0 && (!logs || logs.length === 0)) {
8866
+ function render(s, reqs, logs, quota) {
8867
+ const hasUsage = quota && quota.buckets && quota.buckets.some(b => b.utilization != null);
8868
+ if (s.totalRequests === 0 && (!logs || logs.length === 0) && !hasUsage) {
8730
8869
  $('#content').innerHTML = '<div class="empty">No requests recorded yet. Send a request through the proxy to see telemetry.</div>';
8731
8870
  return;
8732
8871
  }
@@ -8744,6 +8883,7 @@ function render(s, reqs, logs) {
8744
8883
  + 'Requests<span class="tab-badge">' + reqs.length + '</span></div>'
8745
8884
  + '<div class="tab' + (activeTab === 'logs' ? ' active' : '') + '" data-tab="logs" onclick="switchTab(&apos;logs&apos;)">'
8746
8885
  + 'Logs<span class="tab-badge">' + logs.length + '</span></div>'
8886
+ + '<div class="tab' + (activeTab === 'usage' ? ' active' : '') + '" data-tab="usage" onclick="switchTab(&apos;usage&apos;)">Usage</div>'
8747
8887
  + '</div>';
8748
8888
 
8749
8889
  // ==================== Overview tab ====================
@@ -8893,6 +9033,11 @@ function render(s, reqs, logs) {
8893
9033
  }
8894
9034
  html += '</div>'; // end logs panel
8895
9035
 
9036
+ // ==================== Usage tab ====================
9037
+ html += '<div id="panel-usage" class="tab-panel' + (activeTab === 'usage' ? ' active' : '') + '">';
9038
+ html += renderUsage(quota);
9039
+ html += '</div>'; // end usage panel
9040
+
8896
9041
  $('#content').innerHTML = html;
8897
9042
  }
8898
9043
 
@@ -8903,6 +9048,101 @@ function card(label, value, detail) {
8903
9048
  + '</div>';
8904
9049
  }
8905
9050
 
9051
+ // ---- Usage tab helpers (mirror src/telemetry/profileUsage.ts; unit-tested there) ----
9052
+ function classifyUtil(u) {
9053
+ if (u == null || !isFinite(u)) return '';
9054
+ if (u >= 0.85) return 'high';
9055
+ if (u >= 0.6) return 'warn';
9056
+ return '';
9057
+ }
9058
+ function resetIn(resetsAt) {
9059
+ if (resetsAt == null || !isFinite(resetsAt)) return '';
9060
+ var ms = resetsAt - Date.now();
9061
+ if (ms <= 0) return 'resetting…';
9062
+ var m = Math.floor(ms / 60000);
9063
+ if (m < 60) return 'resets in ' + Math.max(1, m) + 'm';
9064
+ var h = Math.floor(m / 60), rm = m % 60;
9065
+ if (h < 24) return 'resets in ' + h + 'h' + (rm ? ' ' + rm + 'm' : '');
9066
+ var d = Math.floor(h / 24), rh = h % 24;
9067
+ return 'resets in ' + d + 'd' + (rh ? ' ' + rh + 'h' : '');
9068
+ }
9069
+ function pct(u) { return Math.round(Math.max(0, u) * 100); }
9070
+
9071
+ function usageCard(title, bucket) {
9072
+ if (!bucket || bucket.utilization == null) {
9073
+ return '<div class="ucard"><div class="ucard-head"><span class="ucard-title">' + title + '</span></div>'
9074
+ + '<div class="ucard-pct" style="color:var(--muted)">—</div>'
9075
+ + '<div class="ucard-sub">No data yet</div></div>';
9076
+ }
9077
+ var u = bucket.utilization;
9078
+ var cls = classifyUtil(u);
9079
+ var fill = Math.min(100, pct(u));
9080
+ return '<div class="ucard ' + cls + '">'
9081
+ + '<div class="ucard-head"><span class="ucard-title">' + title + '</span>'
9082
+ + '<span class="ucard-reset">' + resetIn(bucket.resetsAt) + '</span></div>'
9083
+ + '<div class="ucard-pct">' + pct(u) + '<span style="font-size:16px;font-weight:500;color:var(--muted)">%</span></div>'
9084
+ + '<div class="ubar"><div class="ubar-fill" style="width:' + fill + '%"></div></div>'
9085
+ + '<div class="ucard-sub">of your ' + title.split('·')[1].trim() + ' allowance used</div>'
9086
+ + '</div>';
9087
+ }
9088
+
9089
+ // Weekly pace: actual vs. expected (even) consumption at this point in the 7-day window.
9090
+ function paceCard(weekly) {
9091
+ if (!weekly || weekly.utilization == null || weekly.resetsAt == null) {
9092
+ return '<div class="ucard"><div class="ucard-head"><span class="ucard-title">Weekly Pace</span></div>'
9093
+ + '<div class="ucard-pct" style="color:var(--muted)">—</div>'
9094
+ + '<div class="ucard-sub">Needs weekly usage data</div></div>';
9095
+ }
9096
+ var WEEK = 7 * 86400000;
9097
+ var start = weekly.resetsAt - WEEK;
9098
+ var elapsed = Math.max(0, Math.min(1, (Date.now() - start) / WEEK));
9099
+ var actual = pct(weekly.utilization);
9100
+ var expected = Math.round(elapsed * 100);
9101
+ var delta = actual - expected;
9102
+ var projected = elapsed >= 0.1 ? Math.round((Math.max(0, weekly.utilization) / elapsed) * 100) : null;
9103
+
9104
+ var pill, label;
9105
+ if (delta > 7) { pill = 'ahead'; label = '+' + delta + '% ahead of pace'; }
9106
+ else if (delta < -7) { pill = 'under'; label = Math.abs(delta) + '% under pace'; }
9107
+ else { pill = 'on'; label = 'On pace'; }
9108
+ if (projected != null && projected >= 100) { pill = 'over'; label = 'On track to run out'; }
9109
+
9110
+ var fill = Math.min(100, actual);
9111
+ var mark = Math.min(100, expected);
9112
+ var proj = projected == null ? '—' : projected + '%';
9113
+ return '<div class="ucard">'
9114
+ + '<div class="ucard-head"><span class="ucard-title">Weekly Pace</span>'
9115
+ + '<span class="ucard-reset">' + Math.round(elapsed * 100) + '% through week</span></div>'
9116
+ + '<div style="margin-top:8px"><span class="pace-pill ' + pill + '">' + label + '</span></div>'
9117
+ + '<div class="ubar"><div class="ubar-fill" style="width:' + fill + '%;background:' + (pill === 'over' ? 'var(--red)' : pill === 'ahead' ? 'var(--yellow)' : 'var(--green)') + '"></div>'
9118
+ + '<div class="ubar-marker" style="left:' + mark + '%" title="Expected at even pace"></div></div>'
9119
+ + '<div class="ucard-sub">' + actual + '% used vs ' + expected + '% expected · at this rate ~' + proj + ' by reset</div>'
9120
+ + '</div>';
9121
+ }
9122
+
9123
+ function renderUsage(quota) {
9124
+ if (!quota || !quota.buckets) {
9125
+ return '<div class="empty">Usage data unavailable.</div>';
9126
+ }
9127
+ var by = {};
9128
+ quota.buckets.forEach(function (b) { by[b.type] = b; });
9129
+ var session = by['five_hour'], weekly = by['seven_day'];
9130
+ if ((!session || session.utilization == null) && (!weekly || weekly.utilization == null)) {
9131
+ return '<div class="empty">No usage data yet — Anthropic reports it after your first request through Meridian.</div>';
9132
+ }
9133
+ var h = '<div class="usage-cards">'
9134
+ + usageCard('Session · 5h', session)
9135
+ + usageCard('Weekly · 7d', weekly)
9136
+ + paceCard(weekly)
9137
+ + '</div>';
9138
+ var asOf = quota.asOf ? new Date(quota.asOf).toLocaleTimeString() : '';
9139
+ h += '<div class="usage-note">'
9140
+ + (quota.profile ? 'Profile: ' + quota.profile + ' · ' : '')
9141
+ + 'Reported by Anthropic' + (asOf ? ' · as of ' + asOf : '')
9142
+ + '</div>';
9143
+ return h;
9144
+ }
9145
+
8906
9146
  $('#autoRefresh').addEventListener('change', function() {
8907
9147
  clearInterval(timer);
8908
9148
  if (this.checked) timer = setInterval(refresh, 5000);
@@ -10145,50 +10385,6 @@ ${text.slice(0, 2000)}` : text.slice(0, 2000);
10145
10385
  return createHash("sha256").update(seed).digest("hex").slice(0, 16);
10146
10386
  }
10147
10387
 
10148
- // src/proxy/tools.ts
10149
- var BLOCKED_BUILTIN_TOOLS = [
10150
- "Read",
10151
- "Write",
10152
- "Edit",
10153
- "Bash",
10154
- "Glob",
10155
- "Grep",
10156
- "NotebookEdit",
10157
- "WebFetch",
10158
- "WebSearch",
10159
- "TodoWrite"
10160
- ];
10161
- var CLAUDE_CODE_ONLY_TOOLS = [
10162
- "CronCreate",
10163
- "CronDelete",
10164
- "CronList",
10165
- "EnterPlanMode",
10166
- "ExitPlanMode",
10167
- "EnterWorktree",
10168
- "ExitWorktree",
10169
- "Monitor",
10170
- "NotebookEdit",
10171
- "PushNotification",
10172
- "RemoteTrigger",
10173
- "ScheduleWakeup",
10174
- "TodoWrite",
10175
- "AskUserQuestion",
10176
- "Skill",
10177
- "Agent",
10178
- "TaskOutput",
10179
- "TaskStop",
10180
- "WebSearch"
10181
- ];
10182
- var MCP_SERVER_NAME = "opencode";
10183
- var ALLOWED_MCP_TOOLS = [
10184
- `mcp__${MCP_SERVER_NAME}__read`,
10185
- `mcp__${MCP_SERVER_NAME}__write`,
10186
- `mcp__${MCP_SERVER_NAME}__edit`,
10187
- `mcp__${MCP_SERVER_NAME}__bash`,
10188
- `mcp__${MCP_SERVER_NAME}__glob`,
10189
- `mcp__${MCP_SERVER_NAME}__grep`
10190
- ];
10191
-
10192
10388
  // src/proxy/transforms/opencode.ts
10193
10389
  var openCodeTransforms = [
10194
10390
  {
@@ -10988,6 +11184,34 @@ var openAiAdapter = {
10988
11184
  name: "openai"
10989
11185
  };
10990
11186
 
11187
+ // src/proxy/adapters/cherry.ts
11188
+ var CHERRY_WEB_TOOLS = ["WebSearch", "WebFetch"];
11189
+ var isWebTool = (t) => CHERRY_WEB_TOOLS.includes(t);
11190
+ var CHERRY_BLOCKED_BUILTIN_TOOLS = BLOCKED_BUILTIN_TOOLS.filter((t) => !isWebTool(t));
11191
+ var CHERRY_INCOMPATIBLE_TOOLS = CLAUDE_CODE_ONLY_TOOLS.filter((t) => !isWebTool(t));
11192
+ var cherryAdapter = {
11193
+ ...openCodeAdapter,
11194
+ name: "cherry",
11195
+ usesPassthrough() {
11196
+ return false;
11197
+ },
11198
+ supportsThinking() {
11199
+ return false;
11200
+ },
11201
+ getBlockedBuiltinTools() {
11202
+ return CHERRY_BLOCKED_BUILTIN_TOOLS;
11203
+ },
11204
+ getAgentIncompatibleTools() {
11205
+ return CHERRY_INCOMPATIBLE_TOOLS;
11206
+ },
11207
+ getAllowedMcpTools() {
11208
+ return [...CHERRY_WEB_TOOLS];
11209
+ },
11210
+ getCoreToolNames() {
11211
+ return [];
11212
+ }
11213
+ };
11214
+
10991
11215
  // src/proxy/adapters/detect.ts
10992
11216
  var ADAPTER_MAP = {
10993
11217
  opencode: openCodeAdapter,
@@ -10998,6 +11222,8 @@ var ADAPTER_MAP = {
10998
11222
  forgecode: forgeCodeAdapter,
10999
11223
  "claude-code": claudeCodeAdapter,
11000
11224
  claudecode: claudeCodeAdapter,
11225
+ cherry: cherryAdapter,
11226
+ cherrystudio: cherryAdapter,
11001
11227
  openai: openAiAdapter
11002
11228
  };
11003
11229
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
@@ -16919,6 +17145,27 @@ function structuredOutputText(value) {
16919
17145
  return JSON.stringify(value);
16920
17146
  }
16921
17147
 
17148
+ // src/proxy/transforms/cherry.ts
17149
+ var cherryTransforms = [
17150
+ {
17151
+ name: "cherry-core",
17152
+ adapters: ["cherry"],
17153
+ onRequest(ctx) {
17154
+ return {
17155
+ ...ctx,
17156
+ blockedTools: CHERRY_BLOCKED_BUILTIN_TOOLS,
17157
+ incompatibleTools: CHERRY_INCOMPATIBLE_TOOLS,
17158
+ allowedMcpTools: [...CHERRY_WEB_TOOLS],
17159
+ coreToolNames: [],
17160
+ passthrough: false,
17161
+ hidesInternalTools: true,
17162
+ supportsThinking: false,
17163
+ shouldTrackFileChanges: false
17164
+ };
17165
+ }
17166
+ }
17167
+ ];
17168
+
16922
17169
  // src/proxy/transforms/registry.ts
16923
17170
  var ADAPTER_TRANSFORMS = {
16924
17171
  opencode: openCodeTransforms,
@@ -16927,6 +17174,7 @@ var ADAPTER_TRANSFORMS = {
16927
17174
  pi: piTransforms,
16928
17175
  forgecode: forgeCodeTransforms,
16929
17176
  passthrough: passthroughTransforms,
17177
+ cherry: cherryTransforms,
16930
17178
  openai: openCodeTransforms
16931
17179
  };
16932
17180
  function getAdapterTransforms(adapterName) {
@@ -18080,6 +18328,10 @@ function createProxyServer(config = {}) {
18080
18328
  if (body.messages.length === 0) {
18081
18329
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Cannot be empty — at least one message is required" } }, 400);
18082
18330
  }
18331
+ const serverTools = detectServerTools(body.tools);
18332
+ if (serverTools.length > 0) {
18333
+ return c.json({ type: "error", error: { type: "invalid_request_error", message: serverToolErrorMessage(serverTools) } }, 400);
18334
+ }
18083
18335
  const parsedOutputFormat = parseOutputFormat(body.output_config, body.tools);
18084
18336
  if (!parsedOutputFormat.ok) {
18085
18337
  return c.json({ type: "error", error: { type: "invalid_request_error", message: parsedOutputFormat.message } }, 400);
@@ -18302,6 +18554,9 @@ function createProxyServer(config = {}) {
18302
18554
  const capturedSignatures = new Set;
18303
18555
  const capturedToolNames = new Set;
18304
18556
  let sawDuplicateToolUse = false;
18557
+ const earlyStopEnabled = passthrough && process.env.MERIDIAN_PASSTHROUGH_EARLY_STOP !== "0";
18558
+ const earlyStop = createEarlyStopTracker();
18559
+ let earlyStopFired = false;
18305
18560
  const toolChoice = body.tool_choice;
18306
18561
  const forceSingleToolUse = !!toolChoice && (toolChoice.type === "tool" || toolChoice.disable_parallel_tool_use === true);
18307
18562
  const fileChanges = [];
@@ -18664,6 +18919,19 @@ function createProxyServer(config = {}) {
18664
18919
  claudeLog("passthrough.loop_break", { mode: "non_stream", assistantMessages, captured: capturedToolUses.length });
18665
18920
  break;
18666
18921
  }
18922
+ if (earlyStopEnabled) {
18923
+ if (message.type === "assistant") {
18924
+ noteAssistantContent(earlyStop, message.message?.content);
18925
+ } else if (message.type === "user") {
18926
+ noteUserContent(earlyStop, message.message?.content);
18927
+ if (shouldEarlyStop(earlyStop)) {
18928
+ earlyStopFired = true;
18929
+ claudeLog("passthrough.early_stop", { mode: "non_stream", captured: capturedToolUses.length });
18930
+ requestAbort.abort("passthrough turn complete");
18931
+ break;
18932
+ }
18933
+ }
18934
+ }
18667
18935
  if (message.type === "assistant") {
18668
18936
  assistantMessages += 1;
18669
18937
  if (message.uuid) {
@@ -18687,6 +18955,16 @@ function createProxyServer(config = {}) {
18687
18955
  claudeLog("passthrough.toolsearch_filtered", { mode: "non_stream" });
18688
18956
  continue;
18689
18957
  }
18958
+ if (pipelineCtx.hidesInternalTools) {
18959
+ if (b.type === "tool_use") {
18960
+ claudeLog("internal_tool.hidden", { mode: "non_stream", name: b.name });
18961
+ continue;
18962
+ }
18963
+ if ((b.type === "thinking" || b.type === "redacted_thinking") && !sdkFeatures.thinkingPassthrough) {
18964
+ claudeLog("internal_tool.thinking_stripped", { mode: "non_stream", type: b.type });
18965
+ continue;
18966
+ }
18967
+ }
18690
18968
  if (passthrough && !pipelineCtx.supportsThinking && !sdkFeatures.thinkingPassthrough && (b.type === "thinking" || b.type === "redacted_thinking")) {
18691
18969
  claudeLog("passthrough.thinking_stripped", { mode: "non_stream", type: b.type });
18692
18970
  continue;
@@ -18898,6 +19176,7 @@ Subprocess stderr: ${stderrOutput}`;
18898
19176
  let textEventsForwarded = 0;
18899
19177
  let bytesSent = 0;
18900
19178
  let streamClosed = false;
19179
+ let awaitingEarlyStopDrain = false;
18901
19180
  claudeLog("upstream.start", { mode: "stream", model });
18902
19181
  const safeEnqueue = (payload, source) => {
18903
19182
  if (streamClosed)
@@ -19191,7 +19470,7 @@ Subprocess stderr: ${stderrOutput}`;
19191
19470
  }));
19192
19471
  try {
19193
19472
  for await (const message of guardedResponse) {
19194
- if (streamClosed) {
19473
+ if (streamClosed && !awaitingEarlyStopDrain) {
19195
19474
  break;
19196
19475
  }
19197
19476
  if (message.session_id) {
@@ -19200,6 +19479,34 @@ Subprocess stderr: ${stderrOutput}`;
19200
19479
  if (message.type === "assistant" && message.uuid) {
19201
19480
  sdkUuidMap.push(message.uuid);
19202
19481
  }
19482
+ if (earlyStopEnabled) {
19483
+ if (message.type === "assistant") {
19484
+ noteAssistantContent(earlyStop, message.message?.content);
19485
+ } else if (message.type === "user") {
19486
+ noteUserContent(earlyStop, message.message?.content);
19487
+ if (shouldEarlyStop(earlyStop) && streamedToolUseIds.size > 0) {
19488
+ earlyStopFired = true;
19489
+ claudeLog("passthrough.early_stop", { mode: "stream", captured: capturedToolUses.length, drained: awaitingEarlyStopDrain });
19490
+ safeEnqueue(encoder.encode(`event: message_delta
19491
+ data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
19492
+
19493
+ `), "early_stop");
19494
+ safeEnqueue(encoder.encode(`event: message_stop
19495
+ data: ${JSON.stringify({ type: "message_stop" })}
19496
+
19497
+ `), "early_stop");
19498
+ requestAbort.abort("passthrough turn complete");
19499
+ awaitingEarlyStopDrain = false;
19500
+ if (!streamClosed) {
19501
+ streamClosed = true;
19502
+ try {
19503
+ controller.close();
19504
+ } catch {}
19505
+ }
19506
+ break;
19507
+ }
19508
+ }
19509
+ }
19203
19510
  if (message.type === "result") {
19204
19511
  const resultUsage = message.usage;
19205
19512
  if (resultUsage)
@@ -19264,6 +19571,12 @@ data: ${JSON.stringify({ type: "message_stop" })}
19264
19571
  }
19265
19572
  if (eventType === "content_block_start") {
19266
19573
  const block = event.content_block;
19574
+ if (pipelineCtx.hidesInternalTools && (block?.type === "tool_use" || (block?.type === "thinking" || block?.type === "redacted_thinking") && !sdkFeatures.thinkingPassthrough)) {
19575
+ if (eventIndex !== undefined)
19576
+ skipBlockIndices.add(eventIndex);
19577
+ claudeLog("internal_tool.hidden", { mode: "stream", type: block?.type, name: block?.name, index: eventIndex });
19578
+ continue;
19579
+ }
19267
19580
  if (passthrough && !pipelineCtx.supportsThinking && !sdkFeatures.thinkingPassthrough && (block?.type === "thinking" || block?.type === "redacted_thinking")) {
19268
19581
  if (eventIndex !== undefined)
19269
19582
  skipBlockIndices.add(eventIndex);
@@ -19368,6 +19681,10 @@ data: ${JSON.stringify({ type: "message_stop" })}
19368
19681
  `), "passthrough_tool_stream_stop");
19369
19682
  streamClosed = true;
19370
19683
  controller.close();
19684
+ if (earlyStopEnabled) {
19685
+ awaitingEarlyStopDrain = true;
19686
+ continue;
19687
+ }
19371
19688
  break;
19372
19689
  }
19373
19690
  if (eventType === "content_block_delta") {
@@ -19639,7 +19956,7 @@ Subprocess stderr: ${stderrOutput}`;
19639
19956
  } : classifyError(errMsg);
19640
19957
  claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
19641
19958
  const sdkTerm = extractSdkTermination(errMsg);
19642
- const canRecoverAsToolUse = (sdkTerm.reason === "max_turns" || sdkTerm.reason === "aborted" && sawDuplicateToolUse) && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
19959
+ const canRecoverAsToolUse = (sdkTerm.reason === "max_turns" || sdkTerm.reason === "aborted" && (sawDuplicateToolUse || earlyStopFired)) && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
19643
19960
  if (canRecoverAsToolUse) {
19644
19961
  diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
19645
19962
  model,
@@ -19970,7 +20287,7 @@ data: ${JSON.stringify({
19970
20287
  });
19971
20288
  });
19972
20289
  app.get("/profiles", async (c) => {
19973
- const { profilePageHtml } = await import("./profilePage-k0faye28.js");
20290
+ const { profilePageHtml } = await import("./profilePage-txxpbc44.js");
19974
20291
  return c.html(profilePageHtml);
19975
20292
  });
19976
20293
  app.post("/profiles/active", async (c) => {
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-q238a2ad.js";
4
+ } from "./cli-da9hc1s1.js";
5
5
  import"./cli-cx463q74.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-4rqtm83g.js";
@@ -20,6 +20,7 @@ var WINDOW_LABELS = {
20
20
  seven_day_cowork: "7d Cowork",
21
21
  seven_day_omelette: "7d Omelette"
22
22
  };
23
+ var SEVEN_DAYS_MS = 7 * 86400000;
23
24
 
24
25
  // src/telemetry/profilePage.ts
25
26
  var profilePageHtml = `<!DOCTYPE html>
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cherry Studio adapter.
3
+ *
4
+ * Cherry Studio (https://github.com/CherryHQ/cherry-studio) is a desktop chat
5
+ * client — not a coding agent. Pointed at Meridian it works for plain chat,
6
+ * but its web search failed (#481): the model reported it had "no WebSearch or
7
+ * WebFetch tool exposed". That's because Meridian blocks the SDK's built-in
8
+ * WebSearch/WebFetch globally — those are blocked for coding agents like
9
+ * OpenCode, which ship their own web-search equivalents (`websearch_web_search_exa`).
10
+ *
11
+ * Unlike a coding agent, Cherry Studio does NOT execute tools itself; it relies
12
+ * on Claude's own built-in web search. So this adapter:
13
+ * - unblocks the SDK's built-in WebSearch/WebFetch (verified to work on the
14
+ * Max/OAuth path — the SDK runs the search server-side and returns grounded
15
+ * results), and
16
+ * - runs in internal (non-passthrough) mode so the SDK actually executes the
17
+ * search instead of trying to hand it back to a client that can't run it.
18
+ *
19
+ * It exposes only the web tools — no filesystem MCP tools — since a chat client
20
+ * has no business reading or writing files on the proxy host.
21
+ *
22
+ * Detection: selected via `x-meridian-agent: cherry` (or `cherrystudio`) or the
23
+ * `MERIDIAN_DEFAULT_AGENT=cherry` env var. Cherry Studio doesn't send a
24
+ * Meridian-specific header, so a user running a dedicated Meridian for it should
25
+ * set the env default. (If a distinctive User-Agent is confirmed, add UA
26
+ * detection in detect.ts.)
27
+ *
28
+ * NOTE: agent-specific. See ARCHITECTURE.md "Agent-Specific Logic".
29
+ */
30
+ import type { AgentAdapter } from "../adapter";
31
+ /** The SDK built-in web tools Cherry Studio wants Claude to run itself. */
32
+ export declare const CHERRY_WEB_TOOLS: readonly ["WebSearch", "WebFetch"];
33
+ /** Built-ins blocked for Cherry — everything OpenCode blocks EXCEPT the web tools. */
34
+ export declare const CHERRY_BLOCKED_BUILTIN_TOOLS: string[];
35
+ /** Incompatible tools for Cherry — CLAUDE_CODE_ONLY_TOOLS minus the web tools. */
36
+ export declare const CHERRY_INCOMPATIBLE_TOOLS: string[];
37
+ export declare const cherryAdapter: AgentAdapter;
38
+ //# sourceMappingURL=cherry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cherry.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/cherry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAI9C,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,oCAAqC,CAAA;AAIlE,sFAAsF;AACtF,eAAO,MAAM,4BAA4B,UAAmD,CAAA;AAE5F,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,UAAoD,CAAA;AAE1F,eAAO,MAAM,aAAa,EAAE,YAiC3B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA8C9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CAgDtD"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAkD9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CAgDtD"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Passthrough early-stop tracking.
3
+ *
4
+ * In passthrough mode the PreToolUse hook denies every client tool call
5
+ * ("forwarded to client — end your turn"), but the SDK then invokes the model
6
+ * one more time to digest the deny. That digest turn is discarded by the proxy
7
+ * yet fully billed — and on always-thinking models (Fable) it costs a whole
8
+ * thinking pass per tool step, roughly doubling per-step output spend and
9
+ * adding a full model round-trip of latency.
10
+ *
11
+ * The fix: the SDK emits each denied call's tool_result as a `user` message in
12
+ * the stream (verified against the real SDK) BEFORE it fires the digest turn's
13
+ * API request. So the proxy can watch the stream, and the moment every
14
+ * client-forwarded tool_use from the assistant turn has its deny persisted,
15
+ * abort the query — the digest turn never generates. Because the denies are
16
+ * already recorded in the SDK session history at that point, the session stays
17
+ * coherent and can be stored + resumed (unlike the #580 duplicate-abort case,
18
+ * which fires mid-hook before persistence).
19
+ *
20
+ * Pure module — no I/O, no imports from server.ts or session/.
21
+ */
22
+ export interface EarlyStopTracker {
23
+ /** tool_use ids of client-forwarded calls awaiting a persisted deny result */
24
+ expected: Set<string>;
25
+ /** subset of `expected` whose tool_result has been observed in the stream */
26
+ resolved: Set<string>;
27
+ /** true once shouldEarlyStop has returned true — it fires at most once */
28
+ fired: boolean;
29
+ }
30
+ export declare function createEarlyStopTracker(): EarlyStopTracker;
31
+ /**
32
+ * Is this content block a tool call that the proxy forwards to the client
33
+ * (as opposed to an internal tool the SDK executes itself)?
34
+ *
35
+ * Client tools appear either with the passthrough MCP prefix (mcp__oc__read)
36
+ * or as bare names (read) — the SDK strips the prefix in some event paths.
37
+ * Internal MCP tools (mcp__opencode__*) and ToolSearch are excluded.
38
+ */
39
+ export declare function isClientForwardedToolUse(block: unknown): boolean;
40
+ /**
41
+ * Record the client-forwarded tool_use ids from an assistant message's content.
42
+ */
43
+ export declare function noteAssistantContent(tracker: EarlyStopTracker, content: unknown): void;
44
+ /**
45
+ * Record persisted tool_results from a user message's content. Only results
46
+ * matching an expected id count — unrelated results are ignored.
47
+ */
48
+ export declare function noteUserContent(tracker: EarlyStopTracker, content: unknown): void;
49
+ /**
50
+ * True exactly once: when at least one client tool call was forwarded and
51
+ * every forwarded call's deny has been observed in the stream. At that point
52
+ * the digest turn hasn't generated yet — the caller should abort the query.
53
+ */
54
+ export declare function shouldEarlyStop(tracker: EarlyStopTracker): boolean;
55
+ //# sourceMappingURL=passthroughEarlyStop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passthroughEarlyStop.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughEarlyStop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQhE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAOtF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAQjF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAQlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"sdkFeatures.d.ts","sourceRoot":"","sources":["../../src/proxy/sdkFeatures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,gBAAgB,EAAE,OAAO,CAAA;IACzB,kFAAkF;IAClF,kBAAkB,EAAE,OAAO,CAAA;IAC3B,4DAA4D;IAC5D,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAAA;IAC7C,4CAA4C;IAC5C,mBAAmB,EAAE,OAAO,CAAA;IAC5B,iFAAiF;IACjF,YAAY,EAAE,OAAO,CAAA;IACrB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAA;IACrB,+CAA+C;IAC/C,QAAQ,EAAE,OAAO,CAAA;IACjB,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;AAqFpE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAU1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,SAAS,CAKhG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAOtE;AAKD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAgC5E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAInG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI9D"}
1
+ {"version":3,"file":"sdkFeatures.d.ts","sourceRoot":"","sources":["../../src/proxy/sdkFeatures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,gBAAgB,EAAE,OAAO,CAAA;IACzB,kFAAkF;IAClF,kBAAkB,EAAE,OAAO,CAAA;IAC3B,4DAA4D;IAC5D,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,wDAAwD;IACxD,MAAM,EAAE,OAAO,CAAA;IACf,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAA;IACjB,0DAA0D;IAC1D,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,CAAA;IAC7C,4CAA4C;IAC5C,mBAAmB,EAAE,OAAO,CAAA;IAC5B,iFAAiF;IACjF,YAAY,EAAE,OAAO,CAAA;IACrB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAA;IACrB,+CAA+C;IAC/C,QAAQ,EAAE,OAAO,CAAA;IACjB,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAA;AA4FpE;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAU1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,GAAG,SAAS,CAKhG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAOtE;AAKD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAgC5E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAInG;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAkCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA2Q7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAkiGhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAoCnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AA2Q7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA+pGhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
@@ -19,6 +19,20 @@ export declare const BLOCKED_BUILTIN_TOOLS: string[];
19
19
  * are blocked so Claude uses the agent's version instead of the SDK's.
20
20
  */
21
21
  export declare const CLAUDE_CODE_ONLY_TOOLS: string[];
22
+ /**
23
+ * Return the distinct native-server-tool `type`s present in a request's `tools`
24
+ * array, in first-seen order. Empty when there are none (the common case) or
25
+ * when `tools` isn't an array. Pure — no I/O.
26
+ */
27
+ export declare function detectServerTools(tools: unknown): string[];
28
+ /**
29
+ * Build the actionable error returned when a request carries native server
30
+ * tools. It tells the user to route that specific call at the real Anthropic
31
+ * API (with their own key) rather than through Meridian — the plugin already
32
+ * supports a separate per-provider baseURL, so this is a config fix on their
33
+ * side, not something Meridian can execute on the Max subscription.
34
+ */
35
+ export declare function serverToolErrorMessage(types: string[]): string;
22
36
  /** MCP server name used by the calling agent */
23
37
  export declare const MCP_SERVER_NAME = "opencode";
24
38
  /** MCP tools that are allowed through the proxy's tool filter */
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/proxy/tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,UAIjC,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,UAuBlC,CAAA;AAED,gDAAgD;AAChD,eAAO,MAAM,eAAe,aAAa,CAAA;AAEzC,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,UAO7B,CAAA"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/proxy/tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,UAIjC,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,UAuBlC,CAAA;AAeD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAU1D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAS9D;AAED,gDAAgD;AAChD,eAAO,MAAM,eAAe,aAAa,CAAA;AAEzC,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,UAO7B,CAAA"}
@@ -56,6 +56,16 @@ export interface RequestContext {
56
56
  supportsThinking: boolean;
57
57
  shouldTrackFileChanges: boolean;
58
58
  leaksCwdViaSystemReminder: boolean;
59
+ /**
60
+ * The client is a pure chat client (no tool-execution loop) that runs the
61
+ * SDK in internal mode purely to reach Claude's own built-in tools (e.g.
62
+ * Cherry Studio using WebSearch). The SDK executes those tools itself, so the
63
+ * proxy must NOT surface the internal tool_use blocks — the client can't run
64
+ * them and would loop or choke — and should strip thinking blocks it can't
65
+ * render. Default false: normal internal-mode agents (Droid) keep current
66
+ * behaviour (their internal tools are mcp__-prefixed and already hidden).
67
+ */
68
+ hidesInternalTools?: boolean;
59
69
  prefersStreaming?: boolean;
60
70
  extractFileChangesFromToolUse?: (toolName: string, toolInput: unknown) => FileChange[];
61
71
  metadata: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/proxy/transform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/proxy/transform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Transform } from "../transform";
2
+ /**
3
+ * Cherry Studio transform — supplies the SDK tool config at request time.
4
+ *
5
+ * The runtime tool policy lives here (server.ts reads `pipelineCtx.*`, not the
6
+ * adapter methods). Cherry is a chat client that wants Claude's own built-in
7
+ * web search, so we:
8
+ * - allow only WebSearch/WebFetch (no filesystem MCP tools),
9
+ * - keep those web tools OUT of the disallowed lists, and
10
+ * - run non-passthrough so the SDK executes the search internally.
11
+ *
12
+ * See adapters/cherry.ts for the full rationale and #481.
13
+ */
14
+ export declare const cherryTransforms: Transform[];
15
+ //# sourceMappingURL=cherry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cherry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/cherry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AAO7D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,EAwBvC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAqB7C,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAE9E"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAuB7C,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAE9E"}
package/dist/server.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  runObserveHook,
12
12
  runTransformHook,
13
13
  startProxyServer
14
- } from "./cli-q238a2ad.js";
14
+ } from "./cli-da9hc1s1.js";
15
15
  import"./cli-cx463q74.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-4rqtm83g.js";
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QA0VlB,CAAA"}
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QAsdlB,CAAA"}
@@ -54,4 +54,33 @@ export declare function formatExtraUsage(eu: {
54
54
  utilization: number | null;
55
55
  currency: string;
56
56
  } | null | undefined): ExtraUsageDisplay | null;
57
+ /**
58
+ * Weekly-pace comparison for the 7-day usage window.
59
+ *
60
+ * Compares how much of the weekly allowance has been consumed against how much
61
+ * *time* has elapsed in the window — the "am I burning it too fast?" question.
62
+ * The window resets at `resetsAt` and started 7 days earlier, so the elapsed
63
+ * fraction gives the expected (even-pace) utilization.
64
+ *
65
+ * Returns null when there isn't enough data to compute a meaningful pace
66
+ * (missing utilization or reset time) — callers should hide the widget then.
67
+ *
68
+ * `now` is injectable for tests; production callers omit it.
69
+ */
70
+ export interface WeeklyPace {
71
+ /** Actual utilization, rounded to a percent (may exceed 100). */
72
+ actualPct: number;
73
+ /** Expected utilization at this point in the window, 0..100. */
74
+ expectedPct: number;
75
+ /** actualPct − expectedPct; positive means ahead of (faster than) pace. */
76
+ deltaPct: number;
77
+ /** Extrapolated end-of-window utilization at the current rate; null if it's
78
+ * too early in the window to project without wild swings. */
79
+ projectedPct: number | null;
80
+ /** Consumption relative to even pace. */
81
+ status: "under" | "on" | "ahead";
82
+ /** Position in the window, 0..1 (fraction of the 7 days elapsed). */
83
+ elapsedFraction: number;
84
+ }
85
+ export declare function computeWeeklyPace(utilization: number | null | undefined, resetsAt: number | null | undefined, now?: number): WeeklyPace | null;
57
86
  //# sourceMappingURL=profileUsage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"profileUsage.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQhD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAChD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,CAKvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAY1G;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;CACpB;AACD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE;IACnC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,IAAI,GAAG,SAAS,GAAG,iBAAiB,GAAG,IAAI,CAiB9C"}
1
+ {"version":3,"file":"profileUsage.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQhD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAChD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,CAKvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAY1G;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;CACpB;AACD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE;IACnC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,IAAI,GAAG,SAAS,GAAG,iBAAiB,GAAG,IAAI,CAiB9C;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAA;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB;kEAC8D;IAC9D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,yCAAyC;IACzC,MAAM,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAA;IAChC,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAA;CACxB;AAQD,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,GAAG,GAAE,MAAmB,GACvB,UAAU,GAAG,IAAI,CAoBnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.46.0",
3
+ "version": "1.48.0",
4
4
  "description": "Local Anthropic API powered by your Claude Max subscription. One subscription, every agent.",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",