@rynfar/meridian 1.43.0 → 1.44.1

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 (39) hide show
  1. package/README.md +44 -9
  2. package/dist/{cli-7k1fcprd.js → cli-1kbcm3yn.js} +37 -20
  3. package/dist/{cli-1x5gqsqc.js → cli-6rezv582.js} +206 -64
  4. package/dist/{cli-rtab0qa6.js → cli-je60fevk.js} +38 -20
  5. package/dist/cli.js +25 -18
  6. package/dist/env.d.ts +16 -0
  7. package/dist/env.d.ts.map +1 -1
  8. package/dist/{profileCli-0h4nc2h8.js → profileCli-xcmmr5w4.js} +208 -46
  9. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  10. package/dist/proxy/adapters/detect.d.ts.map +1 -1
  11. package/dist/proxy/adapters/droid.d.ts.map +1 -1
  12. package/dist/proxy/adapters/openai.d.ts +23 -0
  13. package/dist/proxy/adapters/openai.d.ts.map +1 -0
  14. package/dist/proxy/adapters/opencode.d.ts.map +1 -1
  15. package/dist/proxy/adapters/pi.d.ts.map +1 -1
  16. package/dist/proxy/effort.d.ts +21 -0
  17. package/dist/proxy/effort.d.ts.map +1 -0
  18. package/dist/proxy/openai.d.ts +12 -0
  19. package/dist/proxy/openai.d.ts.map +1 -1
  20. package/dist/proxy/query.d.ts +3 -2
  21. package/dist/proxy/query.d.ts.map +1 -1
  22. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  23. package/dist/proxy/server.d.ts +1 -0
  24. package/dist/proxy/server.d.ts.map +1 -1
  25. package/dist/proxy/setup.d.ts +9 -0
  26. package/dist/proxy/setup.d.ts.map +1 -1
  27. package/dist/proxy/streamIdleGuard.d.ts +28 -0
  28. package/dist/proxy/streamIdleGuard.d.ts.map +1 -0
  29. package/dist/proxy/tokenRefresh.d.ts +2 -0
  30. package/dist/proxy/tokenRefresh.d.ts.map +1 -1
  31. package/dist/proxy/transforms/opencode.d.ts.map +1 -1
  32. package/dist/proxy/transforms/pi.d.ts.map +1 -1
  33. package/dist/proxy/transforms/registry.d.ts.map +1 -1
  34. package/dist/proxy/types.d.ts +7 -0
  35. package/dist/proxy/types.d.ts.map +1 -1
  36. package/dist/server.js +5 -3
  37. package/dist/{setup-v5pnqe04.js → setup-6c11e8d6.js} +4 -2
  38. package/dist/{tokenRefresh-swetnf89.js → tokenRefresh-pvc2q8ea.js} +1 -1
  39. package/package.json +4 -3
@@ -34,7 +34,7 @@ import {
34
34
  } from "./cli-yeazzt32.js";
35
35
  import {
36
36
  checkPluginConfigured
37
- } from "./cli-rtab0qa6.js";
37
+ } from "./cli-je60fevk.js";
38
38
  import {
39
39
  claudeLog,
40
40
  createPlatformCredentialStore,
@@ -43,7 +43,7 @@ import {
43
43
  startBackgroundRefresh,
44
44
  stopBackgroundRefresh,
45
45
  withClaudeLogContext
46
- } from "./cli-7k1fcprd.js";
46
+ } from "./cli-1kbcm3yn.js";
47
47
  import {
48
48
  __commonJS,
49
49
  __esm,
@@ -1242,7 +1242,7 @@ function getFeaturesForAdapter(adapterName) {
1242
1242
  };
1243
1243
  }
