@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 +17 -0
- package/dist/{cli-1v1narm9.js → cli-da9hc1s1.js} +239 -46
- package/dist/cli.js +1 -1
- package/dist/proxy/adapters/cherry.d.ts +38 -0
- package/dist/proxy/adapters/cherry.d.ts.map +1 -0
- package/dist/proxy/adapters/detect.d.ts.map +1 -1
- package/dist/proxy/passthroughEarlyStop.d.ts +55 -0
- package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -0
- package/dist/proxy/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/tools.d.ts +14 -0
- package/dist/proxy/tools.d.ts.map +1 -1
- package/dist/proxy/transform.d.ts +10 -0
- package/dist/proxy/transform.d.ts.map +1 -1
- package/dist/proxy/transforms/cherry.d.ts +15 -0
- package/dist/proxy/transforms/cherry.d.ts.map +1 -0
- package/dist/proxy/transforms/registry.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
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
|
@@ -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;
|
|
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;
|
|
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;
|
|
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"}
|
package/dist/proxy/tools.d.ts
CHANGED
|
@@ -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;
|
|
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
package/package.json
CHANGED