@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.
Files changed (33) 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-syjqjygv.js → cli-nzvbaqjp.js} +413 -54
  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/models.d.ts +26 -8
  14. package/dist/proxy/models.d.ts.map +1 -1
  15. package/dist/proxy/oauthUsage.d.ts.map +1 -1
  16. package/dist/proxy/openai.d.ts +15 -0
  17. package/dist/proxy/openai.d.ts.map +1 -1
  18. package/dist/proxy/openaiResponses.d.ts +4 -11
  19. package/dist/proxy/openaiResponses.d.ts.map +1 -1
  20. package/dist/proxy/retryAfter.d.ts +90 -0
  21. package/dist/proxy/retryAfter.d.ts.map +1 -0
  22. package/dist/proxy/routing.d.ts +11 -0
  23. package/dist/proxy/routing.d.ts.map +1 -1
  24. package/dist/proxy/server.d.ts.map +1 -1
  25. package/dist/proxy/sessionTree.d.ts +113 -0
  26. package/dist/proxy/sessionTree.d.ts.map +1 -0
  27. package/dist/server.js +3 -3
  28. package/dist/telemetry/dashboard.d.ts.map +1 -1
  29. package/dist/telemetry/routes.d.ts +10 -1
  30. package/dist/telemetry/routes.d.ts.map +1 -1
  31. package/dist/telemetry/types.d.ts +16 -0
  32. package/dist/telemetry/types.d.ts.map +1 -1
  33. 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);
@@ -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?.input_tokens ?? 0;
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.input_tokens ?? 0;
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?.input_tokens ?? 0;
24211
+ const input = totalAnthropicInputTokens(usage);
23992
24212
  const output = usage?.output_tokens ?? 0;
23993
- 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
+ };
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 inputTokens = 0;
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
- inputTokens = event.message?.usage?.input_tokens ?? 0;
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 (typeof event.usage?.output_tokens === "number")
24190
- outputTokens = event.usage.output_tokens;
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: { input_tokens: inputTokens, output_tokens: outputTokens, total_tokens: inputTokens + outputTokens },
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: { "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
+ }
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 priorityCooldownUntil(profileId, now) {
35537
- const windows = rateLimitStore.getAll(profileId).map((e) => ({
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
- 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);
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
- 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
+ });
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
- 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);
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({ 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
+ });
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.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
+ }));
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 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);
39794
40153
  const extendedContext = subscriptionIncludesExtendedContext(authStatus?.subscriptionType);
39795
40154
  return c.json({ object: "list", data: buildModelList(extendedContext) });
39796
40155
  });