@rynfar/meridian 1.58.1 → 1.58.3
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-ndsqa67x.js → cli-deynz4ef.js} +245 -123
- package/dist/cli.js +1 -1
- package/dist/proxy/adapters/crush.d.ts.map +1 -1
- package/dist/proxy/adapters/detect.d.ts.map +1 -1
- package/dist/proxy/adapters/pi.d.ts.map +1 -1
- package/dist/proxy/errors.d.ts +23 -1
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/messages.d.ts +48 -0
- package/dist/proxy/messages.d.ts.map +1 -1
- package/dist/proxy/rateLimitStore.d.ts +17 -1
- package/dist/proxy/rateLimitStore.d.ts.map +1 -1
- package/dist/proxy/sanitize.d.ts +23 -0
- package/dist/proxy/sanitize.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/transforms/claudecode.d.ts +17 -0
- package/dist/proxy/transforms/claudecode.d.ts.map +1 -0
- package/dist/proxy/transforms/registry.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
|
@@ -4366,6 +4366,13 @@ import { join as join8 } from "node:path";
|
|
|
4366
4366
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
4367
4367
|
|
|
4368
4368
|
// src/proxy/rateLimitStore.ts
|
|
4369
|
+
var MIN_EPOCH_MS = 100000000000;
|
|
4370
|
+
function toEpochMs(value) {
|
|
4371
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
|
|
4372
|
+
return;
|
|
4373
|
+
return value < MIN_EPOCH_MS ? Math.round(value * 1000) : value;
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4369
4376
|
class RateLimitStore {
|
|
4370
4377
|
byProfile = new Map;
|
|
4371
4378
|
record(profileId, info, observedAt = Date.now()) {
|
|
@@ -4377,7 +4384,12 @@ class RateLimitStore {
|
|
|
4377
4384
|
buckets = new Map;
|
|
4378
4385
|
this.byProfile.set(profileId, buckets);
|
|
4379
4386
|
}
|
|
4380
|
-
buckets.set(key, {
|
|
4387
|
+
buckets.set(key, {
|
|
4388
|
+
...info,
|
|
4389
|
+
resetsAt: toEpochMs(info.resetsAt),
|
|
4390
|
+
overageResetsAt: toEpochMs(info.overageResetsAt),
|
|
4391
|
+
observedAt
|
|
4392
|
+
});
|
|
4381
4393
|
}
|
|
4382
4394
|
getAll(profileId) {
|
|
4383
4395
|
const buckets = this.byProfile.get(profileId);
|
|
@@ -10218,7 +10230,22 @@ var telemetryStore2 = stores.telemetry;
|
|
|
10218
10230
|
var diagnosticLog2 = stores.diagnostics;
|
|
10219
10231
|
|
|
10220
10232
|
// src/proxy/errors.ts
|
|
10221
|
-
function
|
|
10233
|
+
function extendedContextHint(model) {
|
|
10234
|
+
const advise = (v) => ` If you're frequently hitting this, set ${v} to use the 200k model instead.`;
|
|
10235
|
+
if (!model)
|
|
10236
|
+
return advise("MERIDIAN_1M_CONTEXT_SUPPORT=0");
|
|
10237
|
+
const lower = model.toLowerCase();
|
|
10238
|
+
if (!lower.includes("[1m]"))
|
|
10239
|
+
return "";
|
|
10240
|
+
if (lower.includes("fable") || lower.includes("mythos"))
|
|
10241
|
+
return advise("MERIDIAN_FABLE_MODEL=fable");
|
|
10242
|
+
if (lower.includes("opus"))
|
|
10243
|
+
return advise("MERIDIAN_OPUS_MODEL=opus");
|
|
10244
|
+
if (lower.includes("sonnet"))
|
|
10245
|
+
return advise("MERIDIAN_SONNET_MODEL=sonnet");
|
|
10246
|
+
return advise("MERIDIAN_1M_CONTEXT_SUPPORT=0");
|
|
10247
|
+
}
|
|
10248
|
+
function classifyError(errMsg, model) {
|
|
10222
10249
|
const lower = errMsg.toLowerCase();
|
|
10223
10250
|
if (lower.includes("oauth token has expired") || lower.includes("not logged in")) {
|
|
10224
10251
|
return {
|
|
@@ -10235,7 +10262,7 @@ function classifyError(errMsg) {
|
|
|
10235
10262
|
};
|
|
10236
10263
|
}
|
|
10237
10264
|
if (lower.includes("429") || lower.includes("rate limit") || lower.includes("too many requests") || lower.includes("hit your session limit") || lower.includes("usage limit reached")) {
|
|
10238
|
-
const hint = lower.includes("1m") || lower.includes("context") ?
|
|
10265
|
+
const hint = lower.includes("1m") || lower.includes("context") ? extendedContextHint(model) : "";
|
|
10239
10266
|
return {
|
|
10240
10267
|
status: 429,
|
|
10241
10268
|
type: "rate_limit_error",
|
|
@@ -11484,11 +11511,26 @@ function stripCacheControlForHashing(obj) {
|
|
|
11484
11511
|
const { cache_control, ...rest } = obj;
|
|
11485
11512
|
return rest;
|
|
11486
11513
|
}
|
|
11514
|
+
var HASH_IGNORED_BLOCK_TYPES = new Set(["thinking", "redacted_thinking"]);
|
|
11515
|
+
var HASH_HANDLED_BLOCK_TYPES = new Set(["text", "tool_use", "tool_result"]);
|
|
11516
|
+
var HASH_SERIALIZED_BLOCK_TYPES = new Set([
|
|
11517
|
+
"image",
|
|
11518
|
+
"document",
|
|
11519
|
+
"search_result",
|
|
11520
|
+
"server_tool_use",
|
|
11521
|
+
"web_search_tool_result",
|
|
11522
|
+
"web_fetch_tool_result",
|
|
11523
|
+
"code_execution_tool_result",
|
|
11524
|
+
"bash_code_execution_tool_result",
|
|
11525
|
+
"text_editor_code_execution_tool_result",
|
|
11526
|
+
"tool_search_tool_result",
|
|
11527
|
+
"container_upload"
|
|
11528
|
+
]);
|
|
11487
11529
|
function normalizeContent(content) {
|
|
11488
11530
|
if (typeof content === "string")
|
|
11489
11531
|
return content;
|
|
11490
11532
|
if (Array.isArray(content)) {
|
|
11491
|
-
return content.map((block) => {
|
|
11533
|
+
return content.filter((block) => !HASH_IGNORED_BLOCK_TYPES.has(block?.type)).map((block) => {
|
|
11492
11534
|
if (block.type === "text" && block.text)
|
|
11493
11535
|
return block.text;
|
|
11494
11536
|
if (block.type === "tool_use")
|
|
@@ -12195,8 +12237,8 @@ var CRUSH_ALLOWED_MCP_TOOLS2 = [
|
|
|
12195
12237
|
];
|
|
12196
12238
|
var crushAdapter = {
|
|
12197
12239
|
name: "crush",
|
|
12198
|
-
getSessionId(
|
|
12199
|
-
return;
|
|
12240
|
+
getSessionId(c) {
|
|
12241
|
+
return c.req.header("x-session-id") ?? c.req.header("x-session-affinity");
|
|
12200
12242
|
},
|
|
12201
12243
|
extractWorkingDirectory(_body) {
|
|
12202
12244
|
return;
|
|
@@ -12354,6 +12396,96 @@ var passthroughAdapter = {
|
|
|
12354
12396
|
// src/proxy/adapters/pi.ts
|
|
12355
12397
|
init_env();
|
|
12356
12398
|
|
|
12399
|
+
// src/proxy/adapters/claudecode.ts
|
|
12400
|
+
init_env();
|
|
12401
|
+
function extractClaudeCodeClientCwd(body) {
|
|
12402
|
+
let systemText = "";
|
|
12403
|
+
if (typeof body.system === "string") {
|
|
12404
|
+
systemText = body.system;
|
|
12405
|
+
} else if (Array.isArray(body.system)) {
|
|
12406
|
+
systemText = body.system.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(`
|
|
12407
|
+
`);
|
|
12408
|
+
}
|
|
12409
|
+
if (!systemText)
|
|
12410
|
+
return;
|
|
12411
|
+
const match2 = systemText.match(/Primary working directory:\s*([^\n<]+)/i);
|
|
12412
|
+
return match2?.[1]?.trim() || undefined;
|
|
12413
|
+
}
|
|
12414
|
+
function extractClaudeCodeSessionId(body) {
|
|
12415
|
+
if (!body || typeof body !== "object")
|
|
12416
|
+
return;
|
|
12417
|
+
const metadata = body.metadata;
|
|
12418
|
+
if (!metadata || typeof metadata !== "object")
|
|
12419
|
+
return;
|
|
12420
|
+
const rawUserId = metadata.user_id;
|
|
12421
|
+
let userMetadata = rawUserId;
|
|
12422
|
+
if (typeof rawUserId === "string") {
|
|
12423
|
+
try {
|
|
12424
|
+
userMetadata = JSON.parse(rawUserId);
|
|
12425
|
+
} catch {
|
|
12426
|
+
return;
|
|
12427
|
+
}
|
|
12428
|
+
}
|
|
12429
|
+
if (!userMetadata || typeof userMetadata !== "object")
|
|
12430
|
+
return;
|
|
12431
|
+
const sessionId = userMetadata.session_id;
|
|
12432
|
+
return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : undefined;
|
|
12433
|
+
}
|
|
12434
|
+
var claudeCodeAdapter = {
|
|
12435
|
+
name: "claude-code",
|
|
12436
|
+
getSessionId(_c, body) {
|
|
12437
|
+
return extractClaudeCodeSessionId(body);
|
|
12438
|
+
},
|
|
12439
|
+
extractWorkingDirectory(_body) {
|
|
12440
|
+
return;
|
|
12441
|
+
},
|
|
12442
|
+
extractClientWorkingDirectory(body) {
|
|
12443
|
+
return extractClaudeCodeClientCwd(body);
|
|
12444
|
+
},
|
|
12445
|
+
normalizeContent(content) {
|
|
12446
|
+
return normalizeContent(content);
|
|
12447
|
+
},
|
|
12448
|
+
getBlockedBuiltinTools() {
|
|
12449
|
+
return BLOCKED_BUILTIN_TOOLS;
|
|
12450
|
+
},
|
|
12451
|
+
getAgentIncompatibleTools() {
|
|
12452
|
+
return CLAUDE_CODE_ONLY_TOOLS;
|
|
12453
|
+
},
|
|
12454
|
+
getMcpServerName() {
|
|
12455
|
+
return MCP_SERVER_NAME;
|
|
12456
|
+
},
|
|
12457
|
+
getAllowedMcpTools() {
|
|
12458
|
+
return ALLOWED_MCP_TOOLS;
|
|
12459
|
+
},
|
|
12460
|
+
getCoreToolNames() {
|
|
12461
|
+
return ["Read", "Write", "Edit", "Bash", "Glob", "Grep"];
|
|
12462
|
+
},
|
|
12463
|
+
usesPassthrough() {
|
|
12464
|
+
return resolvePassthrough(true);
|
|
12465
|
+
},
|
|
12466
|
+
supportsThinking() {
|
|
12467
|
+
return true;
|
|
12468
|
+
},
|
|
12469
|
+
shouldTrackFileChanges() {
|
|
12470
|
+
return false;
|
|
12471
|
+
},
|
|
12472
|
+
extractFileChangesFromToolUse(toolName, toolInput) {
|
|
12473
|
+
const input = toolInput;
|
|
12474
|
+
const filePath = input?.file_path ?? input?.filePath ?? input?.path;
|
|
12475
|
+
const lowerName = toolName.toLowerCase();
|
|
12476
|
+
if (lowerName === "write" && filePath) {
|
|
12477
|
+
return [{ operation: "wrote", path: String(filePath) }];
|
|
12478
|
+
}
|
|
12479
|
+
if ((lowerName === "edit" || lowerName === "multiedit") && filePath) {
|
|
12480
|
+
return [{ operation: "edited", path: String(filePath) }];
|
|
12481
|
+
}
|
|
12482
|
+
if (lowerName === "bash" && input?.command) {
|
|
12483
|
+
return extractFileChangesFromBash(String(input.command));
|
|
12484
|
+
}
|
|
12485
|
+
return [];
|
|
12486
|
+
}
|
|
12487
|
+
};
|
|
12488
|
+
|
|
12357
12489
|
// src/proxy/transforms/pi.ts
|
|
12358
12490
|
init_env();
|
|
12359
12491
|
var PI_MCP_SERVER_NAME = "pi";
|
|
@@ -12423,8 +12555,8 @@ function extractPiCwd(body) {
|
|
|
12423
12555
|
}
|
|
12424
12556
|
var piAdapter = {
|
|
12425
12557
|
name: "pi",
|
|
12426
|
-
getSessionId(c) {
|
|
12427
|
-
return c.req.header("x-session-affinity");
|
|
12558
|
+
getSessionId(c, body) {
|
|
12559
|
+
return c.req.header("x-session-affinity") ?? extractClaudeCodeSessionId(body);
|
|
12428
12560
|
},
|
|
12429
12561
|
extractWorkingDirectory(body) {
|
|
12430
12562
|
return extractPiCwd(body);
|
|
@@ -12587,96 +12719,6 @@ var forgeCodeAdapter = {
|
|
|
12587
12719
|
}
|
|
12588
12720
|
};
|
|
12589
12721
|
|
|
12590
|
-
// src/proxy/adapters/claudecode.ts
|
|
12591
|
-
init_env();
|
|
12592
|
-
function extractClaudeCodeClientCwd(body) {
|
|
12593
|
-
let systemText = "";
|
|
12594
|
-
if (typeof body.system === "string") {
|
|
12595
|
-
systemText = body.system;
|
|
12596
|
-
} else if (Array.isArray(body.system)) {
|
|
12597
|
-
systemText = body.system.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(`
|
|
12598
|
-
`);
|
|
12599
|
-
}
|
|
12600
|
-
if (!systemText)
|
|
12601
|
-
return;
|
|
12602
|
-
const match2 = systemText.match(/Primary working directory:\s*([^\n<]+)/i);
|
|
12603
|
-
return match2?.[1]?.trim() || undefined;
|
|
12604
|
-
}
|
|
12605
|
-
function extractClaudeCodeSessionId(body) {
|
|
12606
|
-
if (!body || typeof body !== "object")
|
|
12607
|
-
return;
|
|
12608
|
-
const metadata = body.metadata;
|
|
12609
|
-
if (!metadata || typeof metadata !== "object")
|
|
12610
|
-
return;
|
|
12611
|
-
const rawUserId = metadata.user_id;
|
|
12612
|
-
let userMetadata = rawUserId;
|
|
12613
|
-
if (typeof rawUserId === "string") {
|
|
12614
|
-
try {
|
|
12615
|
-
userMetadata = JSON.parse(rawUserId);
|
|
12616
|
-
} catch {
|
|
12617
|
-
return;
|
|
12618
|
-
}
|
|
12619
|
-
}
|
|
12620
|
-
if (!userMetadata || typeof userMetadata !== "object")
|
|
12621
|
-
return;
|
|
12622
|
-
const sessionId = userMetadata.session_id;
|
|
12623
|
-
return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : undefined;
|
|
12624
|
-
}
|
|
12625
|
-
var claudeCodeAdapter = {
|
|
12626
|
-
name: "claude-code",
|
|
12627
|
-
getSessionId(_c, body) {
|
|
12628
|
-
return extractClaudeCodeSessionId(body);
|
|
12629
|
-
},
|
|
12630
|
-
extractWorkingDirectory(_body) {
|
|
12631
|
-
return;
|
|
12632
|
-
},
|
|
12633
|
-
extractClientWorkingDirectory(body) {
|
|
12634
|
-
return extractClaudeCodeClientCwd(body);
|
|
12635
|
-
},
|
|
12636
|
-
normalizeContent(content) {
|
|
12637
|
-
return normalizeContent(content);
|
|
12638
|
-
},
|
|
12639
|
-
getBlockedBuiltinTools() {
|
|
12640
|
-
return BLOCKED_BUILTIN_TOOLS;
|
|
12641
|
-
},
|
|
12642
|
-
getAgentIncompatibleTools() {
|
|
12643
|
-
return CLAUDE_CODE_ONLY_TOOLS;
|
|
12644
|
-
},
|
|
12645
|
-
getMcpServerName() {
|
|
12646
|
-
return MCP_SERVER_NAME;
|
|
12647
|
-
},
|
|
12648
|
-
getAllowedMcpTools() {
|
|
12649
|
-
return ALLOWED_MCP_TOOLS;
|
|
12650
|
-
},
|
|
12651
|
-
getCoreToolNames() {
|
|
12652
|
-
return ["Read", "Write", "Edit", "Bash", "Glob", "Grep"];
|
|
12653
|
-
},
|
|
12654
|
-
usesPassthrough() {
|
|
12655
|
-
return resolvePassthrough(true);
|
|
12656
|
-
},
|
|
12657
|
-
supportsThinking() {
|
|
12658
|
-
return true;
|
|
12659
|
-
},
|
|
12660
|
-
shouldTrackFileChanges() {
|
|
12661
|
-
return false;
|
|
12662
|
-
},
|
|
12663
|
-
extractFileChangesFromToolUse(toolName, toolInput) {
|
|
12664
|
-
const input = toolInput;
|
|
12665
|
-
const filePath = input?.file_path ?? input?.filePath ?? input?.path;
|
|
12666
|
-
const lowerName = toolName.toLowerCase();
|
|
12667
|
-
if (lowerName === "write" && filePath) {
|
|
12668
|
-
return [{ operation: "wrote", path: String(filePath) }];
|
|
12669
|
-
}
|
|
12670
|
-
if ((lowerName === "edit" || lowerName === "multiedit") && filePath) {
|
|
12671
|
-
return [{ operation: "edited", path: String(filePath) }];
|
|
12672
|
-
}
|
|
12673
|
-
if (lowerName === "bash" && input?.command) {
|
|
12674
|
-
return extractFileChangesFromBash(String(input.command));
|
|
12675
|
-
}
|
|
12676
|
-
return [];
|
|
12677
|
-
}
|
|
12678
|
-
};
|
|
12679
|
-
|
|
12680
12722
|
// src/proxy/adapters/openai.ts
|
|
12681
12723
|
var openAiAdapter = {
|
|
12682
12724
|
...openCodeAdapter,
|
|
@@ -12865,7 +12907,7 @@ function detectAdapter(c) {
|
|
|
12865
12907
|
}
|
|
12866
12908
|
}
|
|
12867
12909
|
}
|
|
12868
|
-
if (c.req.header("x-opencode-session")
|
|
12910
|
+
if (c.req.header("x-opencode-session")) {
|
|
12869
12911
|
return openCodeAdapter;
|
|
12870
12912
|
}
|
|
12871
12913
|
const userAgent = c.req.header("user-agent") || "";
|
|
@@ -12878,6 +12920,9 @@ function detectAdapter(c) {
|
|
|
12878
12920
|
if (userAgent.startsWith("Charm-Crush/")) {
|
|
12879
12921
|
return crushAdapter;
|
|
12880
12922
|
}
|
|
12923
|
+
if (c.req.header("x-session-affinity")) {
|
|
12924
|
+
return openCodeAdapter;
|
|
12925
|
+
}
|
|
12881
12926
|
if (userAgent.startsWith("claude-cli/")) {
|
|
12882
12927
|
const claudeCliOverride = (process.env.MERIDIAN_DEFAULT_AGENT || "").toLowerCase();
|
|
12883
12928
|
if (claudeCliOverride && ADAPTER_MAP[claudeCliOverride] && claudeCliOverride !== "claude-code" && claudeCliOverride !== "claudecode") {
|
|
@@ -18818,6 +18863,43 @@ var codexTransforms = [
|
|
|
18818
18863
|
}
|
|
18819
18864
|
];
|
|
18820
18865
|
|
|
18866
|
+
// src/proxy/transforms/claudecode.ts
|
|
18867
|
+
init_env();
|
|
18868
|
+
var claudeCodeTransforms = [
|
|
18869
|
+
{
|
|
18870
|
+
name: "claudecode-core",
|
|
18871
|
+
adapters: ["claude-code"],
|
|
18872
|
+
onRequest(ctx) {
|
|
18873
|
+
const extractFileChangesFromToolUse = (toolName, toolInput) => {
|
|
18874
|
+
const input = toolInput;
|
|
18875
|
+
const filePath = input?.file_path ?? input?.filePath ?? input?.path;
|
|
18876
|
+
const lowerName = toolName.toLowerCase();
|
|
18877
|
+
if (lowerName === "write" && filePath) {
|
|
18878
|
+
return [{ operation: "wrote", path: String(filePath) }];
|
|
18879
|
+
}
|
|
18880
|
+
if ((lowerName === "edit" || lowerName === "multiedit") && filePath) {
|
|
18881
|
+
return [{ operation: "edited", path: String(filePath) }];
|
|
18882
|
+
}
|
|
18883
|
+
if (lowerName === "bash" && input?.command) {
|
|
18884
|
+
return extractFileChangesFromBash(String(input.command));
|
|
18885
|
+
}
|
|
18886
|
+
return [];
|
|
18887
|
+
};
|
|
18888
|
+
return {
|
|
18889
|
+
...ctx,
|
|
18890
|
+
blockedTools: BLOCKED_BUILTIN_TOOLS,
|
|
18891
|
+
incompatibleTools: CLAUDE_CODE_ONLY_TOOLS,
|
|
18892
|
+
allowedMcpTools: ALLOWED_MCP_TOOLS,
|
|
18893
|
+
coreToolNames: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
|
|
18894
|
+
passthrough: resolvePassthrough(true),
|
|
18895
|
+
supportsThinking: true,
|
|
18896
|
+
shouldTrackFileChanges: false,
|
|
18897
|
+
extractFileChangesFromToolUse
|
|
18898
|
+
};
|
|
18899
|
+
}
|
|
18900
|
+
}
|
|
18901
|
+
];
|
|
18902
|
+
|
|
18821
18903
|
// src/proxy/transforms/registry.ts
|
|
18822
18904
|
var ADAPTER_TRANSFORMS = {
|
|
18823
18905
|
opencode: openCodeTransforms,
|
|
@@ -18827,6 +18909,7 @@ var ADAPTER_TRANSFORMS = {
|
|
|
18827
18909
|
forgecode: forgeCodeTransforms,
|
|
18828
18910
|
passthrough: passthroughTransforms,
|
|
18829
18911
|
cherry: cherryTransforms,
|
|
18912
|
+
"claude-code": claudeCodeTransforms,
|
|
18830
18913
|
openai: openCodeTransforms,
|
|
18831
18914
|
codex: [...openCodeTransforms, ...codexTransforms]
|
|
18832
18915
|
};
|
|
@@ -19128,7 +19211,7 @@ var ORCHESTRATION_TAGS = [
|
|
|
19128
19211
|
];
|
|
19129
19212
|
function tagPatterns(tag) {
|
|
19130
19213
|
return [
|
|
19131
|
-
new RegExp(`<${tag}\\b[^>]
|
|
19214
|
+
new RegExp(`<${tag}\\b[^>]*(?<!\\/)>[\\s\\S]*?<\\/${tag}>`, "gi"),
|
|
19132
19215
|
new RegExp(`<${tag}\\b[^>]*\\/>`, "gi")
|
|
19133
19216
|
];
|
|
19134
19217
|
}
|
|
@@ -19147,6 +19230,16 @@ var ALL_PATTERNS = [
|
|
|
19147
19230
|
];
|
|
19148
19231
|
var SYSTEM_REMINDER_PATTERNS = tagPatterns("system-reminder");
|
|
19149
19232
|
var THINKING_TAG_PATTERNS = tagPatterns("thinking");
|
|
19233
|
+
function sanitizeAssistantText(text) {
|
|
19234
|
+
let result = text;
|
|
19235
|
+
for (const pattern of NON_XML_PATTERNS) {
|
|
19236
|
+
pattern.lastIndex = 0;
|
|
19237
|
+
result = result.replace(pattern, "");
|
|
19238
|
+
}
|
|
19239
|
+
return result.replace(/\n{3,}/g, `
|
|
19240
|
+
|
|
19241
|
+
`).trim();
|
|
19242
|
+
}
|
|
19150
19243
|
function sanitizeTextContent(text, opts = {}) {
|
|
19151
19244
|
let result = text;
|
|
19152
19245
|
const patterns = [...ALL_PATTERNS];
|
|
@@ -19783,10 +19876,10 @@ function normalizeStructuredUserContent(content) {
|
|
|
19783
19876
|
}
|
|
19784
19877
|
function flattenAssistantContent(content) {
|
|
19785
19878
|
if (typeof content === "string")
|
|
19786
|
-
return content;
|
|
19879
|
+
return sanitizeAssistantText(content);
|
|
19787
19880
|
if (!Array.isArray(content))
|
|
19788
19881
|
return String(content ?? "");
|
|
19789
|
-
return content.map((b) => b?.type === "text" && b.text ? b.text : "").filter(Boolean).join(`
|
|
19882
|
+
return content.map((b) => b?.type === "text" && b.text ? sanitizeAssistantText(b.text) : "").filter(Boolean).join(`
|
|
19790
19883
|
`);
|
|
19791
19884
|
}
|
|
19792
19885
|
function flattenUserContent(content, sanitizeOpts = {}, toolIndex) {
|
|
@@ -19978,6 +20071,8 @@ function createProxyServer(config = {}) {
|
|
|
19978
20071
|
}
|
|
19979
20072
|
function refinePriorityCooldown(profileId) {
|
|
19980
20073
|
const target = getEffectiveProfiles(finalConfig.profiles).find((p) => p.id === profileId);
|
|
20074
|
+
if ((target?.type ?? "claude-max") !== "claude-max")
|
|
20075
|
+
return;
|
|
19981
20076
|
fetchOAuthUsage({ profileId, claudeConfigDir: target?.claudeConfigDir, force: true }).then((usage) => {
|
|
19982
20077
|
if (!usage || usage.stale)
|
|
19983
20078
|
return;
|
|
@@ -20194,7 +20289,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
20194
20289
|
const envOverrides = explicitModelPin(requestedModel);
|
|
20195
20290
|
const cwdResolution = resolveSdkWorkingDirectory({
|
|
20196
20291
|
envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
|
|
20197
|
-
adapterCwd: adapter.extractWorkingDirectory(body),
|
|
20292
|
+
adapterCwd: adapter.extractWorkingDirectory(body) ?? adapter.extractClientWorkingDirectory?.(body),
|
|
20198
20293
|
fallback: process.cwd()
|
|
20199
20294
|
});
|
|
20200
20295
|
const workingDirectory = cwdResolution.workingDirectory;
|
|
@@ -21151,6 +21246,36 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
21151
21246
|
const streamedToolUseIds = new Set;
|
|
21152
21247
|
const openClientBlocks = new Set;
|
|
21153
21248
|
const resolvePendingStore = passthrough && earlyStopEnabled && !isIndependentSession && profileSessionId ? registerPendingStore(profileSessionId) : () => {};
|
|
21249
|
+
let pendingEarlyStop = false;
|
|
21250
|
+
let pendingEarlyStopAt = 0;
|
|
21251
|
+
const fireEarlyStop = (reason) => {
|
|
21252
|
+
earlyStopFired = true;
|
|
21253
|
+
claudeLog("passthrough.early_stop", {
|
|
21254
|
+
mode: "stream",
|
|
21255
|
+
captured: capturedToolUses.length,
|
|
21256
|
+
drained: awaitingEarlyStopDrain,
|
|
21257
|
+
reason,
|
|
21258
|
+
deferredMs: pendingEarlyStopAt ? Date.now() - pendingEarlyStopAt : 0
|
|
21259
|
+
});
|
|
21260
|
+
pendingEarlyStop = false;
|
|
21261
|
+
flushOpenClientBlocks("early_stop");
|
|
21262
|
+
safeEnqueue(encoder.encode(`event: message_delta
|
|
21263
|
+
data: ${JSON.stringify({ type: "message_delta", delta: { stop_reason: "tool_use", stop_sequence: null }, usage: { output_tokens: lastUsage?.output_tokens ?? 0 } })}
|
|
21264
|
+
|
|
21265
|
+
`), "early_stop");
|
|
21266
|
+
safeEnqueue(encoder.encode(`event: message_stop
|
|
21267
|
+
data: ${JSON.stringify({ type: "message_stop" })}
|
|
21268
|
+
|
|
21269
|
+
`), "early_stop");
|
|
21270
|
+
requestAbort.abort("passthrough turn complete");
|
|
21271
|
+
awaitingEarlyStopDrain = false;
|
|
21272
|
+
if (!streamClosed) {
|
|
21273
|
+
streamClosed = true;
|
|
21274
|
+
try {
|
|
21275
|
+
controller.close();
|
|
21276
|
+
} catch {}
|
|
21277
|
+
}
|
|
21278
|
+
};
|
|
21154
21279
|
const flushOpenClientBlocks = (source) => {
|
|
21155
21280
|
if (openClientBlocks.size === 0)
|
|
21156
21281
|
return;
|
|
@@ -21465,26 +21590,19 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
21465
21590
|
} else if (message.type === "user") {
|
|
21466
21591
|
noteUserContent(earlyStop, message.message?.content);
|
|
21467
21592
|
if (shouldEarlyStop(earlyStop) && streamedToolUseIds.size > 0) {
|
|
21468
|
-
|
|
21469
|
-
|
|
21470
|
-
|
|
21471
|
-
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
|
|
21475
|
-
|
|
21476
|
-
|
|
21477
|
-
|
|
21478
|
-
|
|
21479
|
-
|
|
21480
|
-
awaitingEarlyStopDrain = false;
|
|
21481
|
-
if (!streamClosed) {
|
|
21482
|
-
streamClosed = true;
|
|
21483
|
-
try {
|
|
21484
|
-
controller.close();
|
|
21485
|
-
} catch {}
|
|
21593
|
+
if (openClientBlocks.size > 0) {
|
|
21594
|
+
if (!pendingEarlyStop) {
|
|
21595
|
+
pendingEarlyStop = true;
|
|
21596
|
+
pendingEarlyStopAt = Date.now();
|
|
21597
|
+
claudeLog("passthrough.early_stop_deferred", {
|
|
21598
|
+
openBlocks: openClientBlocks.size,
|
|
21599
|
+
captured: capturedToolUses.length
|
|
21600
|
+
});
|
|
21601
|
+
}
|
|
21602
|
+
} else {
|
|
21603
|
+
fireEarlyStop("immediate");
|
|
21604
|
+
break;
|
|
21486
21605
|
}
|
|
21487
|
-
break;
|
|
21488
21606
|
}
|
|
21489
21607
|
}
|
|
21490
21608
|
}
|
|
@@ -21661,6 +21779,10 @@ data: ${JSON.stringify(event)}
|
|
|
21661
21779
|
const idx = event.index;
|
|
21662
21780
|
if (typeof idx === "number")
|
|
21663
21781
|
openClientBlocks.delete(idx);
|
|
21782
|
+
if (pendingEarlyStop && openClientBlocks.size === 0) {
|
|
21783
|
+
fireEarlyStop("blocks_closed");
|
|
21784
|
+
break;
|
|
21785
|
+
}
|
|
21664
21786
|
}
|
|
21665
21787
|
if (passthrough && eventType === "message_delta" && event.delta?.stop_reason === "tool_use" && streamedToolUseIds.size > 0) {
|
|
21666
21788
|
flushOpenClientBlocks("drain_close");
|
|
@@ -21951,7 +22073,7 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
21951
22073
|
status: 504,
|
|
21952
22074
|
type: "upstream_timeout",
|
|
21953
22075
|
message: `Upstream stalled: no data for ${error.sinceLastMs}ms`
|
|
21954
|
-
} : classifyError(errMsg);
|
|
22076
|
+
} : classifyError(errMsg, model);
|
|
21955
22077
|
claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
|
|
21956
22078
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
21957
22079
|
const canRecoverAsToolUse = (sdkTerm.reason === "max_turns" || sdkTerm.reason === "aborted" && (sawDuplicateToolUse || earlyStopFired)) && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
|
package/dist/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crush.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/crush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAgB9C,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"crush.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/crush.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAgB9C,eAAO,MAAM,YAAY,EAAE,YAiG1B,CAAA;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0F9C,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0F9C,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CAkFtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0C9C,eAAO,MAAM,SAAS,EAAE,YAoJvB,CAAA;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
package/dist/proxy/errors.d.ts
CHANGED
|
@@ -7,10 +7,32 @@ export interface ClassifiedError {
|
|
|
7
7
|
type: string;
|
|
8
8
|
message: string;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Remediation advice for a rate limit hit on an extended-context request.
|
|
12
|
+
*
|
|
13
|
+
* `model` is the resolved variant (`opus[1m]`, `fable`, `sonnet`, …). The advice
|
|
14
|
+
* has to name the tier that actually failed: the three per-tier variables have
|
|
15
|
+
* opposite polarity, because Sonnet 1M costs Extra Usage on every plan while
|
|
16
|
+
* Fable and Opus 1M are included on Max/Team (#717). So Sonnet opts *in* to 1M
|
|
17
|
+
* and the other two opt *out* of it.
|
|
18
|
+
*
|
|
19
|
+
* Returns "" rather than guessing when there is nothing useful to say:
|
|
20
|
+
*
|
|
21
|
+
* - The request did not use extended context. Suggesting a 1M variable is
|
|
22
|
+
* irrelevant, and for Sonnet — which is 200k by default — the old advice
|
|
23
|
+
* was a literal no-op: the user set the variable to the value it already
|
|
24
|
+
* had, saw no change, and reasonably concluded Meridian was broken (#716).
|
|
25
|
+
* - The tier is unrecognized, where the global switch is the only safe advice.
|
|
26
|
+
*/
|
|
27
|
+
export declare function extendedContextHint(model?: string): string;
|
|
10
28
|
/**
|
|
11
29
|
* Detect specific SDK errors and return helpful messages to the client.
|
|
30
|
+
*
|
|
31
|
+
* `model` is the resolved variant for this request, used only to target the
|
|
32
|
+
* extended-context remediation hint. Optional because the outer error handler
|
|
33
|
+
* may not have assigned it yet when a request fails early.
|
|
12
34
|
*/
|
|
13
|
-
export declare function classifyError(errMsg: string): ClassifiedError;
|
|
35
|
+
export declare function classifyError(errMsg: string, model?: string): ClassifiedError;
|
|
14
36
|
/**
|
|
15
37
|
* Detect errors caused by an expired or missing OAuth access token.
|
|
16
38
|
* Triggers an inline token refresh + retry in server.ts.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/proxy/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/proxy/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAc1D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAoH7E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAM3D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAO3D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAI3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,GAAG,SAAS,CAAA;IAC5D,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;wCAEoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAuBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAuCpE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GACA,MAAM,CAYR"}
|
package/dist/proxy/messages.d.ts
CHANGED
|
@@ -1,11 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Message parsing and normalization utilities.
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* Content block types that contribute nothing to the lineage hash.
|
|
6
|
+
*
|
|
7
|
+
* Thinking blocks are model-emitted reasoning. Their `signature` is an opaque,
|
|
8
|
+
* high-entropy, per-generation blob, and `redacted_thinking` carries only
|
|
9
|
+
* opaque `data` — none of it identifies the conversation prefix, which is the
|
|
10
|
+
* only question the hash exists to answer.
|
|
11
|
+
*
|
|
12
|
+
* They are dropped entirely rather than reduced to their text, because the
|
|
13
|
+
* failure mode is a client that stops echoing thinking blocks once a tool loop
|
|
14
|
+
* finishes — a common pattern, since only the active loop strictly needs them.
|
|
15
|
+
* Hashing `thinking:<text>` would still change that message's hash the moment
|
|
16
|
+
* the block disappears; contributing nothing is what makes the hash stable
|
|
17
|
+
* across its presence, absence, and any re-signature (#710).
|
|
18
|
+
*
|
|
19
|
+
* Deliberate trade-off: two prefixes differing ONLY in thinking now hash alike.
|
|
20
|
+
* That is correct — thinking always accompanies the same text and tool calls in
|
|
21
|
+
* its own turn, and those are still hashed, so it never distinguishes two
|
|
22
|
+
* genuinely different prefixes on its own.
|
|
23
|
+
*/
|
|
24
|
+
export declare const HASH_IGNORED_BLOCK_TYPES: Set<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Block types with an explicit case in {@link normalizeContent}, hashed on
|
|
27
|
+
* their semantic fields only.
|
|
28
|
+
*/
|
|
29
|
+
export declare const HASH_HANDLED_BLOCK_TYPES: Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Block types deliberately serialized whole by the fallback.
|
|
32
|
+
*
|
|
33
|
+
* Reviewed against `ContentBlockParam` and confirmed to carry no
|
|
34
|
+
* per-generation opaque field: their payloads (`source`, `content`, `title`,
|
|
35
|
+
* `file_id`) are stable for the same logical content, so serializing them is
|
|
36
|
+
* safe. `cache_control` is stripped separately.
|
|
37
|
+
*
|
|
38
|
+
* The `*_tool_use` / `*_tool_result` entries reference `id` / `tool_use_id`,
|
|
39
|
+
* which ARE per-generation — but that matches how `tool_use` and `tool_result`
|
|
40
|
+
* are already hashed, and clients echo the same ids back. Same accepted risk,
|
|
41
|
+
* not a new one.
|
|
42
|
+
*
|
|
43
|
+
* Membership here is a decision, not a default. `sdk-block-type-coverage`
|
|
44
|
+
* fails when the installed SDK adds a type absent from all three sets, so a
|
|
45
|
+
* new block type cannot silently land in the fallback the way `thinking` did
|
|
46
|
+
* (#710).
|
|
47
|
+
*/
|
|
48
|
+
export declare const HASH_SERIALIZED_BLOCK_TYPES: Set<string>;
|
|
4
49
|
/**
|
|
5
50
|
* Normalize message content to a string for hashing and comparison.
|
|
6
51
|
* Handles both string content and array content (Anthropic content blocks).
|
|
7
52
|
* Strips cache_control metadata to ensure hash stability across requests.
|
|
8
53
|
*
|
|
54
|
+
* Used only for lineage hashing — see {@link HASH_IGNORED_BLOCK_TYPES}, which
|
|
55
|
+
* drops content a display-oriented normalizer would need to keep.
|
|
56
|
+
*
|
|
9
57
|
* NOTE: OpenCode sends content as a string on the first request but as
|
|
10
58
|
* an array on subsequent ones. This normalizer handles both formats.
|
|
11
59
|
* Other agents may behave differently — this will move to the adapter pattern.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/proxy/messages.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/proxy/messages.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,wBAAwB,aAA6C,CAAA;AAElF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,aAA+C,CAAA;AAEpF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,2BAA2B,aAYtC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAmBrD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAUtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAM7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,CAKzH;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAerF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAU3D;AAED,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,aAAyC,CAAA;AAEtE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iCAAiC,CAC/C,CAAC,SAAS;IAAE,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,EAC3C,UAAU,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAyCtB;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,8EAA8E;IAC9E,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAyCD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC9C,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAyB3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAO3D"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* type: "rate_limit_event",
|
|
10
10
|
* rate_limit_info: {
|
|
11
11
|
* status: "allowed" | "allowed_warning" | "rejected",
|
|
12
|
-
* resetsAt?: number, // epoch
|
|
12
|
+
* resetsAt?: number, // epoch SECONDS — see toEpochMs
|
|
13
13
|
* rateLimitType?: "five_hour" | "seven_day"
|
|
14
14
|
* | "seven_day_opus" | "seven_day_sonnet"
|
|
15
15
|
* | "overage",
|
|
@@ -39,6 +39,22 @@
|
|
|
39
39
|
* used by tests.
|
|
40
40
|
*/
|
|
41
41
|
import type { SDKRateLimitInfo } from "@anthropic-ai/claude-agent-sdk";
|
|
42
|
+
/**
|
|
43
|
+
* Normalize an SDK reset timestamp to epoch milliseconds.
|
|
44
|
+
*
|
|
45
|
+
* The SDK reports these in epoch SECONDS while everything downstream —
|
|
46
|
+
* `Date.now()` comparisons in the priority-cooldown chain, the documented
|
|
47
|
+
* `/v1/usage/quota` contract, and consumers like the telemetry badge and Pylon
|
|
48
|
+
* — is epoch milliseconds. Proven by observing one profile's `five_hour` reset
|
|
49
|
+
* from both sources minutes apart: OAuth reported `1785234600292` and the SDK
|
|
50
|
+
* reported `1785234600` for the same instant (#708).
|
|
51
|
+
*
|
|
52
|
+
* Detects the unit rather than multiplying unconditionally, because
|
|
53
|
+
* `SDKRateLimitInfo.resetsAt` is typed `number` with no documented unit. An
|
|
54
|
+
* unconditional `* 1000` would silently corrupt every value if the SDK ever
|
|
55
|
+
* switched to milliseconds; this stays correct under either.
|
|
56
|
+
*/
|
|
57
|
+
export declare function toEpochMs(value: number | undefined): number | undefined;
|
|
42
58
|
export interface RateLimitEntry extends SDKRateLimitInfo {
|
|
43
59
|
/** When this entry was captured (epoch ms). */
|
|
44
60
|
observedAt: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimitStore.d.ts","sourceRoot":"","sources":["../../src/proxy/rateLimitStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"rateLimitStore.d.ts","sourceRoot":"","sources":["../../src/proxy/rateLimitStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAYtE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGvE;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,qDAAqD;AACrD,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,GAAG,SAAS,CAAA;AAE3F,cAAM,cAAc;IAClB,gFAAgF;IAChF,OAAO,CAAC,SAAS,CAA6D;IAE9E;;;;;;;;OAQG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,EAAE,UAAU,GAAE,MAAmB,GAAG,IAAI;IAkB3G,kEAAkE;IAClE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE;IAM3C,8EAA8E;IAC9E,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,cAAc,GAAG,SAAS;IAI3E,yEAAyE;IACzE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAOhC;;;;OAIG;IACH,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;CAIhC;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,gBAAuB,CAAA;AAElD,wCAAwC;AACxC,OAAO,EAAE,cAAc,IAAI,uBAAuB,EAAE,CAAA"}
|
package/dist/proxy/sanitize.d.ts
CHANGED
|
@@ -31,5 +31,28 @@ export interface SanitizeOptions {
|
|
|
31
31
|
* Designed to be called on individual content blocks (not concatenated
|
|
32
32
|
* prompt strings) to eliminate cross-block regex matching risk.
|
|
33
33
|
*/
|
|
34
|
+
/**
|
|
35
|
+
* Strip only the branded, harness-generated markers from ASSISTANT text.
|
|
36
|
+
*
|
|
37
|
+
* Assistant content is replayed into the prompt as `[Assistant: …]` context and
|
|
38
|
+
* has never been sanitized — the unimplemented half of #167 (see #724). The
|
|
39
|
+
* concrete leak is Meridian's own doing: `server.ts` appends its "Files
|
|
40
|
+
* changed:" summary onto the assistant's last text block, the client echoes
|
|
41
|
+
* that assistant turn back on the next turn, and the summary replays into the
|
|
42
|
+
* model's context verbatim. `NON_XML_PATTERNS` has carried a pattern for it all
|
|
43
|
+
* along, which could never fire because the sanitizer only ran on user text.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately NOT the XML tag allowlist. Assistant text is model OUTPUT, and a
|
|
46
|
+
* model asked about configuration will legitimately write `<env>…</env>` — so
|
|
47
|
+
* stripping the allowlist here would delete the model's own answer, which is
|
|
48
|
+
* exactly the failure #720 was about. `NON_XML_PATTERNS` is safe because every
|
|
49
|
+
* entry is uniquely branded (Meridian's summary, oh-my-opencode's markers, the
|
|
50
|
+
* background-task marker): no model emits them by accident.
|
|
51
|
+
*
|
|
52
|
+
* `tool_result` content is deliberately left alone too — it is tool-generated
|
|
53
|
+
* and can legitimately contain anything, including text that merely looks like
|
|
54
|
+
* a marker. Corrupting real tool output is worse than replaying a wrapper.
|
|
55
|
+
*/
|
|
56
|
+
export declare function sanitizeAssistantText(text: string): string;
|
|
34
57
|
export declare function sanitizeTextContent(text: string, opts?: SanitizeOptions): string;
|
|
35
58
|
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/proxy/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/proxy/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA4GH,MAAM,WAAW,eAAe;IAC9B;oCACgC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;8CAE0C;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED;;;;;GAKG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,MAAM,CAapF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAkDnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAI1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAGvD,YAAY,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAA;AAKpB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAkDnG,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EAEpB,KAAK,aAAa,EAGnB,MAAM,mBAAmB,CAAA;AAI1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAqR7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA8kIhF;AAWD,wBAAgB,gCAAgC,IAAI,IAAI,CAavD;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmGhG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Transform } from "../transform";
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code transform — supplies the SDK tool config at request time.
|
|
4
|
+
*
|
|
5
|
+
* server.ts reads `pipelineCtx.*`, never the adapter methods, so without an
|
|
6
|
+
* entry here a Claude Code client gets the createRequestContext defaults:
|
|
7
|
+
* `blockedTools: []` and `passthrough: undefined`. That means the SDK
|
|
8
|
+
* subprocess runs the client's task with its own built-in Read/Write/Bash on
|
|
9
|
+
* the proxy host while the client executes the same tool calls locally —
|
|
10
|
+
* every side effect happens twice (#546, same failure mode as OpenCode's).
|
|
11
|
+
*
|
|
12
|
+
* Values mirror claudeCodeAdapter (adapters/claudecode.ts); the parity tests
|
|
13
|
+
* hold the two in sync. Core tool names are PascalCase here — Claude Code's
|
|
14
|
+
* toolkit is Read/Write/Edit/Bash/Glob/Grep, not OpenCode's lowercase names.
|
|
15
|
+
*/
|
|
16
|
+
export declare const claudeCodeTransforms: Transform[];
|
|
17
|
+
//# sourceMappingURL=claudecode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudecode.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/claudecode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAA;AAK7D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,EAsC3C,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/proxy/transforms/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAgC7C,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,SAAS,EAAE,CAE9E"}
|
package/dist/server.js
CHANGED
package/package.json
CHANGED