@rynfar/meridian 1.40.0 → 1.41.1
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-3azh7s3k.js → cli-51a9sav0.js} +811 -93
- package/dist/{cli-m9pfb7h9.js → cli-e289rj3k.js} +79 -46
- package/dist/cli.js +3 -3
- package/dist/{profilePage-77z05e0r.js → profilePage-k0faye28.js} +196 -4
- package/dist/proxy/adapters/droid.d.ts.map +1 -1
- 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/models.d.ts +26 -0
- package/dist/proxy/models.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/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/tokenRefresh.d.ts +33 -1
- package/dist/proxy/tokenRefresh.d.ts.map +1 -1
- package/dist/proxy/tools.d.ts.map +1 -1
- package/dist/proxy/transforms/droid.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-psq94r54.js +19 -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-e289rj3k.js";
|
|
31
32
|
import {
|
|
32
33
|
__commonJS,
|
|
33
34
|
__esm,
|
|
@@ -1178,13 +1179,13 @@ __export(exports_sdkFeatures, {
|
|
|
1178
1179
|
getAllFeatureConfigs: () => getAllFeatureConfigs
|
|
1179
1180
|
});
|
|
1180
1181
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync2 } from "node:fs";
|
|
1181
|
-
import { join as
|
|
1182
|
-
import { homedir as
|
|
1182
|
+
import { join as join5 } from "node:path";
|
|
1183
|
+
import { homedir as homedir3 } from "node:os";
|
|
1183
1184
|
function getConfigPath() {
|
|
1184
|
-
const dir =
|
|
1185
|
+
const dir = join5(homedir3(), ".config", "meridian");
|
|
1185
1186
|
if (!existsSync5(dir))
|
|
1186
1187
|
mkdirSync2(dir, { recursive: true });
|
|
1187
|
-
return
|
|
1188
|
+
return join5(dir, "sdk-features.json");
|
|
1188
1189
|
}
|
|
1189
1190
|
function readConfig() {
|
|
1190
1191
|
const now = Date.now();
|
|
@@ -1282,7 +1283,7 @@ function resetAdapterFeatures(adapterName) {
|
|
|
1282
1283
|
var DEFAULT_FEATURES, ADAPTER_DEFAULTS, cachedConfig = null, lastReadTime = 0, CACHE_TTL_MS = 5000, VALID_CLAUDE_MD_VALUES, VALID_THINKING_VALUES;
|
|
1283
1284
|
var init_sdkFeatures = __esm(() => {
|
|
1284
1285
|
DEFAULT_FEATURES = {
|
|
1285
|
-
codeSystemPrompt:
|
|
1286
|
+
codeSystemPrompt: true,
|
|
1286
1287
|
clientSystemPrompt: true,
|
|
1287
1288
|
claudeMd: "off",
|
|
1288
1289
|
memory: false,
|
|
@@ -1295,7 +1296,11 @@ var init_sdkFeatures = __esm(() => {
|
|
|
1295
1296
|
sdkDebug: false,
|
|
1296
1297
|
additionalDirectories: ""
|
|
1297
1298
|
};
|
|
1298
|
-
ADAPTER_DEFAULTS = {
|
|
1299
|
+
ADAPTER_DEFAULTS = {
|
|
1300
|
+
passthrough: {
|
|
1301
|
+
codeSystemPrompt: false
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1299
1304
|
VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
|
|
1300
1305
|
VALID_THINKING_VALUES = new Set(["adaptive", "enabled", "disabled"]);
|
|
1301
1306
|
});
|
|
@@ -3702,8 +3707,8 @@ var serve = (options, listeningListener) => {
|
|
|
3702
3707
|
};
|
|
3703
3708
|
|
|
3704
3709
|
// src/proxy/server.ts
|
|
3705
|
-
import { homedir as
|
|
3706
|
-
import { join as
|
|
3710
|
+
import { homedir as homedir4 } from "node:os";
|
|
3711
|
+
import { join as join6 } from "node:path";
|
|
3707
3712
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3708
3713
|
|
|
3709
3714
|
// src/proxy/rateLimitStore.ts
|
|
@@ -3730,6 +3735,120 @@ class RateLimitStore {
|
|
|
3730
3735
|
}
|
|
3731
3736
|
var rateLimitStore = new RateLimitStore;
|
|
3732
3737
|
|
|
3738
|
+
// src/proxy/oauthUsage.ts
|
|
3739
|
+
var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
3740
|
+
var OAUTH_BETA_HEADER = "oauth-2025-04-20";
|
|
3741
|
+
var CACHE_TTL_MS_DEFAULT = 30000;
|
|
3742
|
+
var cacheByProfile = new Map;
|
|
3743
|
+
var inflightByProfile = new Map;
|
|
3744
|
+
var DEFAULT_KEY = "__default__";
|
|
3745
|
+
var WINDOW_TYPES = [
|
|
3746
|
+
"five_hour",
|
|
3747
|
+
"seven_day",
|
|
3748
|
+
"seven_day_opus",
|
|
3749
|
+
"seven_day_sonnet",
|
|
3750
|
+
"seven_day_oauth_apps",
|
|
3751
|
+
"seven_day_cowork",
|
|
3752
|
+
"seven_day_omelette"
|
|
3753
|
+
];
|
|
3754
|
+
function parseIsoToMs(raw2) {
|
|
3755
|
+
if (!raw2)
|
|
3756
|
+
return null;
|
|
3757
|
+
const ms = Date.parse(raw2);
|
|
3758
|
+
return Number.isFinite(ms) ? ms : null;
|
|
3759
|
+
}
|
|
3760
|
+
function normalizeUtilization(raw2) {
|
|
3761
|
+
if (typeof raw2 !== "number" || !Number.isFinite(raw2))
|
|
3762
|
+
return null;
|
|
3763
|
+
return Math.max(0, raw2 / 100);
|
|
3764
|
+
}
|
|
3765
|
+
function buildSnapshot(raw2) {
|
|
3766
|
+
const windows = [];
|
|
3767
|
+
for (const key of WINDOW_TYPES) {
|
|
3768
|
+
const w = raw2[key];
|
|
3769
|
+
if (!w)
|
|
3770
|
+
continue;
|
|
3771
|
+
const utilization = normalizeUtilization(w.utilization);
|
|
3772
|
+
const resetsAt = parseIsoToMs(w.resets_at);
|
|
3773
|
+
if (utilization === null && resetsAt === null)
|
|
3774
|
+
continue;
|
|
3775
|
+
windows.push({ type: key, utilization, resetsAt });
|
|
3776
|
+
}
|
|
3777
|
+
const extra = raw2.extra_usage;
|
|
3778
|
+
const extraUsage = extra ? {
|
|
3779
|
+
isEnabled: !!extra.is_enabled,
|
|
3780
|
+
monthlyLimit: extra.monthly_limit ?? 0,
|
|
3781
|
+
usedCredits: extra.used_credits ?? 0,
|
|
3782
|
+
utilization: normalizeUtilization(extra.utilization ?? null),
|
|
3783
|
+
currency: extra.currency ?? "USD"
|
|
3784
|
+
} : null;
|
|
3785
|
+
return { windows, extraUsage, fetchedAt: Date.now() };
|
|
3786
|
+
}
|
|
3787
|
+
async function readAccessToken(store) {
|
|
3788
|
+
const creds = await store.read();
|
|
3789
|
+
return creds?.claudeAiOauth?.accessToken ?? null;
|
|
3790
|
+
}
|
|
3791
|
+
async function callAnthropic(token, signal) {
|
|
3792
|
+
const res = await fetch(OAUTH_USAGE_URL, {
|
|
3793
|
+
headers: {
|
|
3794
|
+
Authorization: `Bearer ${token}`,
|
|
3795
|
+
"anthropic-beta": OAUTH_BETA_HEADER,
|
|
3796
|
+
Accept: "application/json"
|
|
3797
|
+
},
|
|
3798
|
+
signal: signal ?? AbortSignal.timeout(1e4)
|
|
3799
|
+
});
|
|
3800
|
+
if (!res.ok)
|
|
3801
|
+
return { __status: res.status };
|
|
3802
|
+
return await res.json();
|
|
3803
|
+
}
|
|
3804
|
+
async function fetchOAuthUsage(opts) {
|
|
3805
|
+
const ttl = opts?.ttlMs ?? CACHE_TTL_MS_DEFAULT;
|
|
3806
|
+
const cacheKey2 = opts?.profileId ?? DEFAULT_KEY;
|
|
3807
|
+
if (!opts?.force) {
|
|
3808
|
+
const cached = cacheByProfile.get(cacheKey2);
|
|
3809
|
+
if (cached && Date.now() - cached.fetchedAt < ttl)
|
|
3810
|
+
return cached;
|
|
3811
|
+
}
|
|
3812
|
+
const existing = inflightByProfile.get(cacheKey2);
|
|
3813
|
+
if (existing)
|
|
3814
|
+
return existing;
|
|
3815
|
+
const store = opts?.store ?? createPlatformCredentialStore({ claudeConfigDir: opts?.claudeConfigDir });
|
|
3816
|
+
const promise = (async () => {
|
|
3817
|
+
try {
|
|
3818
|
+
const token = await readAccessToken(store);
|
|
3819
|
+
if (!token)
|
|
3820
|
+
return null;
|
|
3821
|
+
let result = await callAnthropic(token);
|
|
3822
|
+
if ("__status" in result && result.__status === 401) {
|
|
3823
|
+
claudeLog("oauth_usage.token_refresh_attempt", { profile: cacheKey2 });
|
|
3824
|
+
const refreshed = await refreshOAuthToken(store);
|
|
3825
|
+
if (!refreshed) {
|
|
3826
|
+
claudeLog("oauth_usage.refresh_failed", { profile: cacheKey2 });
|
|
3827
|
+
return null;
|
|
3828
|
+
}
|
|
3829
|
+
const newToken = await readAccessToken(store);
|
|
3830
|
+
if (!newToken)
|
|
3831
|
+
return null;
|
|
3832
|
+
result = await callAnthropic(newToken);
|
|
3833
|
+
}
|
|
3834
|
+
if ("__status" in result) {
|
|
3835
|
+
claudeLog("oauth_usage.upstream_error", { profile: cacheKey2, status: result.__status });
|
|
3836
|
+
return null;
|
|
3837
|
+
}
|
|
3838
|
+
const snapshot = buildSnapshot(result);
|
|
3839
|
+
cacheByProfile.set(cacheKey2, snapshot);
|
|
3840
|
+
return snapshot;
|
|
3841
|
+
} catch (err) {
|
|
3842
|
+
claudeLog("oauth_usage.fetch_failed", { profile: cacheKey2, error: err instanceof Error ? err.message : String(err) });
|
|
3843
|
+
return null;
|
|
3844
|
+
} finally {
|
|
3845
|
+
inflightByProfile.delete(cacheKey2);
|
|
3846
|
+
}
|
|
3847
|
+
})();
|
|
3848
|
+
inflightByProfile.set(cacheKey2, promise);
|
|
3849
|
+
return promise;
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3733
3852
|
// src/proxy/types.ts
|
|
3734
3853
|
var DEFAULT_PROXY_CONFIG = {
|
|
3735
3854
|
port: 3456,
|
|
@@ -7993,13 +8112,20 @@ function ensureDefaultAgents(agents, mcpToolNames) {
|
|
|
7993
8112
|
}
|
|
7994
8113
|
}
|
|
7995
8114
|
}
|
|
8115
|
+
function cloneAgentDefinition(def) {
|
|
8116
|
+
return {
|
|
8117
|
+
...def,
|
|
8118
|
+
...def.tools ? { tools: [...def.tools] } : {},
|
|
8119
|
+
...def.disallowedTools ? { disallowedTools: [...def.disallowedTools] } : {}
|
|
8120
|
+
};
|
|
8121
|
+
}
|
|
7996
8122
|
function addCaseVariants(agents) {
|
|
7997
8123
|
const baseNames = Object.keys(agents);
|
|
7998
8124
|
for (const name of baseNames) {
|
|
7999
8125
|
const def = agents[name];
|
|
8000
8126
|
const titleCase = name.replace(/(^|-)(\w)/g, (_m, sep, ch) => sep + ch.toUpperCase());
|
|
8001
8127
|
if (titleCase !== name && !agents[titleCase]) {
|
|
8002
|
-
agents[titleCase] = def;
|
|
8128
|
+
agents[titleCase] = cloneAgentDefinition(def);
|
|
8003
8129
|
}
|
|
8004
8130
|
}
|
|
8005
8131
|
const ALIASES = {
|
|
@@ -8008,10 +8134,50 @@ function addCaseVariants(agents) {
|
|
|
8008
8134
|
};
|
|
8009
8135
|
for (const [alias, target] of Object.entries(ALIASES)) {
|
|
8010
8136
|
if (!agents[alias] && agents[target]) {
|
|
8011
|
-
agents[alias] = agents[target];
|
|
8137
|
+
agents[alias] = cloneAgentDefinition(agents[target]);
|
|
8012
8138
|
}
|
|
8013
8139
|
}
|
|
8014
8140
|
}
|
|
8141
|
+
function getNested(obj, ...keys) {
|
|
8142
|
+
let cur = obj;
|
|
8143
|
+
for (const key of keys) {
|
|
8144
|
+
if (cur === null || typeof cur !== "object")
|
|
8145
|
+
return;
|
|
8146
|
+
cur = cur[key];
|
|
8147
|
+
}
|
|
8148
|
+
return cur;
|
|
8149
|
+
}
|
|
8150
|
+
function parseAgentNamesFromSchema(taskTool) {
|
|
8151
|
+
const enumNames = getNested(taskTool, "input_schema", "properties", "subagent_type", "enum");
|
|
8152
|
+
if (!Array.isArray(enumNames))
|
|
8153
|
+
return [];
|
|
8154
|
+
return enumNames.filter((n) => typeof n === "string");
|
|
8155
|
+
}
|
|
8156
|
+
function buildAgentDefinitionsFromTool(taskTool, mcpToolNames) {
|
|
8157
|
+
const rawDescription = getNested(taskTool, "description");
|
|
8158
|
+
const description = typeof rawDescription === "string" ? rawDescription : "";
|
|
8159
|
+
const fromDescription = buildAgentDefinitions(description, mcpToolNames);
|
|
8160
|
+
if (Object.keys(fromDescription).length > 0)
|
|
8161
|
+
return fromDescription;
|
|
8162
|
+
const names = parseAgentNamesFromSchema(taskTool);
|
|
8163
|
+
if (names.length === 0)
|
|
8164
|
+
return {};
|
|
8165
|
+
const agents = {};
|
|
8166
|
+
for (const name of names) {
|
|
8167
|
+
if (agents[name])
|
|
8168
|
+
continue;
|
|
8169
|
+
const desc = `User-defined agent: ${name}`;
|
|
8170
|
+
agents[name] = {
|
|
8171
|
+
description: desc,
|
|
8172
|
+
prompt: buildAgentPrompt(name, desc),
|
|
8173
|
+
model: "inherit",
|
|
8174
|
+
...mcpToolNames?.length ? { tools: [...mcpToolNames] } : {}
|
|
8175
|
+
};
|
|
8176
|
+
}
|
|
8177
|
+
ensureDefaultAgents(agents, mcpToolNames);
|
|
8178
|
+
addCaseVariants(agents);
|
|
8179
|
+
return agents;
|
|
8180
|
+
}
|
|
8015
8181
|
function buildAgentPrompt(name, description) {
|
|
8016
8182
|
return `You are the "${name}" agent. ${description}
|
|
8017
8183
|
|
|
@@ -8388,8 +8554,6 @@ let timer;
|
|
|
8388
8554
|
let activeTab = 'requests';
|
|
8389
8555
|
let activeLogFilter = 'all';
|
|
8390
8556
|
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
8557
|
function ms(v) {
|
|
8394
8558
|
if (v == null) return '—';
|
|
8395
8559
|
if (v < 1000) return v + 'ms';
|
|
@@ -8991,14 +9155,91 @@ function isExtraUsageRequiredError(errMsg) {
|
|
|
8991
9155
|
const lower = errMsg.toLowerCase();
|
|
8992
9156
|
return lower.includes("extra usage") && lower.includes("1m");
|
|
8993
9157
|
}
|
|
9158
|
+
var STDERR_TAIL_MAX = 500;
|
|
9159
|
+
var RAW_TAIL_MAX = 300;
|
|
9160
|
+
function extractStderrTail(errMsg) {
|
|
9161
|
+
const marker = "Subprocess stderr:";
|
|
9162
|
+
const idx = errMsg.indexOf(marker);
|
|
9163
|
+
if (idx < 0)
|
|
9164
|
+
return;
|
|
9165
|
+
const tail = errMsg.slice(idx + marker.length).trim();
|
|
9166
|
+
if (!tail)
|
|
9167
|
+
return;
|
|
9168
|
+
return tail.length > STDERR_TAIL_MAX ? tail.slice(0, STDERR_TAIL_MAX) : tail;
|
|
9169
|
+
}
|
|
9170
|
+
function makeRawTail(errMsg) {
|
|
9171
|
+
const marker = "Subprocess stderr:";
|
|
9172
|
+
const idx = errMsg.indexOf(marker);
|
|
9173
|
+
const head = (idx >= 0 ? errMsg.slice(0, idx) : errMsg).trim();
|
|
9174
|
+
if (!head)
|
|
9175
|
+
return;
|
|
9176
|
+
return head.length > RAW_TAIL_MAX ? head.slice(0, RAW_TAIL_MAX) : head;
|
|
9177
|
+
}
|
|
9178
|
+
function extractSdkTermination(errMsg) {
|
|
9179
|
+
const stderrTail = extractStderrTail(errMsg);
|
|
9180
|
+
const haystack = `${errMsg}
|
|
9181
|
+
${stderrTail ?? ""}`;
|
|
9182
|
+
const lower = haystack.toLowerCase();
|
|
9183
|
+
if (lower.includes("reached maximum number of turns")) {
|
|
9184
|
+
const m = haystack.match(/Reached maximum number of turns \((\d+)\)/i);
|
|
9185
|
+
return {
|
|
9186
|
+
reason: "max_turns",
|
|
9187
|
+
...m ? { turns: Number(m[1]) } : {},
|
|
9188
|
+
...stderrTail ? { stderrTail } : {}
|
|
9189
|
+
};
|
|
9190
|
+
}
|
|
9191
|
+
if (lower.includes("exited with code") || lower.includes("process exited")) {
|
|
9192
|
+
const m = haystack.match(/exited with code (\d+)/i);
|
|
9193
|
+
return {
|
|
9194
|
+
reason: "process_exit",
|
|
9195
|
+
...m ? { exitCode: Number(m[1]) } : {},
|
|
9196
|
+
...stderrTail ? { stderrTail } : {}
|
|
9197
|
+
};
|
|
9198
|
+
}
|
|
9199
|
+
if (lower.includes("aborterror") || /\baborted\b/.test(lower)) {
|
|
9200
|
+
return {
|
|
9201
|
+
reason: "aborted",
|
|
9202
|
+
...stderrTail ? { stderrTail } : {}
|
|
9203
|
+
};
|
|
9204
|
+
}
|
|
9205
|
+
const rawTail = makeRawTail(errMsg);
|
|
9206
|
+
return {
|
|
9207
|
+
reason: "unknown",
|
|
9208
|
+
...stderrTail ? { stderrTail } : {},
|
|
9209
|
+
...rawTail ? { rawTail } : {}
|
|
9210
|
+
};
|
|
9211
|
+
}
|
|
9212
|
+
function formatSdkTermination(t, ctx) {
|
|
9213
|
+
const parts = [`reason=${t.reason}`];
|
|
9214
|
+
if (t.turns !== undefined)
|
|
9215
|
+
parts.push(`turns=${t.turns}`);
|
|
9216
|
+
if (t.exitCode !== undefined)
|
|
9217
|
+
parts.push(`exit=${t.exitCode}`);
|
|
9218
|
+
if (ctx.model)
|
|
9219
|
+
parts.push(`model=${ctx.model}`);
|
|
9220
|
+
if (ctx.requestSource)
|
|
9221
|
+
parts.push(`source=${ctx.requestSource}`);
|
|
9222
|
+
if (ctx.isResume !== undefined)
|
|
9223
|
+
parts.push(`resume=${ctx.isResume}`);
|
|
9224
|
+
if (ctx.hasDeferredTools !== undefined)
|
|
9225
|
+
parts.push(`deferred=${ctx.hasDeferredTools}`);
|
|
9226
|
+
if (ctx.sdkSessionId)
|
|
9227
|
+
parts.push(`session=${ctx.sdkSessionId.slice(0, 8)}`);
|
|
9228
|
+
if (t.rawTail)
|
|
9229
|
+
parts.push(`raw=${JSON.stringify(t.rawTail)}`);
|
|
9230
|
+
if (t.stderrTail)
|
|
9231
|
+
parts.push(`stderr=${JSON.stringify(t.stderrTail)}`);
|
|
9232
|
+
return `sdk_termination ${parts.join(" ")}`;
|
|
9233
|
+
}
|
|
8994
9234
|
|
|
8995
9235
|
// src/proxy/models.ts
|
|
8996
9236
|
import { exec as execCallback } from "child_process";
|
|
8997
|
-
import { existsSync as existsSync2 } from "fs";
|
|
9237
|
+
import { existsSync as existsSync2, statSync } from "fs";
|
|
8998
9238
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8999
9239
|
import { join as join2, dirname as dirname2 } from "path";
|
|
9000
9240
|
import { promisify } from "util";
|
|
9001
9241
|
var exec = promisify(execCallback);
|
|
9242
|
+
var STUB_SIZE_THRESHOLD = 4096;
|
|
9002
9243
|
var CANONICAL_OPUS_MODEL = "claude-opus-4-7";
|
|
9003
9244
|
var CANONICAL_SONNET_MODEL = "claude-sonnet-4-6";
|
|
9004
9245
|
var CANONICAL_HAIKU_MODEL = "claude-haiku-4-5";
|
|
@@ -9138,40 +9379,92 @@ async function getClaudeAuthStatusAsync(profileId, envOverrides) {
|
|
|
9138
9379
|
}
|
|
9139
9380
|
var cachedClaudePath = null;
|
|
9140
9381
|
var cachedClaudePathPromise = null;
|
|
9382
|
+
var DEFAULT_DEPS = {
|
|
9383
|
+
existsSync: existsSync2,
|
|
9384
|
+
statSync: (p) => statSync(p),
|
|
9385
|
+
exec,
|
|
9386
|
+
resolvePackage: (specifier) => fileURLToPath2(import.meta.resolve(specifier)),
|
|
9387
|
+
envGet: (name) => process.env[name],
|
|
9388
|
+
platform: process.platform,
|
|
9389
|
+
arch: process.arch,
|
|
9390
|
+
isBun: typeof process.versions.bun !== "undefined"
|
|
9391
|
+
};
|
|
9392
|
+
function tryEnvOverride(deps) {
|
|
9393
|
+
const explicit = deps.envGet("MERIDIAN_CLAUDE_PATH");
|
|
9394
|
+
if (!explicit)
|
|
9395
|
+
return null;
|
|
9396
|
+
return deps.existsSync(explicit) ? explicit : null;
|
|
9397
|
+
}
|
|
9398
|
+
function tryBundledBinary(deps) {
|
|
9399
|
+
try {
|
|
9400
|
+
const pkgPath = deps.resolvePackage("@anthropic-ai/claude-code/package.json");
|
|
9401
|
+
const bundled = join2(dirname2(pkgPath), "bin", "claude.exe");
|
|
9402
|
+
if (!deps.existsSync(bundled))
|
|
9403
|
+
return null;
|
|
9404
|
+
const size = deps.statSync(bundled).size;
|
|
9405
|
+
if (size <= STUB_SIZE_THRESHOLD)
|
|
9406
|
+
return null;
|
|
9407
|
+
return bundled;
|
|
9408
|
+
} catch {
|
|
9409
|
+
return null;
|
|
9410
|
+
}
|
|
9411
|
+
}
|
|
9412
|
+
function tryPlatformPackage(deps) {
|
|
9413
|
+
const binName = deps.platform === "win32" ? "claude.exe" : "claude";
|
|
9414
|
+
const candidates = [`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}`];
|
|
9415
|
+
if (deps.platform === "linux") {
|
|
9416
|
+
candidates.push(`@anthropic-ai/claude-code-${deps.platform}-${deps.arch}-musl`);
|
|
9417
|
+
}
|
|
9418
|
+
for (const pkg of candidates) {
|
|
9419
|
+
try {
|
|
9420
|
+
const pkgJson = deps.resolvePackage(`${pkg}/package.json`);
|
|
9421
|
+
const candidate = join2(dirname2(pkgJson), binName);
|
|
9422
|
+
if (deps.existsSync(candidate))
|
|
9423
|
+
return candidate;
|
|
9424
|
+
} catch {}
|
|
9425
|
+
}
|
|
9426
|
+
return null;
|
|
9427
|
+
}
|
|
9428
|
+
async function tryPathLookup(deps) {
|
|
9429
|
+
const cmd = deps.platform === "win32" ? "where claude" : "which claude";
|
|
9430
|
+
try {
|
|
9431
|
+
const { stdout } = await deps.exec(cmd);
|
|
9432
|
+
const candidates = stdout.split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
9433
|
+
for (const candidate of candidates) {
|
|
9434
|
+
if (deps.platform === "win32" && candidate.startsWith("/"))
|
|
9435
|
+
continue;
|
|
9436
|
+
if (deps.existsSync(candidate))
|
|
9437
|
+
return candidate;
|
|
9438
|
+
}
|
|
9439
|
+
} catch {}
|
|
9440
|
+
return null;
|
|
9441
|
+
}
|
|
9442
|
+
function tryLegacySdkCliJs(deps) {
|
|
9443
|
+
if (!deps.isBun)
|
|
9444
|
+
return null;
|
|
9445
|
+
try {
|
|
9446
|
+
const sdkPath = deps.resolvePackage("@anthropic-ai/claude-agent-sdk");
|
|
9447
|
+
const cliJs = join2(dirname2(sdkPath), "cli.js");
|
|
9448
|
+
return deps.existsSync(cliJs) ? cliJs : null;
|
|
9449
|
+
} catch {
|
|
9450
|
+
return null;
|
|
9451
|
+
}
|
|
9452
|
+
}
|
|
9453
|
+
async function resolveClaudeExecutable(deps = DEFAULT_DEPS) {
|
|
9454
|
+
return tryEnvOverride(deps) ?? tryBundledBinary(deps) ?? tryPlatformPackage(deps) ?? await tryPathLookup(deps) ?? tryLegacySdkCliJs(deps);
|
|
9455
|
+
}
|
|
9141
9456
|
async function resolveClaudeExecutableAsync() {
|
|
9142
9457
|
if (cachedClaudePath)
|
|
9143
9458
|
return cachedClaudePath;
|
|
9144
9459
|
if (cachedClaudePathPromise)
|
|
9145
9460
|
return cachedClaudePathPromise;
|
|
9146
9461
|
cachedClaudePathPromise = (async () => {
|
|
9147
|
-
const
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
if (existsSync2(bundledBinary)) {
|
|
9152
|
-
cachedClaudePath = bundledBinary;
|
|
9153
|
-
return bundledBinary;
|
|
9154
|
-
}
|
|
9155
|
-
} catch {}
|
|
9156
|
-
try {
|
|
9157
|
-
const { stdout } = await exec("which claude");
|
|
9158
|
-
const claudePath = stdout.trim();
|
|
9159
|
-
if (claudePath && existsSync2(claudePath)) {
|
|
9160
|
-
cachedClaudePath = claudePath;
|
|
9161
|
-
return claudePath;
|
|
9162
|
-
}
|
|
9163
|
-
} catch {}
|
|
9164
|
-
if (runningUnderBun) {
|
|
9165
|
-
try {
|
|
9166
|
-
const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
|
|
9167
|
-
const sdkCliJs = join2(dirname2(sdkPath), "cli.js");
|
|
9168
|
-
if (existsSync2(sdkCliJs)) {
|
|
9169
|
-
cachedClaudePath = sdkCliJs;
|
|
9170
|
-
return sdkCliJs;
|
|
9171
|
-
}
|
|
9172
|
-
} catch {}
|
|
9462
|
+
const resolved = await resolveClaudeExecutable();
|
|
9463
|
+
if (resolved) {
|
|
9464
|
+
cachedClaudePath = resolved;
|
|
9465
|
+
return resolved;
|
|
9173
9466
|
}
|
|
9174
|
-
throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code");
|
|
9467
|
+
throw new Error("Could not find Claude Code executable. Install via: npm install -g @anthropic-ai/claude-code, " + "or set MERIDIAN_CLAUDE_PATH=/path/to/claude to point at an existing binary.");
|
|
9175
9468
|
})();
|
|
9176
9469
|
try {
|
|
9177
9470
|
return await cachedClaudePathPromise;
|
|
@@ -9216,7 +9509,7 @@ function parseDataUrlImage(url) {
|
|
|
9216
9509
|
}
|
|
9217
9510
|
function translateOpenAiContentToAnthropic(content) {
|
|
9218
9511
|
if (typeof content === "string")
|
|
9219
|
-
return content;
|
|
9512
|
+
return [{ type: "text", text: content }];
|
|
9220
9513
|
const parts = [];
|
|
9221
9514
|
for (const part of content) {
|
|
9222
9515
|
if (part.type === "text" && typeof part.text === "string") {
|
|
@@ -9235,9 +9528,6 @@ function translateOpenAiContentToAnthropic(content) {
|
|
|
9235
9528
|
parts.push({ type: "text", text: "[Unsupported image_url omitted: only data URLs are currently supported]" });
|
|
9236
9529
|
}
|
|
9237
9530
|
}
|
|
9238
|
-
if (parts.length === 1 && parts[0]?.type === "text") {
|
|
9239
|
-
return parts[0].text;
|
|
9240
|
-
}
|
|
9241
9531
|
return parts;
|
|
9242
9532
|
}
|
|
9243
9533
|
function summarizeAnthropicContent(content) {
|
|
@@ -9246,6 +9536,32 @@ function summarizeAnthropicContent(content) {
|
|
|
9246
9536
|
return content.map((part) => {
|
|
9247
9537
|
if (part.type === "text")
|
|
9248
9538
|
return part.text;
|
|
9539
|
+
if (part.type === "thinking")
|
|
9540
|
+
return `
|
|
9541
|
+
<think>
|
|
9542
|
+
` + part.thinking + `
|
|
9543
|
+
</think>
|
|
9544
|
+
`;
|
|
9545
|
+
if (part.type === "tool_use")
|
|
9546
|
+
return `
|
|
9547
|
+
<tool_call name="` + part.name + `">
|
|
9548
|
+
` + JSON.stringify(part.input) + `
|
|
9549
|
+
</tool_call>
|
|
9550
|
+
`;
|
|
9551
|
+
if (part.type === "tool_result") {
|
|
9552
|
+
if (typeof part.content === "string")
|
|
9553
|
+
return `
|
|
9554
|
+
<tool_result>
|
|
9555
|
+
` + part.content + `
|
|
9556
|
+
</tool_result>
|
|
9557
|
+
`;
|
|
9558
|
+
else
|
|
9559
|
+
return part.content.map((c) => c.type === "text" ? `
|
|
9560
|
+
<tool_result>
|
|
9561
|
+
${c.text}
|
|
9562
|
+
</tool_result>
|
|
9563
|
+
` : "").join("");
|
|
9564
|
+
}
|
|
9249
9565
|
if (part.type === "image")
|
|
9250
9566
|
return "[Image attached]";
|
|
9251
9567
|
return "";
|
|
@@ -9257,18 +9573,89 @@ function translateOpenAiToAnthropic(body) {
|
|
|
9257
9573
|
return null;
|
|
9258
9574
|
const systemParts = [];
|
|
9259
9575
|
const turns = [];
|
|
9576
|
+
const tools = [];
|
|
9260
9577
|
for (const msg of messages) {
|
|
9261
9578
|
const text = extractOpenAiContent(msg.content ?? "");
|
|
9262
9579
|
if (msg.role === "system") {
|
|
9263
9580
|
if (text)
|
|
9264
9581
|
systemParts.push(text);
|
|
9582
|
+
} else if (msg.role === "tool") {
|
|
9583
|
+
turns.push({
|
|
9584
|
+
role: "user",
|
|
9585
|
+
content: [{
|
|
9586
|
+
type: "tool_result",
|
|
9587
|
+
tool_use_id: msg.tool_call_id ?? "",
|
|
9588
|
+
content: translateOpenAiContentToAnthropic(msg.content ?? "")
|
|
9589
|
+
}]
|
|
9590
|
+
});
|
|
9591
|
+
} else if (msg.role === "assistant") {
|
|
9592
|
+
const msgContent = translateOpenAiContentToAnthropic(msg.content ?? "");
|
|
9593
|
+
const content = [];
|
|
9594
|
+
const toolCalls = msg.tool_calls ?? null;
|
|
9595
|
+
const firstBlock = msgContent[0];
|
|
9596
|
+
const endOfThink = firstBlock?.type === "text" && firstBlock.text.startsWith("<think>") ? firstBlock.text.indexOf("</think>") : -1;
|
|
9597
|
+
if (firstBlock?.type === "text" && firstBlock.text.startsWith("<think>") && endOfThink !== -1) {
|
|
9598
|
+
const thinking = firstBlock.text.substring("<think>".length, endOfThink);
|
|
9599
|
+
let textStart = endOfThink + "</think>".length;
|
|
9600
|
+
if (firstBlock.text[textStart] === `
|
|
9601
|
+
`)
|
|
9602
|
+
textStart += 1;
|
|
9603
|
+
const text2 = firstBlock.text.substring(textStart);
|
|
9604
|
+
content.push({ type: "thinking", thinking });
|
|
9605
|
+
if (text2.length)
|
|
9606
|
+
content.push({ type: "text", text: text2 });
|
|
9607
|
+
if (msgContent.length > 1)
|
|
9608
|
+
content.push(...msgContent.slice(1));
|
|
9609
|
+
} else {
|
|
9610
|
+
content.push(...msgContent);
|
|
9611
|
+
}
|
|
9612
|
+
if (toolCalls) {
|
|
9613
|
+
const calls = toolCalls.filter((call) => call.type === "function").map((call) => {
|
|
9614
|
+
let input;
|
|
9615
|
+
try {
|
|
9616
|
+
const parsed = JSON.parse(call.function.arguments);
|
|
9617
|
+
input = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { __raw: call.function.arguments };
|
|
9618
|
+
} catch {
|
|
9619
|
+
input = { __raw: call.function.arguments };
|
|
9620
|
+
}
|
|
9621
|
+
return {
|
|
9622
|
+
type: "tool_use",
|
|
9623
|
+
id: call.id,
|
|
9624
|
+
name: call.function.name,
|
|
9625
|
+
input
|
|
9626
|
+
};
|
|
9627
|
+
});
|
|
9628
|
+
content.push(...calls);
|
|
9629
|
+
}
|
|
9630
|
+
let finalContent = content;
|
|
9631
|
+
if (content.length === 1 && content[0]?.type === "text") {
|
|
9632
|
+
finalContent = content[0].text;
|
|
9633
|
+
}
|
|
9634
|
+
turns.push({
|
|
9635
|
+
role: "assistant",
|
|
9636
|
+
content: finalContent
|
|
9637
|
+
});
|
|
9265
9638
|
} else {
|
|
9266
9639
|
turns.push({
|
|
9267
|
-
role:
|
|
9640
|
+
role: "user",
|
|
9268
9641
|
content: translateOpenAiContentToAnthropic(msg.content ?? "")
|
|
9269
9642
|
});
|
|
9270
9643
|
}
|
|
9271
9644
|
}
|
|
9645
|
+
const reqTools = body.tools ?? [];
|
|
9646
|
+
for (const reqTool of reqTools) {
|
|
9647
|
+
if (reqTool.type === "function") {
|
|
9648
|
+
const tool = reqTool.function;
|
|
9649
|
+
tools.push({
|
|
9650
|
+
name: tool.name,
|
|
9651
|
+
description: tool.description ?? "",
|
|
9652
|
+
input_schema: tool.parameters,
|
|
9653
|
+
strict: tool.strict
|
|
9654
|
+
});
|
|
9655
|
+
} else {
|
|
9656
|
+
return null;
|
|
9657
|
+
}
|
|
9658
|
+
}
|
|
9272
9659
|
let systemPrompt = systemParts.join(`
|
|
9273
9660
|
`);
|
|
9274
9661
|
let messagesToSend = turns;
|
|
@@ -9289,6 +9676,7 @@ ${historyBlock}` : historyBlock;
|
|
|
9289
9676
|
model: body.model ?? "claude-sonnet-4-6",
|
|
9290
9677
|
messages: messagesToSend,
|
|
9291
9678
|
max_tokens: body.max_tokens ?? body.max_completion_tokens ?? 8192,
|
|
9679
|
+
tools,
|
|
9292
9680
|
stream: body.stream ?? false
|
|
9293
9681
|
};
|
|
9294
9682
|
if (systemPrompt)
|
|
@@ -9302,10 +9690,23 @@ ${historyBlock}` : historyBlock;
|
|
|
9302
9690
|
function toFinishReason(stopReason) {
|
|
9303
9691
|
if (stopReason === "max_tokens")
|
|
9304
9692
|
return "length";
|
|
9693
|
+
else if (stopReason === "tool_use")
|
|
9694
|
+
return "tool_calls";
|
|
9305
9695
|
return "stop";
|
|
9306
9696
|
}
|
|
9307
|
-
function translateAnthropicToOpenAi(response, completionId, model, created) {
|
|
9308
|
-
const
|
|
9697
|
+
function translateAnthropicToOpenAi(response, completionId, model, created, options) {
|
|
9698
|
+
const contentBlocks = response.content ?? [];
|
|
9699
|
+
const content = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
|
|
9700
|
+
const toolCalls = contentBlocks.filter((b) => b.type === "tool_use").map((b) => ({
|
|
9701
|
+
type: "function",
|
|
9702
|
+
id: b.id,
|
|
9703
|
+
function: {
|
|
9704
|
+
name: b.name,
|
|
9705
|
+
arguments: JSON.stringify(b.input)
|
|
9706
|
+
}
|
|
9707
|
+
}));
|
|
9708
|
+
const thinkingPassthrough = options?.thinkingPassthrough;
|
|
9709
|
+
const thinking = thinkingPassthrough !== false ? contentBlocks.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") : "";
|
|
9309
9710
|
const promptTokens = response.usage?.input_tokens ?? 0;
|
|
9310
9711
|
const completionTokens = response.usage?.output_tokens ?? 0;
|
|
9311
9712
|
return {
|
|
@@ -9315,7 +9716,12 @@ function translateAnthropicToOpenAi(response, completionId, model, created) {
|
|
|
9315
9716
|
model,
|
|
9316
9717
|
choices: [{
|
|
9317
9718
|
index: 0,
|
|
9318
|
-
message: {
|
|
9719
|
+
message: {
|
|
9720
|
+
role: "assistant",
|
|
9721
|
+
content: content || null,
|
|
9722
|
+
reasoning_content: thinking.length ? thinking : undefined,
|
|
9723
|
+
tool_calls: toolCalls.length ? toolCalls : undefined
|
|
9724
|
+
},
|
|
9319
9725
|
finish_reason: toFinishReason(response.stop_reason)
|
|
9320
9726
|
}],
|
|
9321
9727
|
usage: {
|
|
@@ -9325,7 +9731,16 @@ function translateAnthropicToOpenAi(response, completionId, model, created) {
|
|
|
9325
9731
|
}
|
|
9326
9732
|
};
|
|
9327
9733
|
}
|
|
9328
|
-
function
|
|
9734
|
+
function createSseTranslator(ctx) {
|
|
9735
|
+
let toolCallIndex = -1;
|
|
9736
|
+
return (event) => {
|
|
9737
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block.name === "string") {
|
|
9738
|
+
toolCallIndex++;
|
|
9739
|
+
}
|
|
9740
|
+
return translateAnthropicSseEvent(event, ctx.completionId, ctx.model, ctx.created, toolCallIndex, ctx.thinkingPassthrough);
|
|
9741
|
+
};
|
|
9742
|
+
}
|
|
9743
|
+
function translateAnthropicSseEvent(event, completionId, model, created, toolCallNum, thinkingPassthrough) {
|
|
9329
9744
|
if (event.type === "message_start") {
|
|
9330
9745
|
return {
|
|
9331
9746
|
id: completionId,
|
|
@@ -9344,6 +9759,69 @@ function translateAnthropicSseEvent(event, completionId, model, created) {
|
|
|
9344
9759
|
choices: [{ index: 0, delta: { content: event.delta.text }, finish_reason: null }]
|
|
9345
9760
|
};
|
|
9346
9761
|
}
|
|
9762
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block?.name === "string") {
|
|
9763
|
+
return {
|
|
9764
|
+
id: completionId,
|
|
9765
|
+
object: "chat.completion.chunk",
|
|
9766
|
+
created,
|
|
9767
|
+
model,
|
|
9768
|
+
choices: [{
|
|
9769
|
+
index: 0,
|
|
9770
|
+
delta: {
|
|
9771
|
+
tool_calls: [{
|
|
9772
|
+
type: "function",
|
|
9773
|
+
index: toolCallNum,
|
|
9774
|
+
id: event.content_block?.id,
|
|
9775
|
+
function: {
|
|
9776
|
+
name: event.content_block.name,
|
|
9777
|
+
arguments: ""
|
|
9778
|
+
}
|
|
9779
|
+
}]
|
|
9780
|
+
},
|
|
9781
|
+
finish_reason: null
|
|
9782
|
+
}]
|
|
9783
|
+
};
|
|
9784
|
+
}
|
|
9785
|
+
if (event.type === "content_block_delta" && event.delta?.type === "input_json_delta" && typeof event.delta?.partial_json === "string") {
|
|
9786
|
+
return {
|
|
9787
|
+
id: completionId,
|
|
9788
|
+
object: "chat.completion.chunk",
|
|
9789
|
+
created,
|
|
9790
|
+
model,
|
|
9791
|
+
choices: [{
|
|
9792
|
+
index: 0,
|
|
9793
|
+
delta: {
|
|
9794
|
+
tool_calls: [{
|
|
9795
|
+
index: toolCallNum,
|
|
9796
|
+
function: {
|
|
9797
|
+
arguments: event.delta.partial_json
|
|
9798
|
+
}
|
|
9799
|
+
}]
|
|
9800
|
+
},
|
|
9801
|
+
finish_reason: null
|
|
9802
|
+
}]
|
|
9803
|
+
};
|
|
9804
|
+
}
|
|
9805
|
+
if (event.type === "content_block_delta" && event.delta?.type === "thinking_delta") {
|
|
9806
|
+
if (thinkingPassthrough === false) {
|
|
9807
|
+
return null;
|
|
9808
|
+
}
|
|
9809
|
+
if (typeof event.delta?.thinking === "string") {
|
|
9810
|
+
return {
|
|
9811
|
+
id: completionId,
|
|
9812
|
+
object: "chat.completion.chunk",
|
|
9813
|
+
created,
|
|
9814
|
+
model,
|
|
9815
|
+
choices: [{
|
|
9816
|
+
index: 0,
|
|
9817
|
+
delta: {
|
|
9818
|
+
reasoning_content: event.delta?.thinking
|
|
9819
|
+
},
|
|
9820
|
+
finish_reason: null
|
|
9821
|
+
}]
|
|
9822
|
+
};
|
|
9823
|
+
}
|
|
9824
|
+
}
|
|
9347
9825
|
if (event.type === "message_delta" && event.delta?.stop_reason) {
|
|
9348
9826
|
return {
|
|
9349
9827
|
id: completionId,
|
|
@@ -9660,7 +10138,11 @@ var CLAUDE_CODE_ONLY_TOOLS = [
|
|
|
9660
10138
|
"ExitPlanMode",
|
|
9661
10139
|
"EnterWorktree",
|
|
9662
10140
|
"ExitWorktree",
|
|
10141
|
+
"Monitor",
|
|
9663
10142
|
"NotebookEdit",
|
|
10143
|
+
"PushNotification",
|
|
10144
|
+
"RemoteTrigger",
|
|
10145
|
+
"ScheduleWakeup",
|
|
9664
10146
|
"TodoWrite",
|
|
9665
10147
|
"AskUserQuestion",
|
|
9666
10148
|
"Skill",
|
|
@@ -9695,8 +10177,8 @@ var openCodeTransforms = [
|
|
|
9695
10177
|
let sdkAgents = {};
|
|
9696
10178
|
if (Array.isArray(body.tools)) {
|
|
9697
10179
|
const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
|
|
9698
|
-
if (taskTool
|
|
9699
|
-
sdkAgents =
|
|
10180
|
+
if (taskTool) {
|
|
10181
|
+
sdkAgents = buildAgentDefinitionsFromTool(taskTool, [...allowedMcpTools]);
|
|
9700
10182
|
}
|
|
9701
10183
|
}
|
|
9702
10184
|
let sdkHooks = undefined;
|
|
@@ -9802,9 +10284,9 @@ var openCodeAdapter = {
|
|
|
9802
10284
|
if (!Array.isArray(body.tools))
|
|
9803
10285
|
return {};
|
|
9804
10286
|
const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
|
|
9805
|
-
if (!taskTool
|
|
10287
|
+
if (!taskTool)
|
|
9806
10288
|
return {};
|
|
9807
|
-
return
|
|
10289
|
+
return buildAgentDefinitionsFromTool(taskTool, [...mcpToolNames]);
|
|
9808
10290
|
},
|
|
9809
10291
|
buildSdkHooks(body, sdkAgents) {
|
|
9810
10292
|
const validAgentNames = Object.keys(sdkAgents);
|
|
@@ -9860,6 +10342,10 @@ var DROID_ALLOWED_MCP_TOOLS = [
|
|
|
9860
10342
|
`mcp__${DROID_MCP_SERVER_NAME}__glob`,
|
|
9861
10343
|
`mcp__${DROID_MCP_SERVER_NAME}__grep`
|
|
9862
10344
|
];
|
|
10345
|
+
function resolveDroidPassthrough() {
|
|
10346
|
+
const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
|
|
10347
|
+
return envVal === "1" || envVal === "true" || envVal === "yes";
|
|
10348
|
+
}
|
|
9863
10349
|
var droidTransforms = [
|
|
9864
10350
|
{
|
|
9865
10351
|
name: "droid-core",
|
|
@@ -9871,7 +10357,7 @@ var droidTransforms = [
|
|
|
9871
10357
|
incompatibleTools: CLAUDE_CODE_ONLY_TOOLS,
|
|
9872
10358
|
allowedMcpTools: DROID_ALLOWED_MCP_TOOLS,
|
|
9873
10359
|
sdkAgents: {},
|
|
9874
|
-
passthrough:
|
|
10360
|
+
passthrough: resolveDroidPassthrough(),
|
|
9875
10361
|
leaksCwdViaSystemReminder: true
|
|
9876
10362
|
};
|
|
9877
10363
|
}
|
|
@@ -9942,7 +10428,8 @@ var droidAdapter = {
|
|
|
9942
10428
|
return "";
|
|
9943
10429
|
},
|
|
9944
10430
|
usesPassthrough() {
|
|
9945
|
-
|
|
10431
|
+
const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
|
|
10432
|
+
return envVal === "1" || envVal === "true" || envVal === "yes";
|
|
9946
10433
|
}
|
|
9947
10434
|
};
|
|
9948
10435
|
|
|
@@ -10504,10 +10991,6 @@ function detectAdapter(c) {
|
|
|
10504
10991
|
return defaultAdapter;
|
|
10505
10992
|
}
|
|
10506
10993
|
|
|
10507
|
-
// src/proxy/query.ts
|
|
10508
|
-
import { join as join3 } from "node:path";
|
|
10509
|
-
import { homedir as homedir2 } from "node:os";
|
|
10510
|
-
|
|
10511
10994
|
// src/mcpTools.ts
|
|
10512
10995
|
import { createSdkMcpServer as createSdkMcpServer2, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
10513
10996
|
import * as fs from "node:fs/promises";
|
|
@@ -11962,7 +12445,7 @@ var emitWarning = (msg, type, code, fn) => {
|
|
|
11962
12445
|
var AC = globalThis.AbortController;
|
|
11963
12446
|
var AS = globalThis.AbortSignal;
|
|
11964
12447
|
if (typeof AC === "undefined") {
|
|
11965
|
-
AS = class
|
|
12448
|
+
AS = class AbortSignal2 {
|
|
11966
12449
|
onabort;
|
|
11967
12450
|
_onabort = [];
|
|
11968
12451
|
reason;
|
|
@@ -16200,9 +16683,16 @@ function createOpencodeMcpServer() {
|
|
|
16200
16683
|
}
|
|
16201
16684
|
|
|
16202
16685
|
// src/proxy/query.ts
|
|
16686
|
+
function stripConfigDir(env2) {
|
|
16687
|
+
if (!("CLAUDE_CONFIG_DIR" in env2))
|
|
16688
|
+
return env2;
|
|
16689
|
+
const out = { ...env2 };
|
|
16690
|
+
delete out.CLAUDE_CONFIG_DIR;
|
|
16691
|
+
return out;
|
|
16692
|
+
}
|
|
16203
16693
|
function computePassthroughMaxTurns(resumeSessionId, hasDeferredTools, advisorModel) {
|
|
16204
16694
|
const hasResume = !!resumeSessionId;
|
|
16205
|
-
const base = hasResume && hasDeferredTools ? 4 :
|
|
16695
|
+
const base = hasResume && hasDeferredTools ? 4 : 3;
|
|
16206
16696
|
const advisorBump = advisorModel ? 3 : 0;
|
|
16207
16697
|
return base + advisorBump;
|
|
16208
16698
|
}
|
|
@@ -16304,10 +16794,9 @@ function buildQueryOptions(ctx) {
|
|
|
16304
16794
|
} : {},
|
|
16305
16795
|
...onStderr ? { stderr: onStderr } : {},
|
|
16306
16796
|
env: {
|
|
16307
|
-
...cleanEnv,
|
|
16797
|
+
...sharedMemory ? stripConfigDir(cleanEnv) : cleanEnv,
|
|
16308
16798
|
ENABLE_TOOL_SEARCH: hasDeferredTools ? "true" : "false",
|
|
16309
16799
|
...passthrough ? { ENABLE_CLAUDEAI_MCP_SERVERS: "false" } : {},
|
|
16310
|
-
...sharedMemory ? { CLAUDE_CONFIG_DIR: join3(homedir2(), ".claude") } : {},
|
|
16311
16800
|
...process.getuid?.() === 0 ? { IS_SANDBOX: "1" } : {}
|
|
16312
16801
|
},
|
|
16313
16802
|
...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
|
|
@@ -16342,7 +16831,7 @@ function getAdapterTransforms(adapterName) {
|
|
|
16342
16831
|
|
|
16343
16832
|
// src/proxy/plugins/loader.ts
|
|
16344
16833
|
import { readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
16345
|
-
import { join as
|
|
16834
|
+
import { join as join3, isAbsolute as isAbsolute2, extname } from "path";
|
|
16346
16835
|
|
|
16347
16836
|
// src/proxy/plugins/validation.ts
|
|
16348
16837
|
var KNOWN_ADAPTERS = ["opencode", "crush", "droid", "pi", "forgecode", "passthrough"];
|
|
@@ -16426,7 +16915,7 @@ async function loadPlugins(pluginDir, configPath) {
|
|
|
16426
16915
|
const loaded = [];
|
|
16427
16916
|
const seenNames = new Set;
|
|
16428
16917
|
for (const { filename, entry } of ordered) {
|
|
16429
|
-
const filePath = isAbsolute2(filename) ? filename :
|
|
16918
|
+
const filePath = isAbsolute2(filename) ? filename : join3(pluginDir, filename);
|
|
16430
16919
|
if (entry && !entry.enabled) {
|
|
16431
16920
|
loaded.push({
|
|
16432
16921
|
name: filename,
|
|
@@ -16802,12 +17291,12 @@ import {
|
|
|
16802
17291
|
openSync,
|
|
16803
17292
|
readFileSync as readFileSync3,
|
|
16804
17293
|
renameSync,
|
|
16805
|
-
statSync,
|
|
17294
|
+
statSync as statSync2,
|
|
16806
17295
|
unlinkSync,
|
|
16807
17296
|
writeFileSync
|
|
16808
17297
|
} from "node:fs";
|
|
16809
|
-
import { homedir as
|
|
16810
|
-
import { join as
|
|
17298
|
+
import { homedir as homedir2 } from "node:os";
|
|
17299
|
+
import { join as join4 } from "node:path";
|
|
16811
17300
|
var DEFAULT_MAX_STORED_SESSIONS = 1e4;
|
|
16812
17301
|
var STALE_LOCK_THRESHOLD_MS = 30000;
|
|
16813
17302
|
function getMaxStoredSessions() {
|
|
@@ -16831,7 +17320,7 @@ function acquireLock(lockPath) {
|
|
|
16831
17320
|
return false;
|
|
16832
17321
|
}
|
|
16833
17322
|
try {
|
|
16834
|
-
const stat =
|
|
17323
|
+
const stat = statSync2(lockPath);
|
|
16835
17324
|
if (Date.now() - stat.mtimeMs > STALE_LOCK_THRESHOLD_MS) {
|
|
16836
17325
|
unlinkSync(lockPath);
|
|
16837
17326
|
const fd = openSync(lockPath, "wx");
|
|
@@ -16858,11 +17347,11 @@ function getStorePath() {
|
|
|
16858
17347
|
if (!existsSync4(dir)) {
|
|
16859
17348
|
mkdirSync(dir, { recursive: true });
|
|
16860
17349
|
}
|
|
16861
|
-
return
|
|
17350
|
+
return join4(dir, "sessions.json");
|
|
16862
17351
|
}
|
|
16863
17352
|
function getDefaultCacheDir() {
|
|
16864
|
-
const newDir =
|
|
16865
|
-
const oldDir =
|
|
17353
|
+
const newDir = join4(homedir2(), ".cache", "meridian");
|
|
17354
|
+
const oldDir = join4(homedir2(), ".cache", "opencode-claude-max-proxy");
|
|
16866
17355
|
if (existsSync4(newDir))
|
|
16867
17356
|
return newDir;
|
|
16868
17357
|
if (existsSync4(oldDir)) {
|
|
@@ -17380,8 +17869,8 @@ function createProxyServer(config = {}) {
|
|
|
17380
17869
|
const sessionDiscoveredTools = new Map;
|
|
17381
17870
|
const sessionToolCache = new Map;
|
|
17382
17871
|
const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
|
|
17383
|
-
const pluginDir = finalConfig.pluginDir ??
|
|
17384
|
-
const pluginConfigPath = finalConfig.pluginConfigPath ??
|
|
17872
|
+
const pluginDir = finalConfig.pluginDir ?? join6(homedir4(), ".config", "meridian", "plugins");
|
|
17873
|
+
const pluginConfigPath = finalConfig.pluginConfigPath ?? join6(homedir4(), ".config", "meridian", "plugins.json");
|
|
17385
17874
|
let loadedPlugins = [];
|
|
17386
17875
|
let pluginTransforms = [];
|
|
17387
17876
|
const app = new Hono2;
|
|
@@ -17447,6 +17936,9 @@ function createProxyServer(config = {}) {
|
|
|
17447
17936
|
if (!Array.isArray(body.messages)) {
|
|
17448
17937
|
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
|
|
17449
17938
|
}
|
|
17939
|
+
if (body.messages.length === 0) {
|
|
17940
|
+
return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Cannot be empty — at least one message is required" } }, 400);
|
|
17941
|
+
}
|
|
17450
17942
|
const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
|
|
17451
17943
|
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
|
|
17452
17944
|
const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
|
|
@@ -17455,10 +17947,11 @@ function createProxyServer(config = {}) {
|
|
|
17455
17947
|
const workingDirectory = (process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR) || adapter.extractWorkingDirectory(body) || process.cwd();
|
|
17456
17948
|
const clientWorkingDirectory = adapter.extractClientWorkingDirectory?.(body) || workingDirectory;
|
|
17457
17949
|
const {
|
|
17458
|
-
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS,
|
|
17459
17950
|
ANTHROPIC_API_KEY: _dropApiKey,
|
|
17460
17951
|
ANTHROPIC_BASE_URL: _dropBaseUrl,
|
|
17461
17952
|
ANTHROPIC_AUTH_TOKEN: _dropAuthToken,
|
|
17953
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS,
|
|
17954
|
+
CLAUDE_CODE_USE_POWERSHELL_TOOL: _dropUsePowershell,
|
|
17462
17955
|
...cleanEnv
|
|
17463
17956
|
} = process.env;
|
|
17464
17957
|
const sdkModelDefaults = resolveSdkModelDefaults();
|
|
@@ -17698,7 +18191,7 @@ function createProxyServer(config = {}) {
|
|
|
17698
18191
|
});
|
|
17699
18192
|
return {
|
|
17700
18193
|
decision: "block",
|
|
17701
|
-
reason: "
|
|
18194
|
+
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
18195
|
};
|
|
17703
18196
|
}]
|
|
17704
18197
|
}]
|
|
@@ -17717,7 +18210,7 @@ function createProxyServer(config = {}) {
|
|
|
17717
18210
|
const upstreamStartAt = Date.now();
|
|
17718
18211
|
let firstChunkAt;
|
|
17719
18212
|
let currentSessionId;
|
|
17720
|
-
const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] :
|
|
18213
|
+
const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
|
|
17721
18214
|
while (sdkUuidMap.length < allMessages.length)
|
|
17722
18215
|
sdkUuidMap.push(null);
|
|
17723
18216
|
claudeLog("upstream.start", { mode: "non_stream", model });
|
|
@@ -17762,7 +18255,7 @@ function createProxyServer(config = {}) {
|
|
|
17762
18255
|
taskBudget,
|
|
17763
18256
|
betas,
|
|
17764
18257
|
settingSources,
|
|
17765
|
-
codeSystemPrompt: sdkFeatures.codeSystemPrompt
|
|
18258
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt,
|
|
17766
18259
|
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
17767
18260
|
memory: sdkFeatures.memory,
|
|
17768
18261
|
dreaming: sdkFeatures.dreaming,
|
|
@@ -17824,7 +18317,7 @@ function createProxyServer(config = {}) {
|
|
|
17824
18317
|
taskBudget,
|
|
17825
18318
|
betas,
|
|
17826
18319
|
settingSources,
|
|
17827
|
-
codeSystemPrompt: sdkFeatures.codeSystemPrompt
|
|
18320
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt,
|
|
17828
18321
|
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
17829
18322
|
memory: sdkFeatures.memory,
|
|
17830
18323
|
dreaming: sdkFeatures.dreaming,
|
|
@@ -17935,6 +18428,12 @@ function createProxyServer(config = {}) {
|
|
|
17935
18428
|
lastStopReason = message.message.stop_reason;
|
|
17936
18429
|
}
|
|
17937
18430
|
}
|
|
18431
|
+
if (message.type === "result") {
|
|
18432
|
+
const resultUsage = message.usage;
|
|
18433
|
+
if (resultUsage) {
|
|
18434
|
+
lastUsage = { ...lastUsage, ...resultUsage };
|
|
18435
|
+
}
|
|
18436
|
+
}
|
|
17938
18437
|
}
|
|
17939
18438
|
claudeLog("upstream.completed", {
|
|
17940
18439
|
mode: "non_stream",
|
|
@@ -18114,11 +18613,12 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18114
18613
|
throw error;
|
|
18115
18614
|
}
|
|
18116
18615
|
};
|
|
18117
|
-
const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] :
|
|
18616
|
+
const sdkUuidMap = cachedSession?.sdkMessageUuids ? [...cachedSession.sdkMessageUuids] : [];
|
|
18118
18617
|
while (sdkUuidMap.length < allMessages.length)
|
|
18119
18618
|
sdkUuidMap.push(null);
|
|
18120
18619
|
let messageStartEmitted = false;
|
|
18121
18620
|
let lastUsage;
|
|
18621
|
+
const streamedToolUseIds = new Set;
|
|
18122
18622
|
try {
|
|
18123
18623
|
let currentSessionId;
|
|
18124
18624
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
@@ -18156,7 +18656,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18156
18656
|
taskBudget,
|
|
18157
18657
|
betas,
|
|
18158
18658
|
settingSources,
|
|
18159
|
-
codeSystemPrompt: sdkFeatures.codeSystemPrompt
|
|
18659
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt,
|
|
18160
18660
|
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
18161
18661
|
memory: sdkFeatures.memory,
|
|
18162
18662
|
dreaming: sdkFeatures.dreaming,
|
|
@@ -18218,7 +18718,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18218
18718
|
taskBudget,
|
|
18219
18719
|
betas,
|
|
18220
18720
|
settingSources,
|
|
18221
|
-
codeSystemPrompt: sdkFeatures.codeSystemPrompt
|
|
18721
|
+
codeSystemPrompt: sdkFeatures.codeSystemPrompt,
|
|
18222
18722
|
clientSystemPrompt: sdkFeatures.clientSystemPrompt === false ? false : undefined,
|
|
18223
18723
|
memory: sdkFeatures.memory,
|
|
18224
18724
|
dreaming: sdkFeatures.dreaming,
|
|
@@ -18309,7 +18809,6 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18309
18809
|
const skipBlockIndices = new Set;
|
|
18310
18810
|
const taskToolBlockIndices = new Set;
|
|
18311
18811
|
const taskToolJsonBuffer = new Map;
|
|
18312
|
-
const streamedToolUseIds = new Set;
|
|
18313
18812
|
let nextClientBlockIndex = 0;
|
|
18314
18813
|
const sdkToClientIndex = new Map;
|
|
18315
18814
|
try {
|
|
@@ -18672,6 +19171,127 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
18672
19171
|
});
|
|
18673
19172
|
const streamErr = classifyError(errMsg);
|
|
18674
19173
|
claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
|
|
19174
|
+
const sdkTerm = extractSdkTermination(errMsg);
|
|
19175
|
+
const canRecoverAsToolUse = sdkTerm.reason === "max_turns" && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
|
|
19176
|
+
if (canRecoverAsToolUse) {
|
|
19177
|
+
diagnosticLog2.session(`${requestMeta.requestId} sdk_termination_recovered ${formatSdkTermination(sdkTerm, {
|
|
19178
|
+
model,
|
|
19179
|
+
requestSource,
|
|
19180
|
+
isResume,
|
|
19181
|
+
hasDeferredTools,
|
|
19182
|
+
sdkSessionId: resumeSessionId
|
|
19183
|
+
})} captured=${capturedToolUses.length}`, requestMeta.requestId);
|
|
19184
|
+
const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
|
|
19185
|
+
for (let i = 0;i < unseenToolUses.length; i++) {
|
|
19186
|
+
const tu = unseenToolUses[i];
|
|
19187
|
+
const blockIndex = eventsForwarded + i;
|
|
19188
|
+
safeEnqueue(encoder.encode(`event: content_block_start
|
|
19189
|
+
data: ${JSON.stringify({
|
|
19190
|
+
type: "content_block_start",
|
|
19191
|
+
index: blockIndex,
|
|
19192
|
+
content_block: { type: "tool_use", id: tu.id, name: tu.name, input: {} }
|
|
19193
|
+
})}
|
|
19194
|
+
|
|
19195
|
+
`), "recover_tool_block_start");
|
|
19196
|
+
safeEnqueue(encoder.encode(`event: content_block_delta
|
|
19197
|
+
data: ${JSON.stringify({
|
|
19198
|
+
type: "content_block_delta",
|
|
19199
|
+
index: blockIndex,
|
|
19200
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(tu.input) }
|
|
19201
|
+
})}
|
|
19202
|
+
|
|
19203
|
+
`), "recover_tool_input");
|
|
19204
|
+
safeEnqueue(encoder.encode(`event: content_block_stop
|
|
19205
|
+
data: ${JSON.stringify({
|
|
19206
|
+
type: "content_block_stop",
|
|
19207
|
+
index: blockIndex
|
|
19208
|
+
})}
|
|
19209
|
+
|
|
19210
|
+
`), "recover_tool_block_stop");
|
|
19211
|
+
}
|
|
19212
|
+
safeEnqueue(encoder.encode(`event: message_delta
|
|
19213
|
+
data: ${JSON.stringify({
|
|
19214
|
+
type: "message_delta",
|
|
19215
|
+
delta: { stop_reason: "tool_use", stop_sequence: null },
|
|
19216
|
+
usage: { output_tokens: 0 }
|
|
19217
|
+
})}
|
|
19218
|
+
|
|
19219
|
+
`), "recover_message_delta");
|
|
19220
|
+
safeEnqueue(encoder.encode(`event: message_stop
|
|
19221
|
+
data: {"type":"message_stop"}
|
|
19222
|
+
|
|
19223
|
+
`), "recover_message_stop");
|
|
19224
|
+
const recoverTotalMs = Date.now() - requestStartAt;
|
|
19225
|
+
const recoverQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
19226
|
+
telemetryStore2.record({
|
|
19227
|
+
requestId: requestMeta.requestId,
|
|
19228
|
+
timestamp: Date.now(),
|
|
19229
|
+
adapter: adapter.name,
|
|
19230
|
+
requestSource,
|
|
19231
|
+
model,
|
|
19232
|
+
requestModel: body.model || undefined,
|
|
19233
|
+
mode: "stream",
|
|
19234
|
+
isResume,
|
|
19235
|
+
isPassthrough: passthrough,
|
|
19236
|
+
hasDeferredTools,
|
|
19237
|
+
deferredToolCount: hasDeferredTools ? deferredToolCount : undefined,
|
|
19238
|
+
toolCount,
|
|
19239
|
+
lineageType,
|
|
19240
|
+
messageCount: allMessages.length,
|
|
19241
|
+
sdkSessionId: resumeSessionId,
|
|
19242
|
+
status: 200,
|
|
19243
|
+
queueWaitMs: recoverQueueWaitMs,
|
|
19244
|
+
proxyOverheadMs: upstreamStartAt - requestStartAt - recoverQueueWaitMs,
|
|
19245
|
+
ttfbMs: firstChunkAt ? firstChunkAt - upstreamStartAt : null,
|
|
19246
|
+
upstreamDurationMs: Date.now() - upstreamStartAt,
|
|
19247
|
+
totalDurationMs: recoverTotalMs,
|
|
19248
|
+
contentBlocks: eventsForwarded + unseenToolUses.length,
|
|
19249
|
+
textEvents: textEventsForwarded,
|
|
19250
|
+
error: null
|
|
19251
|
+
});
|
|
19252
|
+
if (!streamClosed) {
|
|
19253
|
+
try {
|
|
19254
|
+
controller.close();
|
|
19255
|
+
} catch {}
|
|
19256
|
+
streamClosed = true;
|
|
19257
|
+
}
|
|
19258
|
+
return;
|
|
19259
|
+
}
|
|
19260
|
+
diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
|
|
19261
|
+
model,
|
|
19262
|
+
requestSource,
|
|
19263
|
+
isResume,
|
|
19264
|
+
hasDeferredTools,
|
|
19265
|
+
sdkSessionId: resumeSessionId
|
|
19266
|
+
})}`, requestMeta.requestId);
|
|
19267
|
+
const streamErrTotalMs = Date.now() - requestStartAt;
|
|
19268
|
+
const streamErrQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
19269
|
+
telemetryStore2.record({
|
|
19270
|
+
requestId: requestMeta.requestId,
|
|
19271
|
+
timestamp: Date.now(),
|
|
19272
|
+
adapter: adapter.name,
|
|
19273
|
+
requestSource,
|
|
19274
|
+
model,
|
|
19275
|
+
requestModel: body.model || undefined,
|
|
19276
|
+
mode: "stream",
|
|
19277
|
+
isResume,
|
|
19278
|
+
isPassthrough: passthrough,
|
|
19279
|
+
hasDeferredTools,
|
|
19280
|
+
deferredToolCount: hasDeferredTools ? deferredToolCount : undefined,
|
|
19281
|
+
toolCount,
|
|
19282
|
+
lineageType,
|
|
19283
|
+
messageCount: allMessages.length,
|
|
19284
|
+
sdkSessionId: resumeSessionId,
|
|
19285
|
+
status: streamErr.status,
|
|
19286
|
+
queueWaitMs: streamErrQueueWaitMs,
|
|
19287
|
+
proxyOverheadMs: upstreamStartAt - requestStartAt - streamErrQueueWaitMs,
|
|
19288
|
+
ttfbMs: firstChunkAt ? firstChunkAt - upstreamStartAt : null,
|
|
19289
|
+
upstreamDurationMs: Date.now() - upstreamStartAt,
|
|
19290
|
+
totalDurationMs: streamErrTotalMs,
|
|
19291
|
+
contentBlocks: eventsForwarded,
|
|
19292
|
+
textEvents: textEventsForwarded,
|
|
19293
|
+
error: streamErr.type
|
|
19294
|
+
});
|
|
18675
19295
|
if (messageStartEmitted) {
|
|
18676
19296
|
safeEnqueue(encoder.encode(`event: message_delta
|
|
18677
19297
|
data: ${JSON.stringify({
|
|
@@ -18719,6 +19339,10 @@ data: ${JSON.stringify({
|
|
|
18719
19339
|
});
|
|
18720
19340
|
const classified = classifyError(errMsg);
|
|
18721
19341
|
claudeLog("proxy.error", { error: errMsg, classified: classified.type });
|
|
19342
|
+
const sdkTerm = extractSdkTermination(errMsg);
|
|
19343
|
+
diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
|
|
19344
|
+
requestSource: c.req.header("x-meridian-source")?.slice(0, 64) || undefined
|
|
19345
|
+
})}`, requestMeta.requestId);
|
|
18722
19346
|
const errorQueueWaitMs = requestMeta.queueStartedAt - requestMeta.queueEnteredAt;
|
|
18723
19347
|
telemetryStore2.record({
|
|
18724
19348
|
requestId: requestMeta.requestId,
|
|
@@ -18860,7 +19484,7 @@ data: ${JSON.stringify({
|
|
|
18860
19484
|
});
|
|
18861
19485
|
});
|
|
18862
19486
|
app.get("/profiles", async (c) => {
|
|
18863
|
-
const { profilePageHtml } = await import("./profilePage-
|
|
19487
|
+
const { profilePageHtml } = await import("./profilePage-k0faye28.js");
|
|
18864
19488
|
return c.html(profilePageHtml);
|
|
18865
19489
|
});
|
|
18866
19490
|
app.post("/profiles/active", async (c) => {
|
|
@@ -18952,9 +19576,14 @@ data: ${JSON.stringify({
|
|
|
18952
19576
|
const completionId = `chatcmpl-${randomUUID()}`;
|
|
18953
19577
|
const created = Math.floor(Date.now() / 1000);
|
|
18954
19578
|
const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : "claude-sonnet-4-6";
|
|
19579
|
+
const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
|
|
19580
|
+
const adapter = detectAdapter(c);
|
|
19581
|
+
const sdkFeatures = getFeaturesForAdapter2(adapter.name);
|
|
18955
19582
|
if (!anthropicBody.stream) {
|
|
18956
19583
|
const anthropicRes = await internalRes.json();
|
|
18957
|
-
return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created
|
|
19584
|
+
return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
|
|
19585
|
+
thinkingPassthrough: sdkFeatures.thinkingPassthrough
|
|
19586
|
+
}));
|
|
18958
19587
|
}
|
|
18959
19588
|
const encoder = new TextEncoder;
|
|
18960
19589
|
const readable = new ReadableStream({
|
|
@@ -18967,6 +19596,7 @@ data: ${JSON.stringify({
|
|
|
18967
19596
|
const decoder = new TextDecoder;
|
|
18968
19597
|
let buffer = "";
|
|
18969
19598
|
let streamError = null;
|
|
19599
|
+
const translate = createSseTranslator({ completionId, model, created, thinkingPassthrough: sdkFeatures.thinkingPassthrough });
|
|
18970
19600
|
try {
|
|
18971
19601
|
while (true) {
|
|
18972
19602
|
const { done, value } = await reader.read();
|
|
@@ -18990,7 +19620,7 @@ data: ${JSON.stringify({
|
|
|
18990
19620
|
}
|
|
18991
19621
|
if (typeof event.type !== "string")
|
|
18992
19622
|
continue;
|
|
18993
|
-
const chunk =
|
|
19623
|
+
const chunk = translate(event);
|
|
18994
19624
|
if (chunk)
|
|
18995
19625
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
|
|
18996
19626
|
|
|
@@ -19021,11 +19651,21 @@ data: ${JSON.stringify({
|
|
|
19021
19651
|
const isMax = authStatus?.subscriptionType === "max";
|
|
19022
19652
|
return c.json({ object: "list", data: buildModelList(isMax) });
|
|
19023
19653
|
});
|
|
19024
|
-
app.get("/v1/usage/quota", (c) => {
|
|
19025
|
-
const
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19654
|
+
app.get("/v1/usage/quota", async (c) => {
|
|
19655
|
+
const sdkEntries = rateLimitStore.getAll().filter((entry) => entry.rateLimitType !== undefined);
|
|
19656
|
+
const requestedProfile = c.req.query("profile");
|
|
19657
|
+
const profilesList = getEffectiveProfiles(finalConfig.profiles);
|
|
19658
|
+
const targetProfileId = requestedProfile || getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
|
|
19659
|
+
const targetProfile = targetProfileId ? profilesList.find((p) => p.id === targetProfileId) : undefined;
|
|
19660
|
+
const oauth = await fetchOAuthUsage({
|
|
19661
|
+
profileId: targetProfileId ?? undefined,
|
|
19662
|
+
claudeConfigDir: targetProfile?.claudeConfigDir
|
|
19663
|
+
});
|
|
19664
|
+
const byType = new Map;
|
|
19665
|
+
for (const entry of sdkEntries) {
|
|
19666
|
+
const type = entry.rateLimitType;
|
|
19667
|
+
byType.set(type, {
|
|
19668
|
+
type,
|
|
19029
19669
|
status: entry.status,
|
|
19030
19670
|
utilization: entry.utilization ?? null,
|
|
19031
19671
|
resetsAt: entry.resetsAt ?? null,
|
|
@@ -19035,7 +19675,85 @@ data: ${JSON.stringify({
|
|
|
19035
19675
|
overageDisabledReason: entry.overageDisabledReason ?? null,
|
|
19036
19676
|
surpassedThreshold: entry.surpassedThreshold ?? null,
|
|
19037
19677
|
observedAt: entry.observedAt
|
|
19038
|
-
})
|
|
19678
|
+
});
|
|
19679
|
+
}
|
|
19680
|
+
if (oauth) {
|
|
19681
|
+
for (const w of oauth.windows) {
|
|
19682
|
+
const existing = byType.get(w.type);
|
|
19683
|
+
const status = (w.utilization ?? 0) >= 1 ? "rejected" : (w.utilization ?? 0) >= 0.8 ? "allowed_warning" : "allowed";
|
|
19684
|
+
byType.set(w.type, {
|
|
19685
|
+
type: w.type,
|
|
19686
|
+
status: existing?.status === "rejected" ? "rejected" : status,
|
|
19687
|
+
utilization: w.utilization ?? existing?.utilization ?? null,
|
|
19688
|
+
resetsAt: w.resetsAt ?? existing?.resetsAt ?? null,
|
|
19689
|
+
isUsingOverage: existing?.isUsingOverage ?? false,
|
|
19690
|
+
overageStatus: existing?.overageStatus ?? null,
|
|
19691
|
+
overageResetsAt: existing?.overageResetsAt ?? null,
|
|
19692
|
+
overageDisabledReason: existing?.overageDisabledReason ?? null,
|
|
19693
|
+
surpassedThreshold: existing?.surpassedThreshold ?? null,
|
|
19694
|
+
observedAt: oauth.fetchedAt
|
|
19695
|
+
});
|
|
19696
|
+
}
|
|
19697
|
+
}
|
|
19698
|
+
return c.json({
|
|
19699
|
+
profile: targetProfileId ?? null,
|
|
19700
|
+
buckets: Array.from(byType.values()),
|
|
19701
|
+
extraUsage: oauth?.extraUsage ?? null,
|
|
19702
|
+
sources: {
|
|
19703
|
+
oauth: oauth ? { fetchedAt: oauth.fetchedAt } : null,
|
|
19704
|
+
sdk: { entryCount: sdkEntries.length }
|
|
19705
|
+
},
|
|
19706
|
+
asOf: Date.now()
|
|
19707
|
+
});
|
|
19708
|
+
});
|
|
19709
|
+
app.get("/v1/usage/quota/all", async (c) => {
|
|
19710
|
+
const profilesList = getEffectiveProfiles(finalConfig.profiles);
|
|
19711
|
+
const activeId = getActiveProfileId() || finalConfig.defaultProfile || profilesList[0]?.id || null;
|
|
19712
|
+
if (profilesList.length === 0) {
|
|
19713
|
+
const oauth = await fetchOAuthUsage({});
|
|
19714
|
+
return c.json({
|
|
19715
|
+
profiles: [{
|
|
19716
|
+
id: "default",
|
|
19717
|
+
isActive: true,
|
|
19718
|
+
windows: oauth?.windows ?? [],
|
|
19719
|
+
extraUsage: oauth?.extraUsage ?? null,
|
|
19720
|
+
fetchedAt: oauth?.fetchedAt ?? null,
|
|
19721
|
+
error: oauth ? null : "no_token"
|
|
19722
|
+
}],
|
|
19723
|
+
activeProfile: "default",
|
|
19724
|
+
asOf: Date.now()
|
|
19725
|
+
});
|
|
19726
|
+
}
|
|
19727
|
+
const results = await Promise.all(profilesList.map(async (p) => {
|
|
19728
|
+
const type = p.type ?? "claude-max";
|
|
19729
|
+
if (type !== "claude-max") {
|
|
19730
|
+
return {
|
|
19731
|
+
id: p.id,
|
|
19732
|
+
isActive: p.id === activeId,
|
|
19733
|
+
type,
|
|
19734
|
+
windows: [],
|
|
19735
|
+
extraUsage: null,
|
|
19736
|
+
fetchedAt: null,
|
|
19737
|
+
error: "not_oauth"
|
|
19738
|
+
};
|
|
19739
|
+
}
|
|
19740
|
+
const oauth = await fetchOAuthUsage({
|
|
19741
|
+
profileId: p.id,
|
|
19742
|
+
claudeConfigDir: p.claudeConfigDir
|
|
19743
|
+
});
|
|
19744
|
+
return {
|
|
19745
|
+
id: p.id,
|
|
19746
|
+
isActive: p.id === activeId,
|
|
19747
|
+
type,
|
|
19748
|
+
windows: oauth?.windows ?? [],
|
|
19749
|
+
extraUsage: oauth?.extraUsage ?? null,
|
|
19750
|
+
fetchedAt: oauth?.fetchedAt ?? null,
|
|
19751
|
+
error: oauth ? null : "no_token"
|
|
19752
|
+
};
|
|
19753
|
+
}));
|
|
19754
|
+
return c.json({
|
|
19755
|
+
profiles: results,
|
|
19756
|
+
activeProfile: activeId,
|
|
19039
19757
|
asOf: Date.now()
|
|
19040
19758
|
});
|
|
19041
19759
|
});
|