@rynfar/meridian 1.47.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`:
@@ -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 = {
@@ -10269,50 +10385,6 @@ ${text.slice(0, 2000)}` : text.slice(0, 2000);
10269
10385
  return createHash("sha256").update(seed).digest("hex").slice(0, 16);
10270
10386
  }
10271
10387
 
10272
- // src/proxy/tools.ts
10273
- var BLOCKED_BUILTIN_TOOLS = [
10274
- "Read",
10275
- "Write",
10276
- "Edit",
10277
- "Bash",
10278
- "Glob",
10279
- "Grep",
10280
- "NotebookEdit",
10281
- "WebFetch",
10282
- "WebSearch",
10283
- "TodoWrite"
10284
- ];
10285
- var CLAUDE_CODE_ONLY_TOOLS = [
10286
- "CronCreate",
10287
- "CronDelete",
10288
- "CronList",
10289
- "EnterPlanMode",
10290
- "ExitPlanMode",
10291
- "EnterWorktree",
10292
- "ExitWorktree",
10293
- "Monitor",
10294
- "NotebookEdit",
10295
- "PushNotification",
10296
- "RemoteTrigger",
10297
- "ScheduleWakeup",
10298
- "TodoWrite",
10299
- "AskUserQuestion",
10300
- "Skill",
10301
- "Agent",
10302
- "TaskOutput",
10303
- "TaskStop",
10304
- "WebSearch"
10305
- ];
10306
- var MCP_SERVER_NAME = "opencode";
10307
- var ALLOWED_MCP_TOOLS = [
10308
- `mcp__${MCP_SERVER_NAME}__read`,
10309
- `mcp__${MCP_SERVER_NAME}__write`,
10310
- `mcp__${MCP_SERVER_NAME}__edit`,
10311
- `mcp__${MCP_SERVER_NAME}__bash`,
10312
- `mcp__${MCP_SERVER_NAME}__glob`,
10313
- `mcp__${MCP_SERVER_NAME}__grep`
10314
- ];
10315
-
10316
10388
  // src/proxy/transforms/opencode.ts
