@rynfar/meridian 1.24.5 → 1.26.5

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.
@@ -1,10 +1,15 @@
1
1
  import {
2
- __export,
3
- __require,
2
+ checkPluginConfigured
3
+ } from "./cli-rtab0qa6.js";
4
+ import {
4
5
  claudeLog,
5
6
  refreshOAuthToken,
6
7
  withClaudeLogContext
7
- } from "./cli-jd4atcxs.js";
8
+ } from "./cli-m9pfb7h9.js";
9
+ import {
10
+ __export,
11
+ __require
12
+ } from "./cli-a05ws7rb.js";
8
13
 
9
14
  // node_modules/hono/dist/compose.js
10
15
  var compose = (middleware, onError, onNotFound) => {
@@ -6384,6 +6389,11 @@ class DiagnosticLogStore {
6384
6389
  }
6385
6390
  }
6386
6391
  var diagnosticLog = new DiagnosticLogStore;
6392
+ // src/telemetry/routes.ts
6393
+ import { existsSync, readFileSync } from "node:fs";
6394
+ import { resolve, dirname } from "node:path";
6395
+ import { fileURLToPath } from "node:url";
6396
+
6387
6397
  // src/telemetry/dashboard.ts
6388
6398
  var dashboardHtml = `<!DOCTYPE html>
6389
6399
  <html lang="en">
@@ -6391,6 +6401,7 @@ var dashboardHtml = `<!DOCTYPE html>
6391
6401
  <meta charset="utf-8">
6392
6402
  <meta name="viewport" content="width=device-width, initial-scale=1">
6393
6403
  <title>Meridian — Telemetry</title>
6404
+ <link rel="icon" type="image/svg+xml" href="/telemetry/icon.svg">
6394
6405
  <style>