1244
1244
  function getAllFeatureConfigs() {
1245
- const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough"];
1245
+ const adapters = ["opencode", "crush", "forgecode", "pi", "droid", "passthrough", "openai"];
1246
1246
  const result = {};
1247
1247
  for (const name of adapters) {
1248
1248
  result[name] = getFeaturesForAdapter(name);
@@ -1314,6 +1314,9 @@ var init_sdkFeatures = __esm(() => {
1314
1314
  ADAPTER_DEFAULTS = {
1315
1315
  passthrough: {
1316
1316
  codeSystemPrompt: false
1317
+ },
1318
+ openai: {
1319
+ codeSystemPrompt: false
1317
1320
  }
1318
1321
  };
1319
1322
  VALID_CLAUDE_MD_VALUES = new Set(["off", "project", "full"]);
@@ -3750,6 +3753,57 @@ class RateLimitStore {
3750
3753
  }
3751
3754
  var rateLimitStore = new RateLimitStore;
3752
3755
 
3756
+ // src/proxy/streamIdleGuard.ts
3757
+ class UpstreamIdleError extends Error {
3758
+ idleMs;
3759
+ sinceLastMs;
3760
+ constructor(idleMs, sinceLastMs) {
3761
+ super(`upstream idle for ${sinceLastMs}ms (limit ${idleMs}ms)`);
3762
+ this.name = "UpstreamIdleError";
3763
+ this.idleMs = idleMs;
3764
+ this.sinceLastMs = sinceLastMs;
3765
+ }
3766
+ }
3767
+ async function* guardUpstreamIdle(source, idleMs, onStall) {
3768
+ if (idleMs <= 0) {
3769
+ yield* source;
3770
+ return;
3771
+ }
3772
+ const it = source[Symbol.asyncIterator]();
3773
+ let lastAt = Date.now();
3774
+ try {
3775
+ while (true) {
3776
+ const nextP = it.next();
3777
+ nextP.catch(() => {});
3778
+ let timer;
3779
+ const idle = new Promise((_, reject) => {
3780
+ const remaining = Math.max(0, idleMs - (Date.now() - lastAt));
3781
+ timer = setTimeout(() => {
3782
+ const sinceLastMs = Date.now() - lastAt;
3783
+ try {
3784
+ onStall?.(sinceLastMs);
3785
+ } catch {}
3786
+ reject(new UpstreamIdleError(idleMs, sinceLastMs));
3787
+ }, remaining);
3788
+ });
3789
+ let res;
3790
+ try {
3791
+ res = await Promise.race([nextP, idle]);
3792
+ } finally {
3793
+ if (timer)
3794
+ clearTimeout(timer);
3795
+ }
3796
+ if (res.done)
3797
+ return;
3798
+ lastAt = Date.now();
3799
+ yield res.value;
3800
+ }
3801
+ } finally {
3802
+ const returnP = it.return?.(undefined);
3803
+ returnP?.catch(() => {});
3804
+ }
3805
+ }
3806
+
3753
3807
  // src/proxy/oauthUsage.ts
3754
3808
  var OAUTH_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
3755
3809
  var OAUTH_BETA_HEADER = "oauth-2025-04-20";
@@ -3889,7 +3943,7 @@ var DEFAULT_PROXY_CONFIG = {
3889
3943
  host: "127.0.0.1",
3890
3944
  debug: (process.env.MERIDIAN_DEBUG ?? process.env.CLAUDE_PROXY_DEBUG) === "1",
3891
3945
  idleTimeoutSeconds: 120,
3892
- silent: false,
3946
+ silent: (process.env.MERIDIAN_SILENT ?? process.env.CLAUDE_PROXY_SILENT) === "1",
3893
3947
  profiles: undefined,
3894
3948
  defaultProfile: undefined,
3895
3949
  version: undefined
@@ -3903,6 +3957,14 @@ function envBool(suffix) {
3903
3957
  const val = env(suffix);
3904
3958
  return val === "1" || val === "true" || val === "yes";
3905
3959
  }
3960
+ function resolvePassthrough(defaultValue) {
3961
+ const val = env("PASSTHROUGH");
3962
+ if (val === "1" || val === "true" || val === "yes")
3963
+ return true;
3964
+ if (val === "0" || val === "false" || val === "no")
3965
+ return false;
3966
+ return defaultValue;
3967
+ }
3906
3968
  function envInt(suffix, defaultValue) {
3907
3969
  const val = env(suffix);
3908
3970
  if (!val)
@@ -9479,6 +9541,10 @@ ${historyBlock}` : historyBlock;
9479
9541
  result.temperature = body.temperature;
9480
9542
  if (body.top_p !== undefined)
9481
9543
  result.top_p = body.top_p;
9544
+ if (body.reasoning_effort !== undefined)
9545
+ result.reasoning_effort = body.reasoning_effort;
9546
+ if (body.output_config?.effort !== undefined)
9547
+ result.output_config = { effort: body.output_config.effort };
9482
9548
  return result;
9483
9549
  }
9484
9550
  function toFinishReason(stopReason) {
@@ -9974,8 +10040,7 @@ var openCodeTransforms = [
9974
10040
  const incompatibleTools = CLAUDE_CODE_ONLY_TOOLS;
9975
10041
  const allowedMcpTools = ALLOWED_MCP_TOOLS;
9976
10042
  const coreToolNames = ["read", "write", "edit", "bash", "glob", "grep"];
9977
- const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
9978
- const passthrough = !(envVal === "0" || envVal === "false" || envVal === "no");
10043
+ const passthrough = resolvePassthrough(true);
9979
10044
  let sdkAgents = {};
9980
10045
  if (Array.isArray(body.tools)) {
9981
10046
  const taskTool = body.tools.find((t) => t.name === "task" || t.name === "Task");
@@ -10070,11 +10135,7 @@ var openCodeAdapter = {
10070
10135
  return ["read", "write", "edit", "bash", "glob", "grep"];
10071
10136
  },
10072
10137
  usesPassthrough() {
10073
- const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
10074
- if (envVal === "0" || envVal === "false" || envVal === "no") {
10075
- return false;
10076
- }
10077
- return true;
10138
+ return resolvePassthrough(true);
10078
10139
  },
10079
10140
  supportsThinking() {
10080
10141
  return true;
@@ -10145,8 +10206,7 @@ var DROID_ALLOWED_MCP_TOOLS = [
10145
10206
  `mcp__${DROID_MCP_SERVER_NAME}__grep`
10146
10207
  ];
10147
10208
  function resolveDroidPassthrough() {
10148
- const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
10149
- return envVal === "1" || envVal === "true" || envVal === "yes";
10209
+ return resolvePassthrough(false);
10150
10210
  }
10151
10211
  var droidTransforms = [
10152
10212
  {
@@ -10230,8 +10290,7 @@ var droidAdapter = {
10230
10290
  return "";
10231
10291
  },
10232
10292
  usesPassthrough() {
10233
- const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
10234
- return envVal === "1" || envVal === "true" || envVal === "yes";
10293
+ return resolvePassthrough(false);
10235
10294
  }
10236
10295
  };
10237
10296
 
@@ -10452,6 +10511,9 @@ var PI_ALLOWED_MCP_TOOLS = [
10452
10511
  `mcp__${PI_MCP_SERVER_NAME}__glob`,
10453
10512
  `mcp__${PI_MCP_SERVER_NAME}__grep`
10454
10513
  ];
10514
+ function resolvePiPassthrough() {
10515
+ return resolvePassthrough(true);
10516
+ }
10455
10517
  var piTransforms = [
10456
10518
  {
10457
10519
  name: "pi-core",
@@ -10474,6 +10536,7 @@ var piTransforms = [
10474
10536
  incompatibleTools: CLAUDE_CODE_ONLY_TOOLS,
10475
10537
  allowedMcpTools: PI_ALLOWED_MCP_TOOLS,
10476
10538
  sdkAgents: {},
10539
+ passthrough: resolvePiPassthrough(),
10477
10540
  supportsThinking: true,
10478
10541
  extractFileChangesFromToolUse
10479
10542
  };
@@ -10542,6 +10605,9 @@ var piAdapter = {
10542
10605
  buildSystemContextAddendum(_body, _sdkAgents) {
10543
10606
  return "";
10544
10607
  },
10608
+ usesPassthrough() {
10609
+ return resolvePassthrough(true);
10610
+ },
10545
10611
  extractFileChangesFromToolUse(toolName, toolInput) {
10546
10612
  const input = toolInput;
10547
10613
  const filePath = input?.filePath ?? input?.file_path ?? input?.path;
@@ -10711,11 +10777,7 @@ var claudeCodeAdapter = {
10711
10777
  return ["Read", "Write", "Edit", "Bash", "Glob", "Grep"];
10712
10778
  },
10713
10779
  usesPassthrough() {
10714
- const envVal = process.env.MERIDIAN_PASSTHROUGH ?? process.env.CLAUDE_PROXY_PASSTHROUGH;
10715
- if (envVal === "0" || envVal === "false" || envVal === "no") {
10716
- return false;
10717
- }
10718
- return true;
10780
+ return resolvePassthrough(true);
10719
10781
  },
10720
10782
  supportsThinking() {
10721
10783
  return true;
@@ -10740,6 +10802,12 @@ var claudeCodeAdapter = {
10740
10802
  }
10741
10803
  };
10742
10804
 
10805
+ // src/proxy/adapters/openai.ts
10806
+ var openAiAdapter = {
10807
+ ...openCodeAdapter,
10808
+ name: "openai"
10809
+ };
10810
+
10743
10811
  // src/proxy/adapters/detect.ts
10744
10812
  var ADAPTER_MAP = {
10745
10813
  opencode: openCodeAdapter,
@@ -10749,7 +10817,8 @@ var ADAPTER_MAP = {
10749
10817
  pi: piAdapter,
10750
10818
  forgecode: forgeCodeAdapter,
10751
10819
  "claude-code": claudeCodeAdapter,
10752
- claudecode: claudeCodeAdapter
10820
+ claudecode: claudeCodeAdapter,
10821
+ openai: openAiAdapter
10753
10822
  };
10754
10823
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
10755
10824
  if (envDefault && !ADAPTER_MAP[envDefault]) {
@@ -16625,6 +16694,12 @@ function buildQueryOptions(ctx) {
16625
16694
  };
16626
16695
  }
16627
16696
 
16697
+ // src/proxy/effort.ts
16698
+ var VALID_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
16699
+ function normalizeEffort(value) {
16700
+ return typeof value === "string" && VALID_EFFORTS.includes(value) ? value : undefined;
16701
+ }
16702
+
16628
16703
  // src/proxy/transforms/registry.ts
16629
16704
  var ADAPTER_TRANSFORMS = {
16630
16705
  opencode: openCodeTransforms,
@@ -16632,7 +16707,8 @@ var ADAPTER_TRANSFORMS = {
16632
16707
  droid: droidTransforms,
16633
16708
  pi: piTransforms,
16634
16709
  forgecode: forgeCodeTransforms,
16635
- passthrough: passthroughTransforms
16710
+ passthrough: passthroughTransforms,
16711
+ openai: openCodeTransforms
16636
16712
  };
16637
16713
  function getAdapterTransforms(adapterName) {
16638
16714
  return ADAPTER_TRANSFORMS[adapterName] ?? [];
@@ -17504,6 +17580,23 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
17504
17580
  // src/proxy/server.ts
17505
17581
  var exec2 = promisify2(execCallback);
17506
17582
  var claudeExecutable = "";
17583
+ var UPSTREAM_IDLE_MS = 90000;
17584
+ function credentialStoreForProfile(profile) {
17585
+ if (profile.type !== "claude-max")
17586
+ return;
17587
+ return createPlatformCredentialStore(profile.env.CLAUDE_CONFIG_DIR ? { claudeConfigDir: profile.env.CLAUDE_CONFIG_DIR } : undefined);
17588
+ }
17589
+ async function ensureFreshTokenForProfiles(config) {
17590
+ const profiles = getEffectiveProfiles(config.profiles);
17591
+ if (profiles.length === 0)
17592
+ return;
17593
+ for (const profile of profiles) {
17594
+ const resolved = resolveProfile(config.profiles, config.defaultProfile, profile.id);
17595
+ const store = credentialStoreForProfile(resolved);
17596
+ if (store)
17597
+ await ensureFreshToken(store).catch(() => {});
17598
+ }
17599
+ }
17507
17600
  var MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
17508
17601
  function hasMultimodalContent(content) {
17509
17602
  if (!Array.isArray(content))
@@ -17627,8 +17720,13 @@ function buildFreshPrompt(messages, sanitizeOpts = {}) {
17627
17720
 
17628
17721
  `) || "";
17629
17722
  }
17723
+ var proxyLogSilent = false;
17724
+ function plog(message) {
17725
+ if (!proxyLogSilent)
17726
+ console.error(message);
17727
+ }
17630
17728
  function logUsage(requestId, usage) {
17631
- console.error(`[PROXY] ${requestId} usage: ${formatUsageSummary(usage)}`);
17729
+ plog(`[PROXY] ${requestId} usage: ${formatUsageSummary(usage)}`);
17632
17730
  }
17633
17731
  function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume, isPassthrough) {
17634
17732
  if (!usage || !sdkSessionId)
@@ -17661,7 +17759,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
17661
17759
  if (anomalies.length > 0) {
17662
17760
  const alerts = formatAnomalyAlerts(requestId, anomalies);
17663
17761
  for (const line of alerts) {
17664
- console.error(line);
17762
+ plog(line);
17665
17763
  }
17666
17764
  for (const a of anomalies) {
17667
17765
  diagnosticLog2.log({
@@ -17675,6 +17773,7 @@ function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume,
17675
17773
  }
17676
17774
  function createProxyServer(config = {}) {
17677
17775
  const finalConfig = { ...DEFAULT_PROXY_CONFIG, ...config };
17776
+ proxyLogSilent = finalConfig.silent;
17678
17777
  const serverVersion = finalConfig.version ?? "unknown";
17679
17778
  restoreActiveProfile(finalConfig.profiles);
17680
17779
  const sessionDiscoveredTools = new Map;
@@ -17782,6 +17881,7 @@ function createProxyServer(config = {}) {
17782
17881
  } = process.env;
17783
17882
  const sdkModelDefaults = resolveSdkModelDefaults();
17784
17883
  const profileEnv = { ...sdkModelDefaults, ...cleanEnv, ...profile.env };
17884
+ const profileCredentialStore = credentialStoreForProfile(profile);
17785
17885
  let systemContext = "";
17786
17886
  if (body.system) {
17787
17887
  if (typeof body.system === "string") {
@@ -17811,15 +17911,15 @@ function createProxyServer(config = {}) {
17811
17911
  const rawBetaHeader = c.req.header("anthropic-beta");
17812
17912
  const betaFilter = filterBetasForProfile(rawBetaHeader, profile.type, getBetaPolicyFromEnv());
17813
17913
  if (betaFilter.stripped.length > 0) {
17814
- console.error(`[PROXY] ${requestMeta.requestId} stripped anthropic-beta(s) for Max profile: ${betaFilter.stripped.join(", ")}`);
17914
+ plog(`[PROXY] ${requestMeta.requestId} stripped anthropic-beta(s) for Max profile: ${betaFilter.stripped.join(", ")}`);
17815
17915
  }
17816
- const effort = effortHeader || body.effort || undefined;
17916
+ const effort = normalizeEffort(effortHeader || body.effort || body.reasoning_effort || body.output_config?.effort);
17817
17917
  let thinking = body.thinking || undefined;
17818
17918
  if (thinkingHeader !== undefined) {
17819
17919
  try {
17820
17920
  thinking = JSON.parse(thinkingHeader);
17821
17921
  } catch (e) {
17822
- console.error(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
17922
+ plog(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
17823
17923
  }
17824
17924
  }
17825
17925
  const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
@@ -17834,7 +17934,7 @@ function createProxyServer(config = {}) {
17834
17934
  if (thinkingBetaStripped) {
17835
17935
  thinking = { type: "disabled" };
17836
17936
  if (betaFilter.stripped.length > 0) {
17837
- console.error(`[PROXY] ${requestMeta.requestId} thinking disabled (thinking beta stripped by ${getBetaPolicyFromEnv()} policy)`);
17937
+ plog(`[PROXY] ${requestMeta.requestId} thinking disabled (thinking beta stripped by ${getBetaPolicyFromEnv()} policy)`);
17838
17938
  }
17839
17939
  }
17840
17940
  const parsedBudget = taskBudgetHeader ? Number.parseInt(taskBudgetHeader, 10) : NaN;
@@ -17861,14 +17961,14 @@ function createProxyServer(config = {}) {
17861
17961
  const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
17862
17962
  const toolCount = body.tools?.length ?? 0;
17863
17963
  const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name}${requestSource ? ` source=${requestSource}` : ""} model=${model} stream=${stream2} tools=${toolCount} 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}`;
17864
- console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
17964
+ plog(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
17865
17965
  diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
17866
17966
  if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
17867
17967
  const recovery = lookupSessionRecovery(profileSessionId);
17868
17968
  if (recovery) {
17869
17969
  const prevId = recovery.previousClaudeSessionId || recovery.claudeSessionId;
17870
17970
  const recoveryMsg = `${requestMeta.requestId} SESSION RECOVERY: previous conversation available. Run: claude --resume ${prevId}`;
17871
- console.error(`[PROXY] ${recoveryMsg}`);
17971
+ plog(`[PROXY] ${recoveryMsg}`);
17872
17972
  diagnosticLog2.session(recoveryMsg, requestMeta.requestId);
17873
17973
  }
17874
17974
  }
@@ -17964,7 +18064,7 @@ function createProxyServer(config = {}) {
17964
18064
  const cached = sessionToolCache.get(profileSessionId);
17965
18065
  if (cached && cached.length > 0) {
17966
18066
  requestTools = cached;
17967
- console.error(`[PROXY] ${requestMeta.requestId} tools_restored: client sent 0 tools but session had ${cached.length} — reusing cached tools to preserve prompt cache`);
18067
+ plog(`[PROXY] ${requestMeta.requestId} tools_restored: client sent 0 tools but session had ${cached.length} — reusing cached tools to preserve prompt cache`);
17968
18068
  }
17969
18069
  }
17970
18070
  if (passthrough && requestTools.length > 0) {
@@ -17977,7 +18077,7 @@ function createProxyServer(config = {}) {
17977
18077
  if (profileSessionId) {
17978
18078
  sessionMcpCache.set(profileSessionId, { key: toolSetKey, mcp: passthroughMcp });
17979
18079
  if (cachedMcp) {
17980
- console.error(`[PROXY] ${requestMeta.requestId} tools_changed: MCP server recreated (prompt cache likely invalidates)`);
18080
+ plog(`[PROXY] ${requestMeta.requestId} tools_changed: MCP server recreated (prompt cache likely invalidates)`);
17981
18081
  }
17982
18082
  }
17983
18083
  }
@@ -17989,7 +18089,7 @@ function createProxyServer(config = {}) {
17989
18089
  const coreSet = coreNames ? new Set(coreNames.map((n) => n.toLowerCase())) : undefined;
17990
18090
  const deferredToolCount = hasDeferredTools && requestTools.length > 0 ? requestTools.filter((t) => t.defer_loading === true || coreSet && !coreSet.has(String(t.name).toLowerCase())).length : 0;
17991
18091
  if (hasDeferredTools) {
17992
- console.error(`[PROXY] ${requestMeta.requestId} deferred=${deferredToolCount}/${toolCount} tools (core: ${coreNames?.join(",") ?? "none"})`);
18092
+ plog(`[PROXY] ${requestMeta.requestId} deferred=${deferredToolCount}/${toolCount} tools (core: ${coreNames?.join(",") ?? "none"})`);
17993
18093
  }
17994
18094
  const mcpPrefix = `mcp__${adapter.getMcpServerName()}__`;
17995
18095
  const trackFileChanges = !(process.env.MERIDIAN_NO_FILE_CHANGES ?? process.env.CLAUDE_PROXY_NO_FILE_CHANGES) && pipelineCtx.shouldTrackFileChanges;
@@ -18050,7 +18150,9 @@ function createProxyServer(config = {}) {
18050
18150
  const RATE_LIMIT_BASE_DELAY_MS = 1000;
18051
18151
  const response = async function* () {
18052
18152
  let rateLimitRetries = 0;
18053
- await ensureFreshToken().catch(() => {});
18153
+ if (profileCredentialStore) {
18154
+ await ensureFreshToken(profileCredentialStore).catch(() => {});
18155
+ }
18054
18156
  let tokenRefreshed = false;
18055
18157
  let didFreshBaseRetry = false;
18056
18158
  while (true) {
@@ -18114,7 +18216,7 @@ function createProxyServer(config = {}) {
18114
18216
  rollbackUuid: undoRollbackUuid,
18115
18217
  resumeSessionId
18116
18218
  });
18117
- console.error(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
18219
+ plog(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
18118
18220
  evictSession(profileSessionId, profileScopedCwd, allMessages);
18119
18221
  sdkUuidMap.length = 0;
18120
18222
  for (let i = 0;i < allMessages.length; i++)
@@ -18170,7 +18272,7 @@ function createProxyServer(config = {}) {
18170
18272
  to: model,
18171
18273
  reason: "extra_usage_required"
18172
18274
  });
18173
- console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
18275
+ plog(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
18174
18276
  continue;
18175
18277
  }
18176
18278
  if (isExtraUsageRequiredError(errMsg) && resumeSessionId && !didFreshBaseRetry) {
@@ -18180,7 +18282,7 @@ function createProxyServer(config = {}) {
18180
18282
  model,
18181
18283
  reason: "extra_usage_required_resume"
18182
18284
  });
18183
- console.error(`[PROXY] ${requestMeta.requestId} extra usage persisted on resumed ${model}, retrying as fresh session`);
18285
+ plog(`[PROXY] ${requestMeta.requestId} extra usage persisted on resumed ${model}, retrying as fresh session`);
18184
18286
  evictSession(profileSessionId, profileScopedCwd, allMessages);
18185
18287
  sdkUuidMap.length = 0;
18186
18288
  for (let i = 0;i < allMessages.length; i++)
@@ -18228,10 +18330,10 @@ function createProxyServer(config = {}) {
18228
18330
  }
18229
18331
  if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
18230
18332
  tokenRefreshed = true;
18231
- const refreshed = await refreshOAuthToken();
18333
+ const refreshed = profileCredentialStore ? await refreshOAuthToken(profileCredentialStore) : false;
18232
18334
  if (refreshed) {
18233
18335
  claudeLog("token_refresh.retrying", { mode: "non_stream" });
18234
- console.error(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
18336
+ plog(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
18235
18337
  continue;
18236
18338
  }
18237
18339
  }
@@ -18245,7 +18347,7 @@ function createProxyServer(config = {}) {
18245
18347
  to: model,
18246
18348
  reason: "rate_limit"
18247
18349
  });
18248
- console.error(`[PROXY] ${requestMeta.requestId} rate-limited on [1m], retrying with ${model}`);
18350
+ plog(`[PROXY] ${requestMeta.requestId} rate-limited on [1m], retrying with ${model}`);
18249
18351
  continue;
18250
18352
  }
18251
18353
  if (rateLimitRetries < MAX_RATE_LIMIT_RETRIES) {
@@ -18258,7 +18360,7 @@ function createProxyServer(config = {}) {
18258
18360
  maxAttempts: MAX_RATE_LIMIT_RETRIES,
18259
18361
  delayMs: delay
18260
18362
  });
18261
- console.error(`[PROXY] ${requestMeta.requestId} rate-limited on ${model}, retry ${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES} in ${delay}ms`);
18363
+ plog(`[PROXY] ${requestMeta.requestId} rate-limited on ${model}, retry ${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES} in ${delay}ms`);
18262
18364
  await new Promise((r) => setTimeout(r, delay));
18263
18365
  continue;
18264
18366
  }
@@ -18334,7 +18436,7 @@ function createProxyServer(config = {}) {
18334
18436
  sessionDiscoveredTools.get(sessId).add(t);
18335
18437
  const newNames = [...discoveredTools].join(", ");
18336
18438
  const allNames = [...sessionDiscoveredTools.get(sessId)];
18337
- console.error(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
18439
+ plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
18338
18440
  }
18339
18441
  } catch (error) {
18340
18442
  const stderrOutput = stderrLines.join(`
@@ -18508,7 +18610,9 @@ Subprocess stderr: ${stderrOutput}`;
18508
18610
  const RATE_LIMIT_BASE_DELAY_MS = 1000;
18509
18611
  const response = async function* () {
18510
18612
  let rateLimitRetries = 0;
18511
- await ensureFreshToken().catch(() => {});
18613
+ if (profileCredentialStore) {
18614
+ await ensureFreshToken(profileCredentialStore).catch(() => {});
18615
+ }
18512
18616
  let tokenRefreshed = false;
18513
18617
  let didFreshBaseRetry = false;
18514
18618
  while (true) {
@@ -18572,7 +18676,7 @@ Subprocess stderr: ${stderrOutput}`;
18572
18676
  rollbackUuid: undoRollbackUuid,
18573
18677
  resumeSessionId
18574
18678
  });
18575
- console.error(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
18679
+ plog(`[PROXY] Stale session UUID, evicting and retrying as fresh session`);
18576
18680
  evictSession(profileSessionId, profileScopedCwd, allMessages);
18577
18681
  sdkUuidMap.length = 0;
18578
18682
  for (let i = 0;i < allMessages.length; i++)
@@ -18628,7 +18732,7 @@ Subprocess stderr: ${stderrOutput}`;
18628
18732
  to: model,
18629
18733
  reason: "extra_usage_required"
18630
18734
  });
18631
- console.error(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
18735
+ plog(`[PROXY] ${requestMeta.requestId} extra usage required for [1m], falling back to ${model} (skipping [1m] for 1h)`);
18632
18736
  continue;
18633
18737
  }
18634
18738
  if (isExtraUsageRequiredError(errMsg) && resumeSessionId && !didFreshBaseRetry) {
@@ -18638,7 +18742,7 @@ Subprocess stderr: ${stderrOutput}`;
18638
18742
  model,
18639
18743
  reason: "extra_usage_required_resume"
18640
18744
  });
18641
- console.error(`[PROXY] ${requestMeta.requestId} extra usage persisted on resumed ${model}, retrying as fresh session`);
18745
+ plog(`[PROXY] ${requestMeta.requestId} extra usage persisted on resumed ${model}, retrying as fresh session`);
18642
18746
  evictSession(profileSessionId, profileScopedCwd, allMessages);
18643
18747
  sdkUuidMap.length = 0;
18644
18748
  for (let i = 0;i < allMessages.length; i++)
@@ -18686,10 +18790,10 @@ Subprocess stderr: ${stderrOutput}`;
18686
18790
  }
18687
18791
  if (isExpiredTokenError(errMsg) && !tokenRefreshed) {
18688
18792
  tokenRefreshed = true;
18689
- const refreshed = await refreshOAuthToken();
18793
+ const refreshed = profileCredentialStore ? await refreshOAuthToken(profileCredentialStore) : false;
18690
18794
  if (refreshed) {
18691
18795
  claudeLog("token_refresh.retrying", { mode: "stream" });
18692
- console.error(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
18796
+ plog(`[PROXY] ${requestMeta.requestId} OAuth token expired — refreshed, retrying`);
18693
18797
  continue;
18694
18798
  }
18695
18799
  }
@@ -18703,7 +18807,7 @@ Subprocess stderr: ${stderrOutput}`;
18703
18807
  to: model,
18704
18808
  reason: "rate_limit"
18705
18809
  });
18706
- console.error(`[PROXY] ${requestMeta.requestId} rate-limited on [1m], retrying with ${model}`);
18810
+ plog(`[PROXY] ${requestMeta.requestId} rate-limited on [1m], retrying with ${model}`);
18707
18811
  continue;
18708
18812
  }
18709
18813
  if (rateLimitRetries < MAX_RATE_LIMIT_RETRIES) {
@@ -18716,7 +18820,7 @@ Subprocess stderr: ${stderrOutput}`;
18716
18820
  maxAttempts: MAX_RATE_LIMIT_RETRIES,
18717
18821
  delayMs: delay
18718
18822
  });
18719
- console.error(`[PROXY] ${requestMeta.requestId} rate-limited on ${model}, retry ${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES} in ${delay}ms`);
18823
+ plog(`[PROXY] ${requestMeta.requestId} rate-limited on ${model}, retry ${rateLimitRetries}/${MAX_RATE_LIMIT_RETRIES} in ${delay}ms`);
18720
18824
  await new Promise((r) => setTimeout(r, delay));
18721
18825
  continue;
18722
18826
  }
@@ -18751,8 +18855,15 @@ Subprocess stderr: ${stderrOutput}`;
18751
18855
  const taskToolJsonBuffer = new Map;
18752
18856
  let nextClientBlockIndex = 0;
18753
18857
  const sdkToClientIndex = new Map;
18858
+ const guardedResponse = guardUpstreamIdle(response, UPSTREAM_IDLE_MS, (sinceLastMs) => claudeLog("upstream.stalled", {
18859
+ mode: "stream",
18860
+ model,
18861
+ sinceLastMs,
18862
+ streamEventsSeen,
18863
+ firstChunkAt: firstChunkAt ?? null
18864
+ }));
18754
18865
  try {
18755
- for await (const message of response) {
18866
+ for await (const message of guardedResponse) {
18756
18867
  if (streamClosed) {
18757
18868
  break;
18758
18869
  }
@@ -18930,7 +19041,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
18930
19041
  sessionDiscoveredTools.get(sessId).add(t);
18931
19042
  const newNames = [...discoveredTools].join(", ");
18932
19043
  const allNames = [...sessionDiscoveredTools.get(sessId)];
18933
- console.error(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
19044
+ plog(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
18934
19045
  }
18935
19046
  if (currentSessionId && !isIndependentSession) {
18936
19047
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
@@ -19109,7 +19220,11 @@ Subprocess stderr: ${stderrOutput}`;
19109
19220
  error: errMsg,
19110
19221
  ...stderrOutput ? { stderr: stderrOutput } : {}
19111
19222
  });
19112
- const streamErr = classifyError(errMsg);
19223
+ const streamErr = error instanceof UpstreamIdleError ? {
19224
+ status: 504,
19225
+ type: "upstream_timeout",
19226
+ message: `Upstream stalled: no data for ${error.sinceLastMs}ms`
19227
+ } : classifyError(errMsg);
19113
19228
  claudeLog("proxy.anthropic.error", { error: errMsg, classified: streamErr.type });
19114
19229
  const sdkTerm = extractSdkTermination(errMsg);
19115
19230
  const canRecoverAsToolUse = sdkTerm.reason === "max_turns" && passthrough && capturedToolUses.length > 0 && messageStartEmitted;
@@ -19449,7 +19564,7 @@ data: ${JSON.stringify({
19449
19564
  setActiveProfile(body.profile);
19450
19565
  clearSessionCache();
19451
19566
  rateLimitStore.clear();
19452
- console.error(`[PROXY] Active profile switched to: ${body.profile} (session + rate-limit caches cleared)`);
19567
+ plog(`[PROXY] Active profile switched to: ${body.profile} (session + rate-limit caches cleared)`);
19453
19568
  return c.json({ success: true, activeProfile: body.profile });
19454
19569
  });
19455
19570
  app.get("/plugins/list", async (c) => {
@@ -19473,7 +19588,7 @@ data: ${JSON.stringify({
19473
19588
  loadedPlugins = await loadPlugins(pluginDir, pluginConfigPath);
19474
19589
  pluginTransforms = getActiveTransforms(loadedPlugins);
19475
19590
  const active = loadedPlugins.filter((p) => p.status === "active").length;
19476
- console.error(`[PROXY] Plugins reloaded: ${active} active`);
19591
+ plog(`[PROXY] Plugins reloaded: ${active} active`);
19477
19592
  return c.json({
19478
19593
  success: true,
19479
19594
  plugins: loadedPlugins.map((p) => ({
@@ -19492,10 +19607,12 @@ data: ${JSON.stringify({
19492
19607
  return c.html(pluginPageHtml);
19493
19608
  });
19494
19609
  app.post("/auth/refresh", async (c) => {
19495
- const success = await refreshOAuthToken();
19610
+ const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
19611
+ const store = credentialStoreForProfile(profile);
19612
+ const success = store ? await refreshOAuthToken(store) : false;
19496
19613
  if (success) {
19497
19614
  rateLimitStore.clear();
19498
- return c.json({ success: true, message: "OAuth token refreshed successfully" });
19615
+ return c.json({ success: true, message: "OAuth token refreshed successfully", profile: profile.id });
19499
19616
  }
19500
19617
  return c.json({ success: false, message: "Token refresh failed. If the problem persists, run 'claude login'." }, 500);
19501
19618
  });
@@ -19505,7 +19622,10 @@ data: ${JSON.stringify({
19505
19622
  if (!anthropicBody) {
19506
19623
  return c.json({ type: "error", error: { type: "invalid_request_error", message: "messages: Field required" } }, 400);
19507
19624
  }
19508
- const internalHeaders = { "Content-Type": "application/json" };
19625
+ const internalHeaders = {
19626
+ "Content-Type": "application/json",
19627
+ "x-meridian-agent": "openai"
19628
+ };
19509
19629
  const xApiKey = c.req.header("x-api-key");
19510
19630
  if (xApiKey)
19511
19631
  internalHeaders["x-api-key"] = xApiKey;
@@ -19526,8 +19646,7 @@ data: ${JSON.stringify({
19526
19646
  const created = Math.floor(Date.now() / 1000);
19527
19647
  const model = typeof rawBody.model === "string" && rawBody.model ? rawBody.model : "claude-sonnet-4-6";
19528
19648
  const { getFeaturesForAdapter: getFeaturesForAdapter2 } = (init_sdkFeatures(), __toCommonJS(exports_sdkFeatures));
19529
- const adapter = detectAdapter(c);
19530
- const sdkFeatures = getFeaturesForAdapter2(adapter.name);
19649
+ const sdkFeatures = getFeaturesForAdapter2("openai");
19531
19650
  if (!anthropicBody.stream) {
19532
19651
  const anthropicRes = await internalRes.json();
19533
19652
  return c.json(translateAnthropicToOpenAi(anthropicRes, completionId, model, created, {
@@ -19758,7 +19877,7 @@ data: ${JSON.stringify({
19758
19877
  });
19759
19878
  });
19760
19879
  app.all("*", (c) => {
19761
- console.error(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
19880
+ plog(`[PROXY] UNHANDLED ${c.req.method} ${c.req.url}`);
19762
19881
  return c.json({ error: { type: "not_found", message: `Endpoint not supported: ${c.req.method} ${new URL(c.req.url).pathname}` } }, 404);
19763
19882
  });
19764
19883
  async function initPluginsAsync() {
@@ -19769,19 +19888,34 @@ data: ${JSON.stringify({
19769
19888
  const active = loadedPlugins.filter((p) => p.status === "active").length;
19770
19889
  const disabled = loadedPlugins.filter((p) => p.status === "disabled").length;
19771
19890
  const errored = loadedPlugins.filter((p) => p.status === "error").length;
19772
- console.error(`[PROXY] Plugins loaded: ${active} active, ${disabled} disabled, ${errored} errors`);
19891
+ plog(`[PROXY] Plugins loaded: ${active} active, ${disabled} disabled, ${errored} errors`);
19773
19892
  }
19774
19893
  } catch (err) {
19775
- console.error(`[PROXY] Plugin loading failed: ${err instanceof Error ? err.message : String(err)}`);
19894
+ plog(`[PROXY] Plugin loading failed: ${err instanceof Error ? err.message : String(err)}`);
19776
19895
  }
19777
19896
  }
19778
19897
  return { app, config: finalConfig, initPlugins: initPluginsAsync };
19779
19898
  }
19899
+ var processErrorHandlersInstalled = false;
19900
+ function installProxyProcessErrorHandlers() {
19901
+ if (processErrorHandlersInstalled)
19902
+ return;
19903
+ processErrorHandlersInstalled = true;
19904
+ process.on("uncaughtException", (err) => {
19905
+ console.error(`[PROXY] Uncaught exception (recovered): ${err.message}`);
19906
+ });
19907
+ process.on("unhandledRejection", (reason) => {
19908
+ console.error(`[PROXY] Unhandled rejection (recovered): ${reason instanceof Error ? reason.message : reason}`);
19909
+ });
19910
+ }
19780
19911
  async function startProxyServer(config = {}) {
19781
19912
  claudeExecutable = await resolveClaudeExecutableAsync();
19782
19913
  const { app, config: finalConfig, initPlugins } = createProxyServer(config);
19783
19914
  if (initPlugins)
19784
19915
  await initPlugins();
19916
+ if (finalConfig.installProcessErrorHandlers) {
19917
+ installProxyProcessErrorHandlers();
19918
+ }
19785
19919
  const server = serve({
19786
19920
  fetch: app.fetch,
19787
19921
  port: finalConfig.port,
@@ -19819,6 +19953,13 @@ Or use a different port:`);
19819
19953
  }
19820
19954
  });
19821
19955
  startBackgroundRefresh();
19956
+ const PROFILE_TOKEN_REFRESH_MS = 45000;
19957
+ ensureFreshTokenForProfiles(finalConfig);
19958
+ const profileTokenRefreshInterval = setInterval(() => {
19959
+ ensureFreshTokenForProfiles(finalConfig);
19960
+ }, PROFILE_TOKEN_REFRESH_MS);
19961
+ if (profileTokenRefreshInterval.unref)
19962
+ profileTokenRefreshInterval.unref();
19822
19963
  let authKeepaliveInterval;
19823
19964
  const effectiveProfiles = getEffectiveProfiles(finalConfig.profiles);
19824
19965
  if (effectiveProfiles.length > 0) {
@@ -19840,6 +19981,7 @@ Or use a different port:`);
19840
19981
  server,
19841
19982
  config: finalConfig,
19842
19983
  async close() {
19984
+ clearInterval(profileTokenRefreshInterval);
19843
19985
  if (authKeepaliveInterval)
19844
19986
  clearInterval(authKeepaliveInterval);
19845
19987
  stopBackgroundRefresh();
@@ -19850,4 +19992,4 @@ Or use a different port:`);
19850
19992
  };
19851
19993
  }
19852
19994
 
19853
- export { runTransformHook, runObserveHook, buildPipeline, createRequestContext, computeLineageHash, hashMessage, computeMessageHashes, getMaxSessionsLimit, clearSessionCache, createProxyServer, startProxyServer };
19995
+ export { runTransformHook, runObserveHook, buildPipeline, createRequestContext, computeLineageHash, hashMessage, computeMessageHashes, getMaxSessionsLimit, clearSessionCache, createProxyServer, installProxyProcessErrorHandlers, startProxyServer };