10317
10389
  var openCodeTransforms = [
10318
10390
  {
@@ -11112,6 +11184,34 @@ var openAiAdapter = {
11112
11184
  name: "openai"
11113
11185
  };
11114
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
+
11115
11215
  // src/proxy/adapters/detect.ts
11116
11216
  var ADAPTER_MAP = {
11117
11217
  opencode: openCodeAdapter,
@@ -11122,6 +11222,8 @@ var ADAPTER_MAP = {
11122
11222
  forgecode: forgeCodeAdapter,
11123
11223
  "claude-code": claudeCodeAdapter,
11124
11224
  claudecode: claudeCodeAdapter,
11225
+ cherry: cherryAdapter,
11226
+ cherrystudio: cherryAdapter,
11125
11227
  openai: openAiAdapter
11126
11228
  };
11127
11229
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
@@ -17043,6 +17145,27 @@ function structuredOutputText(value) {
17043
17145
  return JSON.stringify(value);
17044
17146
  }
17045
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
+
17046
17169
  // src/proxy/transforms/registry.ts
17047
17170
  var ADAPTER_TRANSFORMS = {
17048
17171
  opencode: openCodeTransforms,
@@ -17051,6 +17174,7 @@ var ADAPTER_TRANSFORMS = {
17051
17174
  pi: piTransforms,
17052
17175
  forgecode: forgeCodeTransforms,
17053
17176
  passthrough: passthroughTransforms,
17177
+ cherry: cherryTransforms,
17054
17178
  openai: openCodeTransforms
17055
17179
  };
17056
17180
  function getAdapterTransforms(adapterName) {
@@ -18204,6 +18328,10 @@ function createProxyServer(config = {}) {
18204
18328
  if (body.messages.length === 0) {
18205
18329
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Cannot be empty — at least one message is required" } }, 400);
18206
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
+ }
18207
18335
  const parsedOutputFormat = parseOutputFormat(body.output_config, body.tools);
18208
18336
  if (!parsedOutputFormat.ok) {
18209
18337
  return c.json({ type: "error", error: { type: "invalid_request_error", message: parsedOutputFormat.message } }, 400);
@@ -18426,6 +18554,9 @@ function createProxyServer(config = {}) {
18426
18554
  const capturedSignatures = new Set;
18427
18555
  const capturedToolNames = new Set;
18428
18556
  let sawDuplicateToolUse = false;
18557
+ const earlyStopEnabled = passthrough && process.env.MERIDIAN_PASSTHROUGH_EARLY_STOP !== "0";
18558
+ const earlyStop = createEarlyStopTracker();
18559
+ let earlyStopFired = false;
18429
18560
  const toolChoice = body.tool_choice;
18430
18561
  const forceSingleToolUse = !!toolChoice && (toolChoice.type === "tool" || toolChoice.disable_parallel_tool_use === true);
18431
18562
  const fileChanges = [];
@@ -18788,6 +18919,19 @@ function createProxyServer(config = {}) {
18788
18919
  claudeLog("passthrough.loop_break", { mode: "non_stream", assistantMessages, captured: capturedToolUses.length });
18789
18920
  break;
18790
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
+ }
18791
18935
  if (message.type === "assistant") {
18792
18936
  assistantMessages += 1;
18793
18937
  if (message.uuid) {
@@ -18811,6 +18955,16 @@ function createProxyServer(config = {}) {
18811
18955
  claudeLog("passthrough.toolsearch_filtered", { mode: "non_stream" });
18812
18956
  continue;
18813
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
+ }
18814
18968
  if (passthrough && !pipelineCtx.supportsThinking && !sdkFeatures.thinkingPassthrough && (b.type === "thinking" || b.type === "redacted_thinking")) {
18815
18969
  claudeLog("passthrough.thinking_stripped", { mode: "non_stream", type: b.type });
18816
18970
  continue;
@@ -19022,6 +19176,7 @@ Subprocess stderr: ${stderrOutput}`;
19022
19176
  let textEventsForwarded = 0;
19023
19177
  let bytesSent = 0;
19024
19178
  let streamClosed = false;
19179
+ let awaitingEarlyStopDrain = false;
19025
19180
  claudeLog("upstream.start", { mode: "stream", model });
19026
19181
  const safeEnqueue = (payload, source) => {
19027
19182
  if (streamClosed)
@@ -19315,7 +19470,7 @@ Subprocess stderr: ${stderrOutput}`;
19315
19470
  }));
19316
19471
  try {
19317
19472
  for await (const message of guardedResponse) {
19318
- if (streamClosed) {
19473
+ if (streamClosed && !awaitingEarlyStopDrain) {
19319
19474
  break;
19320
19475
  }
19321
19476
  if (message.session_id) {
@@ -19324,6 +19479,34 @@ Subprocess stderr: ${stderrOutput}`;
19324
19479
  if (message.type === "assistant" && message.uuid) {
19325
19480
  sdkUuidMap.push(message.uuid);
19326
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
+ }
19327
19510
  if (message.type === "result") {
19328
19511
  const resultUsage = message.usage;
19329
19512
  if (resultUsage)
@@ -19388,6 +19571,12 @@ data: ${JSON.stringify({ type: "message_stop" })}
19388
19571
  }
19389
19572
  if (eventType === "content_block_start") {
19390
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
+ }
19391
19580
  if (passthrough && !pipelineCtx.supportsThinking && !sdkFeatures.thinkingPassthrough && (block?.type === "thinking" || block?.type === "redacted_thinking")) {
19392
19581
  if (eventIndex !== undefined)
19393
19582
  skipBlockIndices.add(eventIndex);
@@ -19492,6 +19681,10 @@ data: ${JSON.stringify({ type: "message_stop" })}
19492
19681
  `), "passthrough_tool_stream_stop");
19493
19682
  streamClosed = true;
19494
19683
  controller.close();
19684
+ if (earlyStopEnabled) {
19685
+ awaitingEarlyStopDrain = true;
19686
+ continue;
19687
+ }
19495
19688
  break;
19496
19689
  }
19497
19690
  if (eventType === "content_block_delta") {
@@ -19763,7 +19956,7 @@ Subprocess stderr: ${stderrOutput}`;
19763
19956
  } : classifyError(errMsg);
19764
19957
  claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
19765
19958
  const sdkTerm = extractSdkTermination(errMsg);
19766
- 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;
19767
19960
  if (canRecoverAsToolUse) {
19768
19961
  diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
19769
19962
  model,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-1v1narm9.js";
4
+ } from "./cli-da9hc1s1.js";
5
5
  import"./cli-cx463q74.js";
6
6
  import"./cli-sry5aqdj.js";
7
7
  import"./cli-4rqtm83g.js";
@@ -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-1v1narm9.js";
14
+ } from "./cli-da9hc1s1.js";
15
15
  import"./cli-cx463q74.js";
16
16
  import"./cli-sry5aqdj.js";
17
17
  import"./cli-4rqtm83g.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.47.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",