6395
6406
  :root {
6396
6407
  --bg: #0d1117; --surface: #161b22; --border: #30363d;
@@ -6712,11 +6723,21 @@ timer = setInterval(refresh, 5000);
6712
6723
  </html>`;
6713
6724
 
6714
6725
  // src/telemetry/routes.ts
6726
+ var _iconPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "assets", "icon.svg");
6727
+ var _iconSvg = existsSync(_iconPath) ? readFileSync(_iconPath, "utf-8") : null;
6715
6728
  function createTelemetryRoutes() {
6716
6729
  const routes = new Hono2;
6717
6730
  routes.get("/", (c) => {
6718
6731
  return c.html(dashboardHtml);
6719
6732
  });
6733
+ routes.get("/icon.svg", (c) => {
6734
+ if (!_iconSvg)
6735
+ return c.notFound();
6736
+ return c.body(_iconSvg, 200, {
6737
+ "Content-Type": "image/svg+xml",
6738
+ "Cache-Control": "public, max-age=3600"
6739
+ });
6740
+ });
6720
6741
  routes.get("/requests", (c) => {
6721
6742
  const limit = Number.parseInt(c.req.query("limit") || "50", 10);
6722
6743
  const since = c.req.query("since") ? Number.parseInt(c.req.query("since"), 10) : undefined;
@@ -6971,9 +6992,9 @@ function isExtraUsageRequiredError(errMsg) {
6971
6992
 
6972
6993
  // src/proxy/models.ts
6973
6994
  import { exec as execCallback } from "child_process";
6974
- import { existsSync } from "fs";
6975
- import { fileURLToPath } from "url";
6976
- import { join, dirname } from "path";
6995
+ import { existsSync as existsSync2 } from "fs";
6996
+ import { fileURLToPath as fileURLToPath2 } from "url";
6997
+ import { join, dirname as dirname2 } from "path";
6977
6998
  import { promisify } from "util";
6978
6999
  var exec = promisify(execCallback);
6979
7000
  var AUTH_STATUS_CACHE_TTL_MS = 60000;
@@ -6988,19 +7009,35 @@ function supports1mContext(model) {
6988
7009
  return false;
6989
7010
  return true;
6990
7011
  }
6991
- function mapModelToClaudeModel(model, subscriptionType) {
7012
+ function mapModelToClaudeModel(model, subscriptionType, agentMode) {
6992
7013
  if (model.includes("haiku"))
6993
7014
  return "haiku";
6994
7015
  const use1m = supports1mContext(model);
6995
- if (model.includes("opus"))
6996
- return use1m ? "opus[1m]" : "opus";
7016
+ const isSubagent = agentMode === "subagent";
7017
+ if (model.includes("opus")) {
7018
+ if (use1m && !isSubagent && !isExtendedContextKnownUnavailable())
7019
+ return "opus[1m]";
7020
+ return "opus";
7021
+ }
6997
7022
  const sonnetOverride = process.env.MERIDIAN_SONNET_MODEL ?? process.env.CLAUDE_PROXY_SONNET_MODEL;
6998
7023
  if (sonnetOverride === "sonnet" || sonnetOverride === "sonnet[1m]")
6999
7024
  return sonnetOverride;
7000
7025
  if (!use1m)
7001
7026
  return "sonnet";
7027
+ if (isSubagent)
7028
+ return "sonnet";
7029
+ if (isExtendedContextKnownUnavailable())
7030
+ return "sonnet";
7002
7031
  return subscriptionType === "max" ? "sonnet[1m]" : "sonnet";
7003
7032
  }
7033
+ var EXTRA_USAGE_RETRY_MS = 60 * 60 * 1000;
7034
+ var extraUsageUnavailableAt = 0;
7035
+ function recordExtendedContextUnavailable() {
7036
+ extraUsageUnavailableAt = Date.now();
7037
+ }
7038
+ function isExtendedContextKnownUnavailable() {
7039
+ return extraUsageUnavailableAt > 0 && Date.now() - extraUsageUnavailableAt < EXTRA_USAGE_RETRY_MS;
7040
+ }
7004
7041
  function stripExtendedContext(model) {
7005
7042
  if (model === "opus[1m]")
7006
7043
  return "opus";
@@ -7051,9 +7088,9 @@ async function resolveClaudeExecutableAsync() {
7051
7088
  const runningUnderBun = typeof process.versions.bun !== "undefined";
7052
7089
  if (runningUnderBun) {
7053
7090
  try {
7054
- const sdkPath = fileURLToPath(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
7055
- const sdkCliJs = join(dirname(sdkPath), "cli.js");
7056
- if (existsSync(sdkCliJs)) {
7091
+ const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
7092
+ const sdkCliJs = join(dirname2(sdkPath), "cli.js");
7093
+ if (existsSync2(sdkCliJs)) {
7057
7094
  cachedClaudePath = sdkCliJs;
7058
7095
  return sdkCliJs;
7059
7096
  }
@@ -7062,16 +7099,16 @@ async function resolveClaudeExecutableAsync() {
7062
7099
  try {
7063
7100
  const { stdout } = await exec("which claude");
7064
7101
  const claudePath = stdout.trim();
7065
- if (claudePath && existsSync(claudePath)) {
7102
+ if (claudePath && existsSync2(claudePath)) {
7066
7103
  cachedClaudePath = claudePath;
7067
7104
  return claudePath;
7068
7105
  }
7069
7106
  } catch {}
7070
7107
  if (!runningUnderBun) {
7071
7108
  try {
7072
- const sdkPath = fileURLToPath(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
7073
- const sdkCliJs = join(dirname(sdkPath), "cli.js");
7074
- if (existsSync(sdkCliJs)) {
7109
+ const sdkPath = fileURLToPath2(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
7110
+ const sdkCliJs = join(dirname2(sdkPath), "cli.js");
7111
+ if (existsSync2(sdkCliJs)) {
7075
7112
  cachedClaudePath = sdkCliJs;
7076
7113
  return sdkCliJs;
7077
7114
  }
@@ -7091,6 +7128,146 @@ function isClosedControllerError(error) {
7091
7128
  return error.message.includes("Controller is already closed");
7092
7129
  }
7093
7130
 
7131
+ // src/proxy/openai.ts
7132
+ function extractOpenAiContent(content) {
7133
+ if (typeof content === "string")
7134
+ return content;
7135
+ return content.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
7136
+ }
7137
+ function translateOpenAiToAnthropic(body) {
7138
+ const messages = body.messages ?? [];
7139
+ if (messages.length === 0)
7140
+ return null;
7141
+ const systemParts = [];
7142
+ const turns = [];
7143
+ for (const msg of messages) {
7144
+ const text = extractOpenAiContent(msg.content ?? "");
7145
+ if (msg.role === "system") {
7146
+ if (text)
7147
+ systemParts.push(text);
7148
+ } else {
7149
+ turns.push({
7150
+ role: msg.role === "assistant" ? "assistant" : "user",
7151
+ content: text
7152
+ });
7153
+ }
7154
+ }
7155
+ let systemPrompt = systemParts.join(`
7156
+ `);
7157
+ let messagesToSend = turns;
7158
+ if (turns.length > 1) {
7159
+ const history = turns.slice(0, -1).map((m) => `${m.role}: ${m.content}`).join(`
7160
+ `);
7161
+ const historyBlock = `<conversation_history>
7162
+ ${history}
7163
+ </conversation_history>
7164
+
7165
+ ` + `Continue this conversation naturally. Respond to the user's latest message.`;
7166
+ systemPrompt = systemPrompt ? `${systemPrompt}
7167
+
7168
+ ${historyBlock}` : historyBlock;
7169
+ messagesToSend = turns.slice(-1);
7170
+ }
7171
+ const result = {
7172
+ model: body.model ?? "claude-sonnet-4-6",
7173
+ messages: messagesToSend,
7174
+ max_tokens: body.max_tokens ?? body.max_completion_tokens ?? 8192,
7175
+ stream: body.stream ?? false
7176
+ };
7177
+ if (systemPrompt)
7178
+ result.system = systemPrompt;
7179
+ if (body.temperature !== undefined)
7180
+ result.temperature = body.temperature;
7181
+ if (body.top_p !== undefined)
7182
+ result.top_p = body.top_p;
7183
+ return result;
7184
+ }
7185
+ function toFinishReason(stopReason) {
7186
+ if (stopReason === "max_tokens")
7187
+ return "length";
7188
+ return "stop";
7189
+ }
7190
+ function translateAnthropicToOpenAi(response, completionId, model, created) {
7191
+ const content = (response.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
7192
+ const promptTokens = response.usage?.input_tokens ?? 0;
7193
+ const completionTokens = response.usage?.output_tokens ?? 0;
7194
+ return {
7195
+ id: completionId,
7196
+ object: "chat.completion",
7197
+ created,
7198
+ model,
7199
+ choices: [{
7200
+ index: 0,
7201
+ message: { role: "assistant", content },
7202
+ finish_reason: toFinishReason(response.stop_reason)
7203
+ }],
7204
+ usage: {
7205
+ prompt_tokens: promptTokens,
7206
+ completion_tokens: completionTokens,
7207
+ total_tokens: promptTokens + completionTokens
7208
+ }
7209
+ };
7210
+ }
7211
+ function translateAnthropicSseEvent(event, completionId, model, created) {
7212
+ if (event.type === "message_start") {
7213
+ return {
7214
+ id: completionId,
7215
+ object: "chat.completion.chunk",
7216
+ created,
7217
+ model,
7218
+ choices: [{ index: 0, delta: { role: "assistant", content: "" }, finish_reason: null }]
7219
+ };
7220
+ }
7221
+ if (event.type === "content_block_delta" && event.delta?.type === "text_delta" && typeof event.delta.text === "string") {
7222
+ return {
7223
+ id: completionId,
7224
+ object: "chat.completion.chunk",
7225
+ created,
7226
+ model,
7227
+ choices: [{ index: 0, delta: { content: event.delta.text }, finish_reason: null }]
7228
+ };
7229
+ }
7230
+ if (event.type === "message_delta" && event.delta?.stop_reason) {
7231
+ return {
7232
+ id: completionId,
7233
+ object: "chat.completion.chunk",
7234
+ created,
7235
+ model,
7236
+ choices: [{ index: 0, delta: {}, finish_reason: toFinishReason(event.delta.stop_reason) }]
7237
+ };
7238
+ }
7239
+ return null;
7240
+ }
7241
+ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000)) {
7242
+ const extendedContext = isMaxSubscription ? 1e6 : 200000;
7243
+ return [
7244
+ {
7245
+ id: "claude-sonnet-4-6",
7246
+ object: "model",
7247
+ created: now,
7248
+ owned_by: "anthropic",
7249
+ display_name: "Claude Sonnet 4.6",
7250
+ context_window: extendedContext
7251
+ },
7252
+ {
7253
+ id: "claude-opus-4-6",
7254
+ object: "model",
7255
+ created: now,
7256
+ owned_by: "anthropic",
7257
+ display_name: "Claude Opus 4.6",
7258
+ context_window: extendedContext
7259
+ },
7260
+ {
7261
+ id: "claude-haiku-4-5-20251001",
7262
+ object: "model",
7263
+ created: now,
7264
+ owned_by: "anthropic",
7265
+ display_name: "Claude Haiku 4.5",
7266
+ context_window: 200000
7267
+ }
7268
+ ];
7269
+ }
7270
+
7094
7271
  // src/proxy/messages.ts
