@rynfar/meridian 1.40.0 → 1.41.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.
@@ -25,9 +25,10 @@ import {
25
25
  } from "./cli-rtab0qa6.js";
26
26
  import {
27
27
  claudeLog,
28
+ createPlatformCredentialStore,
28
29
  refreshOAuthToken,
29
30
  withClaudeLogContext
30
- } from "./cli-m9pfb7h9.js";
31
+ } from "./cli-0eky480v.js";
31
32
  import {
32
33
  __commonJS,
33
34
  __esm,
@@ -3730,6 +3731,120 @@ class RateLimitStore {
3730
3731
  }
3731
3732
  var rateLimitStore = new RateLimitStore;
3732
3733
 
3734
+ // src/proxy/oauthUsage.ts
3735
+ var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
3736
+ var OAUTH_BETA_HEADER = "oauth-2025-04-20";
3737
+ var CACHE_TTL_MS_DEFAULT = 30000;
3738
+ var cacheByProfile = new Map;
3739
+ var inflightByProfile = new Map;
3740
+ var DEFAULT_KEY = "__default__";
3741
+ var WINDOW_TYPES = [
3742
+ "five_hour",
3743
+ "seven_day",
3744
+ "seven_day_opus",
3745
+ "seven_day_sonnet",
3746
+ "seven_day_oauth_apps",
3747
+ "seven_day_cowork",
3748
+ "seven_day_omelette"
3749
+ ];
3750
+ function parseIsoToMs(raw2) {
3751
+ if (!raw2)
3752
+ return null;
3753
+ const ms = Date.parse(raw2);
3754
+ return Number.isFinite(ms) ? ms : null;
3755
+ }
3756
+ function normalizeUtilization(raw2) {
3757
+ if (typeof raw2 !== "number" || !Number.isFinite(raw2))
3758
+ return null;
3759
+ return Math.max(0, raw2 / 100);
3760
+ }
3761
+ function buildSnapshot(raw2) {
3762
+ const windows = [];
3763
+ for (const key of WINDOW_TYPES) {
3764
+ const w = raw2[key];
3765
+ if (!w)
3766
+ continue;
3767
+ const utilization = normalizeUtilization(w.utilization);
3768
+ const resetsAt = parseIsoToMs(w.resets_at);
3769
+ if (utilization === null && resetsAt === null)
3770
+ continue;
3771
+ windows.push({ type: key, utilization, resetsAt });
3772
+ }
3773
+ const extra = raw2.extra_usage;
3774
+ const extraUsage = extra ? {
3775
+ isEnabled: !!extra.is_enabled,
3776
+ monthlyLimit: extra.monthly_limit ?? 0,
3777
+ usedCredits: extra.used_credits ?? 0,
3778
+ utilization: normalizeUtilization(extra.utilization ?? null),
3779
+ currency: extra.currency ?? "USD"
3780
+ } : null;
3781
+ return { windows, extraUsage, fetchedAt: Date.now() };
3782
+ }
3783
+ async function readAccessToken(store) {
3784
+ const creds = await store.read();
3785
+ return creds?.claudeAiOauth?.accessToken ?? null;
3786
+ }
3787
+ async function callAnthropic(token, signal) {
3788
+ const res = await fetch(OAUTH_USAGE_URL, {
3789
+ headers: {
3790
+ Authorization: `Bearer ${token}`,
3791
+ "anthropic-beta": OAUTH_BETA_HEADER,
3792
+ Accept: "application/json"
3793
+ },
3794
+ signal: signal ?? AbortSignal.timeout(1e4)
3795
+ });
3796
+ if (!res.ok)
3797
+ return { __status: res.status };
3798
+ return await res.json();
3799
+ }
3800
+ async function fetchOAuthUsage(opts) {
3801
+ const ttl = opts?.ttlMs ?? CACHE_TTL_MS_DEFAULT;
3802
+ const cacheKey2 = opts?.profileId ?? DEFAULT_KEY;
3803
+ if (!opts?.force) {
3804
+ const cached = cacheByProfile.get(cacheKey2);
3805
+ if (cached && Date.now() - cached.fetchedAt < ttl)
3806
+ return cached;
3807
+ }
3808
+ const existing = inflightByProfile.get(cacheKey2);
3809
+ if (existing)
3810
+ return existing;
3811
+ const store = opts?.store ?? createPlatformCredentialStore({ claudeConfigDir: opts?.claudeConfigDir });
3812
+ const promise = (async () => {
3813
+ try {
3814
+ const token = await readAccessToken(store);
3815
+ if (!token)
3816
+ return null;
3817
+ let result = await callAnthropic(token);
3818
+ if ("__status" in result && result.__status === 401) {
3819
+ claudeLog("oauth_usage.token_refresh_attempt", { profile: cacheKey2 });
3820
+ const refreshed = await refreshOAuthToken(store);
3821
+ if (!refreshed) {
3822
+ claudeLog("oauth_usage.refresh_failed", { profile: cacheKey2 });
3823
+ return null;
3824
+ }
3825
+ const newToken = await readAccessToken(store);
3826
+ if (!newToken)
3827
+ return null;
3828
+ result = await callAnthropic(newToken);
3829
+ }
3830
+ if ("__status" in result) {
3831
+ claudeLog("oauth_usage.upstream_error", { profile: cacheKey2, status: result.__status });
3832
+ return null;
3833
+ }
3834
+ const snapshot = buildSnapshot(result);
3835
+ cacheByProfile.set(cacheKey2, snapshot);
3836
+ return snapshot;
3837
+ } catch (err) {
3838
+ claudeLog("oauth_usage.fetch_failed", { profile: cacheKey2, error: err instanceof Error ? err.message : String(err) });
3839
+ return null;
3840
+ } finally {
3841
+ inflightByProfile.delete(cacheKey2);
3842
+ }
3843
+ })();
3844
+ inflightByProfile.set(cacheKey2, promise);
3845
+ return promise;
3846
+ }
3847
+
3733
3848
  // src/proxy/types.ts
3734
3849
  var DEFAULT_PROXY_CONFIG = {
3735
3850
  port: 3456,
@@ -7993,13 +8108,20 @@ function ensureDefaultAgents(agents, mcpToolNames) {
7993
8108
  }
7994
8109
  }
7995
8110
  }
