@rynfar/meridian 1.65.1 → 1.66.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-pdpry6q0.js → cli-5jxyma6z.js} +5 -2
- package/dist/{cli-0ed6j0vk.js → cli-m9wqgk1q.js} +50 -16
- package/dist/{cli-7cts44b5.js → cli-nzvbaqjp.js} +415 -55
- package/dist/cli.js +5 -5
- package/dist/{profileCli-ap7eg985.js → profileCli-qhcr87tm.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.map +1 -1
- package/dist/proxy/models.d.ts +26 -8
- 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/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 +1 -1
|
@@ -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-m9wqgk1q.js";
|
|
57
58
|
import {
|
|
58
59
|
claudeLog,
|
|
59
60
|
createPlatformCredentialStore,
|
|
@@ -2827,7 +2828,7 @@ function extractClaudeCodeClientCwd(body) {
|
|
|
2827
2828
|
const match2 = systemText.match(/Primary working directory:\s*([^\n<]+)/i);
|
|
2828
2829
|
return match2?.[1]?.trim() || undefined;
|
|
2829
2830
|
}
|
|
2830
|
-
function
|
|
2831
|
+
function extractClaudeCodeSessionIdentity(body) {
|
|
2831
2832
|
if (!body || typeof body !== "object")
|
|
2832
2833
|
return;
|
|
2833
2834
|
const metadata = body.metadata;
|
|
@@ -2845,7 +2846,17 @@ function extractClaudeCodeSessionId(body) {
|
|
|
2845
2846
|
if (!userMetadata || typeof userMetadata !== "object")
|
|
2846
2847
|
return;
|
|
2847
2848
|
const sessionId = userMetadata.session_id;
|
|
2848
|
-
|
|
2849
|
+
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
2850
|
+
return;
|
|
2851
|
+
const parentSessionId = userMetadata.parent_session_id;
|
|
2852
|
+
const parent = typeof parentSessionId === "string" && parentSessionId.length > 0 && parentSessionId !== sessionId ? parentSessionId : undefined;
|
|
2853
|
+
return parent ? { sessionId, parentSessionId: parent } : { sessionId };
|
|
2854
|
+
}
|
|
2855
|
+
function extractClaudeCodeSessionId(body) {
|
|
2856
|
+
return extractClaudeCodeSessionIdentity(body)?.sessionId;
|
|
2857
|
+
}
|
|
2858
|
+
function extractClaudeCodeParentSessionId(body) {
|
|
2859
|
+
return extractClaudeCodeSessionIdentity(body)?.parentSessionId;
|
|
2849
2860
|
}
|
|
2850
2861
|
var claudeCodeAdapter;
|
|
2851
2862
|
var init_claudecode = __esm(() => {
|
|
@@ -2858,6 +2869,9 @@ var init_claudecode = __esm(() => {
|
|
|
2858
2869
|
getSessionId(_c, body) {
|
|
2859
2870
|
return extractClaudeCodeSessionId(body);
|
|
2860
2871
|
},
|
|
2872
|
+
getParentSessionId(_c, body) {
|
|
2873
|
+
return extractClaudeCodeParentSessionId(body);
|
|
2874
|
+
},
|
|
2861
2875
|
extractWorkingDirectory(_body) {
|
|
2862
2876
|
return;
|
|
2863
2877
|
},
|
|
@@ -3121,6 +3135,11 @@ var init_prime = __esm(() => {
|
|
|
3121
3135
|
getSessionId(c, body) {
|
|
3122
3136
|
return c.req.header("x-session-affinity") ?? extractClaudeCodeSessionId(body);
|
|
3123
3137
|
},
|
|
3138
|
+
getParentSessionId(c, body) {
|
|
3139
|
+
if (c.req.header("x-session-affinity"))
|
|
3140
|
+
return;
|
|
3141
|
+
return extractClaudeCodeParentSessionId(body);
|
|
3142
|
+
},
|
|
3124
3143
|
extractWorkingDirectory(body) {
|
|
3125
3144
|
return extractPrimeCwd(body);
|
|
3126
3145
|
},
|
|
@@ -6567,6 +6586,129 @@ function linkRequestAbort(signal) {
|
|
|
6567
6586
|
};
|
|
6568
6587
|
}
|
|
6569
6588
|
|
|
6589
|
+
// src/proxy/sessionTree.ts
|
|
6590
|
+
var EMPTY_CANCELLATION = { keys: [], requestIds: [] };
|
|
6591
|
+
var MAX_SUBTREE_DEPTH = 64;
|
|
6592
|
+
function truncateSessionKey(key, length = 8) {
|
|
6593
|
+
return key.length > length ? `${key.slice(0, length)}…` : key;
|
|
6594
|
+
}
|
|
6595
|
+
|
|
6596
|
+
class SessionTreeRegistry {
|
|
6597
|
+
nextToken = 1;
|
|
6598
|
+
entries = new Map;
|
|
6599
|
+
childrenByParent = new Map;
|
|
6600
|
+
propagations = 0;
|
|
6601
|
+
cancelledDescendants = 0;
|
|
6602
|
+
register(entry) {
|
|
6603
|
+
const token = this.nextToken++;
|
|
6604
|
+
this.entries.set(token, entry);
|
|
6605
|
+
const indexedParent = entry.parentKey && entry.parentKey !== entry.sessionKey ? entry.parentKey : undefined;
|
|
6606
|
+
if (indexedParent) {
|
|
6607
|
+
let siblings = this.childrenByParent.get(indexedParent);
|
|
6608
|
+
if (!siblings) {
|
|
6609
|
+
siblings = new Set;
|
|
6610
|
+
this.childrenByParent.set(indexedParent, siblings);
|
|
6611
|
+
}
|
|
6612
|
+
siblings.add(token);
|
|
6613
|
+
}
|
|
6614
|
+
let released = false;
|
|
6615
|
+
return {
|
|
6616
|
+
release: () => {
|
|
6617
|
+
if (released)
|
|
6618
|
+
return;
|
|
6619
|
+
released = true;
|
|
6620
|
+
this.entries.delete(token);
|
|
6621
|
+
if (!indexedParent)
|
|
6622
|
+
return;
|
|
6623
|
+
const siblings = this.childrenByParent.get(indexedParent);
|
|
6624
|
+
if (!siblings)
|
|
6625
|
+
return;
|
|
6626
|
+
siblings.delete(token);
|
|
6627
|
+
if (siblings.size === 0)
|
|
6628
|
+
this.childrenByParent.delete(indexedParent);
|
|
6629
|
+
}
|
|
6630
|
+
};
|
|
6631
|
+
}
|
|
6632
|
+
descendantsOf(sessionKey) {
|
|
6633
|
+
const visitedKeys = new Set([sessionKey]);
|
|
6634
|
+
let frontier = [sessionKey];
|
|
6635
|
+
const found = [];
|
|
6636
|
+
for (let depth = 0;depth < MAX_SUBTREE_DEPTH && frontier.length > 0; depth++) {
|
|
6637
|
+
const next = [];
|
|
6638
|
+
for (const parentKey of frontier) {
|
|
6639
|
+
const tokens = this.childrenByParent.get(parentKey);
|
|
6640
|
+
if (!tokens)
|
|
6641
|
+
continue;
|
|
6642
|
+
for (const token of tokens) {
|
|
6643
|
+
const entry = this.entries.get(token);
|
|
6644
|
+
if (!entry)
|
|
6645
|
+
continue;
|
|
6646
|
+
found.push(entry);
|
|
6647
|
+
if (visitedKeys.has(entry.sessionKey))
|
|
6648
|
+
continue;
|
|
6649
|
+
visitedKeys.add(entry.sessionKey);
|
|
6650
|
+
next.push(entry.sessionKey);
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6653
|
+
frontier = next;
|
|
6654
|
+
}
|
|
6655
|
+
return found;
|
|
6656
|
+
}
|
|
6657
|
+
liveRequestsFor(sessionKey) {
|
|
6658
|
+
const found = [];
|
|
6659
|
+
for (const entry of this.entries.values()) {
|
|
6660
|
+
if (entry.sessionKey === sessionKey)
|
|
6661
|
+
found.push(entry);
|
|
6662
|
+
}
|
|
6663
|
+
return found;
|
|
6664
|
+
}
|
|
6665
|
+
cancelDescendants(sessionKey, reason) {
|
|
6666
|
+
return this.cancel(sessionKey, { reason });
|
|
6667
|
+
}
|
|
6668
|
+
cancelSubtree(sessionKey, reason) {
|
|
6669
|
+
return this.cancel(sessionKey, { reason, includeSelf: true });
|
|
6670
|
+
}
|
|
6671
|
+
cancel(sessionKey, options) {
|
|
6672
|
+
const descendants = this.descendantsOf(sessionKey);
|
|
6673
|
+
const targets = options.includeSelf ? [...this.liveRequestsFor(sessionKey), ...descendants] : descendants;
|
|
6674
|
+
if (targets.length === 0)
|
|
6675
|
+
return EMPTY_CANCELLATION;
|
|
6676
|
+
const keys = [];
|
|
6677
|
+
const requestIds = [];
|
|
6678
|
+
for (const entry of targets) {
|
|
6679
|
+
try {
|
|
6680
|
+
entry.abort(options.reason);
|
|
6681
|
+
} catch {}
|
|
6682
|
+
if (!keys.includes(entry.sessionKey))
|
|
6683
|
+
keys.push(entry.sessionKey);
|
|
6684
|
+
requestIds.push(entry.requestId);
|
|
6685
|
+
}
|
|
6686
|
+
this.propagations++;
|
|
6687
|
+
this.cancelledDescendants += descendants.length;
|
|
6688
|
+
return { keys, requestIds };
|
|
6689
|
+
}
|
|
6690
|
+
stats() {
|
|
6691
|
+
let linked = 0;
|
|
6692
|
+
for (const entry of this.entries.values()) {
|
|
6693
|
+
if (entry.parentKey)
|
|
6694
|
+
linked++;
|
|
6695
|
+
}
|
|
6696
|
+
return {
|
|
6697
|
+
tracked: this.entries.size,
|
|
6698
|
+
linked,
|
|
6699
|
+
propagations: this.propagations,
|
|
6700
|
+
cancelledDescendants: this.cancelledDescendants
|
|
6701
|
+
};
|
|
6702
|
+
}
|
|
6703
|
+
clear() {
|
|
6704
|
+
this.entries.clear();
|
|
6705
|
+
this.childrenByParent.clear();
|
|
6706
|
+
this.propagations = 0;
|
|
6707
|
+
this.cancelledDescendants = 0;
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6710
|
+
var processSessionTree = new SessionTreeRegistry;
|
|
6711
|
+
|
|
6570
6712
|
// src/proxy/concurrency.ts
|
|
6571
6713
|
var DEFAULT_MAX_CONCURRENT = 10;
|
|
6572
6714
|
var didWarnInvalidMaxConcurrent = false;
|
|
@@ -6746,6 +6888,57 @@ async function closeServerWithGracePeriod(server, options) {
|
|
|
6746
6888
|
await closePromise;
|
|
6747
6889
|
}
|
|
6748
6890
|
|
|
6891
|
+
// src/proxy/retryAfter.ts
|
|
6892
|
+
var RETRYABLE_STATUSES = new Set([429, 503, 529]);
|
|
6893
|
+
var OVERLOADED_RETRY_AFTER_SECONDS = 5;
|
|
6894
|
+
var RATE_LIMIT_DEFAULT_RETRY_AFTER_SECONDS = 60;
|
|
6895
|
+
var RETRY_AFTER_MIN_SECONDS = 1;
|
|
6896
|
+
var RETRY_AFTER_MAX_SECONDS = 24 * 60 * 60;
|
|
6897
|
+
function parseRetryAfterMs(raw2, now = Date.now()) {
|
|
6898
|
+
if (!raw2)
|
|
6899
|
+
return null;
|
|
6900
|
+
const seconds = Number(raw2);
|
|
6901
|
+
if (Number.isFinite(seconds))
|
|
6902
|
+
return Math.max(0, seconds * 1000);
|
|
6903
|
+
const retryAt = Date.parse(raw2);
|
|
6904
|
+
return Number.isFinite(retryAt) ? Math.max(0, retryAt - now) : null;
|
|
6905
|
+
}
|
|
6906
|
+
function extractRetryAfterSeconds(errMsg) {
|
|
6907
|
+
if (!errMsg)
|
|
6908
|
+
return null;
|
|
6909
|
+
const match2 = errMsg.match(/retry[-_ ]?after"?\s*[:=]\s*"?(\d+)/i);
|
|
6910
|
+
if (!match2?.[1])
|
|
6911
|
+
return null;
|
|
6912
|
+
const seconds = Number(match2[1]);
|
|
6913
|
+
return Number.isFinite(seconds) ? seconds : null;
|
|
6914
|
+
}
|
|
6915
|
+
function retryAfterSeconds(input) {
|
|
6916
|
+
if (!RETRYABLE_STATUSES.has(input.status))
|
|
6917
|
+
return null;
|
|
6918
|
+
const now = input.now ?? Date.now();
|
|
6919
|
+
const upstreamMs = parseRetryAfterMs(input.upstreamRetryAfter, now);
|
|
6920
|
+
if (upstreamMs !== null)
|
|
6921
|
+
return clamp(Math.ceil(upstreamMs / 1000));
|
|
6922
|
+
const embedded = extractRetryAfterSeconds(input.errorMessage);
|
|
6923
|
+
if (embedded !== null)
|
|
6924
|
+
return clamp(embedded);
|
|
6925
|
+
if (input.resetAtMs != null && input.resetAtMs > now) {
|
|
6926
|
+
return clamp(Math.ceil((input.resetAtMs - now) / 1000));
|
|
6927
|
+
}
|
|
6928
|
+
return input.status === 429 ? RATE_LIMIT_DEFAULT_RETRY_AFTER_SECONDS : OVERLOADED_RETRY_AFTER_SECONDS;
|
|
6929
|
+
}
|
|
6930
|
+
function retryAfterHeaders(seconds) {
|
|
6931
|
+
return seconds === null ? {} : { "Retry-After": String(seconds) };
|
|
6932
|
+
}
|
|
6933
|
+
function retryAfterBodyFields(seconds) {
|
|
6934
|
+
return seconds === null ? {} : { retry_after: seconds };
|
|
6935
|
+
}
|
|
6936
|
+
function clamp(seconds) {
|
|
6937
|
+
if (!Number.isFinite(seconds))
|
|
6938
|
+
return RATE_LIMIT_DEFAULT_RETRY_AFTER_SECONDS;
|
|
6939
|
+
return Math.min(RETRY_AFTER_MAX_SECONDS, Math.max(RETRY_AFTER_MIN_SECONDS, Math.round(seconds)));
|
|
6940
|
+
}
|
|
6941
|
+
|
|
6749
6942
|
// src/proxy/oauthUsage.ts
|
|
6750
6943
|
var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
6751
6944
|
var OAUTH_BETA_HEADER = "oauth-2025-04-20";
|
|
@@ -6776,15 +6969,6 @@ function normalizeUtilization(raw2) {
|
|
|
6776
6969
|
return null;
|
|
6777
6970
|
return Math.max(0, raw2 / 100);
|
|
6778
6971
|
}
|
|
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
6972
|
function modelScopedWindowType(limit) {
|
|
6789
6973
|
if (limit.kind !== "weekly_scoped")
|
|
6790
6974
|
return null;
|
|
@@ -22081,6 +22265,13 @@ function render(s, reqs, logs) {
|
|
|
22081
22265
|
+ card('Median TTFB', ms(s.ttfb.p50), 'p95: ' + ms(s.ttfb.p95))
|
|
22082
22266
|
+ card('Proxy Overhead', ms(s.proxyOverhead.p50), 'p95: ' + ms(s.proxyOverhead.p95))
|
|
22083
22267
|
+ card('Queue Wait', ms(s.queueWait.p50), 'p95: ' + ms(s.queueWait.p95))
|
|
22268
|
+
// Only rendered when a subagent tree has actually been seen: for a
|
|
22269
|
+
// single-agent client these numbers are permanently zero and would just be
|
|
22270
|
+
// a dead tile. Counts are cumulative, not windowed.
|
|
22271
|
+
+ ((s.sessionTree && (s.sessionTree.linked > 0 || s.sessionTree.cancelledDescendants > 0))
|
|
22272
|
+
? card('Subtree Cancels', s.sessionTree.cancelledDescendants,
|
|
22273
|
+
s.sessionTree.linked + ' linked live / ' + s.sessionTree.propagations + ' propagations')
|
|
22274
|
+
: '')
|
|
22084
22275
|
+ '</div>';
|
|
22085
22276
|
|
|
22086
22277
|
// Token usage cards
|
|
@@ -22283,7 +22474,7 @@ timer = setInterval(refresh, 5000);
|
|
|
22283
22474
|
// src/telemetry/routes.ts
|
|
22284
22475
|
var _iconPath = resolve2(dirname2(fileURLToPath(import.meta.url)), "..", "..", "assets", "icon.svg");
|
|
22285
22476
|
var _iconSvg = existsSync3(_iconPath) ? readFileSync2(_iconPath, "utf-8") : null;
|
|
22286
|
-
function createTelemetryRoutes() {
|
|
22477
|
+
function createTelemetryRoutes(deps = {}) {
|
|
22287
22478
|
const routes = new Hono2;
|
|
22288
22479
|
routes.get("/", (c) => {
|
|
22289
22480
|
return c.html(dashboardHtml);
|
|
@@ -22310,7 +22501,8 @@ function createTelemetryRoutes() {
|
|
|
22310
22501
|
routes.get("/summary", (c) => {
|
|
22311
22502
|
const windowMs = Number.parseInt(c.req.query("window") || "3600000", 10);
|
|
22312
22503
|
const summary = telemetryStore2.summarize(windowMs);
|
|
22313
|
-
|
|
22504
|
+
const sessionTree = deps.getSessionTree?.();
|
|
22505
|
+
return c.json(sessionTree ? { ...summary, sessionTree } : summary);
|
|
22314
22506
|
});
|
|
22315
22507
|
routes.get("/logs", (c) => {
|
|
22316
22508
|
const limit = Number.parseInt(c.req.query("limit") || "100", 10);
|
|
@@ -22716,6 +22908,7 @@ var BILLING_SIGNALS = [
|
|
|
22716
22908
|
/insufficient (?:credit|funds|balance)/
|
|
22717
22909
|
];
|
|
22718
22910
|
var HIT_YOUR_LIMIT = /hit your (?:[\w-]+ )?limit/;
|
|
22911
|
+
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;
|
|
22719
22912
|
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*$/;
|
|
22720
22913
|
var HTTP_401 = /(?:^|[^0-9a-f])401(?![0-9a-f]|:\d)/;
|
|
22721
22914
|
var HTTP_429 = /(?:^|[^0-9a-f])429(?![0-9a-f]|:\d)/;
|
|
@@ -22737,7 +22930,7 @@ function classifyError(errMsg, model) {
|
|
|
22737
22930
|
message: "Claude authentication expired or invalid. Run 'claude login' in your terminal to re-authenticate, then restart the proxy."
|
|
22738
22931
|
};
|
|
22739
22932
|
}
|
|
22740
|
-
if (HTTP_429.test(lower) || lower.includes("rate limit") || lower.includes("too many requests") || HIT_YOUR_LIMIT.test(lower) || lower.includes("usage limit reached") || OUT_OF_USAGE_CREDITS.test(lower)) {
|
|
22933
|
+
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)) {
|
|
22741
22934
|
const hint = lower.includes("1m") || lower.includes("context") ? extendedContextHint(model) : "";
|
|
22742
22935
|
return {
|
|
22743
22936
|
status: 429,
|
|
@@ -23354,6 +23547,23 @@ function stopUpdateCheck() {
|
|
|
23354
23547
|
}
|
|
23355
23548
|
|
|
23356
23549
|
// src/proxy/openai.ts
|
|
23550
|
+
var ANTHROPIC_USAGE_FIELDS = [
|
|
23551
|
+
"input_tokens",
|
|
23552
|
+
"output_tokens",
|
|
23553
|
+
"cache_read_input_tokens",
|
|
23554
|
+
"cache_creation_input_tokens"
|
|
23555
|
+
];
|
|
23556
|
+
function mergeAnthropicUsage(current, update) {
|
|
23557
|
+
const merged = { ...current };
|
|
23558
|
+
for (const field of ANTHROPIC_USAGE_FIELDS) {
|
|
23559
|
+
if (typeof update[field] === "number")
|
|
23560
|
+
merged[field] = update[field];
|
|
23561
|
+
}
|
|
23562
|
+
return merged;
|
|
23563
|
+
}
|
|
23564
|
+
function totalAnthropicInputTokens(usage) {
|
|
23565
|
+
return (usage?.input_tokens ?? 0) + (usage?.cache_read_input_tokens ?? 0) + (usage?.cache_creation_input_tokens ?? 0);
|
|
23566
|
+
}
|
|
23357
23567
|
function extractOpenAiContent(content) {
|
|
23358
23568
|
if (typeof content === "string")
|
|
23359
23569
|
return content;
|
|
@@ -23595,7 +23805,7 @@ function translateAnthropicToOpenAi(response, completionId, model, created, opti
|
|
|
23595
23805
|
}));
|
|
23596
23806
|
const thinkingPassthrough = options?.thinkingPassthrough;
|
|
23597
23807
|
const thinking = thinkingPassthrough !== false ? contentBlocks.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") : "";
|
|
23598
|
-
const promptTokens = response.usage
|
|
23808
|
+
const promptTokens = totalAnthropicInputTokens(response.usage);
|
|
23599
23809
|
const completionTokens = response.usage?.output_tokens ?? 0;
|
|
23600
23810
|
return {
|
|
23601
23811
|
id: completionId,
|
|
@@ -23615,7 +23825,11 @@ function translateAnthropicToOpenAi(response, completionId, model, created, opti
|
|
|
23615
23825
|
usage: {
|
|
23616
23826
|
prompt_tokens: promptTokens,
|
|
23617
23827
|
completion_tokens: completionTokens,
|
|
23618
|
-
total_tokens: promptTokens + completionTokens
|
|
23828
|
+
total_tokens: promptTokens + completionTokens,
|
|
23829
|
+
prompt_tokens_details: {
|
|
23830
|
+
cached_tokens: response.usage?.cache_read_input_tokens ?? 0,
|
|
23831
|
+
cache_write_tokens: response.usage?.cache_creation_input_tokens ?? 0
|
|
23832
|
+
}
|
|
23619
23833
|
}
|
|
23620
23834
|
};
|
|
23621
23835
|
}
|
|
@@ -23626,15 +23840,18 @@ function createSseTranslator(ctx) {
|
|
|
23626
23840
|
if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block.name === "string") {
|
|
23627
23841
|
toolCallIndex++;
|
|
23628
23842
|
}
|
|
23843
|
+
if (event.type === "message_start" && event.message?.usage) {
|
|
23844
|
+
lastUsage = mergeAnthropicUsage(lastUsage, event.message.usage);
|
|
23845
|
+
}
|
|
23629
23846
|
if (event.type === "message_delta" && event.usage) {
|
|
23630
|
-
lastUsage = event.usage;
|
|
23847
|
+
lastUsage = mergeAnthropicUsage(lastUsage, event.usage);
|
|
23631
23848
|
}
|
|
23632
23849
|
return translateAnthropicSseEvent(event, ctx.completionId, ctx.model, ctx.created, toolCallIndex, ctx.thinkingPassthrough);
|
|
23633
23850
|
};
|
|
23634
23851
|
translate.buildUsageChunk = () => {
|
|
23635
23852
|
if (!ctx.includeUsage || !lastUsage)
|
|
23636
23853
|
return null;
|
|
23637
|
-
const promptTokens = lastUsage
|
|
23854
|
+
const promptTokens = totalAnthropicInputTokens(lastUsage);
|
|
23638
23855
|
const completionTokens = lastUsage.output_tokens ?? 0;
|
|
23639
23856
|
return {
|
|
23640
23857
|
id: ctx.completionId,
|
|
@@ -23645,7 +23862,11 @@ function createSseTranslator(ctx) {
|
|
|
23645
23862
|
usage: {
|
|
23646
23863
|
prompt_tokens: promptTokens,
|
|
23647
23864
|
completion_tokens: completionTokens,
|
|
23648
|
-
total_tokens: promptTokens + completionTokens
|
|
23865
|
+
total_tokens: promptTokens + completionTokens,
|
|
23866
|
+
prompt_tokens_details: {
|
|
23867
|
+
cached_tokens: lastUsage.cache_read_input_tokens ?? 0,
|
|
23868
|
+
cache_write_tokens: lastUsage.cache_creation_input_tokens ?? 0
|
|
23869
|
+
}
|
|
23649
23870
|
}
|
|
23650
23871
|
};
|
|
23651
23872
|
};
|
|
@@ -23987,9 +24208,17 @@ function reasoningRequested(body) {
|
|
|
23987
24208
|
return Array.isArray(include) && include.some((v) => typeof v === "string" && v.startsWith("reasoning"));
|
|
23988
24209
|
}
|
|
23989
24210
|
function mapUsage(usage) {
|
|
23990
|
-
const input = usage
|
|
24211
|
+
const input = totalAnthropicInputTokens(usage);
|
|
23991
24212
|
const output = usage?.output_tokens ?? 0;
|
|
23992
|
-
return {
|
|
24213
|
+
return {
|
|
24214
|
+
input_tokens: input,
|
|
24215
|
+
output_tokens: output,
|
|
24216
|
+
total_tokens: input + output,
|
|
24217
|
+
input_tokens_details: {
|
|
24218
|
+
cached_tokens: usage?.cache_read_input_tokens ?? 0,
|
|
24219
|
+
cache_write_tokens: usage?.cache_creation_input_tokens ?? 0
|
|
24220
|
+
}
|
|
24221
|
+
};
|
|
23993
24222
|
}
|
|
23994
24223
|
function toResponsesStatus(stopReason) {
|
|
23995
24224
|
return stopReason === "max_tokens" ? "incomplete" : "completed";
|
|
@@ -24047,8 +24276,7 @@ function translateAnthropicToResponses(res, ctx) {
|
|
|
24047
24276
|
function createResponsesSseTranslator(ctx) {
|
|
24048
24277
|
let seq = 0;
|
|
24049
24278
|
let outputIndex = 0;
|
|
24050
|
-
let
|
|
24051
|
-
let outputTokens = 0;
|
|
24279
|
+
let usage;
|
|
24052
24280
|
let createdEmitted = false;
|
|
24053
24281
|
let stopReason;
|
|
24054
24282
|
const blocks = new Map;
|
|
@@ -24069,7 +24297,8 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24069
24297
|
const out = [];
|
|
24070
24298
|
switch (event.type) {
|
|
24071
24299
|
case "message_start": {
|
|
24072
|
-
|
|
24300
|
+
if (event.message?.usage)
|
|
24301
|
+
usage = mergeAnthropicUsage(usage, event.message.usage);
|
|
24073
24302
|
if (!createdEmitted) {
|
|
24074
24303
|
createdEmitted = true;
|
|
24075
24304
|
out.push(emit("response.created", { response: responseEnvelope("in_progress", { output: [] }) }));
|
|
@@ -24185,8 +24414,8 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24185
24414
|
break;
|
|
24186
24415
|
}
|
|
24187
24416
|
case "message_delta": {
|
|
24188
|
-
if (
|
|
24189
|
-
|
|
24417
|
+
if (event.usage)
|
|
24418
|
+
usage = mergeAnthropicUsage(usage, event.usage);
|
|
24190
24419
|
if (typeof event.delta?.stop_reason === "string")
|
|
24191
24420
|
stopReason = event.delta.stop_reason;
|
|
24192
24421
|
break;
|
|
@@ -24196,7 +24425,7 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24196
24425
|
out.push(emit(status === "incomplete" ? "response.incomplete" : "response.completed", {
|
|
24197
24426
|
response: responseEnvelope(status, {
|
|
24198
24427
|
output: finalOutput,
|
|
24199
|
-
usage:
|
|
24428
|
+
usage: mapUsage(usage),
|
|
24200
24429
|
parallel_tool_calls: true,
|
|
24201
24430
|
tool_choice: "auto",
|
|
24202
24431
|
tools: [],
|
|
@@ -35437,9 +35666,14 @@ function createProxyServer(config2 = {}) {
|
|
|
35437
35666
|
const internalHopToken = randomUUID6();
|
|
35438
35667
|
const errorEnvelope = (shape, type, message) => shape === "anthropic" ? { type: "error", error: { type, message } } : { error: { type, message, code: null } };
|
|
35439
35668
|
const DRAIN_MESSAGE = "Meridian is shutting down and is not accepting new requests. Retry against another instance.";
|
|
35669
|
+
const TRANSIENT_RETRY_AFTER_HEADERS = retryAfterHeaders(OVERLOADED_RETRY_AFTER_SECONDS);
|
|
35440
35670
|
const drainingResponse = (shape = "anthropic") => new Response(JSON.stringify(errorEnvelope(shape, "overloaded_error", DRAIN_MESSAGE)), {
|
|
35441
35671
|
status: 503,
|
|
35442
|
-
headers: {
|
|
35672
|
+
headers: {
|
|
35673
|
+
"Content-Type": "application/json",
|
|
35674
|
+
"x-meridian-draining": "1",
|
|
35675
|
+
...TRANSIENT_RETRY_AFTER_HEADERS
|
|
35676
|
+
}
|
|
35443
35677
|
});
|
|
35444
35678
|
async function relayInnerError(internalRes, shape) {
|
|
35445
35679
|
const errBody = await internalRes.text();
|
|
@@ -35455,6 +35689,9 @@ function createProxyServer(config2 = {}) {
|
|
|
35455
35689
|
const drainingHeader = internalRes.headers.get("x-meridian-draining");
|
|
35456
35690
|
if (drainingHeader)
|
|
35457
35691
|
headers["x-meridian-draining"] = drainingHeader;
|
|
35692
|
+
const innerRetryAfter = internalRes.headers.get("retry-after");
|
|
35693
|
+
if (innerRetryAfter)
|
|
35694
|
+
headers["Retry-After"] = innerRetryAfter;
|
|
35458
35695
|
return new Response(JSON.stringify(payload), { status: internalRes.status, headers });
|
|
35459
35696
|
}
|
|
35460
35697
|
async function* runSdkQueryAttempt(params, signal, requestMeta, mode, activeLocators) {
|
|
@@ -35532,13 +35769,20 @@ function createProxyServer(config2 = {}) {
|
|
|
35532
35769
|
const setting = getSetting("profileOrder");
|
|
35533
35770
|
return Array.isArray(setting) && setting.length > 0 ? setting : undefined;
|
|
35534
35771
|
}
|
|
35535
|
-
function
|
|
35536
|
-
|
|
35772
|
+
function profileCooldownWindows(profileId) {
|
|
35773
|
+
return rateLimitStore.getAll(profileId).map((e) => ({
|
|
35537
35774
|
type: e.rateLimitType ?? "",
|
|
35538
35775
|
resetsAt: e.resetsAt,
|
|
35539
35776
|
exhausted: e.status === "rejected" || (e.utilization ?? 0) >= 1
|
|
35540
35777
|
}));
|
|
35541
|
-
|
|
35778
|
+
}
|
|
35779
|
+
function priorityCooldownUntil(profileId, now) {
|
|
35780
|
+
return resolveCooldownUntil(profileCooldownWindows(profileId), now, PRIORITY_DEFAULT_COOLDOWN_MS);
|
|
35781
|
+
}
|
|
35782
|
+
function observedResetAtMs(profileId, now) {
|
|
35783
|
+
if (!profileId)
|
|
35784
|
+
return null;
|
|
35785
|
+
return findCooldownReset(profileCooldownWindows(profileId), now);
|
|
35542
35786
|
}
|
|
35543
35787
|
function refinePriorityCooldown(profileId) {
|
|
35544
35788
|
const target = getEffectiveProfiles(finalConfig.profiles).find((p) => p.id === profileId);
|
|
@@ -35659,7 +35903,7 @@ function createProxyServer(config2 = {}) {
|
|
|
35659
35903
|
return options.context.json({
|
|
35660
35904
|
type: "error",
|
|
35661
35905
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35662
|
-
}, 503);
|
|
35906
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35663
35907
|
}
|
|
35664
35908
|
attemptOwnerToken = claim.ownerToken;
|
|
35665
35909
|
} catch (error51) {
|
|
@@ -35670,7 +35914,7 @@ function createProxyServer(config2 = {}) {
|
|
|
35670
35914
|
return options.context.json({
|
|
35671
35915
|
type: "error",
|
|
35672
35916
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35673
|
-
}, 503);
|
|
35917
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35674
35918
|
}
|
|
35675
35919
|
}
|
|
35676
35920
|
const settleAttempt = (disposition) => {
|
|
@@ -35690,11 +35934,12 @@ function createProxyServer(config2 = {}) {
|
|
|
35690
35934
|
const unavailableAttemptResponse = () => options.context.json({
|
|
35691
35935
|
type: "error",
|
|
35692
35936
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35693
|
-
}, 503);
|
|
35937
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35694
35938
|
let lastError = null;
|
|
35695
35939
|
let lastStatus = 429;
|
|
35696
35940
|
let previous = null;
|
|
35697
35941
|
let previousReason = "rate_limit_error";
|
|
35942
|
+
let earliestPoolReset = null;
|
|
35698
35943
|
for (const [attempt, candidate] of options.candidateIds.entries()) {
|
|
35699
35944
|
const exposure = { committed: false };
|
|
35700
35945
|
const priorityPublication = options.durableRoute && options.publicationTurn ? {
|
|
@@ -35733,6 +35978,9 @@ function createProxyServer(config2 = {}) {
|
|
|
35733
35978
|
const quotaRefusal = isQuotaRefusal(reason);
|
|
35734
35979
|
const cooldownUntil = quotaRefusal ? priorityCooldownUntil(candidate, Date.now()) : Date.now() + PRIORITY_DEFAULT_COOLDOWN_MS;
|
|
35735
35980
|
priorityExhaustion.mark(candidate, cooldownUntil, reason);
|
|
35981
|
+
if (earliestPoolReset === null || cooldownUntil < earliestPoolReset) {
|
|
35982
|
+
earliestPoolReset = cooldownUntil;
|
|
35983
|
+
}
|
|
35736
35984
|
claudeLog("priority.exhausted", { profile: candidate, until: cooldownUntil, reason });
|
|
35737
35985
|
if (quotaRefusal)
|
|
35738
35986
|
refinePriorityCooldown(candidate);
|
|
@@ -35758,7 +36006,14 @@ data: ${JSON.stringify(lastError)}
|
|
|
35758
36006
|
headers: { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache" }
|
|
35759
36007
|
});
|
|
35760
36008
|
}
|
|
35761
|
-
|
|
36009
|
+
const poolRetryAfter = retryAfterSeconds({
|
|
36010
|
+
status: lastStatus,
|
|
36011
|
+
resetAtMs: earliestPoolReset
|
|
36012
|
+
});
|
|
36013
|
+
return new Response(JSON.stringify(lastError), {
|
|
36014
|
+
status: lastStatus,
|
|
36015
|
+
headers: { "content-type": "application/json", ...retryAfterHeaders(poolRetryAfter) }
|
|
36016
|
+
});
|
|
35762
36017
|
}
|
|
35763
36018
|
app.use("/auth/*", requireAuth);
|
|
35764
36019
|
app.get("/", (c) => {
|
|
@@ -35768,7 +36023,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
35768
36023
|
status: "ok",
|
|
35769
36024
|
service: "meridian",
|
|
35770
36025
|
format: "anthropic",
|
|
35771
|
-
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
|
|
36026
|
+
endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/responses", "/v1/models", "/v1/sessions/:key/cancel", "/v1/design/*", "/design-login", "/telemetry", "/metrics", "/health"]
|
|
35772
36027
|
});
|
|
35773
36028
|
}
|
|
35774
36029
|
return c.html(landingHtml);
|
|
@@ -35939,6 +36194,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
35939
36194
|
}
|
|
35940
36195
|
priorityTerminalCommitted = true;
|
|
35941
36196
|
};
|
|
36197
|
+
let resolvedProfileId;
|
|
35942
36198
|
try {
|
|
35943
36199
|
let makePrompt = function() {
|
|
35944
36200
|
if (structuredMessages) {
|
|
@@ -36016,7 +36272,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36016
36272
|
return c.json({
|
|
36017
36273
|
type: "error",
|
|
36018
36274
|
error: { type: "overloaded_error", message: "Durable priority routing state is unavailable" }
|
|
36019
|
-
}, 503);
|
|
36275
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36020
36276
|
}
|
|
36021
36277
|
if (routeResult.status === "found") {
|
|
36022
36278
|
durableRoute = { routeKey, expectedGeneration: routeResult.generation };
|
|
@@ -36052,7 +36308,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36052
36308
|
return c.json({
|
|
36053
36309
|
type: "error",
|
|
36054
36310
|
error: { type: "overloaded_error", message: "Durable priority session state is unavailable" }
|
|
36055
|
-
}, 503);
|
|
36311
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36056
36312
|
}
|
|
36057
36313
|
routeMappingIsCurrent = mapped.status === "found" && mapped.generation === routeResult.assignment.mappingGeneration;
|
|
36058
36314
|
if (!routeMappingIsCurrent) {
|
|
@@ -36060,7 +36316,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36060
36316
|
return c.json({
|
|
36061
36317
|
type: "error",
|
|
36062
36318
|
error: { type: "overloaded_error", message: "Durable priority session state is unavailable" }
|
|
36063
|
-
}, 503);
|
|
36319
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36064
36320
|
}
|
|
36065
36321
|
durableRoute = { ...durableRoute, forceFreshReplay: true };
|
|
36066
36322
|
}
|
|
@@ -36068,7 +36324,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36068
36324
|
return c.json({
|
|
36069
36325
|
type: "error",
|
|
36070
36326
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
36071
|
-
}, 503);
|
|
36327
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36072
36328
|
} else if (trustedTurn) {
|
|
36073
36329
|
durableRoute = { routeKey, expectedGeneration: routeResult.generation };
|
|
36074
36330
|
publicationTurn = trustedTurn;
|
|
@@ -36091,7 +36347,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36091
36347
|
return c.json({
|
|
36092
36348
|
type: "error",
|
|
36093
36349
|
error: { type: "overloaded_error", message: "Durable priority routing state is unavailable" }
|
|
36094
|
-
}, 503);
|
|
36350
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36095
36351
|
}
|
|
36096
36352
|
const pick2 = choosePriorityProfile(order, (id) => priorityExhaustion.isExhausted(id));
|
|
36097
36353
|
const first = retainOnlyProfile ?? (shouldPromote ? preferred : assignmentIsHealthy ? assignedProfile : pick2?.id ?? preferred);
|
|
@@ -36113,13 +36369,15 @@ data: ${JSON.stringify(lastError)}
|
|
|
36113
36369
|
}
|
|
36114
36370
|
}
|
|
36115
36371
|
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);
|
|
36372
|
+
resolvedProfileId = profile.id;
|
|
36116
36373
|
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
|
|
36117
36374
|
const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
|
|
36118
36375
|
const declaredAgentMode = adapter.getAgentMode?.(c, body) ?? c.req.header("x-opencode-agent-mode") ?? null;
|
|
36119
36376
|
const isSubagentRequest = declaredAgentMode === "subagent" || requestSource?.startsWith("subagent-") === true;
|
|
36120
36377
|
const agentMode = isSubagentRequest ? "subagent" : declaredAgentMode;
|
|
36121
36378
|
const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
|
|
36122
|
-
|
|
36379
|
+
const benchSessionKey = adapter.getSessionId(c, body) || undefined;
|
|
36380
|
+
let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode, profile.id, benchSessionKey);
|
|
36123
36381
|
const envOverrides = explicitModelPin(requestedModel);
|
|
36124
36382
|
const cwdResolution = resolveSdkWorkingDirectory({
|
|
36125
36383
|
envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
|
|
@@ -36824,7 +37082,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36824
37082
|
claudeExecutable = await resolveClaudeExecutableAsync();
|
|
36825
37083
|
}
|
|
36826
37084
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
36827
|
-
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
37085
|
+
const RATE_LIMIT_BASE_DELAY_MS = envInt("RATE_LIMIT_BASE_DELAY_MS", 1000);
|
|
36828
37086
|
const response = async function* () {
|
|
36829
37087
|
let rateLimitRetries = 0;
|
|
36830
37088
|
if (profileCredentialStore) {
|
|
@@ -37084,7 +37342,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
37084
37342
|
if (hasExtendedContext(model)) {
|
|
37085
37343
|
const from = model;
|
|
37086
37344
|
model = stripExtendedContext(model);
|
|
37087
|
-
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
|
|
37345
|
+
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()), benchSessionKey);
|
|
37088
37346
|
claudeLog("upstream.context_fallback", {
|
|
37089
37347
|
mode: "non_stream",
|
|
37090
37348
|
from,
|
|
@@ -37607,7 +37865,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
37607
37865
|
let nextClientBlockIndex = 0;
|
|
37608
37866
|
try {
|
|
37609
37867
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
37610
|
-
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
37868
|
+
const RATE_LIMIT_BASE_DELAY_MS = envInt("RATE_LIMIT_BASE_DELAY_MS", 1000);
|
|
37611
37869
|
const response = async function* () {
|
|
37612
37870
|
let rateLimitRetries = 0;
|
|
37613
37871
|
if (profileCredentialStore) {
|
|
@@ -37865,7 +38123,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
37865
38123
|
if (hasExtendedContext(model)) {
|
|
37866
38124
|
const from = model;
|
|
37867
38125
|
model = stripExtendedContext(model);
|
|
37868
|
-
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
|
|
38126
|
+
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()), benchSessionKey);
|
|
37869
38127
|
claudeLog("upstream.context_fallback", {
|
|
37870
38128
|
mode: "stream",
|
|
37871
38129
|
from,
|
|
@@ -38797,6 +39055,11 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
38797
39055
|
message: `Upstream stalled: no data for ${error51.sinceLastMs}ms`
|
|
38798
39056
|
} : classifyError(errMsg, model);
|
|
38799
39057
|
claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
|
|
39058
|
+
const streamRetryAfter = retryAfterSeconds({
|
|
39059
|
+
status: streamErr.status,
|
|
39060
|
+
errorMessage: errMsg,
|
|
39061
|
+
resetAtMs: observedResetAtMs(profile.id, Date.now())
|
|
39062
|
+
});
|
|
38800
39063
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
38801
39064
|
const canRecoverAsToolUse = canRecoverCapturedToolUses({
|
|
38802
39065
|
reason: sdkTerm.reason,
|
|
@@ -39009,7 +39272,7 @@ data: ${JSON.stringify({
|
|
|
39009
39272
|
safeEnqueue(encoder.encode(`event: error
|
|
39010
39273
|
data: ${JSON.stringify({
|
|
39011
39274
|
type: "error",
|
|
39012
|
-
error: { type: streamErr.type, message: streamErr.message }
|
|
39275
|
+
error: { type: streamErr.type, message: streamErr.message, ...retryAfterBodyFields(streamRetryAfter) }
|
|
39013
39276
|
})}
|
|
39014
39277
|
|
|
39015
39278
|
`), "error_event_before_stop");
|
|
@@ -39021,7 +39284,7 @@ data: {"type":"message_stop"}
|
|
|
39021
39284
|
safeEnqueue(encoder.encode(`event: error
|
|
39022
39285
|
data: ${JSON.stringify({
|
|
39023
39286
|
type: "error",
|
|
39024
|
-
error: { type: streamErr.type, message: streamErr.message }
|
|
39287
|
+
error: { type: streamErr.type, message: streamErr.message, ...retryAfterBodyFields(streamRetryAfter) }
|
|
39025
39288
|
})}
|
|
39026
39289
|
|
|
39027
39290
|
`), "error_event");
|
|
@@ -39045,6 +39308,7 @@ data: ${JSON.stringify({
|
|
|
39045
39308
|
cancel(reason) {
|
|
39046
39309
|
requestAbort.abort(reason);
|
|
39047
39310
|
requestAbort.detach();
|
|
39311
|
+
requestMeta.cascadeSubtreeCancel?.("stream_cancel");
|
|
39048
39312
|
if (!isIndependentSession && (!managedForkTarget || managedForkPublished || clientAssistantContentExposed)) {
|
|
39049
39313
|
evictSession2(profileSessionId, profileScopedCwd, lineageMessages, mappingExpectedGeneration);
|
|
39050
39314
|
claudeLog("passthrough.client_abort_settled", { action: "evict", source: "stream_cancel" });
|
|
@@ -39068,6 +39332,11 @@ data: ${JSON.stringify({
|
|
|
39068
39332
|
error: errMsg
|
|
39069
39333
|
});
|
|
39070
39334
|
const classified = requestAbort.controller.signal.aborted ? { status: 499, type: "request_cancelled", message: "The request was cancelled" } : classifyError(errMsg);
|
|
39335
|
+
const retryAfter = retryAfterSeconds({
|
|
39336
|
+
status: classified.status,
|
|
39337
|
+
errorMessage: errMsg,
|
|
39338
|
+
resetAtMs: observedResetAtMs(resolvedProfileId, Date.now())
|
|
39339
|
+
});
|
|
39071
39340
|
claudeLog("proxy.error", { error: errMsg, classified: classified.type });
|
|
39072
39341
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
39073
39342
|
diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
|
|
@@ -39102,7 +39371,17 @@ data: ${JSON.stringify({
|
|
|
39102
39371
|
textEvents: 0,
|
|
39103
39372
|
error: classified.type
|
|
39104
39373
|
});
|
|
39105
|
-
return new Response(JSON.stringify({
|
|
39374
|
+
return new Response(JSON.stringify({
|
|
39375
|
+
type: "error",
|
|
39376
|
+
error: {
|
|
39377
|
+
type: classified.type,
|
|
39378
|
+
message: classified.message,
|
|
39379
|
+
...retryAfterBodyFields(retryAfter)
|
|
39380
|
+
}
|
|
39381
|
+
}), {
|
|
39382
|
+
status: classified.status,
|
|
39383
|
+
headers: { "Content-Type": "application/json", ...retryAfterHeaders(retryAfter) }
|
|
39384
|
+
});
|
|
39106
39385
|
} finally {
|
|
39107
39386
|
if (!streamOwnsAbortLink) {
|
|
39108
39387
|
await abandonManagedFork("request_complete_without_commit");
|
|
@@ -39111,6 +39390,17 @@ data: ${JSON.stringify({
|
|
|
39111
39390
|
}
|
|
39112
39391
|
});
|
|
39113
39392
|
};
|
|
39393
|
+
const logSubtreeCancel = (parentKey, cancelled, requestId, source) => {
|
|
39394
|
+
const children = cancelled.keys.map((key) => truncateSessionKey(key));
|
|
39395
|
+
claudeLog("session.tree_cancel_propagated", {
|
|
39396
|
+
requestId,
|
|
39397
|
+
source,
|
|
39398
|
+
parent: truncateSessionKey(parentKey),
|
|
39399
|
+
children,
|
|
39400
|
+
requests: cancelled.requestIds.length
|
|
39401
|
+
});
|
|
39402
|
+
diagnosticLog2.session(`${requestId} session_tree_cancel source=${source} parent=${truncateSessionKey(parentKey)} ` + `children=${children.join(",")} requests=${cancelled.requestIds.length}`, requestId);
|
|
39403
|
+
};
|
|
39114
39404
|
const handleWithQueue = async (c, endpoint) => {
|
|
39115
39405
|
if (draining && c.req.header("x-meridian-internal-hop") !== internalHopToken) {
|
|
39116
39406
|
return drainingResponse();
|
|
@@ -39120,6 +39410,20 @@ data: ${JSON.stringify({
|
|
|
39120
39410
|
claudeLog("request.enter", { requestId, endpoint });
|
|
39121
39411
|
let sessionTurnLease;
|
|
39122
39412
|
let crossProcessTurnLease;
|
|
39413
|
+
let sessionTreeRegistration;
|
|
39414
|
+
let detachSubtreeAbortWatch;
|
|
39415
|
+
let subtreeSessionKey;
|
|
39416
|
+
let subtreeCascaded = false;
|
|
39417
|
+
const cascadeSubtreeCancel = (source) => {
|
|
39418
|
+
const parentKey = subtreeSessionKey;
|
|
39419
|
+
if (subtreeCascaded || !parentKey)
|
|
39420
|
+
return;
|
|
39421
|
+
subtreeCascaded = true;
|
|
39422
|
+
const cancelled = processSessionTree.cancelDescendants(parentKey, new Error(`Parent session ${truncateSessionKey(parentKey)} was cancelled`));
|
|
39423
|
+
if (cancelled.requestIds.length === 0)
|
|
39424
|
+
return;
|
|
39425
|
+
logSubtreeCancel(parentKey, cancelled, requestId, source);
|
|
39426
|
+
};
|
|
39123
39427
|
const turnWatchdogAbort = new AbortController;
|
|
39124
39428
|
activeRequestAborts.add(turnWatchdogAbort);
|
|
39125
39429
|
let finished = false;
|
|
@@ -39159,6 +39463,10 @@ data: ${JSON.stringify({
|
|
|
39159
39463
|
} else {
|
|
39160
39464
|
releaseSessionTurn(false);
|
|
39161
39465
|
}
|
|
39466
|
+
detachSubtreeAbortWatch?.();
|
|
39467
|
+
detachSubtreeAbortWatch = undefined;
|
|
39468
|
+
sessionTreeRegistration?.release();
|
|
39469
|
+
sessionTreeRegistration = undefined;
|
|
39162
39470
|
activeRequestAborts.delete(turnWatchdogAbort);
|
|
39163
39471
|
inFlightRequests--;
|
|
39164
39472
|
};
|
|
@@ -39187,6 +39495,21 @@ data: ${JSON.stringify({
|
|
|
39187
39495
|
routingTurnIdentity = adapter.getRoutingTurnIdentity?.(c, body);
|
|
39188
39496
|
const agentSessionId = adapter.getSessionId(c, body);
|
|
39189
39497
|
if (agentSessionId) {
|
|
39498
|
+
sessionTreeRegistration = processSessionTree.register({
|
|
39499
|
+
requestId,
|
|
39500
|
+
sessionKey: agentSessionId,
|
|
39501
|
+
parentKey: adapter.getParentSessionId?.(c, body),
|
|
39502
|
+
abort: (reason) => turnWatchdogAbort.abort(reason)
|
|
39503
|
+
});
|
|
39504
|
+
subtreeSessionKey = agentSessionId;
|
|
39505
|
+
const clientSignal = c.req.raw.signal;
|
|
39506
|
+
if (clientSignal.aborted) {
|
|
39507
|
+
cascadeSubtreeCancel("client_abort");
|
|
39508
|
+
} else {
|
|
39509
|
+
const onClientAbort = () => cascadeSubtreeCancel("client_abort");
|
|
39510
|
+
clientSignal.addEventListener("abort", onClientAbort, { once: true });
|
|
39511
|
+
detachSubtreeAbortWatch = () => clientSignal.removeEventListener("abort", onClientAbort);
|
|
39512
|
+
}
|
|
39190
39513
|
const arrivalProfileIds = new Set(getEffectiveProfiles(finalConfig.profiles).map((profile) => profile.id));
|
|
39191
39514
|
const explicitlyRequestedProfile = c.req.header("x-meridian-profile")?.trim();
|
|
39192
39515
|
if (explicitlyRequestedProfile)
|
|
@@ -39248,7 +39571,7 @@ data: ${JSON.stringify({
|
|
|
39248
39571
|
type: "overloaded_error",
|
|
39249
39572
|
message: "Timed out waiting for another process to finish this session turn"
|
|
39250
39573
|
}
|
|
39251
|
-
}), { status: 529, headers: { "Content-Type": "application/json" } });
|
|
39574
|
+
}), { status: 529, headers: { "Content-Type": "application/json", ...TRANSIENT_RETRY_AFTER_HEADERS } });
|
|
39252
39575
|
}
|
|
39253
39576
|
throw error51;
|
|
39254
39577
|
}
|
|
@@ -39266,7 +39589,8 @@ data: ${JSON.stringify({
|
|
|
39266
39589
|
routingTurnIdentity,
|
|
39267
39590
|
retainSessionTurnFence: () => {
|
|
39268
39591
|
retainSessionTurnFence = true;
|
|
39269
|
-
}
|
|
39592
|
+
},
|
|
39593
|
+
cascadeSubtreeCancel
|
|
39270
39594
|
};
|
|
39271
39595
|
const response = await handleMessages(c, requestMeta, {
|
|
39272
39596
|
body,
|
|
@@ -39286,7 +39610,29 @@ data: ${JSON.stringify({
|
|
|
39286
39610
|
};
|
|
39287
39611
|
app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
|
|
39288
39612
|
app.post("/messages", (c) => handleWithQueue(c, "/messages"));
|
|
39289
|
-
app.
|
|
39613
|
+
app.post("/v1/sessions/:key/cancel", (c) => {
|
|
39614
|
+
const key = c.req.param("key");
|
|
39615
|
+
if (!key) {
|
|
39616
|
+
return c.json({ type: "error", error: { type: "invalid_request_error", message: "Session key is required" } }, 400);
|
|
39617
|
+
}
|
|
39618
|
+
const cancelled = processSessionTree.cancelSubtree(key, new Error("Session cancelled by request"));
|
|
39619
|
+
if (cancelled.requestIds.length > 0) {
|
|
39620
|
+
claudeLog("session.tree_cancel_requested", {
|
|
39621
|
+
session: truncateSessionKey(key),
|
|
39622
|
+
keys: cancelled.keys.map((cancelledKey) => truncateSessionKey(cancelledKey)),
|
|
39623
|
+
requests: cancelled.requestIds.length
|
|
39624
|
+
});
|
|
39625
|
+
diagnosticLog2.session(`session_tree_cancel_requested session=${truncateSessionKey(key)} requests=${cancelled.requestIds.length}`);
|
|
39626
|
+
}
|
|
39627
|
+
return c.json({
|
|
39628
|
+
session: key,
|
|
39629
|
+
cancelled: { sessions: cancelled.keys.length, requests: cancelled.requestIds.length },
|
|
39630
|
+
requestIds: cancelled.requestIds
|
|
39631
|
+
});
|
|
39632
|
+
});
|
|
39633
|
+
app.route("/telemetry", createTelemetryRoutes({
|
|
39634
|
+
getSessionTree: () => processSessionTree.stats()
|
|
39635
|
+
}));
|
|
39290
39636
|
app.get("/settings", (c) => {
|
|
39291
39637
|
const { settingsPageHtml: settingsPageHtml2 } = (init_settingsPage(), __toCommonJS(exports_settingsPage));
|
|
39292
39638
|
return c.html(settingsPageHtml2);
|
|
@@ -39627,6 +39973,12 @@ data: ${JSON.stringify({
|
|
|
39627
39973
|
`);
|
|
39628
39974
|
buffer = lines.pop() ?? "";
|
|
39629
39975
|
for (const line of lines) {
|
|
39976
|
+
if (line.startsWith(":")) {
|
|
39977
|
+
controller.enqueue(encoder.encode(`${line}
|
|
39978
|
+
|
|
39979
|
+
`));
|
|
39980
|
+
continue;
|
|
39981
|
+
}
|
|
39630
39982
|
if (!line.startsWith("data: "))
|
|
39631
39983
|
continue;
|
|
39632
39984
|
const dataStr = line.slice(6).trim();
|
|
@@ -39745,6 +40097,12 @@ data: ${JSON.stringify({
|
|
|
39745
40097
|
`);
|
|
39746
40098
|
buffer = lines.pop() ?? "";
|
|
39747
40099
|
for (const line of lines) {
|
|
40100
|
+
if (line.startsWith(":")) {
|
|
40101
|
+
controller.enqueue(encoder.encode(`${line}
|
|
40102
|
+
|
|
40103
|
+
`));
|
|
40104
|
+
continue;
|
|
40105
|
+
}
|
|
39748
40106
|
if (!line.startsWith("data: "))
|
|
39749
40107
|
continue;
|
|
39750
40108
|
const dataStr = line.slice(6).trim();
|
|
@@ -39789,7 +40147,9 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
39789
40147
|
});
|
|
39790
40148
|
});
|
|
39791
40149
|
app.get("/v1/models", async (c) => {
|
|
39792
|
-
const
|
|
40150
|
+
const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile);
|
|
40151
|
+
const profileEnvOverrides = Object.keys(profile.env).length > 0 ? profile.env : undefined;
|
|
40152
|
+
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, profileEnvOverrides);
|
|
39793
40153
|
const extendedContext = subscriptionIncludesExtendedContext(authStatus?.subscriptionType);
|
|
39794
40154
|
return c.json({ object: "list", data: buildModelList(extendedContext) });
|
|
39795
40155
|
});
|