@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.
Files changed (34) hide show
  1. package/README.md +9 -3
  2. package/dist/{cli-pdpry6q0.js → cli-5jxyma6z.js} +5 -2
  3. package/dist/{cli-0ed6j0vk.js → cli-m9wqgk1q.js} +50 -16
  4. package/dist/{cli-7cts44b5.js → cli-nzvbaqjp.js} +415 -55
  5. package/dist/cli.js +5 -5
  6. package/dist/{profileCli-ap7eg985.js → profileCli-qhcr87tm.js} +1 -1
  7. package/dist/{profiles-4ajzjqhm.js → profiles-wch9h234.js} +1 -1
  8. package/dist/proxy/adapter.d.ts +14 -0
  9. package/dist/proxy/adapter.d.ts.map +1 -1
  10. package/dist/proxy/adapters/claudecode.d.ts +29 -0
  11. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  12. package/dist/proxy/adapters/prime.d.ts.map +1 -1
  13. package/dist/proxy/errors.d.ts.map +1 -1
  14. package/dist/proxy/models.d.ts +26 -8
  15. package/dist/proxy/models.d.ts.map +1 -1
  16. package/dist/proxy/oauthUsage.d.ts.map +1 -1
  17. package/dist/proxy/openai.d.ts +15 -0
  18. package/dist/proxy/openai.d.ts.map +1 -1
  19. package/dist/proxy/openaiResponses.d.ts +4 -11
  20. package/dist/proxy/openaiResponses.d.ts.map +1 -1
  21. package/dist/proxy/retryAfter.d.ts +90 -0
  22. package/dist/proxy/retryAfter.d.ts.map +1 -0
  23. package/dist/proxy/routing.d.ts +11 -0
  24. package/dist/proxy/routing.d.ts.map +1 -1
  25. package/dist/proxy/server.d.ts.map +1 -1
  26. package/dist/proxy/sessionTree.d.ts +113 -0
  27. package/dist/proxy/sessionTree.d.ts.map +1 -0
  28. package/dist/server.js +3 -3
  29. package/dist/telemetry/dashboard.d.ts.map +1 -1
  30. package/dist/telemetry/routes.d.ts +10 -1
  31. package/dist/telemetry/routes.d.ts.map +1 -1
  32. package/dist/telemetry/types.d.ts +16 -0
  33. package/dist/telemetry/types.d.ts.map +1 -1
  34. 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-pdpry6q0.js";
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-0ed6j0vk.js";
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 extractClaudeCodeSessionId(body) {
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
- return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : undefined;
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
- return c.json(summary);
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?.input_tokens ?? 0;
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.input_tokens ?? 0;
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?.input_tokens ?? 0;
24211
+ const input = totalAnthropicInputTokens(usage);
23991
24212
  const output = usage?.output_tokens ?? 0;
23992
- return { input_tokens: input, output_tokens: output, total_tokens: input + output };
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 inputTokens = 0;
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
- inputTokens = event.message?.usage?.input_tokens ?? 0;
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 (typeof event.usage?.output_tokens === "number")
24189
- outputTokens = event.usage.output_tokens;
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: { input_tokens: inputTokens, output_tokens: outputTokens, total_tokens: inputTokens + outputTokens },
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: { "Content-Type": "application/json", "x-meridian-draining": "1" }
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 priorityCooldownUntil(profileId, now) {
35536
- const windows = rateLimitStore.getAll(profileId).map((e) => ({
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
- return resolveCooldownUntil(windows, now, PRIORITY_DEFAULT_COOLDOWN_MS);
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
- return new Response(JSON.stringify(lastError), { status: lastStatus, headers: { "content-type": "application/json" } });
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
- let model = mapModelToClaudeModel(requestedModel, authStatus?.subscriptionType, agentMode, profile.id);
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({ type: "error", error: { type: classified.type, message: classified.message } }), { status: classified.status, headers: { "Content-Type": "application/json" } });
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.route("/telemetry", createTelemetryRoutes());
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 authStatus = await getClaudeAuthStatusAsync();
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
  });