8111
+ function cloneAgentDefinition(def) {
8112
+ return {
8113
+ ...def,
8114
+ ...def.tools ? { tools: [...def.tools] } : {},
8115
+ ...def.disallowedTools ? { disallowedTools: [...def.disallowedTools] } : {}
8116
+ };
8117
+ }
7996
8118
  function addCaseVariants(agents) {
7997
8119
  const baseNames = Object.keys(agents);
7998
8120
  for (const name of baseNames) {
7999
8121
  const def = agents[name];
8000
8122
  const titleCase = name.replace(/(^|-)(\w)/g, (_m, sep, ch) => sep + ch.toUpperCase());
8001
8123
  if (titleCase !== name && !agents[titleCase]) {
8002
- agents[titleCase] = def;
8124
+ agents[titleCase] = cloneAgentDefinition(def);
8003
8125
  }
8004
8126
  }
8005
8127
  const ALIASES = {
@@ -8008,10 +8130,50 @@ function addCaseVariants(agents) {
8008
8130
  };
8009
8131
  for (const [alias, target] of Object.entries(ALIASES)) {
8010
8132
  if (!agents[alias] && agents[target]) {
8011
- agents[alias] = agents[target];
8133
+ agents[alias] = cloneAgentDefinition(agents[target]);
8012
8134
  }
8013
8135
  }
8014
8136
  }
8137
+ function getNested(obj, ...keys) {
8138
+ let cur = obj;
8139
+ for (const key of keys) {
8140
+ if (cur === null || typeof cur !== "object")
8141
+ return;
8142
+ cur = cur[key];
8143
+ }
8144
+ return cur;
8145
+ }
8146
+ function parseAgentNamesFromSchema(taskTool) {
8147
+ const enumNames = getNested(taskTool, "input_schema", "properties", "subagent_type", "enum");
8148
+ if (!Array.isArray(enumNames))
8149
+ return [];
8150
+ return enumNames.filter((n) => typeof n === "string");
8151
+ }
8152
+ function buildAgentDefinitionsFromTool(taskTool, mcpToolNames) {
8153
+ const rawDescription = getNested(taskTool, "description");
8154
+ const description = typeof rawDescription === "string" ? rawDescription : "";
8155
+ const fromDescription = buildAgentDefinitions(description, mcpToolNames);
8156
+ if (Object.keys(fromDescription).length > 0)
8157
+ return fromDescription;
8158
+ const names = parseAgentNamesFromSchema(taskTool);
8159
+ if (names.length === 0)
8160
+ return {};
8161
+ const agents = {};
8162
+ for (const name of names) {
8163
+ if (agents[name])
8164
+ continue;
8165
+ const desc = `User-defined agent: ${name}`;
8166
+ agents[name] = {
8167
+ description: desc,
8168
+ prompt: buildAgentPrompt(name, desc),
8169
+ model: "inherit",
8170
+ ...mcpToolNames?.length ? { tools: [...mcpToolNames] } : {}
8171
+ };
8172
+ }
8173
+ ensureDefaultAgents(agents, mcpToolNames);
8174
+ addCaseVariants(agents);
8175
+ return agents;
8176
+ }
8015
8177
  function buildAgentPrompt(name, description) {
8016
8178
  return `You are the "${name}" agent. ${description}
8017
8179
 
@@ -8388,8 +8550,6 @@ let timer;
8388
8550
  let activeTab = 'requests';
8389
8551
  let activeLogFilter = 'all';
8390
8552
 
8391
-
8392
-
8393
8553
  function ms(v) {
8394
8554
  if (v == null) return '—';
8395
8555
  if (v < 1000) return v + 'ms';
@@ -8991,6 +9151,82 @@ function isExtraUsageRequiredError(errMsg) {
8991
9151
  const lower = errMsg.toLowerCase();
8992
9152
  return lower.includes("extra usage") && lower.includes("1m");
8993
9153
  }
9154
+ var STDERR_TAIL_MAX = 500;
9155
+ var RAW_TAIL_MAX = 300;
9156
+ function extractStderrTail(errMsg) {
9157
+ const marker = "Subprocess stderr:";
9158
+ const idx = errMsg.indexOf(marker);
9159
+ if (idx < 0)
9160
+ return;
9161
+ const tail = errMsg.slice(idx + marker.length).trim();
9162
+ if (!tail)
9163
+ return;
9164
+ return tail.length > STDERR_TAIL_MAX ? tail.slice(0, STDERR_TAIL_MAX) : tail;
9165
+ }
9166
+ function makeRawTail(errMsg) {
9167
+ const marker = "Subprocess stderr:";
9168
+ const idx = errMsg.indexOf(marker);
9169
+ const head = (idx >= 0 ? errMsg.slice(0, idx) : errMsg).trim();
9170
+ if (!head)
9171
+ return;
9172
+ return head.length > RAW_TAIL_MAX ? head.slice(0, RAW_TAIL_MAX) : head;
9173
+ }
9174
+ function extractSdkTermination(errMsg) {
9175
+ const stderrTail = extractStderrTail(errMsg);
9176
+ const haystack = `${errMsg}
9177
+ ${stderrTail ?? ""}`;
9178
+ const lower = haystack.toLowerCase();
9179
+ if (lower.includes("reached maximum number of turns")) {
9180
+ const m = haystack.match(/Reached maximum number of turns \((\d+)\)/i);
9181
+ return {
9182
+ reason: "max_turns",
9183
+ ...m ? { turns: Number(m[1]) } : {},
9184
+ ...stderrTail ? { stderrTail } : {}
9185
+ };
9186
+ }
9187
+ if (lower.includes("exited with code") || lower.includes("process exited")) {
9188
+ const m = haystack.match(/exited with code (\d+)/i);
9189
+ return {
9190
+ reason: "process_exit",
9191
+ ...m ? { exitCode: Number(m[1]) } : {},
9192
+ ...stderrTail ? { stderrTail } : {}
9193
+ };
9194
+ }
9195
+ if (lower.includes("aborterror") || /\baborted\b/.test(lower)) {
9196
+ return {
9197
+ reason: "aborted",
9198
+ ...stderrTail ? { stderrTail } : {}
9199
+ };
9200
+ }
9201
+ const rawTail = makeRawTail(errMsg);
9202
+ return {
9203
+ reason: "unknown",
9204
+ ...stderrTail ? { stderrTail } : {},
9205
+ ...rawTail ? { rawTail } : {}
9206
+ };
9207
+ }
9208
+ function formatSdkTermination(t, ctx) {
9209
+ const parts = [`reason=${t.reason}`];
9210
+ if (t.turns !== undefined)
9211
+ parts.push(`turns=${t.turns}`);
9212
+ if (t.exitCode !== undefined)
9213
+ parts.push(`exit=${t.exitCode}`);
9214
+ if (ctx.model)
9215
+ parts.push(`model=${ctx.model}`);
9216
+ if (ctx.requestSource)
9217
+ parts.push(`source=${ctx.requestSource}`);
9218
+ if (ctx.isResume !== undefined)
9219
+ parts.push(`resume=${ctx.isResume}`);
9220
+ if (ctx.hasDeferredTools !== undefined)
9221
+ parts.push(`deferred=${ctx.hasDeferredTools}`);
9222
+ if (ctx.sdkSessionId)
9223
+ parts.push(`session=${ctx.sdkSessionId.slice(0, 8)}`);
9224
+ if (t.rawTail)
9225
+ parts.push(`raw=${JSON.stringify(t.rawTail)}`);
9226
+ if (t.stderrTail)
9227
+ parts.push(`stderr=${JSON.stringify(t.stderrTail)}`);
9228
+ return `sdk_termination ${parts.join(" ")}`;
9229
+ }
8994
9230
 
8995
9231
  // src/proxy/models.ts
8996
9232
  import { exec as execCallback } from "child_process";
@@ -9216,7 +9452,7 @@ function parseDataUrlImage(url) {
9216
9452
  }
9217
9453
  function translateOpenAiContentToAnthropic(content) {
9218
9454
  if (typeof content === "string")
9219
- return content;
9455
+ return [{ type: "text", text: content }];
9220
9456
  const parts = [];
9221
9457
  for (const part of content) {
9222
9458
  if (part.type === "text" && typeof part.text === "string") {
@@ -9235,9 +9471,6 @@ function translateOpenAiContentToAnthropic(content) {
9235
9471
  parts.push({ type: "text", text: "[Unsupported image_url omitted: only data URLs are currently supported]" });
9236
9472
  }
9237
9473
  }
9238
- if (parts.length === 1 && parts[0]?.type === "text") {
9239
- return parts[0].text;
9240
- }
9241
9474
  return parts;
9242
9475
  }
9243
9476
  function summarizeAnthropicContent(content) {
@@ -9246,6 +9479,32 @@ function summarizeAnthropicContent(content) {
9246
9479
  return content.map((part) => {
9247
9480
  if (part.type === "text")
9248
9481
  return part.text;
9482
+ if (part.type === "thinking")
9483
+ return `
9484
+ <think>
9485
+ ` + part.thinking + `
9486
+ </think>
9487
+ `;
9488
+ if (part.type === "tool_use")
9489
+ return `
9490
+ <tool_call name="` + part.name + `">
9491
+ ` + JSON.stringify(part.input) + `
9492
+ </tool_call>
9493
+ `;
9494
+ if (part.type === "tool_result") {
9495
+ if (typeof part.content === "string")
9496
+ return `
9497
+ <tool_result>
9498
+ ` + part.content + `
9499
+ </tool_result>
9500
+ `;
9501
+ else
9502
+ return part.content.map((c) => c.type === "text" ? `
9503
+ <tool_result>
9504
+ ${c.text}
9505
+ </tool_result>
9506
+ ` : "").join("");
9507
+ }
9249
9508
  if (part.type === "image")
9250
9509
  return "[Image attached]";
9251
9510
  return "";
@@ -9257,18 +9516,89 @@ function translateOpenAiToAnthropic(body) {
9257
9516
  return null;
9258
9517
  const systemParts = [];
9259
9518
  const turns = [];
9519
+ const tools = [];
9260
9520
  for (const msg of messages) {
9261
9521
  const text = extractOpenAiContent(msg.content ?? "");
9262
9522
  if (msg.role === "system") {
9263
9523
  if (text)
9264
9524
  systemParts.push(text);
9525
+ } else if (msg.role === "tool") {
9526
+ turns.push({
9527
+ role: "user",
9528
+ content: [{
9529
+ type: "tool_result",
9530
+ tool_use_id: msg.tool_call_id ?? "",
9531
+ content: translateOpenAiContentToAnthropic(msg.content ?? "")
9532
+ }]
9533
+ });
9534
+ } else if (msg.role === "assistant") {
9535
+ const msgContent = translateOpenAiContentToAnthropic(msg.content ?? "");
9536
+ const content = [];
9537
+ const toolCalls = msg.tool_calls ?? null;
9538
+ const firstBlock = msgContent[0];
9539
+ const endOfThink = firstBlock?.type === "text" && firstBlock.text.startsWith("<think>") ? firstBlock.text.indexOf("</think>") : -1;
9540
+ if (firstBlock?.type === "text" && firstBlock.text.startsWith("<think>") && endOfThink !== -1) {
9541
+ const thinking = firstBlock.text.substring("<think>".length, endOfThink);
9542
+ let textStart = endOfThink + "</think>".length;
9543
+ if (firstBlock.text[textStart] === `
9544
+ `)
9545
+ textStart += 1;
9546
+ const text2 = firstBlock.text.substring(textStart);
9547
+ content.push({ type: "thinking", thinking });
9548
+ if (text2.length)
9549
+ content.push({ type: "text", text: text2 });
9550
+ if (msgContent.length > 1)
9551
+ content.push(...msgContent.slice(1));
9552
+ } else {
9553
+ content.push(...msgContent);
9554
+ }
9555
+ if (toolCalls) {
9556
+ const calls = toolCalls.filter((call) => call.type === "function").map((call) => {
9557
+ let input;
9558
+ try {
9559
+ const parsed = JSON.parse(call.function.arguments);
9560
+ input = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { __raw: call.function.arguments };
9561
+ } catch {
9562
+ input = { __raw: call.function.arguments };
9563
+ }
9564
+ return {
9565
+ type: "tool_use",
9566
+ id: call.id,
9567
+ name: call.function.name,
9568
+ input
9569
+ };
9570
+ });
9571
+ content.push(...calls);
9572
+ }
9573
+ let finalContent = content;
9574
+ if (content.length === 1 && content[0]?.type === "text") {
9575
+ finalContent = content[0].text;
9576
+ }
9577
+ turns.push({
9578
+ role: "assistant",
9579
+ content: finalContent
9580
+ });
9265
9581
  } else {
9266
9582
  turns.push({
9267
- role: msg.role === "assistant" ? "assistant" : "user",
9583
+ role: "user",
9268
9584
  content: translateOpenAiContentToAnthropic(msg.content ?? "")
9269
9585
  });
9270
9586
  }
9271
9587
  }
9588
+ const reqTools = body.tools ?? [];
9589
+ for (const reqTool of reqTools) {
9590
+ if (reqTool.type === "function") {
9591
+ const tool = reqTool.function;
9592
+ tools.push({
9593
+ name: tool.name,
9594
+ description: tool.description ?? "",
9595
+ input_schema: tool.parameters,
9596
+ strict: tool.strict
9597
+ });
9598
+ } else {
9599
+ return null;
9600
+ }
9601
+ }
9272
9602
  let systemPrompt = systemParts.join(`
9273
9603
  `);
9274
9604
  let messagesToSend = turns;
@@ -9289,6 +9619,7 @@ ${historyBlock}` : historyBlock;
9289
9619
  model: body.model ?? "claude-sonnet-4-6",
9290
9620
  messages: messagesToSend,
9291
9621
  max_tokens: body.max_tokens ?? body.max_completion_tokens ?? 8192,
9622
+ tools,
9292
9623
  stream: body.stream ?? false
9293
9624
  };
9294
9625
  if (systemPrompt)
@@ -9302,10 +9633,23 @@ ${historyBlock}` : historyBlock;
9302
9633
  function toFinishReason(stopReason) {
9303
9634
  if (stopReason === "max_tokens")
9304
9635
  return "length";
9636
+ else if (stopReason === "tool_use")
9637
+ return "tool_calls";
9305
9638
  return "stop";
9306
9639
  }
9307
- function translateAnthropicToOpenAi(response, completionId, model, created) {
9308
- const content = (response.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
9640
+ function translateAnthropicToOpenAi(response, completionId, model, created, options) {
9641
+ const contentBlocks = response.content ?? [];
9642
+ const content = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
9643
+ const toolCalls = contentBlocks.filter((b) => b.type === "tool_use").map((b) => ({
9644
+ type: "function",
9645
+ id: b.id,
9646
+ function: {
9647
+ name: b.name,
9648
+ arguments: JSON.stringify(b.input)
9649
+ }
9650
+ }));
9651
+ const thinkingPassthrough = options?.thinkingPassthrough;
9652
+ const thinking = thinkingPassthrough !== false ? contentBlocks.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") : "";
9309
9653
  const promptTokens = response.usage?.input_tokens ?? 0;
9310
9654
  const completionTokens = response.usage?.output_tokens ?? 0;
9311
9655
  return {
@@ -9315,7 +9659,12 @@ function translateAnthropicToOpenAi(response, completionId, model, created) {
9315
9659
  model,
9316
9660
  choices: [{
9317
9661
  index: 0,
9318
- message: { role: "assistant", content },
9662
+ message: {
9663
+ role: "assistant",
9664
+ content: content || null,
9665
+ reasoning_content: thinking.length ? thinking : undefined,
9666
+ tool_calls: toolCalls.length ? toolCalls : undefined
9667
+ },
9319
9668
  finish_reason: toFinishReason(response.stop_reason)
9320
9669
  }],
9321
9670
  usage: {
@@ -9325,7 +9674,16 @@ function translateAnthropicToOpenAi(response, completionId, model, created) {
9325
9674
  }
9326
9675
  };
9327
9676
  }
9328
- function translateAnthropicSseEvent(event, completionId, model, created) {
9677
+ function createSseTranslator(ctx) {
9678
+ let toolCallIndex = -1;
9679
+ return (event) => {
9680
+ if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block.name === "string") {
9681
+ toolCallIndex++;
9682
+ }
9683
+ return translateAnthropicSseEvent(event, ctx.completionId, ctx.model, ctx.created, toolCallIndex, ctx.thinkingPassthrough);
9684
+ };
9685
+ }
9686
+ function translateAnthropicSseEvent(event, completionId, model, created, toolCallNum, thinkingPassthrough) {
9329
9687
  if (event.type === "message_start") {
9330
9688
  return {
9331
9689
  id: completionId,
@@ -9344,6 +9702,69 @@ function translateAnthropicSseEvent(event, completionId, model, created) {
9344
9702
  choices: [{ index: 0, delta: { content: event.delta.text }, finish_reason: null }]
9345
9703
  };
9346
9704
  }
9705
+ if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block?.name === "string") {
9706
+ return {
9707
+ id: completionId,
9708
+ object: "chat.completion.chunk",
9709
+ created,
9710
+ model,
9711
+ choices: [{
9712
+ index: 0,
9713
+ delta: {
9714
+ tool_calls: [{
9715
+ type: "function",
9716
+ index: toolCallNum,
9717
+ id: event.content_block?.id,
9718
+ function: {
9719
+ name: event.content_block.name,
9720
+ arguments: ""
9721
+ }
9722
+ }]
9723
+ },
9724
+ finish_reason: null
9725
+ }]
9726
+ };
9727
+ }
9728
+ if (event.type === "content_block_delta" && event.delta?.type === "input_json_delta" && typeof event.delta?.partial_json === "string") {
9729
+ return {
9730
+ id: completionId,
9731
+ object: "chat.completion.chunk",
9732
+ created,
9733
+ model,
9734
+ choices: [{
9735
+ index: 0,
9736
+ delta: {
9737
+ tool_calls: [{
9738
+ index: toolCallNum,
9739
+ function: {
9740
+ arguments: event.delta.partial_json
9741
+ }
9742
+ }]
9743
+ },
9744
+ finish_reason: null
9745
+ }]
9746
+ };
9747
+ }
9748
+ if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta") {
9749
+ if (thinkingPassthrough === false) {
9750
+ return null;
9751
+ }
9752
+ if (typeof event.delta?.thinking === "string") {
9753
+ return {
9754
+ id: completionId,
9755
+ object: "chat.completion.chunk",
9756
+ created,
9757
+ model,
9758
+ choices: [{
9759
+ index: 0,
9760
+ delta: {
9761
+ reasoning_content: event.delta?.thinking
9762
+ },
9763
+ finish_reason: null
9764
+ }]
9765
+ };
9766
+ }
9767
+ }
9347
9768
  if (event.type === "message_delta" && event.delta?.stop_reason) {
9348
9769
  return {
9349
9770
  id: completionId,
@@ -9660,7 +10081,11 @@ var CLAUDE_CODE_ONLY_TOOLS = [
9660
10081
  "ExitPlanMode",
9661
10082
  "EnterWorktree",
9662
10083
  "ExitWorktree",
10084
+ "Monitor",
9663
10085
  "NotebookEdit",
10086
+ "PushNotification",
10087
+ "RemoteTrigger",
10088
+ "ScheduleWakeup",
9664
10089
  "TodoWrite",
9665
10090
  "AskUserQuestion",
9666
10091
  "Skill",
@@ -9695,8 +10120,8 @@ var openCodeTransforms = [
9695
10120
  let sdkAgents = {};
9696
10121
  if (Array.isArray(body.tools)) {
9697
10122
  const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
9698
- if (taskTool?.description) {
9699
- sdkAgents = buildAgentDefinitions(taskTool.description, [...allowedMcpTools]);
10123
+ if (taskTool) {
10124
+ sdkAgents = buildAgentDefinitionsFromTool(taskTool, [...allowedMcpTools]);
9700
10125
  }
9701
10126
  }
9702
10127
  let sdkHooks = undefined;
@@ -9802,9 +10227,9 @@ var openCodeAdapter = {
9802
10227
  if (!Array.isArray(body.tools))
9803
10228
  return {};
9804
10229
  const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
9805
- if (!taskTool?.description)
10230
+ if (!taskTool)
9806
10231
  return {};
9807
- return buildAgentDefinitions(taskTool.description, [...mcpToolNames]);
10232
+ return buildAgentDefinitionsFromTool(taskTool, [...mcpToolNames]);
9808
10233
  },
9809
10234
  buildSdkHooks(body, sdkAgents) {
9810
10235
  const validAgentNames = Object.keys(sdkAgents);
@@ -11962,7 +12387,7 @@ var emitWarning = (msg, type, code, fn) => {
11962
12387
  var AC = globalThis.AbortController;
11963
12388
  var AS = globalThis.AbortSignal;
11964
12389
  if (typeof AC === "undefined") {
11965
- AS = class AbortSignal {
12390
+ AS = class AbortSignal2 {
11966
12391
  onabort;
11967
12392
  _onabort = [];
11968
12393
  reason;
@@ -16202,7 +16627,7 @@ function createOpencodeMcpServer() {
16202
16627
  // src/proxy/query.ts
16203
16628
  function computePassthroughMaxTurns(resumeSessionId, hasDeferredTools, advisorModel) {
16204
16629
  const hasResume = !!resumeSessionId;
16205
- const base = hasResume && hasDeferredTools ? 4 : hasResume || hasDeferredTools ? 3 : 2;
16630
+ const base = hasResume && hasDeferredTools ? 4 : 3;
16206
16631
  const advisorBump = advisorModel ? 3 : 0;
16207
16632
  return base + advisorBump;
16208
16633
  }
@@ -17698,7 +18123,7 @@ function createProxyServer(config = {}) {
17698
18123
  });
17699
18124
  return {
17700
18125
  decision: "block",
17701
- reason: "Forwarding to client for execution"
18126
+ reason: "This tool call has been forwarded to the client for execution. " + "The result will be delivered in a future turn. " + "Do not retry, do not call additional tools, and do not generate further text — end your turn now."
17702
18127
  };
17703
18128
  }]
17704
18129
  }]
@@ -18119,6 +18544,7 @@ Subprocess stderr: ${stderrOutput}`;
18119
18544
  sdkUuidMap.push(null);
18120
18545
  let messageStartEmitted = false;
18121
18546
  let lastUsage;
18547
+ const streamedToolUseIds = new Set;
18122
18548
  try {
18123
18549
  let currentSessionId;
18124
18550
  const MAX_RATE_LIMIT_RETRIES = 2;
@@ -18309,7 +18735,6 @@ Subprocess stderr: ${stderrOutput}`;
18309
18735
  const skipBlockIndices = new Set;
18310
18736
  const taskToolBlockIndices = new Set;
18311
18737
  const taskToolJsonBuffer = new Map;
18312
- const streamedToolUseIds = new Set;
18313
18738
  let nextClientBlockIndex = 0;
18314
18739
  const sdkToClientIndex = new Map;
18315
18740
  try {
@@ -18672,6 +19097,127 @@ Subprocess stderr: ${stderrOutput}`;
18672
19097
  });