7095
7272
  function stripCacheControlForHashing(obj) {
7096
7273
  if (!obj || typeof obj !== "object")
@@ -7163,6 +7340,17 @@ function createFileChangeHook(changes, mcpPrefix) {
7163
7340
  }]
7164
7341
  };
7165
7342
  }
7343
+ function isLikelyFilePath(s) {
7344
+ if (/[()[\]]/.test(s))
7345
+ return false;
7346
+ if (/^-?\d+$/.test(s))
7347
+ return false;
7348
+ if (/^[{}]$/.test(s))
7349
+ return false;
7350
+ if (!/[\w/.]/.test(s))
7351
+ return false;
7352
+ return true;
7353
+ }
7166
7354
  function extractFileChangesFromBash(command) {
7167
7355
  const changes = [];
7168
7356
  const seen = new Set;
@@ -7177,10 +7365,12 @@ function extractFileChangesFromBash(command) {
7177
7365
  changes.push({ operation, path });
7178
7366
  }
7179
7367
  };
7180
- const redirectRegex = /(?<![0-9])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
7368
+ const redirectRegex = /(?<![0-9=])>{1,2}\s*['"]?([^\s'";&|)]+)['"]?/g;
7181
7369
  let match2;
7182
7370
  while ((match2 = redirectRegex.exec(command)) !== null) {
7183
- addChange("wrote", match2[1]);
7371
+ if (isLikelyFilePath(match2[1])) {
7372
+ addChange("wrote", match2[1]);
7373
+ }
7184
7374
  }
7185
7375
  const teeRegex = /\btee\s+(?:-[a-zA-Z]\s+)*['"]?([^\s'";&|)]+)['"]?/g;
