@rynfar/meridian 1.65.2 → 1.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/{cli-syjqjygv.js → cli-35jx6vmy.js} +447 -56
- package/dist/{cli-pdpry6q0.js → cli-5jxyma6z.js} +5 -2
- package/dist/{cli-0ed6j0vk.js → cli-9e5cxp89.js} +51 -17
- package/dist/cli.js +5 -5
- package/dist/{profileCli-ap7eg985.js → profileCli-1eecbmgk.js} +1 -1
- package/dist/{profiles-4ajzjqhm.js → profiles-wch9h234.js} +1 -1
- package/dist/proxy/adapter.d.ts +14 -0
- package/dist/proxy/adapter.d.ts.map +1 -1
- package/dist/proxy/adapters/claudecode.d.ts +29 -0
- package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
- package/dist/proxy/adapters/prime.d.ts.map +1 -1
- package/dist/proxy/errors.d.ts +1 -1
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/models.d.ts +29 -11
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/oauthUsage.d.ts.map +1 -1
- package/dist/proxy/openai.d.ts +15 -0
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/openaiResponses.d.ts +4 -11
- package/dist/proxy/openaiResponses.d.ts.map +1 -1
- package/dist/proxy/retryAfter.d.ts +90 -0
- package/dist/proxy/retryAfter.d.ts.map +1 -0
- package/dist/proxy/routing.d.ts +11 -0
- package/dist/proxy/routing.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/sessionTree.d.ts +113 -0
- package/dist/proxy/sessionTree.d.ts.map +1 -0
- package/dist/server.js +3 -3
- package/dist/telemetry/dashboard.d.ts.map +1 -1
- package/dist/telemetry/pricing.d.ts.map +1 -1
- package/dist/telemetry/routes.d.ts +10 -1
- package/dist/telemetry/routes.d.ts.map +1 -1
- package/dist/telemetry/types.d.ts +16 -0
- package/dist/telemetry/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
AssignmentStore,
|
|
3
3
|
ProfileExhaustion,
|
|
4
4
|
choosePriorityProfile,
|
|
5
|
+
findCooldownReset,
|
|
5
6
|
getActiveProfileId,
|
|
6
7
|
getEffectiveProfiles,
|
|
7
8
|
getPriorityFailbackPolicy,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
restoreActiveProfile,
|
|
14
15
|
setActiveProfile,
|
|
15
16
|
shouldPromotePriorityAssignment
|
|
16
|
-
} from "./cli-
|
|
17
|
+
} from "./cli-5jxyma6z.js";
|
|
17
18
|
import {
|
|
18
19
|
isTrackedPlugin,
|
|
19
20
|
recordError,
|
|
@@ -53,7 +54,7 @@ import {
|
|
|
53
54
|
resolveSdkModelDefaults,
|
|
54
55
|
stripExtendedContext,
|
|
55
56
|
subscriptionIncludesExtendedContext
|
|
56
|
-
} from "./cli-
|
|
57
|
+
} from "./cli-9e5cxp89.js";
|
|
57
58
|
import {
|
|
58
59
|
claudeLog,
|
|
59
60
|
createPlatformCredentialStore,
|
|
@@ -477,7 +478,9 @@ var init_pricing = __esm(() => {
|
|
|
477
478
|
HAIKU_3 = rates(0.25, 1.25);
|
|
478
479
|
BUILTIN_MODEL_PRICING = {
|
|
479
480
|
fable: FABLE,
|
|
481
|
+
"claude-fable-5-1": FABLE,
|
|
480
482
|
"claude-fable-5": FABLE,
|
|
483
|
+
"claude-mythos-5-1": FABLE,
|
|
481
484
|
"claude-mythos-5": FABLE,
|
|
482
485
|
opus: OPUS,
|
|
483
486
|
"claude-opus-5": OPUS,
|
|
@@ -2827,7 +2830,7 @@ function extractClaudeCodeClientCwd(body) {
|
|
|
2827
2830
|
const match2 = systemText.match(/Primary working directory:\s*([^\n<]+)/i);
|
|
2828
2831
|
return match2?.[1]?.trim() || undefined;
|
|
2829
2832
|
}
|
|
2830
|
-
function
|
|
2833
|
+
function extractClaudeCodeSessionIdentity(body) {
|
|
2831
2834
|
if (!body || typeof body !== "object")
|
|
2832
2835
|
return;
|
|
2833
2836
|
const metadata = body.metadata;
|
|
@@ -2845,7 +2848,17 @@ function extractClaudeCodeSessionId(body) {
|
|
|
2845
2848
|
if (!userMetadata || typeof userMetadata !== "object")
|
|
2846
2849
|
return;
|
|
2847
2850
|
const sessionId = userMetadata.session_id;
|
|
2848
|
-
|
|
2851
|
+
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
2852
|
+
return;
|
|
2853
|
+
const parentSessionId = userMetadata.parent_session_id;
|
|
2854
|
+
const parent = typeof parentSessionId === "string" && parentSessionId.length > 0 && parentSessionId !== sessionId ? parentSessionId : undefined;
|
|
2855
|
+
return parent ? { sessionId, parentSessionId: parent } : { sessionId };
|
|
2856
|
+
}
|
|
2857
|
+
function extractClaudeCodeSessionId(body) {
|
|
2858
|
+
return extractClaudeCodeSessionIdentity(body)?.sessionId;
|
|
2859
|
+
}
|
|
2860
|
+
function extractClaudeCodeParentSessionId(body) {
|
|
2861
|
+
return extractClaudeCodeSessionIdentity(body)?.parentSessionId;
|
|
2849
2862
|
}
|
|
2850
2863
|
var claudeCodeAdapter;
|
|
2851
2864
|
var init_claudecode = __esm(() => {
|
|
@@ -2858,6 +2871,9 @@ var init_claudecode = __esm(() => {
|
|
|
2858
2871
|
getSessionId(_c, body) {
|
|
2859
2872
|
return extractClaudeCodeSessionId(body);
|
|
2860
2873
|
},
|
|
2874
|
+
getParentSessionId(_c, body) {
|
|
2875
|
+
return extractClaudeCodeParentSessionId(body);
|
|
2876
|
+
},
|
|
2861
2877
|
extractWorkingDirectory(_body) {
|
|
2862
2878
|
return;
|
|
2863
2879
|
},
|
|
@@ -3121,6 +3137,11 @@ var init_prime = __esm(() => {
|
|
|
3121
3137
|
getSessionId(c, body) {
|
|
3122
3138
|
return c.req.header("x-session-affinity") ?? extractClaudeCodeSessionId(body);
|
|
3123
3139
|
},
|
|
3140
|
+
getParentSessionId(c, body) {
|
|
3141
|
+
if (c.req.header("x-session-affinity"))
|
|
3142
|
+
return;
|
|
3143
|
+
return extractClaudeCodeParentSessionId(body);
|
|
3144
|
+
},
|
|
3124
3145
|
extractWorkingDirectory(body) {
|
|
3125
3146
|
return extractPrimeCwd(body);
|
|
3126
3147
|
},
|
|
@@ -6567,6 +6588,129 @@ function linkRequestAbort(signal) {
|
|
|
6567
6588
|
};
|
|
6568
6589
|
}
|
|
6569
6590
|
|
|
6591
|
+
// src/proxy/sessionTree.ts
|
|
6592
|
+
var EMPTY_CANCELLATION = { keys: [], requestIds: [] };
|
|
6593
|
+
var MAX_SUBTREE_DEPTH = 64;
|
|
6594
|
+
function truncateSessionKey(key, length = 8) {
|
|
6595
|
+
return key.length > length ? `${key.slice(0, length)}…` : key;
|
|
6596
|
+
}
|
|
6597
|
+
|
|
6598
|
+
class SessionTreeRegistry {
|
|
6599
|
+
nextToken = 1;
|
|
6600
|
+
entries = new Map;
|
|
6601
|
+
childrenByParent = new Map;
|
|
6602
|
+
propagations = 0;
|
|
6603
|
+
cancelledDescendants = 0;
|
|
6604
|
+
register(entry) {
|
|
6605
|
+
const token = this.nextToken++;
|
|
6606
|
+
this.entries.set(token, entry);
|
|
6607
|
+
const indexedParent = entry.parentKey && entry.parentKey !== entry.sessionKey ? entry.parentKey : undefined;
|
|
6608
|
+
if (indexedParent) {
|
|
6609
|
+
let siblings = this.childrenByParent.get(indexedParent);
|
|
6610
|
+
if (!siblings) {
|
|
6611
|
+
siblings = new Set;
|
|
6612
|
+
this.childrenByParent.set(indexedParent, siblings);
|
|
6613
|
+
}
|
|
6614
|
+
siblings.add(token);
|
|
6615
|
+
}
|
|
6616
|
+
let released = false;
|
|
6617
|
+
return {
|
|
6618
|
+
release: () => {
|
|
6619
|
+
if (released)
|
|
6620
|
+
return;
|
|
6621
|
+
released = true;
|
|
6622
|
+
this.entries.delete(token);
|
|
6623
|
+
if (!indexedParent)
|
|
6624
|
+
return;
|
|
6625
|
+
const siblings = this.childrenByParent.get(indexedParent);
|
|
6626
|
+
if (!siblings)
|
|
6627
|
+
return;
|
|
6628
|
+
siblings.delete(token);
|
|
6629
|
+
if (siblings.size === 0)
|
|
6630
|
+
this.childrenByParent.delete(indexedParent);
|
|
6631
|
+
}
|
|
6632
|
+
};
|
|
6633
|
+
}
|
|
6634
|
+
descendantsOf(sessionKey) {
|
|
6635
|
+
const visitedKeys = new Set([sessionKey]);
|
|
6636
|
+
let frontier = [sessionKey];
|
|
6637
|
+
const found = [];
|
|
6638
|
+
for (let depth = 0;depth < MAX_SUBTREE_DEPTH && frontier.length > 0; depth++) {
|
|
6639
|
+
const next = [];
|
|
6640
|
+
for (const parentKey of frontier) {
|
|
6641
|
+
const tokens = this.childrenByParent.get(parentKey);
|
|
6642
|
+
if (!tokens)
|
|
6643
|
+
continue;
|
|
6644
|
+
for (const token of tokens) {
|
|
6645
|
+
const entry = this.entries.get(token);
|
|
6646
|
+
if (!entry)
|
|
6647
|
+
continue;
|
|
6648
|
+
found.push(entry);
|
|
6649
|
+
if (visitedKeys.has(entry.sessionKey))
|
|
6650
|
+
continue;
|
|
6651
|
+
visitedKeys.add(entry.sessionKey);
|
|
6652
|
+
next.push(entry.sessionKey);
|
|
6653
|
+
}
|
|
6654
|
+
}
|
|
6655
|
+
frontier = next;
|
|
6656
|
+
}
|
|
6657
|
+
return found;
|
|
6658
|
+
}
|
|
6659
|
+
liveRequestsFor(sessionKey) {
|
|
6660
|
+
const found = [];
|
|
6661
|
+
for (const entry of this.entries.values()) {
|
|
6662
|
+
if (entry.sessionKey === sessionKey)
|
|
6663
|
+
found.push(entry);
|
|
6664
|
+
}
|
|
6665
|
+
return found;
|
|
6666
|
+
}
|
|
6667
|
+
cancelDescendants(sessionKey, reason) {
|
|
6668
|
+
return this.cancel(sessionKey, { reason });
|
|
6669
|
+
}
|
|
6670
|
+
cancelSubtree(sessionKey, reason) {
|
|
6671
|
+
return this.cancel(sessionKey, { reason, includeSelf: true });
|
|
6672
|
+
}
|
|
6673
|
+
cancel(sessionKey, options) {
|
|
6674
|
+
const descendants = this.descendantsOf(sessionKey);
|
|
6675
|
+
const targets = options.includeSelf ? [...this.liveRequestsFor(sessionKey), ...descendants] : descendants;
|
|
6676
|
+
if (targets.length === 0)
|
|
6677
|
+
return EMPTY_CANCELLATION;
|
|
6678
|
+
const keys = [];
|
|
6679
|
+
const requestIds = [];
|
|
6680
|
+
for (const entry of targets) {
|
|
6681
|
+
try {
|
|
6682
|
+
entry.abort(options.reason);
|
|
6683
|
+
} catch {}
|
|
6684
|
+
if (!keys.includes(entry.sessionKey))
|
|
6685
|
+
keys.push(entry.sessionKey);
|
|
6686
|
+
requestIds.push(entry.requestId);
|
|
6687
|
+
}
|
|
6688
|
+
this.propagations++;
|
|
6689
|
+
this.cancelledDescendants += descendants.length;
|
|
6690
|
+
return { keys, requestIds };
|
|
6691
|
+
}
|
|
6692
|
+
stats() {
|
|
6693
|
+
let linked = 0;
|
|
6694
|
+
for (const entry of this.entries.values()) {
|
|
6695
|
+
if (entry.parentKey)
|
|
6696
|
+
linked++;
|
|
6697
|
+
}
|
|
6698
|
+
return {
|
|
6699
|
+
tracked: this.entries.size,
|
|
6700
|
+
linked,
|
|
6701
|
+
propagations: this.propagations,
|
|
6702
|
+
cancelledDescendants: this.cancelledDescendants
|
|
6703
|
+
};
|
|
6704
|
+
}
|
|
6705
|
+
clear() {
|
|
6706
|
+
this.entries.clear();
|
|
6707
|
+
this.childrenByParent.clear();
|
|
6708
|
+
this.propagations = 0;
|
|
6709
|
+
this.cancelledDescendants = 0;
|
|
6710
|
+
}
|
|
6711
|
+
}
|
|
6712
|
+
var processSessionTree = new SessionTreeRegistry;
|
|
6713
|
+
|
|
6570
6714
|
// src/proxy/concurrency.ts
|
|
6571
6715
|
var DEFAULT_MAX_CONCURRENT = 10;
|
|
6572
6716
|
var didWarnInvalidMaxConcurrent = false;
|
|
@@ -6746,6 +6890,57 @@ async function closeServerWithGracePeriod(server, options) {
|
|
|
6746
6890
|
await closePromise;
|
|
6747
6891
|
}
|
|
6748
6892
|
|
|
6893
|
+
// src/proxy/retryAfter.ts
|
|
6894
|
+
var RETRYABLE_STATUSES = new Set([429, 503, 529]);
|
|
6895
|
+
var OVERLOADED_RETRY_AFTER_SECONDS = 5;
|
|
6896
|
+
var RATE_LIMIT_DEFAULT_RETRY_AFTER_SECONDS = 60;
|
|
6897
|
+
var RETRY_AFTER_MIN_SECONDS = 1;
|
|
6898
|
+
var RETRY_AFTER_MAX_SECONDS = 24 * 60 * 60;
|
|
6899
|
+
function parseRetryAfterMs(raw2, now = Date.now()) {
|
|
6900
|
+
if (!raw2)
|
|
6901
|
+
return null;
|
|
6902
|
+
const seconds = Number(raw2);
|
|
6903
|
+
if (Number.isFinite(seconds))
|
|
6904
|
+
return Math.max(0, seconds * 1000);
|
|
6905
|
+
const retryAt = Date.parse(raw2);
|
|
6906
|
+
return Number.isFinite(retryAt) ? Math.max(0, retryAt - now) : null;
|
|
6907
|
+
}
|
|
6908
|
+
function extractRetryAfterSeconds(errMsg) {
|
|
6909
|
+
if (!errMsg)
|
|
6910
|
+
return null;
|
|
6911
|
+
const match2 = errMsg.match(/retry[-_ ]?after"?\s*[:=]\s*"?(\d+)/i);
|
|
6912
|
+
if (!match2?.[1])
|
|
6913
|
+
return null;
|
|
6914
|
+
const seconds = Number(match2[1]);
|
|
6915
|
+
return Number.isFinite(seconds) ? seconds : null;
|
|
6916
|
+
}
|
|
6917
|
+
function retryAfterSeconds(input) {
|
|
6918
|
+
if (!RETRYABLE_STATUSES.has(input.status))
|
|
6919
|
+
return null;
|
|
6920
|
+
const now = input.now ?? Date.now();
|
|
6921
|
+
const upstreamMs = parseRetryAfterMs(input.upstreamRetryAfter, now);
|
|
6922
|
+
if (upstreamMs !== null)
|
|
6923
|
+
return clamp(Math.ceil(upstreamMs / 1000));
|
|
6924
|
+
const embedded = extractRetryAfterSeconds(input.errorMessage);
|
|
6925
|
+
if (embedded !== null)
|
|
6926
|
+
return clamp(embedded);
|
|
6927
|
+
if (input.resetAtMs != null && input.resetAtMs > now) {
|
|
6928
|
+
return clamp(Math.ceil((input.resetAtMs - now) / 1000));
|
|
6929
|
+
}
|
|
6930
|
+
return input.status === 429 ? RATE_LIMIT_DEFAULT_RETRY_AFTER_SECONDS : OVERLOADED_RETRY_AFTER_SECONDS;
|
|
6931
|
+
}
|
|
6932
|
+
function retryAfterHeaders(seconds) {
|
|
6933
|
+
return seconds === null ? {} : { "Retry-After": String(seconds) };
|
|
6934
|
+
}
|
|
6935
|
+
function retryAfterBodyFields(seconds) {
|
|
6936
|
+
return seconds === null ? {} : { retry_after: seconds };
|
|
6937
|
+
}
|
|
6938
|
+
function clamp(seconds) {
|
|
6939
|
+
if (!Number.isFinite(seconds))
|
|
6940
|
+
return RATE_LIMIT_DEFAULT_RETRY_AFTER_SECONDS;
|
|
6941
|
+
return Math.min(RETRY_AFTER_MAX_SECONDS, Math.max(RETRY_AFTER_MIN_SECONDS, Math.round(seconds)));
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6749
6944
|
// src/proxy/oauthUsage.ts
|
|
6750
6945
|
var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
6751
6946
|
var OAUTH_BETA_HEADER = "oauth-2025-04-20";
|
|
@@ -6776,15 +6971,6 @@ function normalizeUtilization(raw2) {
|
|
|
6776
6971
|
return null;
|
|
6777
6972
|
return Math.max(0, raw2 / 100);
|
|
6778
6973
|
}
|
|
6779
|
-
function parseRetryAfterMs(raw2) {
|
|
6780
|
-
if (!raw2)
|
|
6781
|
-
return null;
|
|
6782
|
-
const seconds = Number(raw2);
|
|
6783
|
-
if (Number.isFinite(seconds))
|
|
6784
|
-
return Math.max(0, seconds * 1000);
|
|
6785
|
-
const retryAt = Date.parse(raw2);
|
|
6786
|
-
return Number.isFinite(retryAt) ? Math.max(0, retryAt - Date.now()) : null;
|
|
6787
|
-
}
|
|
6788
6974
|
function modelScopedWindowType(limit) {
|
|
6789
6975
|
if (limit.kind !== "weekly_scoped")
|
|
6790
6976
|
return null;
|
|
@@ -22081,6 +22267,13 @@ function render(s, reqs, logs) {
|
|
|
22081
22267
|
+ card('Median TTFB', ms(s.ttfb.p50), 'p95: ' + ms(s.ttfb.p95))
|
|
22082
22268
|
+ card('Proxy Overhead', ms(s.proxyOverhead.p50), 'p95: ' + ms(s.proxyOverhead.p95))
|
|
22083
22269
|
+ card('Queue Wait', ms(s.queueWait.p50), 'p95: ' + ms(s.queueWait.p95))
|
|
22270
|
+
// Only rendered when a subagent tree has actually been seen: for a
|
|
22271
|
+
// single-agent client these numbers are permanently zero and would just be
|
|
22272
|
+
// a dead tile. Counts are cumulative, not windowed.
|
|
22273
|
+
+ ((s.sessionTree && (s.sessionTree.linked > 0 || s.sessionTree.cancelledDescendants > 0))
|
|
22274
|
+
? card('Subtree Cancels', s.sessionTree.cancelledDescendants,
|
|
22275
|
+
s.sessionTree.linked + ' linked live / ' + s.sessionTree.propagations + ' propagations')
|
|
22276
|
+
: '')
|
|
22084
22277
|
+ '</div>';
|
|
22085
22278
|
|
|
22086
22279
|
// Token usage cards
|
|
@@ -22283,7 +22476,7 @@ timer = setInterval(refresh, 5000);
|
|
|
22283
22476
|
// src/telemetry/routes.ts
|
|
22284
22477
|
var _iconPath = resolve2(dirname2(fileURLToPath(import.meta.url)), "..", "..", "assets", "icon.svg");
|
|
22285
22478
|
var _iconSvg = existsSync3(_iconPath) ? readFileSync2(_iconPath, "utf-8") : null;
|
|
22286
|
-
function createTelemetryRoutes() {
|
|
22479
|
+
function createTelemetryRoutes(deps = {}) {
|
|
22287
22480
|
const routes = new Hono2;
|
|
22288
22481
|
routes.get("/", (c) => {
|
|
22289
22482
|
return c.html(dashboardHtml);
|
|
@@ -22310,7 +22503,8 @@ function createTelemetryRoutes() {
|
|
|
22310
22503
|
routes.get("/summary", (c) => {
|
|
22311
22504
|
const windowMs = Number.parseInt(c.req.query("window") || "3600000", 10);
|
|
22312
22505
|
const summary = telemetryStore2.summarize(windowMs);
|
|
22313
|
-
|
|
22506
|
+
const sessionTree = deps.getSessionTree?.();
|
|
22507
|
+
return c.json(sessionTree ? { ...summary, sessionTree } : summary);
|
|
22314
22508
|
});
|
|
22315
22509
|
routes.get("/logs", (c) => {
|
|
22316
22510
|
const limit = Number.parseInt(c.req.query("limit") || "100", 10);
|
|
@@ -22713,15 +22907,23 @@ var BILLING_SIGNALS = [
|
|
|
22713
22907
|
/payment (?:method|required|failed|declined|details|info)/,
|
|
22714
22908
|
/update your payment/,
|
|
22715
22909
|
/(?:out of|draw from|draws from) extra usage/,
|
|
22716
|
-
/insufficient (?:credit|funds|balance)
|
|
22910
|
+
/insufficient (?:credit|funds|balance)/,
|
|
22911
|
+
/^\s*(?:(?:error|api error|claude code returned an error result|subprocess stderr):\s*)*your (?:group|organization|org)(?:'|’)s usage limit is set to \$\d/m
|
|
22717
22912
|
];
|
|
22718
22913
|
var HIT_YOUR_LIMIT = /hit your (?:[\w-]+ )?limit/;
|
|
22719
22914
|
var HIT_YOUR_SPEND_LIMIT = /^\s*(?:(?:error|api error|claude code returned an error result|subprocess stderr):\s*)*you(?:'|’)ve hit your (?:[\w'’-]+ ){0,4}(?:spend|usage) limit/m;
|
|
22915
|
+
var REACHED_YOUR_TIER_LIMIT = /^[ \t]*(?:(?:error|api error|claude code returned an error result|subprocess stderr):[ \t]*)*you(?:'|’)ve reached your (?:claude )?(?:fable|mythos|opus|sonnet|haiku)(?: \d+(?:\.\d+)*)? limit(?:(?:[.!][ \t]+|[ \t]+)(?:(?:run[ \t]+)?\/usage-credits(?:[ \t]+to[ \t]+continue)?(?:[ \t]+or[ \t]+switch[ \t]+models[ \t]+with[ \t]+\/model)?|\/model[ \t]+to[ \t]+switch[ \t]+models)\.?|[.!]?)[ \t\r]*$/m;
|
|
22720
22916
|
var OUT_OF_USAGE_CREDITS = /^\s*(?:(?:error|api error|claude code returned an error result):\s*)*you(?:'|’)re out of usage credits(?:[.!]\s*)?(?:\/model to switch models\.?)?\s*$/;
|
|
22721
22917
|
var HTTP_401 = /(?:^|[^0-9a-f])401(?![0-9a-f]|:\d)/;
|
|
22722
22918
|
var HTTP_429 = /(?:^|[^0-9a-f])429(?![0-9a-f]|:\d)/;
|
|
22723
22919
|
var HTTP_500 = /(?:^|[^0-9a-f])500(?![0-9a-f]|:\d)/;
|
|
22724
22920
|
var HTTP_503 = /(?:^|[^0-9a-f])503(?![0-9a-f]|:\d)/;
|
|
22921
|
+
var OVERFLOW_PHRASES = [
|
|
22922
|
+
String.raw`prompt is too long`,
|
|
22923
|
+
String.raw`input length and .?max_tokens.? exceed context limit`,
|
|
22924
|
+
String.raw`context[_ ]length[_ ]exceeded`
|
|
22925
|
+
];
|
|
22926
|
+
var CONTEXT_OVERFLOW_SIGNALS = OVERFLOW_PHRASES.map((phrase) => new RegExp(String.raw`(?:^\s*(?:(?:error|api error|claude code returned an error result|subprocess stderr):\s*)*|"message"\s*:\s*")` + phrase, "m"));
|
|
22725
22927
|
function classifyError(errMsg, model) {
|
|
22726
22928
|
const lower = errMsg.toLowerCase();
|
|
22727
22929
|
if (lower.includes("oauth token has expired") || lower.includes("not logged in")) {
|
|
@@ -22738,7 +22940,7 @@ function classifyError(errMsg, model) {
|
|
|
22738
22940
|
message: "Claude authentication expired or invalid. Run 'claude login' in your terminal to re-authenticate, then restart the proxy."
|
|
22739
22941
|
};
|
|
22740
22942
|
}
|
|
22741
|
-
if (HTTP_429.test(lower) || lower.includes("rate limit") || lower.includes("too many requests") || HIT_YOUR_LIMIT.test(lower) || HIT_YOUR_SPEND_LIMIT.test(lower) || lower.includes("usage limit reached") || OUT_OF_USAGE_CREDITS.test(lower)) {
|
|
22943
|
+
if (HTTP_429.test(lower) || lower.includes("rate limit") || lower.includes("too many requests") || HIT_YOUR_LIMIT.test(lower) || HIT_YOUR_SPEND_LIMIT.test(lower) || REACHED_YOUR_TIER_LIMIT.test(lower) || lower.includes("usage limit reached") || OUT_OF_USAGE_CREDITS.test(lower)) {
|
|
22742
22944
|
const hint = lower.includes("1m") || lower.includes("context") ? extendedContextHint(model) : "";
|
|
22743
22945
|
return {
|
|
22744
22946
|
status: 429,
|
|
@@ -22753,6 +22955,13 @@ function classifyError(errMsg, model) {
|
|
|
22753
22955
|
message: "Claude Max subscription issue. Check your subscription status at https://claude.ai/settings/subscription"
|
|
22754
22956
|
};
|
|
22755
22957
|
}
|
|
22958
|
+
if (CONTEXT_OVERFLOW_SIGNALS.some((rx) => rx.test(lower))) {
|
|
22959
|
+
return {
|
|
22960
|
+
status: 400,
|
|
22961
|
+
type: "invalid_request_error",
|
|
22962
|
+
message: "Prompt exceeds the model's context window. Compact or trim the conversation before retrying — an identical retry fails the same way."
|
|
22963
|
+
};
|
|
22964
|
+
}
|
|
22756
22965
|
if (lower.includes("exited with code") || lower.includes("process exited")) {
|
|
22757
22966
|
const codeMatch = errMsg.match(/exited with code (\d+)/);
|
|
22758
22967
|
const code = codeMatch ? codeMatch[1] : "unknown";
|
|
@@ -22908,6 +23117,12 @@ ${stderrTail ?? ""}`;
|
|
|
22908
23117
|
...stderrTail ? { stderrTail } : {}
|
|
22909
23118
|
};
|
|
22910
23119
|
}
|
|
23120
|
+
if (CONTEXT_OVERFLOW_SIGNALS.some((rx) => rx.test(lower))) {
|
|
23121
|
+
return {
|
|
23122
|
+
reason: "context_overflow",
|
|
23123
|
+
...stderrTail ? { stderrTail } : {}
|
|
23124
|
+
};
|
|
23125
|
+
}
|
|
22911
23126
|
if (lower.includes("exited with code") || lower.includes("process exited")) {
|
|
22912
23127
|
const m = haystack.match(/exited with code (\d+)/i);
|
|
22913
23128
|
return {
|
|
@@ -23355,6 +23570,23 @@ function stopUpdateCheck() {
|
|
|
23355
23570
|
}
|
|
23356
23571
|
|
|
23357
23572
|
// src/proxy/openai.ts
|
|
23573
|
+
var ANTHROPIC_USAGE_FIELDS = [
|
|
23574
|
+
"input_tokens",
|
|
23575
|
+
"output_tokens",
|
|
23576
|
+
"cache_read_input_tokens",
|
|
23577
|
+
"cache_creation_input_tokens"
|
|
23578
|
+
];
|
|
23579
|
+
function mergeAnthropicUsage(current, update) {
|
|
23580
|
+
const merged = { ...current };
|
|
23581
|
+
for (const field of ANTHROPIC_USAGE_FIELDS) {
|
|
23582
|
+
if (typeof update[field] === "number")
|
|
23583
|
+
merged[field] = update[field];
|
|
23584
|
+
}
|
|
23585
|
+
return merged;
|
|
23586
|
+
}
|
|
23587
|
+
function totalAnthropicInputTokens(usage) {
|
|
23588
|
+
return (usage?.input_tokens ?? 0) + (usage?.cache_read_input_tokens ?? 0) + (usage?.cache_creation_input_tokens ?? 0);
|
|
23589
|
+
}
|
|
23358
23590
|
function extractOpenAiContent(content) {
|
|
23359
23591
|
if (typeof content === "string")
|
|
23360
23592
|
return content;
|
|
@@ -23596,7 +23828,7 @@ function translateAnthropicToOpenAi(response, completionId, model, created, opti
|
|
|
23596
23828
|
}));
|
|
23597
23829
|
const thinkingPassthrough = options?.thinkingPassthrough;
|
|
23598
23830
|
const thinking = thinkingPassthrough !== false ? contentBlocks.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") : "";
|
|
23599
|
-
const promptTokens = response.usage
|
|
23831
|
+
const promptTokens = totalAnthropicInputTokens(response.usage);
|
|
23600
23832
|
const completionTokens = response.usage?.output_tokens ?? 0;
|
|
23601
23833
|
return {
|
|
23602
23834
|
id: completionId,
|
|
@@ -23616,7 +23848,11 @@ function translateAnthropicToOpenAi(response, completionId, model, created, opti
|
|
|
23616
23848
|
usage: {
|
|
23617
23849
|
prompt_tokens: promptTokens,
|
|
23618
23850
|
completion_tokens: completionTokens,
|
|
23619
|
-
total_tokens: promptTokens + completionTokens
|
|
23851
|
+
total_tokens: promptTokens + completionTokens,
|
|
23852
|
+
prompt_tokens_details: {
|
|
23853
|
+
cached_tokens: response.usage?.cache_read_input_tokens ?? 0,
|
|
23854
|
+
cache_write_tokens: response.usage?.cache_creation_input_tokens ?? 0
|
|
23855
|
+
}
|
|
23620
23856
|
}
|
|
23621
23857
|
};
|
|
23622
23858
|
}
|
|
@@ -23627,15 +23863,18 @@ function createSseTranslator(ctx) {
|
|
|
23627
23863
|
if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block.name === "string") {
|
|
23628
23864
|
toolCallIndex++;
|
|
23629
23865
|
}
|
|
23866
|
+
if (event.type === "message_start" && event.message?.usage) {
|
|
23867
|
+
lastUsage = mergeAnthropicUsage(lastUsage, event.message.usage);
|
|
23868
|
+
}
|
|
23630
23869
|
if (event.type === "message_delta" && event.usage) {
|
|
23631
|
-
lastUsage = event.usage;
|
|
23870
|
+
lastUsage = mergeAnthropicUsage(lastUsage, event.usage);
|
|
23632
23871
|
}
|
|
23633
23872
|
return translateAnthropicSseEvent(event, ctx.completionId, ctx.model, ctx.created, toolCallIndex, ctx.thinkingPassthrough);
|
|
23634
23873
|
};
|
|
23635
23874
|
translate.buildUsageChunk = () => {
|
|
23636
23875
|
if (!ctx.includeUsage || !lastUsage)
|
|
23637
23876
|
return null;
|
|
23638
|
-
const promptTokens = lastUsage
|
|
23877
|
+
const promptTokens = totalAnthropicInputTokens(lastUsage);
|
|
23639
23878
|
const completionTokens = lastUsage.output_tokens ?? 0;
|
|
23640
23879
|
return {
|
|
23641
23880
|
id: ctx.completionId,
|
|
@@ -23646,7 +23885,11 @@ function createSseTranslator(ctx) {
|
|
|
23646
23885
|
usage: {
|
|
23647
23886
|
prompt_tokens: promptTokens,
|
|
23648
23887
|
completion_tokens: completionTokens,
|
|
23649
|
-
total_tokens: promptTokens + completionTokens
|
|
23888
|
+
total_tokens: promptTokens + completionTokens,
|
|
23889
|
+
prompt_tokens_details: {
|
|
23890
|
+
cached_tokens: lastUsage.cache_read_input_tokens ?? 0,
|
|
23891
|
+
cache_write_tokens: lastUsage.cache_creation_input_tokens ?? 0
|
|
23892
|
+
}
|
|
23650
23893
|
}
|
|
23651
23894
|
};
|
|
23652
23895
|
};
|
|
@@ -23818,6 +24061,15 @@ function buildModelList(extendedContextIncluded, now = Math.floor(Date.now() / 1
|
|
|
23818
24061
|
context_window: extendedContextIncluded ? 1e6 : 200000,
|
|
23819
24062
|
capabilities: FULL_CAPABILITIES
|
|
23820
24063
|
},
|
|
24064
|
+
{
|
|
24065
|
+
id: "claude-fable-5-1",
|
|
24066
|
+
object: "model",
|
|
24067
|
+
created: now,
|
|
24068
|
+
owned_by: "anthropic",
|
|
24069
|
+
display_name: "Claude Fable 5.1",
|
|
24070
|
+
context_window: extendedContextIncluded ? 1e6 : 200000,
|
|
24071
|
+
capabilities: FULL_CAPABILITIES
|
|
24072
|
+
},
|
|
23821
24073
|
{
|
|
23822
24074
|
id: "claude-fable-5",
|
|
23823
24075
|
object: "model",
|
|
@@ -23988,9 +24240,17 @@ function reasoningRequested(body) {
|
|
|
23988
24240
|
return Array.isArray(include) && include.some((v) => typeof v === "string" && v.startsWith("reasoning"));
|
|
23989
24241
|
}
|
|
23990
24242
|
function mapUsage(usage) {
|
|
23991
|
-
const input = usage
|
|
24243
|
+
const input = totalAnthropicInputTokens(usage);
|
|
23992
24244
|
const output = usage?.output_tokens ?? 0;
|
|
23993
|
-
return {
|
|
24245
|
+
return {
|
|
24246
|
+
input_tokens: input,
|
|
24247
|
+
output_tokens: output,
|
|
24248
|
+
total_tokens: input + output,
|
|
24249
|
+
input_tokens_details: {
|
|
24250
|
+
cached_tokens: usage?.cache_read_input_tokens ?? 0,
|
|
24251
|
+
cache_write_tokens: usage?.cache_creation_input_tokens ?? 0
|
|
24252
|
+
}
|
|
24253
|
+
};
|
|
23994
24254
|
}
|
|
23995
24255
|
function toResponsesStatus(stopReason) {
|
|
23996
24256
|
return stopReason === "max_tokens" ? "incomplete" : "completed";
|
|
@@ -24048,8 +24308,7 @@ function translateAnthropicToResponses(res, ctx) {
|
|
|
24048
24308
|
function createResponsesSseTranslator(ctx) {
|
|
24049
24309
|
let seq = 0;
|
|
24050
24310
|
let outputIndex = 0;
|
|
24051
|
-
let
|
|
24052
|
-
let outputTokens = 0;
|
|
24311
|
+
let usage;
|
|
24053
24312
|
let createdEmitted = false;
|
|
24054
24313
|
let stopReason;
|
|
24055
24314
|
const blocks = new Map;
|
|
@@ -24070,7 +24329,8 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24070
24329
|
const out = [];
|
|
24071
24330
|
switch (event.type) {
|
|
24072
24331
|
case "message_start": {
|
|
24073
|
-
|
|
24332
|
+
if (event.message?.usage)
|
|
24333
|
+
usage = mergeAnthropicUsage(usage, event.message.usage);
|
|
24074
24334
|
if (!createdEmitted) {
|
|
24075
24335
|
createdEmitted = true;
|
|
24076
24336
|
out.push(emit("response.created", { response: responseEnvelope("in_progress", { output: [] }) }));
|
|
@@ -24186,8 +24446,8 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24186
24446
|
break;
|
|
24187
24447
|
}
|
|
24188
24448
|
case "message_delta": {
|
|
24189
|
-
if (
|
|
24190
|
-
|
|
24449
|
+
if (event.usage)
|
|
24450
|
+
usage = mergeAnthropicUsage(usage, event.usage);
|
|
24191
24451
|
if (typeof event.delta?.stop_reason === "string")
|
|
24192
24452
|
stopReason = event.delta.stop_reason;
|
|
24193
24453
|
break;
|
|
@@ -24197,7 +24457,7 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24197
24457
|
out.push(emit(status === "incomplete" ? "response.incomplete" : "response.completed", {
|
|
24198
24458
|
response: responseEnvelope(status, {
|
|
24199
24459
|
output: finalOutput,
|
|
24200
|
-
usage:
|
|
24460
|
+
usage: mapUsage(usage),
|
|
24201
24461
|
parallel_tool_calls: true,
|
|
24202
24462
|
tool_choice: "auto",
|
|
24203
24463
|
tools: [],
|
|
@@ -35438,9 +35698,14 @@ function createProxyServer(config2 = {}) {
|
|
|
35438
35698
|
const internalHopToken = randomUUID6();
|
|
35439
35699
|
const errorEnvelope = (shape, type, message) => shape === "anthropic" ? { type: "error", error: { type, message } } : { error: { type, message, code: null } };
|
|
35440
35700
|
const DRAIN_MESSAGE = "Meridian is shutting down and is not accepting new requests. Retry against another instance.";
|
|
35701
|
+
const TRANSIENT_RETRY_AFTER_HEADERS = retryAfterHeaders(OVERLOADED_RETRY_AFTER_SECONDS);
|
|
35441
35702
|
const drainingResponse = (shape = "anthropic") => new Response(JSON.stringify(errorEnvelope(shape, "overloaded_error", DRAIN_MESSAGE)), {
|
|
35442
35703
|
status: 503,
|
|
35443
|
-
headers: {
|
|
35704
|
+
headers: {
|
|
35705
|
+
"Content-Type": "application/json",
|
|
35706
|
+
"x-meridian-draining": "1",
|
|
35707
|
+
...TRANSIENT_RETRY_AFTER_HEADERS
|
|
35708
|
+
}
|
|
35444
35709
|
});
|
|
35445
35710
|
async function relayInnerError(internalRes, shape) {
|
|
35446
35711
|
const errBody = await internalRes.text();
|
|
@@ -35456,6 +35721,9 @@ function createProxyServer(config2 = {}) {
|
|
|
35456
35721
|
const drainingHeader = internalRes.headers.get("x-meridian-draining");
|
|
35457
35722
|
if (drainingHeader)
|
|
35458
35723
|
headers["x-meridian-draining"] = drainingHeader;
|
|
35724
|
+
const innerRetryAfter = internalRes.headers.get("retry-after");
|
|
35725
|
+
if (innerRetryAfter)
|
|
35726
|
+
headers["Retry-After"] = innerRetryAfter;
|
|
35459
35727
|
return new Response(JSON.stringify(payload), { status: internalRes.status, headers });
|
|
35460
35728
|
}
|
|
35461
35729
|
async function* runSdkQueryAttempt(params, signal, requestMeta, mode, activeLocators) {
|
|
@@ -35533,13 +35801,20 @@ function createProxyServer(config2 = {}) {
|
|
|
35533
35801
|
const setting = getSetting("profileOrder");
|
|
35534
35802
|
return Array.isArray(setting) && setting.length > 0 ? setting : undefined;
|
|
35535
35803
|
}
|
|
35536
|
-
function
|
|
35537
|
-
|
|
35804
|
+
function profileCooldownWindows(profileId) {
|
|
35805
|
+
return rateLimitStore.getAll(profileId).map((e) => ({
|
|
35538
35806
|
type: e.rateLimitType ?? "",
|
|
35539
35807
|
resetsAt: e.resetsAt,
|
|
35540
35808
|
exhausted: e.status === "rejected" || (e.utilization ?? 0) >= 1
|
|
35541
35809
|
}));
|
|
35542
|
-
|
|
35810
|
+
}
|
|
35811
|
+
function priorityCooldownUntil(profileId, now) {
|
|
35812
|
+
return resolveCooldownUntil(profileCooldownWindows(profileId), now, PRIORITY_DEFAULT_COOLDOWN_MS);
|
|
35813
|
+
}
|
|
35814
|
+
function observedResetAtMs(profileId, now) {
|
|
35815
|
+
if (!profileId)
|
|
35816
|
+
return null;
|
|
35817
|
+
return findCooldownReset(profileCooldownWindows(profileId), now);
|
|
35543
35818
|
}
|
|
35544
35819
|
function refinePriorityCooldown(profileId) {
|
|
35545
35820
|
const target = getEffectiveProfiles(finalConfig.profiles).find((p) => p.id === profileId);
|
|
@@ -35660,7 +35935,7 @@ function createProxyServer(config2 = {}) {
|
|
|
35660
35935
|
return options.context.json({
|
|
35661
35936
|
type: "error",
|
|
35662
35937
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35663
|
-
}, 503);
|
|
35938
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35664
35939
|
}
|
|
35665
35940
|
attemptOwnerToken = claim.ownerToken;
|
|
35666
35941
|
} catch (error51) {
|
|
@@ -35671,7 +35946,7 @@ function createProxyServer(config2 = {}) {
|
|
|
35671
35946
|
return options.context.json({
|
|
35672
35947
|
type: "error",
|
|
35673
35948
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35674
|
-
}, 503);
|
|
35949
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35675
35950
|
}
|
|
35676
35951
|
}
|
|
35677
35952
|
const settleAttempt = (disposition) => {
|
|
@@ -35691,11 +35966,12 @@ function createProxyServer(config2 = {}) {
|
|
|
35691
35966
|
const unavailableAttemptResponse = () => options.context.json({
|
|
35692
35967
|
type: "error",
|
|
35693
35968
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35694
|
-
}, 503);
|
|
35969
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35695
35970
|
let lastError = null;
|
|
35696
35971
|
let lastStatus = 429;
|
|
35697
35972
|
let previous = null;
|
|
35698
35973
|
let previousReason = "rate_limit_error";
|
|
35974
|
+
let earliestPoolReset = null;
|
|
35699
35975
|
for (const [attempt, candidate] of options.candidateIds.entries()) {
|
|
35700
35976
|
const exposure = { committed: false };
|
|
35701
35977
|
const priorityPublication = options.durableRoute && options.publicationTurn ? {
|
|
@@ -35734,6 +36010,9 @@ function createProxyServer(config2 = {}) {
|
|
|
35734
36010
|
const quotaRefusal = isQuotaRefusal(reason);
|
|
35735
36011
|
const cooldownUntil = quotaRefusal ? priorityCooldownUntil(candidate, Date.now()) : Date.now() + PRIORITY_DEFAULT_COOLDOWN_MS;
|
|
35736
36012
|
priorityExhaustion.mark(candidate, cooldownUntil, reason);
|
|
36013
|
+
if (earliestPoolReset === null || cooldownUntil < earliestPoolReset) {
|
|
36014
|
+
earliestPoolReset = cooldownUntil;
|
|
36015
|
+
}
|
|
35737
36016
|
claudeLog("priority.exhausted", { profile: candidate, until: cooldownUntil, reason });
|
|
35738
36017
|
if (quotaRefusal)
|
|
35739
36018
|
refinePriorityCooldown(candidate);
|
|
@@ -35759,7 +36038,14 @@ data: ${JSON.stringify(lastError)}
|
|
|
35759
36038
|
headers: { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache" }
|
|
35760
36039
|
});
|
|
35761
36040
|
}
|
|
35762
|
-
|
|
36041
|
+
const poolRetryAfter = retryAfterSeconds({
|
|
36042
|
+
status: lastStatus,
|
|
36043
|
+
resetAtMs: earliestPoolReset
|
|
36044
|
+
});
|
|
36045
|
+
return new Response(JSON.stringify(lastError), {
|
|
36046
|
+
status: lastStatus,
|
|
36047
|
+
headers: { "content-type": "application/json", ...retryAfterHeaders(poolRetryAfter) }
|
|
36048
|
+
});
|
|
35763
36049
|
}
|
|
35764
36050
|
app.use("/auth/*", requireAuth);
|
|
35765
36051
|
app.get("/", (c) => {
|
|
@@ -35769,7 +36055,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
35769
36055
|
status: "ok",
|
|
35770
36056
|
service: "meridian",
|
|
35771
36057
|
format: "anthropic",
|
|
35772
|
-
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
|
|
36058
|
+
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/sessions/:key/cancel", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
|
|
35773
36059
|
});
|
|
35774
36060
|
}
|
|
35775
36061
|
return c.html(landingHtml);
|
|
@@ -35940,6 +36226,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
35940
36226
|
}
|
|
35941
36227
|
priorityTerminalCommitted = true;
|
|
35942
36228
|
};
|
|
36229
|
+
let resolvedProfileId;
|
|
35943
36230
|
try {
|
|
35944
36231
|
let makePrompt = function() {
|
|
35945
36232
|
if (structuredMessages) {
|
|
@@ -36017,7 +36304,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36017
36304
|
return c.json({
|
|
36018
36305
|
type: "error",
|
|
36019
36306
|
error: { type: "overloaded_error", message: "Durable priority routing state is unavailable" }
|
|
36020
|
-
}, 503);
|
|
36307
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36021
36308
|
}
|
|
36022
36309
|
if (routeResult.status === "found") {
|
|
36023
36310
|
durableRoute = { routeKey, expectedGeneration: routeResult.generation };
|
|
@@ -36053,7 +36340,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36053
36340
|
return c.json({
|
|
36054
36341
|
type: "error",
|
|
36055
36342
|
error: { type: "overloaded_error", message: "Durable priority session state is unavailable" }
|
|
36056
|
-
}, 503);
|
|
36343
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36057
36344
|
}
|
|
36058
36345
|
routeMappingIsCurrent = mapped.status === "found" && mapped.generation === routeResult.assignment.mappingGeneration;
|
|
36059
36346
|
if (!routeMappingIsCurrent) {
|
|
@@ -36061,7 +36348,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36061
36348
|
return c.json({
|
|
36062
36349
|
type: "error",
|
|
36063
36350
|
error: { type: "overloaded_error", message: "Durable priority session state is unavailable" }
|
|
36064
|
-
}, 503);
|
|
36351
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36065
36352
|
}
|
|
36066
36353
|
durableRoute = { ...durableRoute, forceFreshReplay: true };
|
|
36067
36354
|
}
|
|
@@ -36069,7 +36356,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36069
36356
|
return c.json({
|
|
36070
36357
|
type: "error",
|
|
36071
36358
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
36072
|
-
}, 503);
|
|
36359
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36073
36360
|
} else if (trustedTurn) {
|
|
36074
36361
|
durableRoute = { routeKey, expectedGeneration: routeResult.generation };
|
|
36075
36362
|
publicationTurn = trustedTurn;
|
|
@@ -36092,7 +36379,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36092
36379
|
return c.json({
|
|
36093
36380
|
type: "error",
|
|
36094
36381
|
error: { type: "overloaded_error", message: "Durable priority routing state is unavailable" }
|
|
36095
|
-
}, 503);
|
|
36382
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36096
36383
|
}
|
|
36097
36384
|
const pick2 = choosePriorityProfile(order, (id) => priorityExhaustion.isExhausted(id));
|
|
36098
36385
|
const first = retainOnlyProfile ?? (shouldPromote ? preferred : assignmentIsHealthy ? assignedProfile : pick2?.id ?? preferred);
|
|
@@ -36114,13 +36401,15 @@ data: ${JSON.stringify(lastError)}
|
|
|
36114
36401
|
}
|
|
36115
36402
|
}
|
|
36116
36403
|
const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, options.forcedProfileId || c.req.header("x-meridian-profile") || undefined, routingMode === "sticky" ? { routingMode, stickySessionKey: adapter.getSessionId(c, body) } : undefined);
|
|
36404
|
+
resolvedProfileId = profile.id;
|
|
36117
36405
|
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
|
|
36118
36406
|
const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
|
|
36119
36407
|
const declaredAgentMode = adapter.getAgentMode?.(c, body) ?? c.req.header("x-opencode-agent-mode") ?? null;
|
|
36120
36408
|
const isSubagentRequest = declaredAgentMode === "subagent" || requestSource?.startsWith("subagent-") === true;
|
|
36121
36409
|
const agentMode = isSubagentRequest ? "subagent" : declaredAgentMode;
|
|
36122
36410
|
const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
|
|
36123
|
-
|
|
36411
|
+
const benchSessionKey = adapter.getSessionId(c, body) || undefined;
|
|
36412
|
+
let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode, profile.id, benchSessionKey);
|
|
36124
36413
|
const envOverrides = explicitModelPin(requestedModel);
|
|
36125
36414
|
const cwdResolution = resolveSdkWorkingDirectory({
|
|
36126
36415
|
envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
|
|
@@ -36825,7 +37114,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36825
37114
|
claudeExecutable = await resolveClaudeExecutableAsync();
|
|
36826
37115
|
}
|
|
36827
37116
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
36828
|
-
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
37117
|
+
const RATE_LIMIT_BASE_DELAY_MS = envInt("RATE_LIMIT_BASE_DELAY_MS", 1000);
|
|
36829
37118
|
const response = async function* () {
|
|
36830
37119
|
let rateLimitRetries = 0;
|
|
36831
37120
|
if (profileCredentialStore) {
|
|
@@ -37085,7 +37374,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
37085
37374
|
if (hasExtendedContext(model)) {
|
|
37086
37375
|
const from = model;
|
|
37087
37376
|
model = stripExtendedContext(model);
|
|
37088
|
-
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
|
|
37377
|
+
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()), benchSessionKey);
|
|
37089
37378
|
claudeLog("upstream.context_fallback", {
|
|
37090
37379
|
mode: "non_stream",
|
|
37091
37380
|
from,
|
|
@@ -37608,7 +37897,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
37608
37897
|
let nextClientBlockIndex = 0;
|
|
37609
37898
|
try {
|
|
37610
37899
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
37611
|
-
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
37900
|
+
const RATE_LIMIT_BASE_DELAY_MS = envInt("RATE_LIMIT_BASE_DELAY_MS", 1000);
|
|
37612
37901
|
const response = async function* () {
|
|
37613
37902
|
let rateLimitRetries = 0;
|
|
37614
37903
|
if (profileCredentialStore) {
|
|
@@ -37866,7 +38155,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
37866
38155
|
if (hasExtendedContext(model)) {
|
|
37867
38156
|
const from = model;
|
|
37868
38157
|
model = stripExtendedContext(model);
|
|
37869
|
-
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
|
|
38158
|
+
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()), benchSessionKey);
|
|
37870
38159
|
claudeLog("upstream.context_fallback", {
|
|
37871
38160
|
mode: "stream",
|
|
37872
38161
|
from,
|
|
@@ -38798,6 +39087,11 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
38798
39087
|
message: `Upstream stalled: no data for ${error51.sinceLastMs}ms`
|
|
38799
39088
|
} : classifyError(errMsg, model);
|
|
38800
39089
|
claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
|
|
39090
|
+
const streamRetryAfter = retryAfterSeconds({
|
|
39091
|
+
status: streamErr.status,
|
|
39092
|
+
errorMessage: errMsg,
|
|
39093
|
+
resetAtMs: observedResetAtMs(profile.id, Date.now())
|
|
39094
|
+
});
|
|
38801
39095
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
38802
39096
|
const canRecoverAsToolUse = canRecoverCapturedToolUses({
|
|
38803
39097
|
reason: sdkTerm.reason,
|
|
@@ -39010,7 +39304,7 @@ data: ${JSON.stringify({
|
|
|
39010
39304
|
safeEnqueue(encoder.encode(`event: error
|
|
39011
39305
|
data: ${JSON.stringify({
|
|
39012
39306
|
type: "error",
|
|
39013
|
-
error: { type: streamErr.type, message: streamErr.message }
|
|
39307
|
+
error: { type: streamErr.type, message: streamErr.message, ...retryAfterBodyFields(streamRetryAfter) }
|
|
39014
39308
|
})}
|
|
39015
39309
|
|
|
39016
39310
|
`), "error_event_before_stop");
|
|
@@ -39022,7 +39316,7 @@ data: {"type":"message_stop"}
|
|
|
39022
39316
|
safeEnqueue(encoder.encode(`event: error
|
|
39023
39317
|
data: ${JSON.stringify({
|
|
39024
39318
|
type: "error",
|
|
39025
|
-
error: { type: streamErr.type, message: streamErr.message }
|
|
39319
|
+
error: { type: streamErr.type, message: streamErr.message, ...retryAfterBodyFields(streamRetryAfter) }
|
|
39026
39320
|
})}
|
|
39027
39321
|
|
|
39028
39322
|
`), "error_event");
|
|
@@ -39046,6 +39340,7 @@ data: ${JSON.stringify({
|
|
|
39046
39340
|
cancel(reason) {
|
|
39047
39341
|
requestAbort.abort(reason);
|
|
39048
39342
|
requestAbort.detach();
|
|
39343
|
+
requestMeta.cascadeSubtreeCancel?.("stream_cancel");
|
|
39049
39344
|
if (!isIndependentSession && (!managedForkTarget || managedForkPublished || clientAssistantContentExposed)) {
|
|
39050
39345
|
evictSession2(profileSessionId, profileScopedCwd, lineageMessages, mappingExpectedGeneration);
|
|
39051
39346
|
claudeLog("passthrough.client_abort_settled", { action: "evict", source: "stream_cancel" });
|
|
@@ -39069,6 +39364,11 @@ data: ${JSON.stringify({
|
|
|
39069
39364
|
error: errMsg
|
|
39070
39365
|
});
|
|
39071
39366
|
const classified = requestAbort.controller.signal.aborted ? { status: 499, type: "request_cancelled", message: "The request was cancelled" } : classifyError(errMsg);
|
|
39367
|
+
const retryAfter = retryAfterSeconds({
|
|
39368
|
+
status: classified.status,
|
|
39369
|
+
errorMessage: errMsg,
|
|
39370
|
+
resetAtMs: observedResetAtMs(resolvedProfileId, Date.now())
|
|
39371
|
+
});
|
|
39072
39372
|
claudeLog("proxy.error", { error: errMsg, classified: classified.type });
|
|
39073
39373
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
39074
39374
|
diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
|
|
@@ -39103,7 +39403,17 @@ data: ${JSON.stringify({
|
|
|
39103
39403
|
textEvents: 0,
|
|
39104
39404
|
error: classified.type
|
|
39105
39405
|
});
|
|
39106
|
-
return new Response(JSON.stringify({
|
|
39406
|
+
return new Response(JSON.stringify({
|
|
39407
|
+
type: "error",
|
|
39408
|
+
error: {
|
|
39409
|
+
type: classified.type,
|
|
39410
|
+
message: classified.message,
|
|
39411
|
+
...retryAfterBodyFields(retryAfter)
|
|
39412
|
+
}
|
|
39413
|
+
}), {
|
|
39414
|
+
status: classified.status,
|
|
39415
|
+
headers: { "Content-Type": "application/json", ...retryAfterHeaders(retryAfter) }
|
|
39416
|
+
});
|
|
39107
39417
|
} finally {
|
|
39108
39418
|
if (!streamOwnsAbortLink) {
|
|
39109
39419
|
await abandonManagedFork("request_complete_without_commit");
|
|
@@ -39112,6 +39422,17 @@ data: ${JSON.stringify({
|
|
|
39112
39422
|
}
|
|
39113
39423
|
});
|
|
39114
39424
|
};
|
|
39425
|
+
const logSubtreeCancel = (parentKey, cancelled, requestId, source) => {
|
|
39426
|
+
const children = cancelled.keys.map((key) => truncateSessionKey(key));
|
|
39427
|
+
claudeLog("session.tree_cancel_propagated", {
|
|
39428
|
+
requestId,
|
|
39429
|
+
source,
|
|
39430
|
+
parent: truncateSessionKey(parentKey),
|
|
39431
|
+
children,
|
|
39432
|
+
requests: cancelled.requestIds.length
|
|
39433
|
+
});
|
|
39434
|
+
diagnosticLog2.session(`${requestId} session_tree_cancel source=${source} parent=${truncateSessionKey(parentKey)} ` + `children=${children.join(",")} requests=${cancelled.requestIds.length}`, requestId);
|
|
39435
|
+
};
|
|
39115
39436
|
const handleWithQueue = async (c, endpoint) => {
|
|
39116
39437
|
if (draining && c.req.header("x-meridian-internal-hop") !== internalHopToken) {
|
|
39117
39438
|
return drainingResponse();
|
|
@@ -39121,6 +39442,20 @@ data: ${JSON.stringify({
|
|
|
39121
39442
|
claudeLog("request.enter", { requestId, endpoint });
|
|
39122
39443
|
let sessionTurnLease;
|
|
39123
39444
|
let crossProcessTurnLease;
|
|
39445
|
+
let sessionTreeRegistration;
|
|
39446
|
+
let detachSubtreeAbortWatch;
|
|
39447
|
+
let subtreeSessionKey;
|
|
39448
|
+
let subtreeCascaded = false;
|
|
39449
|
+
const cascadeSubtreeCancel = (source) => {
|
|
39450
|
+
const parentKey = subtreeSessionKey;
|
|
39451
|
+
if (subtreeCascaded || !parentKey)
|
|
39452
|
+
return;
|
|
39453
|
+
subtreeCascaded = true;
|
|
39454
|
+
const cancelled = processSessionTree.cancelDescendants(parentKey, new Error(`Parent session ${truncateSessionKey(parentKey)} was cancelled`));
|
|
39455
|
+
if (cancelled.requestIds.length === 0)
|
|
39456
|
+
return;
|
|
39457
|
+
logSubtreeCancel(parentKey, cancelled, requestId, source);
|
|
39458
|
+
};
|
|
39124
39459
|
const turnWatchdogAbort = new AbortController;
|
|
39125
39460
|
activeRequestAborts.add(turnWatchdogAbort);
|
|
39126
39461
|
let finished = false;
|
|
@@ -39160,6 +39495,10 @@ data: ${JSON.stringify({
|
|
|
39160
39495
|
} else {
|
|
39161
39496
|
releaseSessionTurn(false);
|
|
39162
39497
|
}
|
|
39498
|
+
detachSubtreeAbortWatch?.();
|
|
39499
|
+
detachSubtreeAbortWatch = undefined;
|
|
39500
|
+
sessionTreeRegistration?.release();
|
|
39501
|
+
sessionTreeRegistration = undefined;
|
|
39163
39502
|
activeRequestAborts.delete(turnWatchdogAbort);
|
|
39164
39503
|
inFlightRequests--;
|
|
39165
39504
|
};
|
|
@@ -39188,6 +39527,21 @@ data: ${JSON.stringify({
|
|
|
39188
39527
|
routingTurnIdentity = adapter.getRoutingTurnIdentity?.(c, body);
|
|
39189
39528
|
const agentSessionId = adapter.getSessionId(c, body);
|
|
39190
39529
|
if (agentSessionId) {
|
|
39530
|
+
sessionTreeRegistration = processSessionTree.register({
|
|
39531
|
+
requestId,
|
|
39532
|
+
sessionKey: agentSessionId,
|
|
39533
|
+
parentKey: adapter.getParentSessionId?.(c, body),
|
|
39534
|
+
abort: (reason) => turnWatchdogAbort.abort(reason)
|
|
39535
|
+
});
|
|
39536
|
+
subtreeSessionKey = agentSessionId;
|
|
39537
|
+
const clientSignal = c.req.raw.signal;
|
|
39538
|
+
if (clientSignal.aborted) {
|
|
39539
|
+
cascadeSubtreeCancel("client_abort");
|
|
39540
|
+
} else {
|
|
39541
|
+
const onClientAbort = () => cascadeSubtreeCancel("client_abort");
|
|
39542
|
+
clientSignal.addEventListener("abort", onClientAbort, { once: true });
|
|
39543
|
+
detachSubtreeAbortWatch = () => clientSignal.removeEventListener("abort", onClientAbort);
|
|
39544
|
+
}
|
|
39191
39545
|
const arrivalProfileIds = new Set(getEffectiveProfiles(finalConfig.profiles).map((profile) => profile.id));
|
|
39192
39546
|
const explicitlyRequestedProfile = c.req.header("x-meridian-profile")?.trim();
|
|
39193
39547
|
if (explicitlyRequestedProfile)
|
|
@@ -39249,7 +39603,7 @@ data: ${JSON.stringify({
|
|
|
39249
39603
|
type: "overloaded_error",
|
|
39250
39604
|
message: "Timed out waiting for another process to finish this session turn"
|
|
39251
39605
|
}
|
|
39252
|
-
}), { status: 529, headers: { "Content-Type": "application/json" } });
|
|
39606
|
+
}), { status: 529, headers: { "Content-Type": "application/json", ...TRANSIENT_RETRY_AFTER_HEADERS } });
|
|
39253
39607
|
}
|
|
39254
39608
|
throw error51;
|
|
39255
39609
|
}
|
|
@@ -39267,7 +39621,8 @@ data: ${JSON.stringify({
|
|
|
39267
39621
|
routingTurnIdentity,
|
|
39268
39622
|
retainSessionTurnFence: () => {
|
|
39269
39623
|
retainSessionTurnFence = true;
|
|
39270
|
-
}
|
|
39624
|
+
},
|
|
39625
|
+
cascadeSubtreeCancel
|
|
39271
39626
|
};
|
|
39272
39627
|
const response = await handleMessages(c, requestMeta, {
|
|
39273
39628
|
body,
|
|
@@ -39287,7 +39642,29 @@ data: ${JSON.stringify({
|
|
|
39287
39642
|
};
|
|
39288
39643
|
app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
|
|
39289
39644
|
app.post("/messages", (c) => handleWithQueue(c, "/messages"));
|
|
39290
|
-
app.
|
|
39645
|
+
app.post("/v1/sessions/:key/cancel", (c) => {
|
|
39646
|
+
const key = c.req.param("key");
|
|
39647
|
+
if (!key) {
|
|
39648
|
+
return c.json({ type: "error", error: { type: "invalid_request_error", message: "Session key is required" } }, 400);
|
|
39649
|
+
}
|
|
39650
|
+
const cancelled = processSessionTree.cancelSubtree(key, new Error("Session cancelled by request"));
|
|
39651
|
+
if (cancelled.requestIds.length > 0) {
|
|
39652
|
+
claudeLog("session.tree_cancel_requested", {
|
|
39653
|
+
session: truncateSessionKey(key),
|
|
39654
|
+
keys: cancelled.keys.map((cancelledKey) => truncateSessionKey(cancelledKey)),
|
|
39655
|
+
requests: cancelled.requestIds.length
|
|
39656
|
+
});
|
|
39657
|
+
diagnosticLog2.session(`session_tree_cancel_requested session=${truncateSessionKey(key)} requests=${cancelled.requestIds.length}`);
|
|
39658
|
+
}
|
|
39659
|
+
return c.json({
|
|
39660
|
+
session: key,
|
|
39661
|
+
cancelled: { sessions: cancelled.keys.length, requests: cancelled.requestIds.length },
|
|
39662
|
+
requestIds: cancelled.requestIds
|
|
39663
|
+
});
|
|
39664
|
+
});
|
|
39665
|
+
app.route("/telemetry", createTelemetryRoutes({
|
|
39666
|
+
getSessionTree: () => processSessionTree.stats()
|
|
39667
|
+
}));
|
|
39291
39668
|
app.get("/settings", (c) => {
|
|
39292
39669
|
const { settingsPageHtml: settingsPageHtml2 } = (init_settingsPage(), __toCommonJS(exports_settingsPage));
|
|
39293
39670
|
return c.html(settingsPageHtml2);
|
|
@@ -39628,6 +40005,12 @@ data: ${JSON.stringify({
|
|
|
39628
40005
|
`);
|
|
39629
40006
|
buffer = lines.pop() ?? "";
|
|
39630
40007
|
for (const line of lines) {
|
|
40008
|
+
if (line.startsWith(":")) {
|
|
40009
|
+
controller.enqueue(encoder.encode(`${line}
|
|
40010
|
+
|
|
40011
|
+
`));
|
|
40012
|
+
continue;
|
|
40013
|
+
}
|
|
39631
40014
|
if (!line.startsWith("data: "))
|
|
39632
40015
|
continue;
|
|
39633
40016
|
const dataStr = line.slice(6).trim();
|
|
@@ -39746,6 +40129,12 @@ data: ${JSON.stringify({
|
|
|
39746
40129
|
`);
|
|
39747
40130
|
buffer = lines.pop() ?? "";
|
|
39748
40131
|
for (const line of lines) {
|
|
40132
|
+
if (line.startsWith(":")) {
|
|
40133
|
+
controller.enqueue(encoder.encode(`${line}
|
|
40134
|
+
|
|
40135
|
+
`));
|
|
40136
|
+
continue;
|
|
40137
|
+
}
|
|
39749
40138
|
if (!line.startsWith("data: "))
|
|
39750
40139
|
continue;
|
|
39751
40140
|
const dataStr = line.slice(6).trim();
|
|
@@ -39790,7 +40179,9 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
39790
40179
|
});
|
|
39791
40180
|
});
|
|
39792
40181
|
app.get("/v1/models", async (c) => {
|
|
39793
|
-
const
|
|
40182
|
+
const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile);
|
|
40183
|
+
const profileEnvOverrides = Object.keys(profile.env).length > 0 ? profile.env : undefined;
|
|
40184
|
+
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, profileEnvOverrides);
|
|
39794
40185
|
const extendedContext = subscriptionIncludesExtendedContext(authStatus?.subscriptionType);
|
|
39795
40186
|
return c.json({ object: "list", data: buildModelList(extendedContext) });
|
|
39796
40187
|
});
|