@rynfar/meridian 1.39.1 → 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.
- package/dist/{cli-m9pfb7h9.js → cli-0eky480v.js} +76 -46
- package/dist/{cli-wv0e3636.js → cli-swjr844z.js} +720 -26
- package/dist/cli.js +3 -3
- package/dist/{profilePage-77z05e0r.js → profilePage-k0faye28.js} +196 -4
- package/dist/proxy/agentDefs.d.ts +2 -0
- package/dist/proxy/agentDefs.d.ts.map +1 -1
- package/dist/proxy/errors.d.ts +42 -0
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/oauthUsage.d.ts +67 -0
- package/dist/proxy/oauthUsage.d.ts.map +1 -0
- package/dist/proxy/openai.d.ts +140 -14
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/rateLimitStore.d.ts +73 -0
- package/dist/proxy/rateLimitStore.d.ts.map +1 -0
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/tokenRefresh.d.ts +22 -1
- package/dist/proxy/tokenRefresh.d.ts.map +1 -1
- package/dist/proxy/tools.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/telemetry/profilePage.d.ts.map +1 -1
- package/dist/telemetry/profileUsage.d.ts +57 -0
- package/dist/telemetry/profileUsage.d.ts.map +1 -0
- package/dist/tokenRefresh-3kh1e8q8.js +17 -0
- package/package.json +3 -3
- package/dist/tokenRefresh-y7d1qvb3.js +0 -11
|
@@ -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-
|
|
31
|
+
} from "./cli-0eky480v.js";
|
|
31
32
|
import {
|
|
32
33
|
__commonJS,
|
|
33
34
|
__esm,
|
|
@@ -3706,6 +3707,144 @@ import { homedir as homedir5 } from "node:os";
|
|
|
3706
3707
|
import { join as join7 } from "node:path";
|
|
3707
3708
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3708
3709
|
|
|
3710
|
+
// src/proxy/rateLimitStore.ts
|
|
3711
|
+
class RateLimitStore {
|
|
3712
|
+
entries = new Map;
|
|
3713
|
+
record(info) {
|
|
3714
|
+
if (!info || typeof info !== "object")
|
|
3715
|
+
return;
|
|
3716
|
+
const key = info.rateLimitType ?? "default";
|
|
3717
|
+
this.entries.set(key, { ...info, observedAt: Date.now() });
|
|
3718
|
+
}
|
|
3719
|
+
getAll() {
|
|
3720
|
+
return Array.from(this.entries.values()).sort((a, b) => b.observedAt - a.observedAt);
|
|
3721
|
+
}
|
|
3722
|
+
get(key) {
|
|
3723
|
+
return this.entries.get(key);
|
|
3724
|
+
}
|
|
3725
|
+
get size() {
|
|
3726
|
+
return this.entries.size;
|
|
3727
|
+
}
|
|
3728
|
+
clear() {
|
|
3729
|
+
this.entries.clear();
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
var rateLimitStore = new RateLimitStore;
|
|
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
|
+
|
|
3709
3848
|
// src/proxy/types.ts
|
|
3710
3849
|
var DEFAULT_PROXY_CONFIG = {
|
|
3711
3850
|
port: 3456,
|
|
@@ -7969,13 +8108,20 @@ function ensureDefaultAgents(agents, mcpToolNames) {
|
|
|
7969
8108
|
}
|
|
7970
8109
|
}
|
|
7971
8110
|
}
|
|
8111
|
+
function cloneAgentDefinition(def) {
|
|
8112
|
+
return {
|
|
8113
|
+
...def,
|
|
8114
|
+
...def.tools ? { tools: [...def.tools] } : {},
|
|
8115
|
+
...def.disallowedTools ? { disallowedTools: [...def.disallowedTools] } : {}
|
|
8116
|
+
};
|
|
8117
|
+
}
|
|
7972
8118
|
function addCaseVariants(agents) {
|
|
7973
8119
|
const baseNames = Object.keys(agents);
|
|
7974
8120
|
for (const name of baseNames) {
|
|
7975
8121
|
const def = agents[name];
|
|
7976
8122
|
const titleCase = name.replace(/(^|-)(\w)/g, (_m, sep, ch) => sep + ch.toUpperCase());
|
|
7977
8123
|
if (titleCase !== name && !agents[titleCase]) {
|
|
7978
|
-
agents[titleCase] = def;
|
|
8124
|
+
agents[titleCase] = cloneAgentDefinition(def);
|
|
7979
8125
|
}
|
|
7980
8126
|
}
|
|
7981
8127
|
const ALIASES = {
|
|
@@ -7984,10 +8130,50 @@ function addCaseVariants(agents) {
|
|
|
7984
8130
|
};
|
|
7985
8131
|
for (const [alias, target] of Object.entries(ALIASES)) {
|
|
7986
8132
|
if (!agents[alias] && agents[target]) {
|
|
7987
|
-
agents[alias] = agents[target];
|
|
8133
|
+
agents[alias] = cloneAgentDefinition(agents[target]);
|
|
7988
8134
|
}
|
|
7989
8135
|
}
|
|
7990
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
|
+
}
|
|
7991
8177
|
function buildAgentPrompt(name, description) {
|
|
7992
8178
|
return `You are the "${name}" agent. ${description}
|
|
7993
8179
|
|
|
@@ -8364,8 +8550,6 @@ let timer;
|
|
|
8364
8550
|
let activeTab = 'requests';
|
|
8365
8551
|
let activeLogFilter = 'all';
|
|
8366
8552
|
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
8553
|
function ms(v) {
|
|
8370
8554
|
if (v == null) return '—';
|
|
8371
8555
|
if (v < 1000) return v + 'ms';
|
|
@@ -8967,6 +9151,82 @@ function isExtraUsageRequiredError(errMsg) {
|
|
|
8967
9151
|
const lower = errMsg.toLowerCase();
|
|
8968
9152
|
return lower.includes("extra usage") && lower.includes("1m");
|
|
8969
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
|
+
}
|
|
8970
9230
|
|
|
8971
9231
|
// src/proxy/models.ts
|
|
8972
9232
|
import { exec as execCallback } from "child_process";
|
|
@@ -9192,7 +9452,7 @@ function parseDataUrlImage(url) {
|
|
|
9192
9452
|
}
|
|
9193
9453
|
function translateOpenAiContentToAnthropic(content) {
|
|
9194
9454
|
if (typeof content === "string")
|
|
9195
|
-
return content;
|
|
9455
|
+
return [{ type: "text", text: content }];
|
|
9196
9456
|
const parts = [];
|
|
9197
9457
|
for (const part of content) {
|
|
9198
9458
|
if (part.type === "text" && typeof part.text === "string") {
|
|
@@ -9211,9 +9471,6 @@ function translateOpenAiContentToAnthropic(content) {
|
|
|
9211
9471
|
parts.push({ type: "text", text: "[Unsupported image_url omitted: only data URLs are currently supported]" });
|
|
9212
9472
|
}
|
|
9213
9473
|
}
|
|
9214
|
-
if (parts.length === 1 && parts[0]?.type === "text") {
|
|
9215
|
-
return parts[0].text;
|
|
9216
|
-
}
|
|
9217
9474
|
return parts;
|
|
9218
9475
|
}
|
|
9219
9476
|
function summarizeAnthropicContent(content) {
|
|
@@ -9222,6 +9479,32 @@ function summarizeAnthropicContent(content) {
|
|
|
9222
9479
|
return content.map((part) => {
|
|
9223
9480
|
if (part.type === "text")
|
|
9224
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
|
+
}
|
|
9225
9508
|
if (part.type === "image")
|
|
9226
9509
|
return "[Image attached]";
|
|
9227
9510
|
return "";
|
|
@@ -9233,18 +9516,89 @@ function translateOpenAiToAnthropic(body) {
|
|
|
9233
9516
|
return null;
|
|
9234
9517
|
const systemParts = [];
|
|
9235
9518
|
const turns = [];
|
|
9519
|
+
const tools = [];
|
|
9236
9520
|
for (const msg of messages) {
|
|
9237
9521
|
const text = extractOpenAiContent(msg.content ?? "");
|
|
9238
9522
|
if (msg.role === "system") {
|
|
9239
9523
|
if (text)
|
|
9240
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
|
+
});
|
|
9241
9581
|
} else {
|
|
9242
9582
|
turns.push({
|
|
9243
|
-
role:
|
|
9583
|
+
role: "user",
|
|
9244
9584
|
content: translateOpenAiContentToAnthropic(msg.content ?? "")
|
|
9245
9585
|
});
|
|
9246
9586
|
}
|
|
9247
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
|
+
}
|
|
9248
9602
|
let systemPrompt = systemParts.join(`
|
|
9249
9603
|
`);
|
|
9250
9604
|
let messagesToSend = turns;
|
|
@@ -9265,6 +9619,7 @@ ${historyBlock}` : historyBlock;
|
|
|
9265
9619
|
model: body.model ?? "claude-sonnet-4-6",
|
|
9266
9620
|
messages: messagesToSend,
|
|
9267
9621
|
max_tokens: body.max_tokens ?? body.max_completion_tokens ?? 8192,
|
|
9622
|
+
tools,
|
|
9268
9623
|
stream: body.stream ?? false
|
|
9269
9624
|
};
|
|
9270
9625
|
if (systemPrompt)
|
|
@@ -9278,10 +9633,23 @@ ${historyBlock}` : historyBlock;
|
|
|
9278
9633
|
function toFinishReason(stopReason) {
|
|
9279
9634
|
if (stopReason === "max_tokens")
|
|
9280
9635
|
return "length";
|
|
9636
|
+
else if (stopReason === "tool_use")
|
|
9637
|
+
return "tool_calls";
|
|
9281
9638
|
return "stop";
|
|
9282
9639
|
}
|
|
9283
|
-
function translateAnthropicToOpenAi(response, completionId, model, created) {
|
|
9284
|
-
const
|
|
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("") : "";
|
|
9285
9653
|
const promptTokens = response.usage?.input_tokens ?? 0;
|
|
9286
9654
|
const completionTokens = response.usage?.output_tokens ?? 0;
|
|
9287
9655
|
return {
|
|
@@ -9291,7 +9659,12 @@ function translateAnthropicToOpenAi(response, completionId, model, created) {
|
|
|
9291
9659
|
model,
|
|
9292
9660
|
choices: [{
|
|
9293
9661
|
index: 0,
|
|
9294
|
-
message: {
|
|
9662
|
+
message: {
|
|
9663
|
+
role: "assistant",
|
|
9664
|
+
content: content || null,
|
|
9665
|
+
reasoning_content: thinking.length ? thinking : undefined,
|
|
9666
|
+
tool_calls: toolCalls.length ? toolCalls : undefined
|
|
9667
|
+
},
|
|
9295
9668
|
finish_reason: toFinishReason(response.stop_reason)
|
|
9296
9669
|
}],
|
|
9297
9670
|
usage: {
|
|
@@ -9301,7 +9674,16 @@ function translateAnthropicToOpenAi(response, completionId, model, created) {
|
|
|
9301
9674
|
}
|
|
9302
9675
|
};
|
|
9303
9676
|
}
|
|
9304
|
-
function
|
|
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) {
|
|
9305
9687
|
if (event.type === "message_start") {
|
|
9306
9688
|
return {
|
|
9307
9689
|
id: completionId,
|
|
@@ -9320,6 +9702,69 @@ function translateAnthropicSseEvent(event, completionId, model, created) {
|
|
|
9320
9702
|
choices: [{ index: 0, delta: { content: event.delta.text }, finish_reason: null }]
|
|
9321
9703
|
};
|
|
9322
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
|
+
}
|
|
9323
9768
|
if (event.type === "message_delta" && event.delta?.stop_reason) {
|
|
9324
9769
|
return {
|
|
9325
9770
|
id: completionId,
|
|
@@ -9636,7 +10081,11 @@ var CLAUDE_CODE_ONLY_TOOLS = [
|
|
|
9636
10081
|
"ExitPlanMode",
|
|
9637
10082
|
"EnterWorktree",
|
|
9638
10083
|
"ExitWorktree",
|
|
10084
|
+
"Monitor",
|
|
9639
10085
|
"NotebookEdit",
|
|
10086
|
+
"PushNotification",
|
|
10087
|
+
"RemoteTrigger",
|
|
10088
|
+
"ScheduleWakeup",
|
|
9640
10089
|
"TodoWrite",
|
|
9641
10090
|
"AskUserQuestion",
|
|
9642
10091
|
"Skill",
|
|
@@ -9671,8 +10120,8 @@ var openCodeTransforms = [
|
|
|
9671
10120
|
let sdkAgents = {};
|
|
9672
10121
|
if (Array.isArray(body.tools)) {
|
|
9673
10122
|
const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
|
|
9674
|
-
if (taskTool
|
|
9675
|
-
sdkAgents =
|
|
10123
|
+
if (taskTool) {
|
|
10124
|
+
sdkAgents = buildAgentDefinitionsFromTool(taskTool, [...allowedMcpTools]);
|
|
9676
10125
|
}
|
|
9677
10126
|
}
|
|
9678
10127
|
let sdkHooks = undefined;
|
|
@@ -9778,9 +10227,9 @@ var openCodeAdapter = {
|
|
|
9778
10227
|
if (!Array.isArray(body.tools))
|
|
9779
10228
|
return {};
|
|
9780
10229
|
const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
|
|
9781
|
-
if (!taskTool
|
|
10230
|
+
if (!taskTool)
|
|
9782
10231
|
return {};
|
|
9783
|
-
return
|
|
10232
|
+
return buildAgentDefinitionsFromTool(taskTool, [...mcpToolNames]);
|
|
9784
10233
|
},
|
|
9785
10234
|
buildSdkHooks(body, sdkAgents) {
|
|
9786
10235
|
const validAgentNames = Object.keys(sdkAgents);
|
|
@@ -11938,7 +12387,7 @@ var emitWarning = (msg, type, code, fn) => {
|
|
|
11938
12387
|
var AC = globalThis.AbortController;
|
|
11939
12388
|
var AS = globalThis.AbortSignal;
|
|
11940
12389
|
if (typeof AC === "undefined") {
|
|
11941
|
-
AS = class
|
|
12390
|
+
AS = class AbortSignal2 {
|
|
11942
12391
|
onabort;
|
|
11943
12392
|
_onabort = [];
|
|
11944
12393
|
reason;
|
|
@@ -16178,7 +16627,7 @@ function createOpencodeMcpServer() {
|
|
|
16178
16627
|
// src/proxy/query.ts
|
|
16179
16628
|
function computePassthroughMaxTurns(resumeSessionId, hasDeferredTools, advisorModel) {
|
|
16180
16629
|
const hasResume = !!resumeSessionId;
|
|
16181
|
-
const base = hasResume && hasDeferredTools ? 4 :
|
|
16630
|
+
const base = hasResume && hasDeferredTools ? 4 : 3;
|
|
16182
16631
|
const advisorBump = advisorModel ? 3 : 0;
|
|
16183
16632
|
return base + advisorBump;
|
|
16184
16633
|
}
|
|
@@ -17674,7 +18123,7 @@ function createProxyServer(config = {}) {
|
|
|
17674
18123
|
});
|
|
17675
18124
|
return {
|
|
17676
18125
|
decision: "block",
|
|
17677
|
-
reason: "
|
|
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."
|
|
17678
18127
|
};
|
|
17679
18128
|
}]
|
|
17680
18129
|
}]
|
|
@@ -17749,6 +18198,9 @@ function createProxyServer(config = {}) {
|
|
|
17749
18198
|
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
|
|
17750
18199
|
advisorModel
|
|
17751
18200
|
}))) {
|
|
18201
|
+
if (event.type === "rate_limit_event") {
|
|
18202
|
+
rateLimitStore.record(event.rate_limit_info);
|
|
18203
|
+
}
|
|
17752
18204
|
if (event.type === "assistant" && !event.error) {
|
|
17753
18205
|
didYieldContent = true;
|
|
17754
18206
|
}
|
|
@@ -18092,6 +18544,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18092
18544
|
sdkUuidMap.push(null);
|
|
18093
18545
|
let messageStartEmitted = false;
|
|
18094
18546
|
let lastUsage;
|
|
18547
|
+
const streamedToolUseIds = new Set;
|
|
18095
18548
|
try {
|
|
18096
18549
|
let currentSessionId;
|
|
18097
18550
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
@@ -18140,6 +18593,9 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18140
18593
|
additionalDirectories: sdkFeatures.additionalDirectories ? sdkFeatures.additionalDirectories.split(",").map((d) => d.trim()).filter(Boolean) : undefined,
|
|
18141
18594
|
advisorModel
|
|
18142
18595
|
}))) {
|
|
18596
|
+
if (event.type === "rate_limit_event") {
|
|
18597
|
+
rateLimitStore.record(event.rate_limit_info);
|
|
18598
|
+
}
|
|
18143
18599
|
if (event.type === "stream_event") {
|
|
18144
18600
|
didYieldClientEvent = true;
|
|
18145
18601
|
}
|
|
@@ -18279,7 +18735,6 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18279
18735
|
const skipBlockIndices = new Set;
|
|
18280
18736
|
const taskToolBlockIndices = new Set;
|
|
18281
18737
|
const taskToolJsonBuffer = new Map;
|
|
18282
|
-
const streamedToolUseIds = new Set;
|
|
18283
18738
|
let nextClientBlockIndex = 0;
|
|
18284
18739
|
const sdkToClientIndex = new Map;
|
|
18285
18740
|
try {
|
|
@@ -18642,6 +19097,127 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18642
19097
|
});
|
|
18643
19098
|
const streamErr = classifyError(errMsg);
|
|
18644
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
|
+
});
|
|
18645
19221
|
if (messageStartEmitted) {
|
|
18646
19222
|
safeEnqueue(encoder.encode(`event: message_delta
|
|
18647
19223
|
data: ${JSON.stringify({
|
|
@@ -18689,6 +19265,10 @@ data: ${JSON.stringify({
|
|
|
18689
19265
|
});
|
|
18690
19266
|
const classified = classifyError(errMsg);
|
|
18691
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);
|
|
18692
19272
|
const errorQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
18693
19273
|
telemetryStore2.record({
|
|
18694
19274
|
requestId: requestMeta.requestId,
|
|
@@ -18830,7 +19410,7 @@ data: ${JSON.stringify({
|
|
|
18830
19410
|
});
|
|
18831
19411
|
});
|
|
18832
19412
|
app.get("/profiles", async (c) => {
|
|
18833
|
-
const { profilePageHtml } = await import("./profilePage-
|
|
19413
|
+
const { profilePageHtml } = await import("./profilePage-k0faye28.js");
|
|
18834
19414
|
return c.html(profilePageHtml);
|
|
18835
19415
|
});
|
|
18836
19416
|
app.post("/profiles/active", async (c) => {
|
|
@@ -18852,7 +19432,8 @@ data: ${JSON.stringify({
|
|
|
18852
19432
|
}
|
|
18853
19433
|
setActiveProfile(body.profile);
|
|
18854
19434
|
clearSessionCache();
|
|
18855
|
-
|
|
19435
|
+
rateLimitStore.clear();
|
|
19436
|
+
console.error(`[PROXY] Active profile switched to: ${body.profile} (session + rate-limit caches cleared)`);
|
|
18856
19437
|
return c.json({ success: true, activeProfile: body.profile });
|
|
18857
19438
|
});
|
|
18858
19439
|
app.get("/plugins/list", async (c) => {
|
|
@@ -18897,6 +19478,7 @@ data: ${JSON.stringify({
|
|
|
18897
19478
|
app.post("/auth/refresh", async (c) => {
|
|
18898
19479
|
const success = await refreshOAuthToken();
|
|
18899
19480
|
if (success) {
|
|
19481
|
+
rateLimitStore.clear();
|
|
18900
19482
|
return c.json({ success: true, message: "OAuth token refreshed successfully" });
|
|
18901
19483
|
}
|
|
18902
19484
|
return c.json({ success: false, message: "Token refresh failed. If the problem persists, run 'claude login'." }, 500);
|
|
@@ -18920,9 +19502,14 @@ data: ${JSON.stringify({
|
|
|
18920
19502
|
const completionId = `chatcmpl-${randomUUID()}`;
|
|
18921
19503
|
const created = Math.floor(Date.now() / 1000);
|
|
18922
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);
|
|
18923
19508
|
if (!anthropicBody.stream) {
|
|
18924
19509
|
const anthropicRes = await internalRes.json();
|
|
18925
|
-
return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created
|
|
19510
|
+
return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
|
|
19511
|
+
thinkingPassthrough: sdkFeatures.thinkingPassthrough
|
|
19512
|
+
}));
|
|
18926
19513
|
}
|
|
18927
19514
|
const encoder = new TextEncoder;
|
|
18928
19515
|
const readable = new ReadableStream({
|
|
@@ -18935,6 +19522,7 @@ data: ${JSON.stringify({
|
|
|
18935
19522
|
const decoder = new TextDecoder;
|
|
18936
19523
|
let buffer = "";
|
|
18937
19524
|
let streamError = null;
|
|
19525
|
+
const translate = createSseTranslator({ completionId, model, created, thinkingPassthrough: sdkFeatures.thinkingPassthrough });
|
|
18938
19526
|
try {
|
|
18939
19527
|
while (true) {
|
|
18940
19528
|
const { done, value } = await reader.read();
|
|
@@ -18958,7 +19546,7 @@ data: ${JSON.stringify({
|
|
|
18958
19546
|
}
|
|
18959
19547
|
if (typeof event.type !== "string")
|
|
18960
19548
|
continue;
|
|
18961
|
-
const chunk =
|
|
19549
|
+
const chunk = translate(event);
|
|
18962
19550
|
if (chunk)
|
|
18963
19551
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
|
|
18964
19552
|
|
|
@@ -18989,6 +19577,112 @@ data: ${JSON.stringify({
|
|
|
18989
19577
|
const isMax = authStatus?.subscriptionType === "max";
|
|
18990
19578
|
return c.json({ object: "list", data: buildModelList(isMax) });
|
|
18991
19579
|
});
|
|
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,
|
|
19595
|
+
status: entry.status,
|
|
19596
|
+
utilization: entry.utilization ?? null,
|
|
19597
|
+
resetsAt: entry.resetsAt ?? null,
|
|
19598
|
+
isUsingOverage: entry.isUsingOverage ?? false,
|
|
19599
|
+
overageStatus: entry.overageStatus ?? null,
|
|
19600
|
+
overageResetsAt: entry.overageResetsAt ?? null,
|
|
19601
|
+
overageDisabledReason: entry.overageDisabledReason ?? null,
|
|
19602
|
+
surpassedThreshold: entry.surpassedThreshold ?? null,
|
|
19603
|
+
observedAt: entry.observedAt
|
|
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,
|
|
19683
|
+
asOf: Date.now()
|
|
19684
|
+
});
|
|
19685
|
+
});
|
|
18992
19686
|
app.get("/v1/sessions/:claudeSessionId/context-usage", (c) => {
|
|
18993
19687
|
const claudeSessionId = c.req.param("claudeSessionId");
|
|
18994
19688
|
const session = getSessionByClaudeId(claudeSessionId);
|