7186
7376
  while ((match2 = teeRegex.exec(command)) !== null) {
@@ -9148,7 +9338,7 @@ minimatch.escape = escape;
9148
9338
  minimatch.unescape = unescape;
9149
9339
 
9150
9340
  // node_modules/glob/dist/esm/glob.js
9151
- import { fileURLToPath as fileURLToPath3 } from "node:url";
9341
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
9152
9342
 
9153
9343
  // node_modules/lru-cache/dist/esm/index.js
9154
9344
  var defaultPerf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
@@ -10311,7 +10501,7 @@ class LRUCache {
10311
10501
 
10312
10502
  // node_modules/path-scurry/dist/esm/index.js
10313
10503
  import { posix, win32 } from "node:path";
10314
- import { fileURLToPath as fileURLToPath2 } from "node:url";
10504
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
10315
10505
  import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps } from "fs";
10316
10506
  import * as actualFS from "node:fs";
10317
10507
  import { lstat, readdir, readlink, realpath } from "node:fs/promises";
@@ -10858,10 +11048,10 @@ class Minipass extends EventEmitter {
10858
11048
  return this[ENCODING] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
10859
11049
  }
10860
11050
  async promise() {
10861
- return new Promise((resolve, reject) => {
11051
+ return new Promise((resolve2, reject) => {
10862
11052
  this.on(DESTROYED, () => reject(new Error("stream destroyed")));
10863
11053
  this.on("error", (er) => reject(er));
10864
- this.on("end", () => resolve());
11054
+ this.on("end", () => resolve2());
10865
11055
  });
10866
11056
  }
10867
11057
  [Symbol.asyncIterator]() {
@@ -10880,7 +11070,7 @@ class Minipass extends EventEmitter {
10880
11070
  return Promise.resolve({ done: false, value: res });
10881
11071
  if (this[EOF])
10882
11072
  return stop();
10883
- let resolve;
11073
+ let resolve2;
10884
11074
  let reject;
10885
11075
  const onerr = (er) => {
10886
11076
  this.off("data", ondata);
@@ -10894,19 +11084,19 @@ class Minipass extends EventEmitter {
10894
11084
  this.off("end", onend);
10895
11085
  this.off(DESTROYED, ondestroy);
10896
11086
  this.pause();
10897
- resolve({ value, done: !!this[EOF] });
11087
+ resolve2({ value, done: !!this[EOF] });
10898
11088
  };
10899
11089
  const onend = () => {
10900
11090
  this.off("error", onerr);
10901
11091
  this.off("data", ondata);
10902
11092
  this.off(DESTROYED, ondestroy);
10903
11093
  stop();
10904
- resolve({ done: true, value: undefined });
11094
+ resolve2({ done: true, value: undefined });
10905
11095
  };
10906
11096
  const ondestroy = () => onerr(new Error("stream destroyed"));
10907
11097
  return new Promise((res2, rej) => {
10908
11098
  reject = rej;
10909
- resolve = res2;
11099
+ resolve2 = res2;
10910
11100
  this.once(DESTROYED, ondestroy);
10911
11101
  this.once("error", onerr);
10912
11102
  this.once("end", onend);
@@ -11606,8 +11796,8 @@ class PathBase {
11606
11796
  if (this.#asyncReaddirInFlight) {
11607
11797
  await this.#asyncReaddirInFlight;
11608
11798
  } else {
11609
- let resolve = () => {};
11610
- this.#asyncReaddirInFlight = new Promise((res) => resolve = res);
11799
+ let resolve2 = () => {};
11800
+ this.#asyncReaddirInFlight = new Promise((res) => resolve2 = res);
11611
11801
  try {
11612
11802
  for (const e of await this.#fs.promises.readdir(fullpath, {
11613
11803
  withFileTypes: true
@@ -11620,7 +11810,7 @@ class PathBase {
11620
11810
  children.provisional = 0;
11621
11811
  }
11622
11812
  this.#asyncReaddirInFlight = undefined;
11623
- resolve();
11813
+ resolve2();
11624
11814
  }
11625
11815
  return children.slice(0, children.provisional);
11626
11816
  }
@@ -11766,7 +11956,7 @@ class PathScurryBase {
11766
11956
  constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS } = {}) {
11767
11957
  this.#fs = fsFromOption(fs);
11768
11958
  if (cwd instanceof URL || cwd.startsWith("file://")) {
11769
- cwd = fileURLToPath2(cwd);
11959
+ cwd = fileURLToPath3(cwd);
11770
11960
  }
11771
11961
  const cwdPath = pathImpl.resolve(cwd);
11772
11962
  this.roots = Object.create(null);
@@ -13070,7 +13260,7 @@ class Glob {
13070
13260
  if (!opts.cwd) {
13071
13261
  this.cwd = "";
13072
13262
  } else if (opts.cwd instanceof URL || opts.cwd.startsWith("file://")) {
13073
- opts.cwd = fileURLToPath3(opts.cwd);
13263
+ opts.cwd = fileURLToPath4(opts.cwd);
13074
13264
  }
13075
13265
  this.cwd = opts.cwd || "";
13076
13266
  this.root = opts.root;
@@ -13415,7 +13605,11 @@ function buildQueryOptions(ctx) {
13415
13605
  undoRollbackUuid,
13416
13606
  sdkHooks,
13417
13607
  adapter,
13418
- onStderr
13608
+ onStderr,
13609
+ effort,
13610
+ thinking,
13611
+ taskBudget,
13612
+ betas
13419
13613
  } = ctx;
13420
13614
  const blockedTools = [...adapter.getBlockedBuiltinTools(), ...adapter.getAgentIncompatibleTools()];
13421
13615
  const mcpServerName = adapter.getMcpServerName();
@@ -13454,7 +13648,11 @@ function buildQueryOptions(ctx) {
13454
13648
  ...Object.keys(sdkAgents).length > 0 ? { agents: sdkAgents } : {},
13455
13649
  ...resumeSessionId ? { resume: resumeSessionId } : {},
13456
13650
  ...isUndo ? { forkSession: true, ...undoRollbackUuid ? { resumeSessionAt: undoRollbackUuid } : {} } : {},
13457
- ...sdkHooks ? { hooks: sdkHooks } : {}
13651
+ ...sdkHooks ? { hooks: sdkHooks } : {},
13652
+ ...effort ? { effort } : {},
13653
+ ...thinking ? { thinking } : {},
13654
+ ...taskBudget ? { taskBudget } : {},
13655
+ ...betas && betas.length > 0 ? { betas } : {}
13458
13656
  }
13459
13657
  };
13460
13658
  }
@@ -13624,10 +13822,10 @@ class LRUMap {
13624
13822
  // src/proxy/sessionStore.ts
13625
13823
  import {
13626
13824
  closeSync,
13627
- existsSync as existsSync2,
13825
+ existsSync as existsSync3,
13628
13826
  mkdirSync,
13629
13827
  openSync,
13630
- readFileSync,
13828
+ readFileSync as readFileSync2,
13631
13829
  renameSync,
13632
13830
  statSync,
13633
13831
  unlinkSync,
@@ -13682,7 +13880,7 @@ var sessionDirOverride = null;
13682
13880
  var skipLocking = false;
13683
13881
  function getStorePath() {
13684
13882
  const dir = sessionDirOverride || process.env.MERIDIAN_SESSION_DIR || process.env.CLAUDE_PROXY_SESSION_DIR || getDefaultCacheDir();
13685
- if (!existsSync2(dir)) {
13883
+ if (!existsSync3(dir)) {
13686
13884
  mkdirSync(dir, { recursive: true });
13687
13885
  }
13688
13886
  return join2(dir, "sessions.json");
@@ -13690,9 +13888,9 @@ function getStorePath() {
13690
13888
  function getDefaultCacheDir() {
13691
13889
  const newDir = join2(homedir(), ".cache", "meridian");
13692
13890
  const oldDir = join2(homedir(), ".cache", "opencode-claude-max-proxy");
13693
- if (existsSync2(newDir))
13891
+ if (existsSync3(newDir))
13694
13892
  return newDir;
13695
- if (existsSync2(oldDir)) {
13893
+ if (existsSync3(oldDir)) {
13696
13894
  try {
13697
13895
  const { symlinkSync } = __require("fs");
13698
13896
  symlinkSync(oldDir, newDir);
@@ -13705,10 +13903,10 @@ function getDefaultCacheDir() {
13705
13903
  }
13706
13904
  function readStore() {
13707
13905
  const path3 = getStorePath();
13708
- if (!existsSync2(path3))
13906
+ if (!existsSync3(path3))
13709
13907
  return {};
13710
13908
  try {
13711
- const data = readFileSync(path3, "utf-8");
13909
+ const data = readFileSync2(path3, "utf-8");
13712
13910
  return JSON.parse(data);
13713
13911
  } catch (e) {
13714
13912
  console.error("[sessionStore] read failed:", e.message);
@@ -13734,7 +13932,19 @@ function lookupSharedSession(key) {
13734
13932
  const store = readStore();
13735
13933
  return store[key];
13736
13934
  }
13737
- function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids) {
13935
+ function lookupSharedSessionByClaudeId(claudeSessionId) {
13936
+ const sessions = Object.values(readStore());
13937
+ let newest;
13938
+ for (const session of sessions) {
13939
+ if (session.claudeSessionId !== claudeSessionId)
13940
+ continue;
13941
+ if (!newest || session.lastUsedAt > newest.lastUsedAt) {
13942
+ newest = session;
13943
+ }
13944
+ }
13945
+ return newest;
13946
+ }
13947
+ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage) {
13738
13948
  const path3 = getStorePath();
13739
13949
  const lockPath = `${path3}.lock`;
13740
13950
  const hasLock = skipLocking ? false : acquireLock(lockPath);
@@ -13751,7 +13961,8 @@ function storeSharedSession(key, claudeSessionId, messageCount, lineageHash, mes
13751
13961
  messageCount: messageCount ?? existing?.messageCount ?? 0,
13752
13962
  lineageHash: lineageHash ?? existing?.lineageHash,
13753
13963
  messageHashes: messageHashes ?? existing?.messageHashes,
13754
- sdkMessageUuids: sdkMessageUuids ?? existing?.sdkMessageUuids
13964
+ sdkMessageUuids: sdkMessageUuids ?? existing?.sdkMessageUuids,
13965
+ contextUsage: contextUsage ?? existing?.contextUsage
13755
13966
  };
13756
13967
  const maxEntries = getMaxStoredSessions();
13757
13968
  const keys = Object.keys(store);
@@ -13897,7 +14108,8 @@ function lookupSession(sessionId, messages, workingDirectory) {
13897
14108
  messageCount: shared.messageCount || 0,
13898
14109
  lineageHash: shared.lineageHash || "",
13899
14110
  messageHashes: shared.messageHashes,
13900
- sdkMessageUuids: shared.sdkMessageUuids
14111
+ sdkMessageUuids: shared.sdkMessageUuids,
14112
+ contextUsage: shared.contextUsage
13901
14113
  };
13902
14114
  const result = verifyLineage(state, messages, sessionId, sessionCache);
13903
14115
  if (result.type === "continuation" || result.type === "compaction") {
@@ -13924,7 +14136,8 @@ function lookupSession(sessionId, messages, workingDirectory) {
13924
14136
  messageCount: shared.messageCount || 0,
13925
14137
  lineageHash: shared.lineageHash || "",
13926
14138
  messageHashes: shared.messageHashes,
13927
- sdkMessageUuids: shared.sdkMessageUuids
14139
+ sdkMessageUuids: shared.sdkMessageUuids,
14140
+ contextUsage: shared.contextUsage
13928
14141
  };
13929
14142
  const result = verifyLineage(state, messages, fp, fingerprintCache);
13930
14143
  if (result.type === "continuation" || result.type === "compaction") {
@@ -13935,7 +14148,34 @@ function lookupSession(sessionId, messages, workingDirectory) {
13935
14148
  }
13936
14149
  return { type: "diverged" };
13937
14150
  }
13938
- function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids) {
14151
+ function getSessionByClaudeId(claudeSessionId) {
14152
+ let newest;
14153
+ const consider = (state) => {
14154
+ if (!state || state.claudeSessionId !== claudeSessionId)
14155
+ return;
14156
+ if (!newest || state.lastAccess > newest.lastAccess) {
14157
+ newest = state;
14158
+ }
14159
+ };
14160
+ for (const state of sessionCache.values())
14161
+ consider(state);
14162
+ for (const state of fingerprintCache.values())
14163
+ consider(state);
14164
+ const shared = lookupSharedSessionByClaudeId(claudeSessionId);
14165
+ if (shared) {
14166
+ consider({
14167
+ claudeSessionId: shared.claudeSessionId,
14168
+ lastAccess: shared.lastUsedAt,
14169
+ messageCount: shared.messageCount || 0,
14170
+ lineageHash: shared.lineageHash || "",
14171
+ messageHashes: shared.messageHashes,
14172
+ sdkMessageUuids: shared.sdkMessageUuids,
14173
+ contextUsage: shared.contextUsage
14174
+ });
14175
+ }
14176
+ return newest;
14177
+ }
14178
+ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sdkMessageUuids, contextUsage) {
13939
14179
  if (!claudeSessionId)
13940
14180
  return;
13941
14181
  const lineageHash = computeLineageHash(messages);
@@ -13946,7 +14186,8 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
13946
14186
  messageCount: messages?.length || 0,
13947
14187
  lineageHash,
13948
14188
  messageHashes,
13949
- sdkMessageUuids
14189
+ sdkMessageUuids,
14190
+ ...contextUsage ? { contextUsage } : {}
13950
14191
  };
13951
14192
  if (sessionId)
13952
14193
  sessionCache.set(sessionId, state);
@@ -13954,8 +14195,9 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
13954
14195
  if (fp)
13955
14196
  fingerprintCache.set(fp, state);
13956
14197
  const key = sessionId || fp;
13957
- if (key)
13958
- storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids);
14198
+ if (key) {
14199
+ storeSharedSession(key, claudeSessionId, state.messageCount, lineageHash, messageHashes, sdkMessageUuids, contextUsage);
14200
+ }
13959
14201
  }
13960
14202
 
13961
14203
  // src/proxy/server.ts
@@ -14033,6 +14275,16 @@ function buildFreshPrompt(messages, stripCacheControl) {
14033
14275
 
14034
14276
  `) || "";
14035
14277
  }
14278
+ function logUsage(requestId, usage) {
14279
+ const fmt = (n) => n > 1000 ? `${Math.round(n / 1000)}k` : String(n);
14280
+ const parts = [
14281
+ `input=${fmt(usage.input_tokens ?? 0)}`,
14282
+ `output=${fmt(usage.output_tokens ?? 0)}`,
14283
+ ...usage.cache_read_input_tokens ? [`cache_read=${fmt(usage.cache_read_input_tokens)}`] : [],
14284
+ ...usage.cache_creation_input_tokens ? [`cache_write=${fmt(usage.cache_creation_input_tokens)}`] : []
14285
+ ];
14286
+ console.error(`[PROXY] ${requestId} usage: ${parts.join(" ")}`);
14287
+ }
14036
14288
  function createProxyServer(config = {}) {
14037
14289
  const finalConfig = { ...DEFAULT_PROXY_CONFIG, ...config };
14038
14290
  const app = new Hono2;
@@ -14044,7 +14296,7 @@ function createProxyServer(config = {}) {
14044
14296
  status: "ok",
14045
14297
  service: "meridian",
14046
14298
  format: "anthropic",
14047
- endpoints: ["/v1/messages", "/messages", "/telemetry", "/health"]
14299
+ endpoints: ["/v1/messages", "/messages", "/v1/chat/completions", "/v1/models", "/telemetry", "/health"]
14048
14300
  });
14049
14301
  }
14050
14302
  return c.html(landingHtml);
@@ -14057,8 +14309,8 @@ function createProxyServer(config = {}) {
14057
14309
  activeSessions++;
14058
14310
  return;
14059
14311
  }
14060
- return new Promise((resolve2) => {
14061
- sessionQueue.push({ resolve: resolve2 });
14312
+ return new Promise((resolve3) => {
14313
+ sessionQueue.push({ resolve: resolve3 });
14062
14314
  });
14063
14315
  }
14064
14316
  function releaseSession() {
@@ -14094,11 +14346,15 @@ function createProxyServer(config = {}) {
14094
14346
  return textPrompt;
14095
14347
  };
14096
14348
  const body = await c.req.json();
14349
+ if (!Array.isArray(body.messages)) {
14350
+ return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
14351
+ }
14097
14352
  const authStatus = await getClaudeAuthStatusAsync();
14098
- let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType);
14353
+ const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
14354
+ let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
14099
14355
  const adapter = detectAdapter(c);
14100
14356
  const adapterStreamPref = adapter.prefersStreaming?.(body);
14101
- const stream2 = adapterStreamPref !== undefined ? adapterStreamPref : body.stream ?? true;
14357
+ const stream2 = adapterStreamPref !== undefined ? adapterStreamPref : body.stream ?? false;
14102
14358
  const workingDirectory = (process.env.MERIDIAN_WORKDIR ?? process.env.CLAUDE_PROXY_WORKDIR) || adapter.extractWorkingDirectory(body) || process.cwd();
14103
14359
  const {
14104
14360
  CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS,
@@ -14116,6 +14372,22 @@ function createProxyServer(config = {}) {
14116
14372
  `);
14117
14373
  }
14118
14374
  }
14375
+ const effortHeader = c.req.header("x-opencode-effort");
14376
+ const thinkingHeader = c.req.header("x-opencode-thinking");
14377
+ const taskBudgetHeader = c.req.header("x-opencode-task-budget");
14378
+ const betaHeader = c.req.header("anthropic-beta");
14379
+ const effort = effortHeader || body.effort || undefined;
14380
+ let thinking = body.thinking || undefined;
14381
+ if (thinkingHeader !== undefined) {
14382
+ try {
14383
+ thinking = JSON.parse(thinkingHeader);
14384
+ } catch (e) {
14385
+ console.error(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
14386
+ }
14387
+ }
14388
+ const parsedBudget = taskBudgetHeader ? Number.parseInt(taskBudgetHeader, 10) : NaN;
14389
+ const taskBudget = Number.isFinite(parsedBudget) ? { total: parsedBudget } : body.task_budget ? { total: body.task_budget.total ?? body.task_budget } : undefined;
14390
+ const betas = betaHeader ? betaHeader.split(",").map((b) => b.trim()).filter(Boolean) : undefined;
14119
14391
  const agentSessionId = adapter.getSessionId(c);
14120
14392
  const lineageResult = lookupSession(agentSessionId, body.messages || [], workingDirectory);
14121
14393
  const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
@@ -14129,7 +14401,7 @@ function createProxyServer(config = {}) {
14129
14401
  }).join(" → ");
14130
14402
  const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
14131
14403
  const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
14132
- const requestLogLine = `${requestMeta.requestId} model=${model} stream=${stream2} tools=${body.tools?.length ?? 0} lineage=${lineageType} session=${resumeSessionId?.slice(0, 8) || "new"}${isUndo && undoRollbackUuid ? ` rollback=${undoRollbackUuid.slice(0, 8)}` : ""} active=${activeSessions}/${MAX_CONCURRENT_SESSIONS} msgCount=${msgCount}`;
14404
+ const requestLogLine = `${requestMeta.requestId} model=${model} stream=${stream2} tools=${body.tools?.length ?? 0} lineage=${lineageType} session=${resumeSessionId?.slice(0, 8) || "new"}${isUndo && undoRollbackUuid ? ` rollback=${undoRollbackUuid.slice(0, 8)}` : ""}${agentMode ? ` agent=${agentMode}` : ""} active=${activeSessions}/${MAX_CONCURRENT_SESSIONS} msgCount=${msgCount}`;
14133
14405
  console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
14134
14406
  diagnosticLog.session(`${requestLogLine}`, requestMeta.requestId);
14135
14407
  claudeLog("request.received", {
@@ -14289,6 +14561,7 @@ function createProxyServer(config = {}) {
14289
14561
  while (sdkUuidMap.length < allMessages.length)
14290
14562
  sdkUuidMap.push(null);
14291
14563
  claudeLog("upstream.start", { mode: "non_stream", model });
14564
+ let lastUsage;
14292
14565
  try {
14293
14566
  if (!claudeExecutable) {
14294
14567
  claudeExecutable = await resolveClaudeExecutableAsync();
@@ -14317,9 +14590,13 @@ function createProxyServer(config = {}) {
14317
14590
  undoRollbackUuid,
14318
14591
  sdkHooks,
14319
14592
  adapter,
14320
- onStderr
14593
+ onStderr,
14594
+ effort,
14595
+ thinking,
14596
+ taskBudget,
14597
+ betas
14321
14598
  }))) {
14322
- if (event.type === "assistant") {
14599
+ if (event.type === "assistant" && !event.error) {
14323
14600
  didYieldContent = true;
14324
14601
  }
14325
14602
  yield event;
@@ -14356,20 +14633,25 @@ function createProxyServer(config = {}) {
14356
14633
  undoRollbackUuid: undefined,
14357
14634
  sdkHooks,
14358
14635
  adapter,
14359
- onStderr
14636
+ onStderr,
14637
+ effort,
14638
+ thinking,
14639
+ taskBudget,
14640
+ betas
14360
14641
  }));
14361
14642
  return;
14362
14643
  }
14363
14644
  if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
14364
14645
  const from = model;
14365
14646
  model = stripExtendedContext(model);
14647
+ recordExtendedContextUnavailable();
14366
14648
  claudeLog("upstream.context_fallback", {
14367
14649
  mode: "non_stream",
14368
14650
  from,
14369
14651
  to: model,
14370
14652
  reason: "extra_usage_required"
14371
14653
  });
14372
- console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model}`);
14654
+ console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
14373
14655
  continue;
14374
14656
  }
14375
14657
  if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
@@ -14437,6 +14719,9 @@ function createProxyServer(config = {}) {
14437
14719
  }
14438
14720
  contentBlocks.push(b);
14439
14721
  }
14722
+ const msgUsage = message.message.usage;
14723
+ if (msgUsage)
14724
+ lastUsage = { ...lastUsage, ...msgUsage };
14440
14725
  }
14441
14726
  }
14442
14727
  claudeLog("upstream.completed", {
@@ -14445,6 +14730,8 @@ function createProxyServer(config = {}) {
14445
14730
  assistantMessages,
14446
14731
  durationMs: Date.now() - upstreamStartAt
14447
14732
  });
14733
+ if (lastUsage)
14734
+ logUsage(requestMeta.requestId, lastUsage);
14448
14735
  } catch (error) {
14449
14736
  const stderrOutput = stderrLines.join(`
14450
14737
  `).trim();
@@ -14529,7 +14816,7 @@ Subprocess stderr: ${stderrOutput}`;
14529
14816
  error: null
14530
14817
  });
14531
14818
  if (currentSessionId) {
14532
- storeSession(agentSessionId, body.messages || [], currentSessionId, workingDirectory, sdkUuidMap);
14819
+ storeSession(agentSessionId, body.messages || [], currentSessionId, workingDirectory, sdkUuidMap, lastUsage);
14533
14820
  }
14534
14821
  const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
14535
14822
  return new Response(JSON.stringify({
@@ -14583,6 +14870,7 @@ Subprocess stderr: ${stderrOutput}`;
14583
14870
  while (sdkUuidMap.length < allMessages.length)
14584
14871
  sdkUuidMap.push(null);
14585
14872
  let messageStartEmitted = false;
14873
+ let lastUsage;
14586
14874
  try {
14587
14875
  let currentSessionId;
14588
14876
  const MAX_RATE_LIMIT_RETRIES = 2;
@@ -14609,7 +14897,11 @@ Subprocess stderr: ${stderrOutput}`;
14609
14897
  undoRollbackUuid,
14610
14898
  sdkHooks,
14611
14899
  adapter,
14612
- onStderr
14900
+ onStderr,
14901
+ effort,
14902
+ thinking,
14903
+ taskBudget,
14904
+ betas
14613
14905
  }))) {
14614
14906
  if (event.type === "stream_event") {
14615
14907
  didYieldClientEvent = true;
@@ -14648,20 +14940,25 @@ Subprocess stderr: ${stderrOutput}`;
14648
14940
  undoRollbackUuid: undefined,
14649
14941
  sdkHooks,
14650
14942
  adapter,
14651
- onStderr
14943
+ onStderr,
14944
+ effort,
14945
+ thinking,
14946
+ taskBudget,
14947
+ betas
14652
14948
  }));
14653
14949
  return;
14654
14950
  }
14655
14951
  if (isExtraUsageRequiredError(errMsg) && hasExtendedContext(model)) {
14656
14952
  const from = model;
14657
14953
  model = stripExtendedContext(model);
14954
+ recordExtendedContextUnavailable();
14658
14955
  claudeLog("upstream.context_fallback", {
14659
14956
  mode: "stream",
14660
14957
  from,
14661
14958
  to: model,
14662
14959
  reason: "extra_usage_required"
14663
14960
  });
14664
- console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model}`);
14961
+ console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
14665
14962
  continue;
14666
14963
  }
14667
14964
  if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
@@ -14757,6 +15054,9 @@ Subprocess stderr: ${stderrOutput}`;
14757
15054
  if (eventType === "message_start") {
14758
15055
  skipBlockIndices.clear();
14759
15056
  sdkToClientIndex.clear();
15057
+ const startUsage = event.message?.usage;
15058
+ if (startUsage)
15059
+ lastUsage = { ...lastUsage, ...startUsage };
14760
15060
  if (messageStartEmitted) {
14761
15061
  continue;
14762
15062
  }
@@ -14789,6 +15089,9 @@ Subprocess stderr: ${stderrOutput}`;
14789
15089
  event.index = sdkToClientIndex.get(eventIndex);
14790
15090
  }
14791
15091
  if (eventType === "message_delta") {
15092
+ const deltaUsage = event.usage;
15093
+ if (deltaUsage)
15094
+ lastUsage = { ...lastUsage, ...deltaUsage };
14792
15095
  const stopReason = event.delta?.stop_reason;
14793
15096
  if (stopReason === "tool_use" && skipBlockIndices.size > 0) {
14794
15097
  continue;
@@ -14830,8 +15133,10 @@ data: ${JSON.stringify({ type: "message_stop" })}
14830
15133
  eventsForwarded,
14831
15134
  textEventsForwarded
14832
15135
  });
15136
+ if (lastUsage)
15137
+ logUsage(requestMeta.requestId, lastUsage);
14833
15138
  if (currentSessionId) {
14834
- storeSession(agentSessionId, body.messages || [], currentSessionId, workingDirectory, sdkUuidMap);
15139
+ storeSession(agentSessionId, body.messages || [], currentSessionId, workingDirectory, sdkUuidMap, lastUsage);
14835
15140
  }
14836
15141
  if (!streamClosed) {
14837
15142
  const unseenToolUses = capturedToolUses.filter((tu) => !streamedToolUseIds.has(tu.id));
@@ -15106,7 +15411,8 @@ data: ${JSON.stringify({
15106
15411
  email: auth.email,
15107
15412
  subscriptionType: auth.subscriptionType
15108
15413
  },
15109
- mode: process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH ? "passthrough" : "internal"
15414
+ mode: process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH ? "passthrough" : "internal",
15415
+ plugin: { opencode: checkPluginConfigured() ? "configured" : "not-configured" }
15110
15416
  });
15111
15417
  } catch {
15112
15418
  return c.json({
@@ -15123,6 +15429,105 @@ data: ${JSON.stringify({
15123
15429
  }
15124
15430
  return c.json({ success: false, message: "Token refresh failed. If the problem persists, run 'claude login'." }, 500);
15125
15431
  });
15432
+ app.post("/v1/chat/completions", async (c) => {
15433
+ const rawBody = await c.req.json();
15434
+ const anthropicBody = translateOpenAiToAnthropic(rawBody);
15435
+ if (!anthropicBody) {
15436
+ return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
15437
+ }
15438
+ const internalReq = new Request("http://internal/v1/messages", {
15439
+ method: "POST",
15440
+ headers: { "Content-Type": "application/json" },
15441
+ body: JSON.stringify(anthropicBody)
15442
+ });
15443
+ const internalRes = await app.fetch(internalReq);
15444
+ if (!internalRes.ok) {
15445
+ const errBody = await internalRes.text();
15446
+ return c.json({ type: "error", error: { type: "upstream_error", message: errBody } }, internalRes.status);
15447
+ }
15448
+ const completionId = `chatcmpl-${randomUUID()}`;
15449
+ const created = Math.floor(Date.now() / 1000);
15450
+ const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : "claude-sonnet-4-6";
15451
+ if (!anthropicBody.stream) {
15452
+ const anthropicRes = await internalRes.json();
15453
+ return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created));
15454
+ }
15455
+ const encoder = new TextEncoder;
15456
+ const readable = new ReadableStream({
15457
+ async start(controller) {
15458
+ const reader = internalRes.body?.getReader();
15459
+ if (!reader) {
15460
+ controller.close();
15461
+ return;
15462
+ }
15463
+ const decoder = new TextDecoder;
15464
+ let buffer = "";
15465
+ let streamError = null;
15466
+ try {
15467
+ while (true) {
15468
+ const { done, value } = await reader.read();
15469
+ if (done)
15470
+ break;
15471
+ buffer += decoder.decode(value, { stream: true });
15472
+ const lines = buffer.split(`
15473
+ `);
15474
+ buffer = lines.pop() ?? "";
15475
+ for (const line of lines) {
15476
+ if (!line.startsWith("data: "))
15477
+ continue;
15478
+ const dataStr = line.slice(6).trim();
15479
+ if (!dataStr)
15480
+ continue;
15481
+ let event;
15482
+ try {
15483
+ event = JSON.parse(dataStr);
15484
+ } catch {
15485
+ continue;
15486
+ }
15487
+ if (typeof event.type !== "string")
15488
+ continue;
15489
+ const chunk = translateAnthropicSseEvent(event, completionId, model, created);
15490
+ if (chunk)
15491
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}
15492
+
15493
+ `));
15494
+ }
15495
+ }
15496
+ } catch (err) {
15497
+ streamError = err instanceof Error ? err : new Error(String(err));
15498
+ } finally {
15499
+ if (!streamError)
15500
+ controller.enqueue(encoder.encode(`data: [DONE]
15501
+
15502
+ `));
15503
+ controller.close();
15504
+ }
15505
+ }
15506
+ });
15507
+ return new Response(readable, {
15508
+ headers: {
15509
+ "Content-Type": "text/event-stream",
15510
+ "Cache-Control": "no-cache",
15511
+ Connection: "keep-alive"
15512
+ }
15513
+ });
15514
+ });
15515
+ app.get("/v1/models", async (c) => {
15516
+ const authStatus = await getClaudeAuthStatusAsync();
15517
+ const isMax = authStatus?.subscriptionType === "max";
15518
+ return c.json({ object: "list", data: buildModelList(isMax) });
15519
+ });
15520
+ app.get("/v1/sessions/:claudeSessionId/context-usage", (c) => {
15521
+ const claudeSessionId = c.req.param("claudeSessionId");
15522
+ const session = getSessionByClaudeId(claudeSessionId);
15523
+ if (!session) {
15524
+ return c.json({ error: "Session not found" }, 404);
15525
+ }
15526
+ if (!session.contextUsage) {
15527
+ return c.json({ error: "No usage data available for this session" }, 404);
15528
+ }
15529
+ return c.json({ session_id: claudeSessionId, context_usage: session.contextUsage });
15530
+ });
15126
15531
  app.all("*", (c) => {
15127
15532
  console.error(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
15128
15533
  return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);
@@ -15166,8 +15571,8 @@ Or use a different port:`);
15166
15571
  server,
15167
15572
  config: finalConfig,
15168
15573
  async close() {
15169
- await new Promise((resolve2, reject) => {
15170
- server.close((err) => err ? reject(err) : resolve2());
15574
+ await new Promise((resolve3, reject) => {
15575
+ server.close((err) => err ? reject(err) : resolve3());
15171
15576
  });
15172
15577
  }
15173
15578
  };