18673
19098
  const streamErr = classifyError(errMsg);
18674
19099
  claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
19100
+ const sdkTerm = extractSdkTermination(errMsg);
19101
+ const canRecoverAsToolUse = sdkTerm.reason === "max_turns" && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
19102
+ if (canRecoverAsToolUse) {
19103
+ diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
19104
+ model,
19105
+ requestSource,
19106
+ isResume,
19107
+ hasDeferredTools,
19108
+ sdkSessionId: resumeSessionId
19109
+ })} captured=${capturedToolUses.length}`, requestMeta.requestId);
19110
+ const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
19111
+ for (let i = 0;i < unseenToolUses.length; i++) {
19112
+ const tu = unseenToolUses[i];
19113
+ const blockIndex = eventsForwarded + i;
19114
+ safeEnqueue(encoder.encode(`event: content_block_start
19115
+ data: ${JSON.stringify({
19116
+ type: "content_block_start",
19117
+ index: blockIndex,
19118
+ content_block: { type: "tool_use", id: tu.id, name: tu.name, input: {} }
19119
+ })}
19120
+
19121
+ `), "recover_tool_block_start");
19122
+ safeEnqueue(encoder.encode(`event: content_block_delta
19123
+ data: ${JSON.stringify({
19124
+ type: "content_block_delta",
19125
+ index: blockIndex,
19126
+ delta: { type: "input_json_delta", partial_json: JSON.stringify(tu.input) }
19127
+ })}
19128
+
19129
+ `), "recover_tool_input");
19130
+ safeEnqueue(encoder.encode(`event: content_block_stop
19131
+ data: ${JSON.stringify({
19132
+ type: "content_block_stop",
19133
+ index: blockIndex
19134
+ })}
19135
+
19136
+ `), "recover_tool_block_stop");
19137
+ }
19138
+ safeEnqueue(encoder.encode(`event: message_delta
19139
+ data: ${JSON.stringify({
19140
+ type: "message_delta",
19141
+ delta: { stop_reason: "tool_use", stop_sequence: null },
19142
+ usage: { output_tokens: 0 }
19143
+ })}
19144
+
19145
+ `), "recover_message_delta");
19146
+ safeEnqueue(encoder.encode(`event: message_stop
19147
+ data: {"type":"message_stop"}
19148
+
19149
+ `), "recover_message_stop");
19150
+ const recoverTotalMs = Date.now() - requestStartAt;
19151
+ const recoverQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
19152
+ telemetryStore2.record({
19153
+ requestId: requestMeta.requestId,
19154
+ timestamp: Date.now(),
19155
+ adapter: adapter.name,
19156
+ requestSource,
19157
+ model,
19158
+ requestModel: body.model || undefined,
19159
+ mode: "stream",
19160
+ isResume,
19161
+ isPassthrough: passthrough,
19162
+ hasDeferredTools,
19163
+ deferredToolCount: hasDeferredTools ? deferredToolCount : undefined,
19164
+ toolCount,
19165
+ lineageType,
19166
+ messageCount: allMessages.length,
19167
+ sdkSessionId: resumeSessionId,
19168
+ status: 200,
19169
+ queueWaitMs: recoverQueueWaitMs,
19170
+ proxyOverheadMs: upstreamStartAt - requestStartAt - recoverQueueWaitMs,
19171
+ ttfbMs: firstChunkAt ? firstChunkAt - upstreamStartAt : null,
19172
+ upstreamDurationMs: Date.now() - upstreamStartAt,
19173
+ totalDurationMs: recoverTotalMs,
19174
+ contentBlocks: eventsForwarded + unseenToolUses.length,
19175
+ textEvents: textEventsForwarded,
19176
+ error: null
19177
+ });
19178
+ if (!streamClosed) {
19179
+ try {
19180
+ controller.close();
19181
+ } catch {}
19182
+ streamClosed = true;
19183
+ }
19184
+ return;
19185
+ }
19186
+ diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
19187
+ model,
19188
+ requestSource,
19189
+ isResume,
19190
+ hasDeferredTools,
19191
+ sdkSessionId: resumeSessionId
19192
+ })}`, requestMeta.requestId);
19193
+ const streamErrTotalMs = Date.now() - requestStartAt;
19194
+ const streamErrQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
19195
+ telemetryStore2.record({
19196
+ requestId: requestMeta.requestId,
19197
+ timestamp: Date.now(),
19198
+ adapter: adapter.name,
19199
+ requestSource,
19200
+ model,
19201
+ requestModel: body.model || undefined,
19202
+ mode: "stream",
19203
+ isResume,
19204
+ isPassthrough: passthrough,
19205
+ hasDeferredTools,
19206
+ deferredToolCount: hasDeferredTools ? deferredToolCount : undefined,
19207
+ toolCount,
19208
+ lineageType,
19209
+ messageCount: allMessages.length,
19210
+ sdkSessionId: resumeSessionId,
19211
+ status: streamErr.status,
19212
+ queueWaitMs: streamErrQueueWaitMs,
19213
+ proxyOverheadMs: upstreamStartAt - requestStartAt - streamErrQueueWaitMs,
19214
+ ttfbMs: firstChunkAt ? firstChunkAt - upstreamStartAt : null,
19215
+ upstreamDurationMs: Date.now() - upstreamStartAt,
19216
+ totalDurationMs: streamErrTotalMs,
19217
+ contentBlocks: eventsForwarded,
19218
+ textEvents: textEventsForwarded,
19219
+ error: streamErr.type
19220
+ });
18675
19221
  if (messageStartEmitted) {
18676
19222
  safeEnqueue(encoder.encode(`event: message_delta
18677
19223
  data: ${JSON.stringify({
@@ -18719,6 +19265,10 @@ data: ${JSON.stringify({
18719
19265
  });
18720
19266
  const classified = classifyError(errMsg);
18721
19267
  claudeLog("proxy.error", { error: errMsg, classified: classified.type });
19268
+ const sdkTerm = extractSdkTermination(errMsg);
19269
+ diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
19270
+ requestSource: c.req.header("x-meridian-source")?.slice(0, 64) || undefined
19271
+ })}`, requestMeta.requestId);
18722
19272
  const errorQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
18723
19273
  telemetryStore2.record({
18724
19274
  requestId: requestMeta.requestId,
@@ -18860,7 +19410,7 @@ data: ${JSON.stringify({
18860
19410
  });
18861
19411
  });
18862
19412
  app.get("/profiles", async (c) => {
18863
- const { profilePageHtml } = await import("./profilePage-77z05e0r.js");
19413
+ const { profilePageHtml } = await import("./profilePage-k0faye28.js");
18864
19414
  return c.html(profilePageHtml);
18865
19415
  });
18866
19416
  app.post("/profiles/active", async (c) => {
@@ -18952,9 +19502,14 @@ data: ${JSON.stringify({
18952
19502
  const completionId = `chatcmpl-${randomUUID()}`;
18953
19503
  const created = Math.floor(Date.now() / 1000);
18954
19504
  const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : "claude-sonnet-4-6";
19505
+ const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
19506
+ const adapter = detectAdapter(c);
19507
+ const sdkFeatures = getFeaturesForAdapter2(adapter.name);
18955
19508
  if (!anthropicBody.stream) {
18956
19509
  const anthropicRes = await internalRes.json();
18957
- return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created));
19510
+ return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
19511
+ thinkingPassthrough: sdkFeatures.thinkingPassthrough
19512
+ }));
18958
19513
  }
18959
19514
  const encoder = new TextEncoder;
18960
19515
  const readable = new ReadableStream({
@@ -18967,6 +19522,7 @@ data: ${JSON.stringify({
18967
19522
  const decoder = new TextDecoder;
18968
19523
  let buffer = "";
18969
19524
  let streamError = null;
19525
+ const translate = createSseTranslator({ completionId, model, created, thinkingPassthrough: sdkFeatures.thinkingPassthrough });
18970
19526
  try {
18971
19527
  while (true) {
18972
19528
  const { done, value } = await reader.read();
@@ -18990,7 +19546,7 @@ data: ${JSON.stringify({
18990
19546
  }
18991
19547
  if (typeof event.type !== "string")
18992
19548
  continue;
18993
- const chunk = translateAnthropicSseEvent(event, completionId, model, created);
19549
+ const chunk = translate(event);
18994
19550
  if (chunk)
18995
19551
  controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
18996
19552
 
@@ -19021,11 +19577,21 @@ data: ${JSON.stringify({
19021
19577
  const isMax = authStatus?.subscriptionType === "max";
19022
19578
  return c.json({ object: "list", data: buildModelList(isMax) });
19023
19579
  });
19024
- app.get("/v1/usage/quota", (c) => {
19025
- const entries = rateLimitStore.getAll().filter((entry) => entry.rateLimitType !== undefined);
19026
- return c.json({
19027
- buckets: entries.map((entry) => ({
19028
- type: entry.rateLimitType,
19580
+ app.get("/v1/usage/quota", async (c) => {
19581
+ const sdkEntries = rateLimitStore.getAll().filter((entry) => entry.rateLimitType !== undefined);
19582
+ const requestedProfile = c.req.query("profile");
19583
+ const profilesList = getEffectiveProfiles(finalConfig.profiles);
19584
+ const targetProfileId = requestedProfile || getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
19585
+ const targetProfile = targetProfileId ? profilesList.find((p) => p.id === targetProfileId) : undefined;
19586
+ const oauth = await fetchOAuthUsage({
19587
+ profileId: targetProfileId ?? undefined,
19588
+ claudeConfigDir: targetProfile?.claudeConfigDir
19589
+ });
19590
+ const byType = new Map;
19591
+ for (const entry of sdkEntries) {
19592
+ const type = entry.rateLimitType;
19593
+ byType.set(type, {
19594
+ type,
19029
19595
  status: entry.status,
19030
19596
  utilization: entry.utilization ?? null,
19031
19597
  resetsAt: entry.resetsAt ?? null,
@@ -19035,7 +19601,85 @@ data: ${JSON.stringify({
19035
19601
  overageDisabledReason: entry.overageDisabledReason ?? null,
19036
19602
  surpassedThreshold: entry.surpassedThreshold ?? null,
19037
19603
  observedAt: entry.observedAt
19038
- })),
19604
+ });
19605
+ }
19606
+ if (oauth) {
19607
+ for (const w of oauth.windows) {
19608
+ const existing = byType.get(w.type);
19609
+ const status = (w.utilization ?? 0) >= 1 ? "rejected" : (w.utilization ?? 0) >= 0.8 ? "allowed_warning" : "allowed";
19610
+ byType.set(w.type, {
19611
+ type: w.type,
19612
+ status: existing?.status === "rejected" ? "rejected" : status,
19613
+ utilization: w.utilization ?? existing?.utilization ?? null,
19614
+ resetsAt: w.resetsAt ?? existing?.resetsAt ?? null,
19615
+ isUsingOverage: existing?.isUsingOverage ?? false,
19616
+ overageStatus: existing?.overageStatus ?? null,
19617
+ overageResetsAt: existing?.overageResetsAt ?? null,
19618
+ overageDisabledReason: existing?.overageDisabledReason ?? null,
19619
+ surpassedThreshold: existing?.surpassedThreshold ?? null,
19620
+ observedAt: oauth.fetchedAt
19621
+ });
19622
+ }
19623
+ }
19624
+ return c.json({
19625
+ profile: targetProfileId ?? null,
19626
+ buckets: Array.from(byType.values()),
19627
+ extraUsage: oauth?.extraUsage ?? null,
19628
+ sources: {
19629
+ oauth: oauth ? { fetchedAt: oauth.fetchedAt } : null,
19630
+ sdk: { entryCount: sdkEntries.length }
19631
+ },
19632
+ asOf: Date.now()
19633
+ });
19634
+ });
19635
+ app.get("/v1/usage/quota/all", async (c) => {
19636
+ const profilesList = getEffectiveProfiles(finalConfig.profiles);
19637
+ const activeId = getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
19638
+ if (profilesList.length === 0) {
19639
+ const oauth = await fetchOAuthUsage({});
19640
+ return c.json({
19641
+ profiles: [{
19642
+ id: "default",
19643
+ isActive: true,
19644
+ windows: oauth?.windows ?? [],
19645
+ extraUsage: oauth?.extraUsage ?? null,
19646
+ fetchedAt: oauth?.fetchedAt ?? null,
19647
+ error: oauth ? null : "no_token"
19648
+ }],
19649
+ activeProfile: "default",
19650
+ asOf: Date.now()
19651
+ });
19652
+ }
19653
+ const results = await Promise.all(profilesList.map(async (p) => {
19654
+ const type = p.type ?? "claude-max";
19655
+ if (type !== "claude-max") {
19656
+ return {
19657
+ id: p.id,
19658
+ isActive: p.id === activeId,
19659
+ type,
19660
+ windows: [],
19661
+ extraUsage: null,
19662
+ fetchedAt: null,
19663
+ error: "not_oauth"
19664
+ };
19665
+ }
19666
+ const oauth = await fetchOAuthUsage({
19667
+ profileId: p.id,
19668
+ claudeConfigDir: p.claudeConfigDir
19669
+ });
19670
+ return {
19671
+ id: p.id,
19672
+ isActive: p.id === activeId,
19673
+ type,
19674
+ windows: oauth?.windows ?? [],
19675
+ extraUsage: oauth?.extraUsage ?? null,
19676
+ fetchedAt: oauth?.fetchedAt ?? null,
19677
+ error: oauth ? null : "no_token"
19678
+ };
19679
+ }));
19680
+ return c.json({
19681
+ profiles: results,
19682
+ activeProfile: activeId,
19039
19683
  asOf: Date.now()
19040
19684
  });
19041
19685
  });