@rynfar/meridian 1.65.2 → 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-syjqjygv.js → cli-nzvbaqjp.js} +413 -54
- 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/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);
|
|
@@ -23355,6 +23547,23 @@ function stopUpdateCheck() {
|
|
|
23355
23547
|
}
|
|
23356
23548
|
|
|
23357
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
|
+
}
|
|
23358
23567
|
function extractOpenAiContent(content) {
|
|
23359
23568
|
if (typeof content === "string")
|
|
23360
23569
|
return content;
|
|
@@ -23596,7 +23805,7 @@ function translateAnthropicToOpenAi(response, completionId, model, created, opti
|
|
|
23596
23805
|
}));
|
|
23597
23806
|
const thinkingPassthrough = options?.thinkingPassthrough;
|
|
23598
23807
|
const thinking = thinkingPassthrough !== false ? contentBlocks.filter((b) => b.type === "thinking").map((b) => b.thinking).join("") : "";
|
|
23599
|
-
const promptTokens = response.usage
|
|
23808
|
+
const promptTokens = totalAnthropicInputTokens(response.usage);
|
|
23600
23809
|
const completionTokens = response.usage?.output_tokens ?? 0;
|
|
23601
23810
|
return {
|
|
23602
23811
|
id: completionId,
|
|
@@ -23616,7 +23825,11 @@ function translateAnthropicToOpenAi(response, completionId, model, created, opti
|
|
|
23616
23825
|
usage: {
|
|
23617
23826
|
prompt_tokens: promptTokens,
|
|
23618
23827
|
completion_tokens: completionTokens,
|
|
23619
|
-
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
|
+
}
|
|
23620
23833
|
}
|
|
23621
23834
|
};
|
|
23622
23835
|
}
|
|
@@ -23627,15 +23840,18 @@ function createSseTranslator(ctx) {
|
|
|
23627
23840
|
if (event.type === "content_block_start" && event.content_block?.type === "tool_use" && typeof event.content_block.name === "string") {
|
|
23628
23841
|
toolCallIndex++;
|
|
23629
23842
|
}
|
|
23843
|
+
if (event.type === "message_start" && event.message?.usage) {
|
|
23844
|
+
lastUsage = mergeAnthropicUsage(lastUsage, event.message.usage);
|
|
23845
|
+
}
|
|
23630
23846
|
if (event.type === "message_delta" && event.usage) {
|
|
23631
|
-
lastUsage = event.usage;
|
|
23847
|
+
lastUsage = mergeAnthropicUsage(lastUsage, event.usage);
|
|
23632
23848
|
}
|
|
23633
23849
|
return translateAnthropicSseEvent(event, ctx.completionId, ctx.model, ctx.created, toolCallIndex, ctx.thinkingPassthrough);
|
|
23634
23850
|
};
|
|
23635
23851
|
translate.buildUsageChunk = () => {
|
|
23636
23852
|
if (!ctx.includeUsage || !lastUsage)
|
|
23637
23853
|
return null;
|
|
23638
|
-
const promptTokens = lastUsage
|
|
23854
|
+
const promptTokens = totalAnthropicInputTokens(lastUsage);
|
|
23639
23855
|
const completionTokens = lastUsage.output_tokens ?? 0;
|
|
23640
23856
|
return {
|
|
23641
23857
|
id: ctx.completionId,
|
|
@@ -23646,7 +23862,11 @@ function createSseTranslator(ctx) {
|
|
|
23646
23862
|
usage: {
|
|
23647
23863
|
prompt_tokens: promptTokens,
|
|
23648
23864
|
completion_tokens: completionTokens,
|
|
23649
|
-
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
|
+
}
|
|
23650
23870
|
}
|
|
23651
23871
|
};
|
|
23652
23872
|
};
|
|
@@ -23988,9 +24208,17 @@ function reasoningRequested(body) {
|
|
|
23988
24208
|
return Array.isArray(include) && include.some((v) => typeof v === "string" && v.startsWith("reasoning"));
|
|
23989
24209
|
}
|
|
23990
24210
|
function mapUsage(usage) {
|
|
23991
|
-
const input = usage
|
|
24211
|
+
const input = totalAnthropicInputTokens(usage);
|
|
23992
24212
|
const output = usage?.output_tokens ?? 0;
|
|
23993
|
-
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
|
+
};
|
|
23994
24222
|
}
|
|
23995
24223
|
function toResponsesStatus(stopReason) {
|
|
23996
24224
|
return stopReason === "max_tokens" ? "incomplete" : "completed";
|
|
@@ -24048,8 +24276,7 @@ function translateAnthropicToResponses(res, ctx) {
|
|
|
24048
24276
|
function createResponsesSseTranslator(ctx) {
|
|
24049
24277
|
let seq = 0;
|
|
24050
24278
|
let outputIndex = 0;
|
|
24051
|
-
let
|
|
24052
|
-
let outputTokens = 0;
|
|
24279
|
+
let usage;
|
|
24053
24280
|
let createdEmitted = false;
|
|
24054
24281
|
let stopReason;
|
|
24055
24282
|
const blocks = new Map;
|
|
@@ -24070,7 +24297,8 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24070
24297
|
const out = [];
|
|
24071
24298
|
switch (event.type) {
|
|
24072
24299
|
case "message_start": {
|
|
24073
|
-
|
|
24300
|
+
if (event.message?.usage)
|
|
24301
|
+
usage = mergeAnthropicUsage(usage, event.message.usage);
|
|
24074
24302
|
if (!createdEmitted) {
|
|
24075
24303
|
createdEmitted = true;
|
|
24076
24304
|
out.push(emit("response.created", { response: responseEnvelope("in_progress", { output: [] }) }));
|
|
@@ -24186,8 +24414,8 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24186
24414
|
break;
|
|
24187
24415
|
}
|
|
24188
24416
|
case "message_delta": {
|
|
24189
|
-
if (
|
|
24190
|
-
|
|
24417
|
+
if (event.usage)
|
|
24418
|
+
usage = mergeAnthropicUsage(usage, event.usage);
|
|
24191
24419
|
if (typeof event.delta?.stop_reason === "string")
|
|
24192
24420
|
stopReason = event.delta.stop_reason;
|
|
24193
24421
|
break;
|
|
@@ -24197,7 +24425,7 @@ function createResponsesSseTranslator(ctx) {
|
|
|
24197
24425
|
out.push(emit(status === "incomplete" ? "response.incomplete" : "response.completed", {
|
|
24198
24426
|
response: responseEnvelope(status, {
|
|
24199
24427
|
output: finalOutput,
|
|
24200
|
-
usage:
|
|
24428
|
+
usage: mapUsage(usage),
|
|
24201
24429
|
parallel_tool_calls: true,
|
|
24202
24430
|
tool_choice: "auto",
|
|
24203
24431
|
tools: [],
|
|
@@ -35438,9 +35666,14 @@ function createProxyServer(config2 = {}) {
|
|
|
35438
35666
|
const internalHopToken = randomUUID6();
|
|
35439
35667
|
const errorEnvelope = (shape, type, message) => shape === "anthropic" ? { type: "error", error: { type, message } } : { error: { type, message, code: null } };
|
|
35440
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);
|
|
35441
35670
|
const drainingResponse = (shape = "anthropic") => new Response(JSON.stringify(errorEnvelope(shape, "overloaded_error", DRAIN_MESSAGE)), {
|
|
35442
35671
|
status: 503,
|
|
35443
|
-
headers: {
|
|
35672
|
+
headers: {
|
|
35673
|
+
"Content-Type": "application/json",
|
|
35674
|
+
"x-meridian-draining": "1",
|
|
35675
|
+
...TRANSIENT_RETRY_AFTER_HEADERS
|
|
35676
|
+
}
|
|
35444
35677
|
});
|
|
35445
35678
|
async function relayInnerError(internalRes, shape) {
|
|
35446
35679
|
const errBody = await internalRes.text();
|
|
@@ -35456,6 +35689,9 @@ function createProxyServer(config2 = {}) {
|
|
|
35456
35689
|
const drainingHeader = internalRes.headers.get("x-meridian-draining");
|
|
35457
35690
|
if (drainingHeader)
|
|
35458
35691
|
headers["x-meridian-draining"] = drainingHeader;
|
|
35692
|
+
const innerRetryAfter = internalRes.headers.get("retry-after");
|
|
35693
|
+
if (innerRetryAfter)
|
|
35694
|
+
headers["Retry-After"] = innerRetryAfter;
|
|
35459
35695
|
return new Response(JSON.stringify(payload), { status: internalRes.status, headers });
|
|
35460
35696
|
}
|
|
35461
35697
|
async function* runSdkQueryAttempt(params, signal, requestMeta, mode, activeLocators) {
|
|
@@ -35533,13 +35769,20 @@ function createProxyServer(config2 = {}) {
|
|
|
35533
35769
|
const setting = getSetting("profileOrder");
|
|
35534
35770
|
return Array.isArray(setting) && setting.length > 0 ? setting : undefined;
|
|
35535
35771
|
}
|
|
35536
|
-
function
|
|
35537
|
-
|
|
35772
|
+
function profileCooldownWindows(profileId) {
|
|
35773
|
+
return rateLimitStore.getAll(profileId).map((e) => ({
|
|
35538
35774
|
type: e.rateLimitType ?? "",
|
|
35539
35775
|
resetsAt: e.resetsAt,
|
|
35540
35776
|
exhausted: e.status === "rejected" || (e.utilization ?? 0) >= 1
|
|
35541
35777
|
}));
|
|
35542
|
-
|
|
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);
|
|
35543
35786
|
}
|
|
35544
35787
|
function refinePriorityCooldown(profileId) {
|
|
35545
35788
|
const target = getEffectiveProfiles(finalConfig.profiles).find((p) => p.id === profileId);
|
|
@@ -35660,7 +35903,7 @@ function createProxyServer(config2 = {}) {
|
|
|
35660
35903
|
return options.context.json({
|
|
35661
35904
|
type: "error",
|
|
35662
35905
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35663
|
-
}, 503);
|
|
35906
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35664
35907
|
}
|
|
35665
35908
|
attemptOwnerToken = claim.ownerToken;
|
|
35666
35909
|
} catch (error51) {
|
|
@@ -35671,7 +35914,7 @@ function createProxyServer(config2 = {}) {
|
|
|
35671
35914
|
return options.context.json({
|
|
35672
35915
|
type: "error",
|
|
35673
35916
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35674
|
-
}, 503);
|
|
35917
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35675
35918
|
}
|
|
35676
35919
|
}
|
|
35677
35920
|
const settleAttempt = (disposition) => {
|
|
@@ -35691,11 +35934,12 @@ function createProxyServer(config2 = {}) {
|
|
|
35691
35934
|
const unavailableAttemptResponse = () => options.context.json({
|
|
35692
35935
|
type: "error",
|
|
35693
35936
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
35694
|
-
}, 503);
|
|
35937
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
35695
35938
|
let lastError = null;
|
|
35696
35939
|
let lastStatus = 429;
|
|
35697
35940
|
let previous = null;
|
|
35698
35941
|
let previousReason = "rate_limit_error";
|
|
35942
|
+
let earliestPoolReset = null;
|
|
35699
35943
|
for (const [attempt, candidate] of options.candidateIds.entries()) {
|
|
35700
35944
|
const exposure = { committed: false };
|
|
35701
35945
|
const priorityPublication = options.durableRoute && options.publicationTurn ? {
|
|
@@ -35734,6 +35978,9 @@ function createProxyServer(config2 = {}) {
|
|
|
35734
35978
|
const quotaRefusal = isQuotaRefusal(reason);
|
|
35735
35979
|
const cooldownUntil = quotaRefusal ? priorityCooldownUntil(candidate, Date.now()) : Date.now() + PRIORITY_DEFAULT_COOLDOWN_MS;
|
|
35736
35980
|
priorityExhaustion.mark(candidate, cooldownUntil, reason);
|
|
35981
|
+
if (earliestPoolReset === null || cooldownUntil < earliestPoolReset) {
|
|
35982
|
+
earliestPoolReset = cooldownUntil;
|
|
35983
|
+
}
|
|
35737
35984
|
claudeLog("priority.exhausted", { profile: candidate, until: cooldownUntil, reason });
|
|
35738
35985
|
if (quotaRefusal)
|
|
35739
35986
|
refinePriorityCooldown(candidate);
|
|
@@ -35759,7 +36006,14 @@ data: ${JSON.stringify(lastError)}
|
|
|
35759
36006
|
headers: { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-cache" }
|
|
35760
36007
|
});
|
|
35761
36008
|
}
|
|
35762
|
-
|
|
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
|
+
});
|
|
35763
36017
|
}
|
|
35764
36018
|
app.use("/auth/*", requireAuth);
|
|
35765
36019
|
app.get("/", (c) => {
|
|
@@ -35769,7 +36023,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
35769
36023
|
status: "ok",
|
|
35770
36024
|
service: "meridian",
|
|
35771
36025
|
format: "anthropic",
|
|
35772
|
-
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"]
|
|
35773
36027
|
});
|
|
35774
36028
|
}
|
|
35775
36029
|
return c.html(landingHtml);
|
|
@@ -35940,6 +36194,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
35940
36194
|
}
|
|
35941
36195
|
priorityTerminalCommitted = true;
|
|
35942
36196
|
};
|
|
36197
|
+
let resolvedProfileId;
|
|
35943
36198
|
try {
|
|
35944
36199
|
let makePrompt = function() {
|
|
35945
36200
|
if (structuredMessages) {
|
|
@@ -36017,7 +36272,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36017
36272
|
return c.json({
|
|
36018
36273
|
type: "error",
|
|
36019
36274
|
error: { type: "overloaded_error", message: "Durable priority routing state is unavailable" }
|
|
36020
|
-
}, 503);
|
|
36275
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36021
36276
|
}
|
|
36022
36277
|
if (routeResult.status === "found") {
|
|
36023
36278
|
durableRoute = { routeKey, expectedGeneration: routeResult.generation };
|
|
@@ -36053,7 +36308,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36053
36308
|
return c.json({
|
|
36054
36309
|
type: "error",
|
|
36055
36310
|
error: { type: "overloaded_error", message: "Durable priority session state is unavailable" }
|
|
36056
|
-
}, 503);
|
|
36311
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36057
36312
|
}
|
|
36058
36313
|
routeMappingIsCurrent = mapped.status === "found" && mapped.generation === routeResult.assignment.mappingGeneration;
|
|
36059
36314
|
if (!routeMappingIsCurrent) {
|
|
@@ -36061,7 +36316,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36061
36316
|
return c.json({
|
|
36062
36317
|
type: "error",
|
|
36063
36318
|
error: { type: "overloaded_error", message: "Durable priority session state is unavailable" }
|
|
36064
|
-
}, 503);
|
|
36319
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36065
36320
|
}
|
|
36066
36321
|
durableRoute = { ...durableRoute, forceFreshReplay: true };
|
|
36067
36322
|
}
|
|
@@ -36069,7 +36324,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36069
36324
|
return c.json({
|
|
36070
36325
|
type: "error",
|
|
36071
36326
|
error: { type: "overloaded_error", message: "Durable priority attempt state is unavailable" }
|
|
36072
|
-
}, 503);
|
|
36327
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36073
36328
|
} else if (trustedTurn) {
|
|
36074
36329
|
durableRoute = { routeKey, expectedGeneration: routeResult.generation };
|
|
36075
36330
|
publicationTurn = trustedTurn;
|
|
@@ -36092,7 +36347,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36092
36347
|
return c.json({
|
|
36093
36348
|
type: "error",
|
|
36094
36349
|
error: { type: "overloaded_error", message: "Durable priority routing state is unavailable" }
|
|
36095
|
-
}, 503);
|
|
36350
|
+
}, 503, TRANSIENT_RETRY_AFTER_HEADERS);
|
|
36096
36351
|
}
|
|
36097
36352
|
const pick2 = choosePriorityProfile(order, (id) => priorityExhaustion.isExhausted(id));
|
|
36098
36353
|
const first = retainOnlyProfile ?? (shouldPromote ? preferred : assignmentIsHealthy ? assignedProfile : pick2?.id ?? preferred);
|
|
@@ -36114,13 +36369,15 @@ data: ${JSON.stringify(lastError)}
|
|
|
36114
36369
|
}
|
|
36115
36370
|
}
|
|
36116
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;
|
|
36117
36373
|
const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
|
|
36118
36374
|
const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
|
|
36119
36375
|
const declaredAgentMode = adapter.getAgentMode?.(c, body) ?? c.req.header("x-opencode-agent-mode") ?? null;
|
|
36120
36376
|
const isSubagentRequest = declaredAgentMode === "subagent" || requestSource?.startsWith("subagent-") === true;
|
|
36121
36377
|
const agentMode = isSubagentRequest ? "subagent" : declaredAgentMode;
|
|
36122
36378
|
const requestedModel = typeof body.model === "string" ? body.model : "sonnet";
|
|
36123
|
-
|
|
36379
|
+
const benchSessionKey = adapter.getSessionId(c, body) || undefined;
|
|
36380
|
+
let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode, profile.id, benchSessionKey);
|
|
36124
36381
|
const envOverrides = explicitModelPin(requestedModel);
|
|
36125
36382
|
const cwdResolution = resolveSdkWorkingDirectory({
|
|
36126
36383
|
envOverride: process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR,
|
|
@@ -36825,7 +37082,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
36825
37082
|
claudeExecutable = await resolveClaudeExecutableAsync();
|
|
36826
37083
|
}
|
|
36827
37084
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
36828
|
-
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
37085
|
+
const RATE_LIMIT_BASE_DELAY_MS = envInt("RATE_LIMIT_BASE_DELAY_MS", 1000);
|
|
36829
37086
|
const response = async function* () {
|
|
36830
37087
|
let rateLimitRetries = 0;
|
|
36831
37088
|
if (profileCredentialStore) {
|
|
@@ -37085,7 +37342,7 @@ data: ${JSON.stringify(lastError)}
|
|
|
37085
37342
|
if (hasExtendedContext(model)) {
|
|
37086
37343
|
const from = model;
|
|
37087
37344
|
model = stripExtendedContext(model);
|
|
37088
|
-
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
|
|
37345
|
+
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()), benchSessionKey);
|
|
37089
37346
|
claudeLog("upstream.context_fallback", {
|
|
37090
37347
|
mode: "non_stream",
|
|
37091
37348
|
from,
|
|
@@ -37608,7 +37865,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
37608
37865
|
let nextClientBlockIndex = 0;
|
|
37609
37866
|
try {
|
|
37610
37867
|
const MAX_RATE_LIMIT_RETRIES = 2;
|
|
37611
|
-
const RATE_LIMIT_BASE_DELAY_MS = 1000;
|
|
37868
|
+
const RATE_LIMIT_BASE_DELAY_MS = envInt("RATE_LIMIT_BASE_DELAY_MS", 1000);
|
|
37612
37869
|
const response = async function* () {
|
|
37613
37870
|
let rateLimitRetries = 0;
|
|
37614
37871
|
if (profileCredentialStore) {
|
|
@@ -37866,7 +38123,7 @@ data: ${JSON.stringify({ type: "content_block_stop", index: idx })}
|
|
|
37866
38123
|
if (hasExtendedContext(model)) {
|
|
37867
38124
|
const from = model;
|
|
37868
38125
|
model = stripExtendedContext(model);
|
|
37869
|
-
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()));
|
|
38126
|
+
recordExtendedContextRateLimited(profile.id, priorityCooldownUntil(profile.id, Date.now()), benchSessionKey);
|
|
37870
38127
|
claudeLog("upstream.context_fallback", {
|
|
37871
38128
|
mode: "stream",
|
|
37872
38129
|
from,
|
|
@@ -38798,6 +39055,11 @@ Subprocess stderr: ${stderrOutput}`;
|
|
|
38798
39055
|
message: `Upstream stalled: no data for ${error51.sinceLastMs}ms`
|
|
38799
39056
|
} : classifyError(errMsg, model);
|
|
38800
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
|
+
});
|
|
38801
39063
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
38802
39064
|
const canRecoverAsToolUse = canRecoverCapturedToolUses({
|
|
38803
39065
|
reason: sdkTerm.reason,
|
|
@@ -39010,7 +39272,7 @@ data: ${JSON.stringify({
|
|
|
39010
39272
|
safeEnqueue(encoder.encode(`event: error
|
|
39011
39273
|
data: ${JSON.stringify({
|
|
39012
39274
|
type: "error",
|
|
39013
|
-
error: { type: streamErr.type, message: streamErr.message }
|
|
39275
|
+
error: { type: streamErr.type, message: streamErr.message, ...retryAfterBodyFields(streamRetryAfter) }
|
|
39014
39276
|
})}
|
|
39015
39277
|
|
|
39016
39278
|
`), "error_event_before_stop");
|
|
@@ -39022,7 +39284,7 @@ data: {"type":"message_stop"}
|
|
|
39022
39284
|
safeEnqueue(encoder.encode(`event: error
|
|
39023
39285
|
data: ${JSON.stringify({
|
|
39024
39286
|
type: "error",
|
|
39025
|
-
error: { type: streamErr.type, message: streamErr.message }
|
|
39287
|
+
error: { type: streamErr.type, message: streamErr.message, ...retryAfterBodyFields(streamRetryAfter) }
|
|
39026
39288
|
})}
|
|
39027
39289
|
|
|
39028
39290
|
`), "error_event");
|
|
@@ -39046,6 +39308,7 @@ data: ${JSON.stringify({
|
|
|
39046
39308
|
cancel(reason) {
|
|
39047
39309
|
requestAbort.abort(reason);
|
|
39048
39310
|
requestAbort.detach();
|
|
39311
|
+
requestMeta.cascadeSubtreeCancel?.("stream_cancel");
|
|
39049
39312
|
if (!isIndependentSession && (!managedForkTarget || managedForkPublished || clientAssistantContentExposed)) {
|
|
39050
39313
|
evictSession2(profileSessionId, profileScopedCwd, lineageMessages, mappingExpectedGeneration);
|
|
39051
39314
|
claudeLog("passthrough.client_abort_settled", { action: "evict", source: "stream_cancel" });
|
|
@@ -39069,6 +39332,11 @@ data: ${JSON.stringify({
|
|
|
39069
39332
|
error: errMsg
|
|
39070
39333
|
});
|
|
39071
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
|
+
});
|
|
39072
39340
|
claudeLog("proxy.error", { error: errMsg, classified: classified.type });
|
|
39073
39341
|
const sdkTerm = extractSdkTermination(errMsg);
|
|
39074
39342
|
diagnosticLog2.error(`${requestMeta.requestId} ${formatSdkTermination(sdkTerm, {
|
|
@@ -39103,7 +39371,17 @@ data: ${JSON.stringify({
|
|
|
39103
39371
|
textEvents: 0,
|
|
39104
39372
|
error: classified.type
|
|
39105
39373
|
});
|
|
39106
|
-
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
|
+
});
|
|
39107
39385
|
} finally {
|
|
39108
39386
|
if (!streamOwnsAbortLink) {
|
|
39109
39387
|
await abandonManagedFork("request_complete_without_commit");
|
|
@@ -39112,6 +39390,17 @@ data: ${JSON.stringify({
|
|
|
39112
39390
|
}
|
|
39113
39391
|
});
|
|
39114
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
|
+
};
|
|
39115
39404
|
const handleWithQueue = async (c, endpoint) => {
|
|
39116
39405
|
if (draining && c.req.header("x-meridian-internal-hop") !== internalHopToken) {
|
|
39117
39406
|
return drainingResponse();
|
|
@@ -39121,6 +39410,20 @@ data: ${JSON.stringify({
|
|
|
39121
39410
|
claudeLog("request.enter", { requestId, endpoint });
|
|
39122
39411
|
let sessionTurnLease;
|
|
39123
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
|
+
};
|
|
39124
39427
|
const turnWatchdogAbort = new AbortController;
|
|
39125
39428
|
activeRequestAborts.add(turnWatchdogAbort);
|
|
39126
39429
|
let finished = false;
|
|
@@ -39160,6 +39463,10 @@ data: ${JSON.stringify({
|
|
|
39160
39463
|
} else {
|
|
39161
39464
|
releaseSessionTurn(false);
|
|
39162
39465
|
}
|
|
39466
|
+
detachSubtreeAbortWatch?.();
|
|
39467
|
+
detachSubtreeAbortWatch = undefined;
|
|
39468
|
+
sessionTreeRegistration?.release();
|
|
39469
|
+
sessionTreeRegistration = undefined;
|
|
39163
39470
|
activeRequestAborts.delete(turnWatchdogAbort);
|
|
39164
39471
|
inFlightRequests--;
|
|
39165
39472
|
};
|
|
@@ -39188,6 +39495,21 @@ data: ${JSON.stringify({
|
|
|
39188
39495
|
routingTurnIdentity = adapter.getRoutingTurnIdentity?.(c, body);
|
|
39189
39496
|
const agentSessionId = adapter.getSessionId(c, body);
|
|
39190
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
|
+
}
|
|
39191
39513
|
const arrivalProfileIds = new Set(getEffectiveProfiles(finalConfig.profiles).map((profile) => profile.id));
|
|
39192
39514
|
const explicitlyRequestedProfile = c.req.header("x-meridian-profile")?.trim();
|
|
39193
39515
|
if (explicitlyRequestedProfile)
|
|
@@ -39249,7 +39571,7 @@ data: ${JSON.stringify({
|
|
|
39249
39571
|
type: "overloaded_error",
|
|
39250
39572
|
message: "Timed out waiting for another process to finish this session turn"
|
|
39251
39573
|
}
|
|
39252
|
-
}), { status: 529, headers: { "Content-Type": "application/json" } });
|
|
39574
|
+
}), { status: 529, headers: { "Content-Type": "application/json", ...TRANSIENT_RETRY_AFTER_HEADERS } });
|
|
39253
39575
|
}
|
|
39254
39576
|
throw error51;
|
|
39255
39577
|
}
|
|
@@ -39267,7 +39589,8 @@ data: ${JSON.stringify({
|
|
|
39267
39589
|
routingTurnIdentity,
|
|
39268
39590
|
retainSessionTurnFence: () => {
|
|
39269
39591
|
retainSessionTurnFence = true;
|
|
39270
|
-
}
|
|
39592
|
+
},
|
|
39593
|
+
cascadeSubtreeCancel
|
|
39271
39594
|
};
|
|
39272
39595
|
const response = await handleMessages(c, requestMeta, {
|
|
39273
39596
|
body,
|
|
@@ -39287,7 +39610,29 @@ data: ${JSON.stringify({
|
|
|
39287
39610
|
};
|
|
39288
39611
|
app.post("/v1/messages", (c) => handleWithQueue(c, "/v1/messages"));
|
|
39289
39612
|
app.post("/messages", (c) => handleWithQueue(c, "/messages"));
|
|
39290
|
-
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
|
+
}));
|
|
39291
39636
|
app.get("/settings", (c) => {
|
|
39292
39637
|
const { settingsPageHtml: settingsPageHtml2 } = (init_settingsPage(), __toCommonJS(exports_settingsPage));
|
|
39293
39638
|
return c.html(settingsPageHtml2);
|
|
@@ -39628,6 +39973,12 @@ data: ${JSON.stringify({
|
|
|
39628
39973
|
`);
|
|
39629
39974
|
buffer = lines.pop() ?? "";
|
|
39630
39975
|
for (const line of lines) {
|
|
39976
|
+
if (line.startsWith(":")) {
|
|
39977
|
+
controller.enqueue(encoder.encode(`${line}
|
|
39978
|
+
|
|
39979
|
+
`));
|
|
39980
|
+
continue;
|
|
39981
|
+
}
|
|
39631
39982
|
if (!line.startsWith("data: "))
|
|
39632
39983
|
continue;
|
|
39633
39984
|
const dataStr = line.slice(6).trim();
|
|
@@ -39746,6 +40097,12 @@ data: ${JSON.stringify({
|
|
|
39746
40097
|
`);
|
|
39747
40098
|
buffer = lines.pop() ?? "";
|
|
39748
40099
|
for (const line of lines) {
|
|
40100
|
+
if (line.startsWith(":")) {
|
|
40101
|
+
controller.enqueue(encoder.encode(`${line}
|
|
40102
|
+
|
|
40103
|
+
`));
|
|
40104
|
+
continue;
|
|
40105
|
+
}
|
|
39749
40106
|
if (!line.startsWith("data: "))
|
|
39750
40107
|
continue;
|
|
39751
40108
|
const dataStr = line.slice(6).trim();
|
|
@@ -39790,7 +40147,9 @@ data: ${JSON.stringify({ response: { id: responseId, status: "failed", error: {
|
|
|
39790
40147
|
});
|
|
39791
40148
|
});
|
|
39792
40149
|
app.get("/v1/models", async (c) => {
|
|
39793
|
-
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);
|
|
39794
40153
|
const extendedContext = subscriptionIncludesExtendedContext(authStatus?.subscriptionType);
|
|
39795
40154
|
return c.json({ object: "list", data: buildModelList(extendedContext) });
|
|
39796
40155
|
});
|