@rynfar/meridian 1.37.3 → 1.37.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.
package/README.md CHANGED
@@ -49,6 +49,95 @@ ANTHROPIC_API_KEY=x ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode
49
49
 
50
50
  The API key value is a placeholder — Meridian authenticates through the Claude Code SDK, not API keys. Most Anthropic-compatible tools require this field to be set, but any value works.
51
51
 
52
+ ### NixOS / Nix Flake
53
+
54
+ Meridian provides a Nix flake for declarative installation.
55
+
56
+ **Add to your flake inputs:**
57
+
58
+ ```nix
59
+ {
60
+ inputs.meridian.url = "github:rynfar/meridian";
61
+ }
62
+ ```
63
+
64
+ **Install the package** (via overlay or directly):
65
+
66
+ ```nix
67
+ # Option A: overlay
68
+ nixpkgs.overlays = [ meridian.overlays.default ];
69
+ environment.systemPackages = [ pkgs.meridian ];
70
+
71
+ # Option B: direct reference
72
+ environment.systemPackages = [ meridian.packages.${system}.meridian ];
73
+ ```
74
+
75
+ **OpenCode plugin** -- the plugin file is included at `${pkgs.meridian}/lib/meridian/plugin/meridian.ts`. Since this path lives in the Nix store, you need to make it available to OpenCode:
76
+
77
+ If you generate your OpenCode config from Nix (e.g. via Home Manager), interpolate the path directly:
78
+
79
+ ```nix
80
+ # home-manager example
81
+ xdg.configFile."opencode/opencode.json".text = builtins.toJSON {
82
+ plugin = [ "${pkgs.meridian}/lib/meridian/plugin/meridian.ts" ];
83
+ };
84
+ ```
85
+
86
+ If you don't manage your OpenCode config through Nix, symlink the plugin to a stable path and reference that instead:
87
+
88
+ ```nix
89
+ # configuration.nix or home-manager
90
+ environment.etc."meridian/plugin/meridian.ts".source =
91
+ "${pkgs.meridian}/lib/meridian/plugin/meridian.ts";
92
+ ```
93
+
94
+ Then in `~/.config/opencode/opencode.json`:
95
+
96
+ ```json
97
+ { "plugin": ["/etc/meridian/plugin/meridian.ts"] }
98
+ ```
99
+
100
+ > **Important:** Do not use `meridian setup` on NixOS. It writes an absolute Nix store path (e.g. `/nix/store/...-meridian-1.x.x/lib/...`) into your OpenCode config, which will break on the next `nixos-rebuild switch` or `home-manager switch` when the store path changes. Use one of the approaches above instead.
101
+
102
+ **Home Manager service** -- run Meridian as a user systemd service:
103
+
104
+ ```nix
105
+ # flake.nix
106
+ {
107
+ inputs.meridian.url = "github:rynfar/meridian";
108
+ }
109
+
110
+ # home-manager config
111
+ {
112
+ imports = [ meridian.homeManagerModules.default ];
113
+
114
+ services.meridian = {
115
+ enable = true;
116
+ settings = {
117
+ port = 3456;
118
+ host = "127.0.0.1";
119
+ # passthrough = true;
120
+ # defaultAgent = "opencode";
121
+ # sonnetModel = "sonnet";
122
+ };
123
+ # Extra env vars not covered by settings
124
+ # environment = {
125
+ # MERIDIAN_MAX_CONCURRENT = "20";
126
+ # };
127
+ };
128
+ }
129
+ ```
130
+
131
+ The service starts automatically on login. Manage it with `systemctl --user {start,stop,restart,status} meridian`.
132
+
133
+ The plugin path is also available as `config.services.meridian.opencode.pluginPath` for use in your OpenCode config:
134
+
135
+ ```nix
136
+ xdg.configFile."opencode/opencode.json".text = builtins.toJSON {
137
+ plugin = [ config.services.meridian.opencode.pluginPath ];
138
+ };
139
+ ```
140
+
52
141
  ## Why Meridian?
53
142
 
54
143
  The Claude Code SDK provides programmatic access to Claude. But your favorite coding tools expect an Anthropic API endpoint. Meridian bridges that gap — it runs locally, accepts standard API requests, and routes them through the SDK. Claude Code does the heavy lifting; Meridian translates the output.
@@ -7782,6 +7782,23 @@ function shouldAlwaysLoad(tool, coreSet) {
7782
7782
  return coreSet.has(tool.name.toLowerCase());
7783
7783
  return true;
7784
7784
  }
7785
+ function computeToolSetKey(tools) {
7786
+ const entries = tools.map((t) => ({
7787
+ name: t.name,
7788
+ defer: t.defer_loading === true,
7789
+ schema: stableStringify(t.input_schema ?? null)
7790
+ })).sort((a, b) => a.name.localeCompare(b.name));
7791
+ return JSON.stringify(entries);
7792
+ }
7793
+ function stableStringify(value) {
7794
+ if (value === null || typeof value !== "object")
7795
+ return JSON.stringify(value);
7796
+ if (Array.isArray(value))
7797
+ return `[${value.map(stableStringify).join(",")}]`;
7798
+ const keys = Object.keys(value).sort();
7799
+ const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`);
7800
+ return `{${parts.join(",")}}`;
7801
+ }
7785
7802
  function stripMcpPrefix(toolName) {
7786
7803
  if (toolName.startsWith(PASSTHROUGH_MCP_PREFIX)) {
7787
7804
  return toolName.slice(PASSTHROUGH_MCP_PREFIX.length);
@@ -7789,6 +7806,71 @@ function stripMcpPrefix(toolName) {
7789
7806
  return toolName;
7790
7807
  }
7791
7808
 
7809
+ // src/utils/lruMap.ts
7810
+ class LRUMap {
7811
+ maxSize;
7812
+ onEvict;
7813
+ map = new Map;
7814
+ constructor(maxSize, onEvict) {
7815
+ this.maxSize = maxSize;
7816
+ this.onEvict = onEvict;
7817
+ }
7818
+ get size() {
7819
+ return this.map.size;
7820
+ }
7821
+ get(key) {
7822
+ const value = this.map.get(key);
7823
+ if (value === undefined)
7824
+ return;
7825
+ this.map.delete(key);
7826
+ this.map.set(key, value);
7827
+ return value;
7828
+ }
7829
+ set(key, value) {
7830
+ if (this.map.has(key)) {
7831
+ this.map.delete(key);
7832
+ } else if (this.map.size >= this.maxSize) {
7833
+ this.evictOldest();
7834
+ }
7835
+ this.map.set(key, value);
7836
+ return this;
7837
+ }
7838
+ has(key) {
7839
+ return this.map.has(key);
7840
+ }
7841
+ delete(key) {
7842
+ return this.map.delete(key);
7843
+ }
7844
+ clear() {
7845
+ this.map.clear();
7846
+ }
7847
+ entries() {
7848
+ return this.map.entries();
7849
+ }
7850
+ keys() {
7851
+ return this.map.keys();
7852
+ }
7853
+ values() {
7854
+ return this.map.values();
7855
+ }
7856
+ forEach(callbackfn) {
7857
+ this.map.forEach((value, key) => callbackfn(value, key, this));
7858
+ }
7859
+ [Symbol.iterator]() {
7860
+ return this.map[Symbol.iterator]();
7861
+ }
7862
+ evictOldest() {
7863
+ const oldestKey = this.map.keys().next().value;
7864
+ if (oldestKey === undefined)
7865
+ return;
7866
+ const oldestValue = this.map.get(oldestKey);
7867
+ if (oldestValue === undefined)
7868
+ return;
7869
+ this.map.delete(oldestKey);
7870
+ this.onEvict?.(oldestKey, oldestValue);
7871
+ }
7872
+ }
7873
+
7792
7874
  // src/telemetry/index.ts
7793
7875
  import { join } from "node:path";
7794
7876
  import { homedir } from "node:os";
@@ -9325,7 +9407,7 @@ function fuzzyMatchAgentName(input, validAgents) {
9325
9407
  var openCodeAdapter = {
9326
9408
  name: "opencode",
9327
9409
  getSessionId(c) {
9328
- return c.req.header("x-opencode-session");
9410
+ return c.req.header("x-opencode-session") ?? c.req.header("x-session-affinity");
9329
9411
  },
9330
9412
  extractWorkingDirectory(body) {
9331
9413
  return extractClientCwd(body);
@@ -9452,6 +9534,9 @@ var droidAdapter = {
9452
9534
  normalizeContent(content) {
9453
9535
  return normalizeContent(content);
9454
9536
  },
9537
+ leaksCwdViaSystemReminder() {
9538
+ return true;
9539
+ },
9455
9540
  getBlockedBuiltinTools() {
9456
9541
  return BLOCKED_BUILTIN_TOOLS;
9457
9542
  },
@@ -15725,7 +15810,6 @@ function formatUsageSummary(usage) {
15725
15810
 
15726
15811
  // src/proxy/sanitize.ts
15727
15812
  var ORCHESTRATION_TAGS = [
15728
- "system-reminder",
15729
15813
  "env",
15730
15814
  "system_information",
15731
15815
  "current_working_directory",
@@ -15754,9 +15838,14 @@ var ALL_PATTERNS = [
15754
15838
  ...SELF_CLOSING_TAG_PATTERNS,
15755
15839
  ...NON_XML_PATTERNS
15756
15840
  ];
15757
- function sanitizeTextContent(text) {
15841
+ var SYSTEM_REMINDER_PATTERNS = [
15842
+ /<system-reminder\b[^>]*>[\s\S]*?<\/system-reminder>/gi,
15843
+ /<system-reminder\b[^>]*\/>/gi
15844
+ ];
15845
+ function sanitizeTextContent(text, opts = {}) {
15758
15846
  let result = text;
15759
- for (const pattern of ALL_PATTERNS) {
15847
+ const patterns = opts.stripSystemReminder ? [...ALL_PATTERNS, ...SYSTEM_REMINDER_PATTERNS] : ALL_PATTERNS;
15848
+ for (const pattern of patterns) {
15760
15849
  pattern.lastIndex = 0;
15761
15850
  result = result.replace(pattern, "");
15762
15851
  }
@@ -15896,71 +15985,6 @@ function verifyLineage(cached, messages, cacheKey2, cache) {
15896
15985
  return { type: "diverged" };
15897
15986
  }
15898
15987
 
15899
- // src/utils/lruMap.ts
15900
- class LRUMap {
15901
- maxSize;
15902
- onEvict;
15903
- map = new Map;
15904
- constructor(maxSize, onEvict) {
15905
- this.maxSize = maxSize;
15906
- this.onEvict = onEvict;
15907
- }
15908
- get size() {
15909
- return this.map.size;
15910
- }
15911
- get(key) {
15912
- const value = this.map.get(key);
15913
- if (value === undefined)
15914
- return;
15915
- this.map.delete(key);
15916
- this.map.set(key, value);
15917
- return value;
15918
- }
15919
- set(key, value) {
15920
- if (this.map.has(key)) {
15921
- this.map.delete(key);
15922
- } else if (this.map.size >= this.maxSize) {
15923
- this.evictOldest();
15924
- }
15925
- this.map.set(key, value);
15926
- return this;
15927
- }
15928
- has(key) {
15929
- return this.map.has(key);
15930
- }
15931
- delete(key) {
15932
- return this.map.delete(key);
15933
- }
15934
- clear() {
15935
- this.map.clear();
15936
- }
15937
- entries() {
15938
- return this.map.entries();
15939
- }
15940
- keys() {
15941
- return this.map.keys();
15942
- }
15943
- values() {
15944
- return this.map.values();
15945
- }
15946
- forEach(callbackfn) {
15947
- this.map.forEach((value, key) => callbackfn(value, key, this));
15948
- }
15949
- [Symbol.iterator]() {
15950
- return this.map[Symbol.iterator]();
15951
- }
15952
- evictOldest() {
15953
- const oldestKey = this.map.keys().next().value;
15954
- if (oldestKey === undefined)
15955
- return;
15956
- const oldestValue = this.map.get(oldestKey);
15957
- if (oldestValue === undefined)
15958
- return;
15959
- this.map.delete(oldestKey);
15960
- this.onEvict?.(oldestKey, oldestValue);
15961
- }
15962
- }
15963
-
15964
15988
  // src/proxy/sessionStore.ts
15965
15989
  import {
15966
15990
  closeSync,
@@ -16360,7 +16384,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
16360
16384
  if (sessionId)
16361
16385
  sessionCache.set(sessionId, state);
16362
16386
  const fp = getConversationFingerprint(messages, workingDirectory);
16363
- if (fp)
16387
+ if (fp && !sessionId)
16364
16388
  fingerprintCache.set(fp, state);
16365
16389
  const key = sessionId || fp;
16366
16390
  if (key) {
@@ -16371,7 +16395,43 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
16371
16395
  // src/proxy/server.ts
16372
16396
  var exec3 = promisify3(execCallback2);
16373
16397
  var claudeExecutable = "";
16374
- function buildFreshPrompt(messages, stripCacheControl) {
16398
+ function flattenAssistantContent(content) {
16399
+ if (typeof content === "string")
16400
+ return content;
16401
+ if (!Array.isArray(content))
16402
+ return String(content ?? "");
16403
+ return content.map((b) => b?.type === "text" && b.text ? b.text : "").filter(Boolean).join(`
16404
+ `);
16405
+ }
16406
+ function flattenUserContent(content, sanitizeOpts = {}) {
16407
+ if (typeof content === "string")
16408
+ return sanitizeTextContent(content, sanitizeOpts);
16409
+ if (!Array.isArray(content))
16410
+ return String(content ?? "");
16411
+ return content.map((b) => {
16412
+ if (b?.type === "text" && b.text)
16413
+ return sanitizeTextContent(b.text, sanitizeOpts);
16414
+ if (b?.type === "tool_result") {
16415
+ const inner = b.content;
16416
+ if (typeof inner === "string")
16417
+ return inner;
16418
+ if (Array.isArray(inner)) {
16419
+ return inner.map((ib) => ib?.type === "text" && ib.text ? ib.text : "").filter(Boolean).join(`
16420
+ `);
16421
+ }
16422
+ return "";
16423
+ }
16424
+ if (b?.type === "image")
16425
+ return "[Image attached]";
16426
+ if (b?.type === "document")
16427
+ return "[Document attached]";
16428
+ if (b?.type === "file")
16429
+ return "[File attached]";
16430
+ return "";
16431
+ }).filter(Boolean).join(`
16432
+ `);
16433
+ }
16434
+ function buildFreshPrompt(messages, stripCacheControl, sanitizeOpts = {}) {
16375
16435
  const MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
16376
16436
  const hasMultimodal = messages.some((m) => Array.isArray(m.content) && m.content.some((b) => MULTIMODAL_TYPES.has(b.type)));
16377
16437
  if (hasMultimodal) {
@@ -16384,28 +16444,14 @@ function buildFreshPrompt(messages, stripCacheControl) {
16384
16444
  parent_tool_use_id: null
16385
16445
  });
16386
16446
  } else {
16387
- let text;
16388
- if (typeof m.content === "string") {
16389
- text = `[Assistant: ${m.content}]`;
16390
- } else if (Array.isArray(m.content)) {
16391
- text = m.content.map((b) => {
16392
- if (b.type === "text" && b.text)
16393
- return `[Assistant: ${b.text}]`;
16394
- if (b.type === "tool_use")
16395
- return `[Tool Use: ${b.name}(${JSON.stringify(b.input)})]`;
16396
- if (b.type === "tool_result")
16397
- return `[Tool Result: ${typeof b.content === "string" ? b.content : JSON.stringify(b.content)}]`;
16398
- return "";
16399
- }).filter(Boolean).join(`
16400
- `);
16401
- } else {
16402
- text = `[Assistant: ${String(m.content)}]`;
16447
+ const assistantText = flattenAssistantContent(m.content);
16448
+ if (assistantText) {
16449
+ structured.push({
16450
+ type: "user",
16451
+ message: { role: "user", content: `[Assistant: ${assistantText}]` },
16452
+ parent_tool_use_id: null
16453
+ });
16403
16454
  }
16404
- structured.push({
16405
- type: "user",
16406
- message: { role: "user", content: text },
16407
- parent_tool_use_id: null
16408
- });
16409
16455
  }
16410
16456
  }
16411
16457
  return async function* () {
@@ -16415,31 +16461,9 @@ function buildFreshPrompt(messages, stripCacheControl) {
16415
16461
  }
16416
16462
  return messages.map((m) => {
16417
16463
  const role = m.role === "assistant" ? "Assistant" : "Human";
16418
- let content;
16419
- if (typeof m.content === "string") {
16420
- content = sanitizeTextContent(m.content);
16421
- } else if (Array.isArray(m.content)) {
16422
- content = m.content.map((block) => {
16423
- if (block.type === "text" && block.text)
16424
- return sanitizeTextContent(block.text);
16425
- if (block.type === "tool_use")
16426
- return `[Tool Use: ${block.name}(${JSON.stringify(block.input)})]`;
16427
- if (block.type === "tool_result")
16428
- return `[Tool Result for ${block.tool_use_id}: ${typeof block.content === "string" ? block.content : JSON.stringify(block.content)}]`;
16429
- if (block.type === "image")
16430
- return "[Image attached]";
16431
- if (block.type === "document")
16432
- return "[Document attached]";
16433
- if (block.type === "file")
16434
- return "[File attached]";
16435
- return "";
16436
- }).filter(Boolean).join(`
16437
- `);
16438
- } else {
16439
- content = String(m.content);
16440
- }
16441
- return `${role}: ${content}`;
16442
- }).join(`
16464
+ const content = m.role === "assistant" ? flattenAssistantContent(m.content) : flattenUserContent(m.content, sanitizeOpts);
16465
+ return content ? `${role}: ${content}` : "";
16466
+ }).filter(Boolean).join(`
16443
16467
 
16444
16468
  `) || "";
16445
16469
  }
@@ -16495,6 +16519,7 @@ function createProxyServer(config = {}) {
16495
16519
  restoreActiveProfile(finalConfig.profiles);
16496
16520
  const sessionDiscoveredTools = new Map;
16497
16521
  const sessionToolCache = new Map;
16522
+ const sessionMcpCache = new LRUMap(getMaxSessionsLimit());
16498
16523
  const app = new Hono2;
16499
16524
  app.use("*", cors());
16500
16525
  app.use("/v1/*", requireAuth);
@@ -16569,6 +16594,7 @@ function createProxyServer(config = {}) {
16569
16594
  const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
16570
16595
  const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
16571
16596
  const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
16597
+ const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
16572
16598
  let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
16573
16599
  const adapterStreamPref = adapter.prefersStreaming?.(body);
16574
16600
  const stream2 = adapterStreamPref !== undefined ? adapterStreamPref : body.stream ?? false;
@@ -16628,7 +16654,11 @@ function createProxyServer(config = {}) {
16628
16654
  const agentSessionId = adapter.getSessionId(c);
16629
16655
  const profileSessionId = profile.id !== "default" && agentSessionId ? `${profile.id}:${agentSessionId}` : agentSessionId;
16630
16656
  const profileScopedCwd = profile.id !== "default" ? `${workingDirectory}::profile=${profile.id}` : workingDirectory;
16631
- const lineageResult = lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
16657
+ const isIndependentSession = requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-") || false;
16658
+ let lineageResult = isIndependentSession ? { type: "diverged" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
16659
+ if (lineageResult.type === "undo" && adapter.name === "opencode" && !agentSessionId) {
16660
+ lineageResult = { type: "diverged" };
16661
+ }
16632
16662
  const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
16633
16663
  const isUndo = lineageResult.type === "undo";
16634
16664
  const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
@@ -16641,10 +16671,10 @@ function createProxyServer(config = {}) {
16641
16671
  const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
16642
16672
  const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
16643
16673
  const toolCount = body.tools?.length ?? 0;
16644
- const requestLogLine = `${requestMeta.requestId} adapter=${adapter.name} 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}`;
16674
+ 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}`;
16645
16675
  console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
16646
16676
  diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
16647
- if (lineageResult.type === "diverged" && profileSessionId) {
16677
+ if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
16648
16678
  const recovery = lookupSessionRecovery(profileSessionId);
16649
16679
  if (recovery) {
16650
16680
  const prevId = recovery.previousClaudeSessionId || recovery.claudeSessionId;
@@ -16666,6 +16696,9 @@ function createProxyServer(config = {}) {
16666
16696
  claudeLog("debug.agents", { names: validAgentNames, count: validAgentNames.length });
16667
16697
  }
16668
16698
  systemContext += adapter.buildSystemContextAddendum?.(body, sdkAgents) ?? "";
16699
+ const sanitizeOpts = {
16700
+ stripSystemReminder: adapter.leaksCwdViaSystemReminder?.() ?? false
16701
+ };
16669
16702
  const allMessages = body.messages || [];
16670
16703
  let messagesToConvert;
16671
16704
  if ((isResume || isUndo) && cachedSession) {
@@ -16709,59 +16742,23 @@ function createProxyServer(config = {}) {
16709
16742
  parent_tool_use_id: null
16710
16743
  });
16711
16744
  } else {
16712
- let text;
16713
- if (typeof m.content === "string") {
16714
- text = `[Assistant: ${m.content}]`;
16715
- } else if (Array.isArray(m.content)) {
16716
- text = m.content.map((b) => {
16717
- if (b.type === "text" && b.text)
16718
- return `[Assistant: ${b.text}]`;
16719
- if (b.type === "tool_use")
16720
- return `[Tool Use: ${b.name}(${JSON.stringify(b.input)})]`;
16721
- if (b.type === "tool_result")
16722
- return `[Tool Result: ${typeof b.content === "string" ? b.content : JSON.stringify(b.content)}]`;
16723
- return "";
16724
- }).filter(Boolean).join(`
16725
- `);
16726
- } else {
16727
- text = `[Assistant: ${String(m.content)}]`;
16745
+ const assistantText = flattenAssistantContent(m.content);
16746
+ if (assistantText) {
16747
+ structuredMessages.push({
16748
+ type: "user",
16749
+ message: { role: "user", content: `[Assistant: ${assistantText}]` },
16750
+ parent_tool_use_id: null
16751
+ });
16728
16752
  }
16729
- structuredMessages.push({
16730
- type: "user",
16731
- message: { role: "user", content: text },
16732
- parent_tool_use_id: null
16733
- });
16734
16753
  }
16735
16754
  }
16736
16755
  }
16737
16756
  } else {
16738
16757
  textPrompt = messagesToConvert?.map((m) => {
16739
16758
  const role = m.role === "assistant" ? "Assistant" : "Human";
16740
- let content;
16741
- if (typeof m.content === "string") {
16742
- content = sanitizeTextContent(m.content);
16743
- } else if (Array.isArray(m.content)) {
16744
- content = m.content.map((block) => {
16745
- if (block.type === "text" && block.text)
16746
- return sanitizeTextContent(block.text);
16747
- if (block.type === "tool_use")
16748
- return `[Tool Use: ${block.name}(${JSON.stringify(block.input)})]`;
16749
- if (block.type === "tool_result")
16750
- return `[Tool Result for ${block.tool_use_id}: ${typeof block.content === "string" ? block.content : JSON.stringify(block.content)}]`;
16751
- if (block.type === "image")
16752
- return "[Image attached]";
16753
- if (block.type === "document")
16754
- return "[Document attached]";
16755
- if (block.type === "file")
16756
- return "[File attached]";
16757
- return "";
16758
- }).filter(Boolean).join(`
16759
- `);
16760
- } else {
16761
- content = String(m.content);
16762
- }
16763
- return `${role}: ${content}`;
16764
- }).join(`
16759
+ const content = m.role === "assistant" ? flattenAssistantContent(m.content) : flattenUserContent(m.content, sanitizeOpts);
16760
+ return content ? `${role}: ${content}` : "";
16761
+ }).filter(Boolean).join(`
16765
16762
 
16766
16763
  `) || "";
16767
16764
  }
@@ -16780,7 +16777,19 @@ function createProxyServer(config = {}) {
16780
16777
  }
16781
16778
  }
16782
16779
  if (passthrough && requestTools.length > 0) {
16783
- passthroughMcp = createPassthroughMcpServer(requestTools, adapter.getCoreToolNames?.());
16780
+ const toolSetKey = computeToolSetKey(requestTools);
16781
+ const cachedMcp = profileSessionId ? sessionMcpCache.get(profileSessionId) : undefined;
16782
+ if (cachedMcp && cachedMcp.key === toolSetKey) {
16783
+ passthroughMcp = cachedMcp.mcp;
16784
+ } else {
16785
+ passthroughMcp = createPassthroughMcpServer(requestTools, adapter.getCoreToolNames?.());
16786
+ if (profileSessionId) {
16787
+ sessionMcpCache.set(profileSessionId, { key: toolSetKey, mcp: passthroughMcp });
16788
+ if (cachedMcp) {
16789
+ console.error(`[PROXY] ${requestMeta.requestId} tools_changed: MCP server recreated (prompt cache likely invalidates)`);
16790
+ }
16791
+ }
16792
+ }
16784
16793
  if (profileSessionId)
16785
16794
  sessionToolCache.set(profileSessionId, requestTools);
16786
16795
  }
@@ -16903,7 +16912,7 @@ function createProxyServer(config = {}) {
16903
16912
  for (let i = 0;i < allMessages.length; i++)
16904
16913
  sdkUuidMap.push(null);
16905
16914
  yield* query(buildQueryOptions({
16906
- prompt: buildFreshPrompt(allMessages, stripCacheControl),
16915
+ prompt: buildFreshPrompt(allMessages, stripCacheControl, sanitizeOpts),
16907
16916
  model,
16908
16917
  workingDirectory,
16909
16918
  systemContext,
@@ -17146,7 +17155,7 @@ Subprocess stderr: ${stderrOutput}`;
17146
17155
  cacheCreationInputTokens: lastUsage?.cache_creation_input_tokens,
17147
17156
  cacheHitRate: computeCacheHitRate(lastUsage)
17148
17157
  });
17149
- if (currentSessionId) {
17158
+ if (currentSessionId && !isIndependentSession) {
17150
17159
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
17151
17160
  }
17152
17161
  const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
@@ -17272,7 +17281,7 @@ Subprocess stderr: ${stderrOutput}`;
17272
17281
  for (let i = 0;i < allMessages.length; i++)
17273
17282
  sdkUuidMap.push(null);
17274
17283
  yield* query(buildQueryOptions({
17275
- prompt: buildFreshPrompt(allMessages, stripCacheControl),
17284
+ prompt: buildFreshPrompt(allMessages, stripCacheControl, sanitizeOpts),
17276
17285
  model,
17277
17286
  workingDirectory,
17278
17287
  systemContext,
@@ -17530,7 +17539,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
17530
17539
  const allNames = [...sessionDiscoveredTools.get(sessId)];
17531
17540
  console.error(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
17532
17541
  }
17533
- if (currentSessionId) {
17542
+ if (currentSessionId && !isIndependentSession) {
17534
17543
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
17535
17544
  }
17536
17545
  if (!streamClosed) {
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-76s66ede.js";
4
+ } from "./cli-yahkdc3h.js";
5
5
  import"./cli-pr79d7nw.js";
6
6
  import"./cli-rtab0qa6.js";
7
7
  import"./cli-m9pfb7h9.js";
@@ -125,6 +125,16 @@ export interface AgentAdapter {
125
125
  * the extra block is noisy. When undefined, the proxy defaults to true.
126
126
  */
127
127
  shouldTrackFileChanges?(): boolean;
128
+ /**
129
+ * Whether this agent leaks CWD/env info through `<system-reminder>` blocks
130
+ * in user messages (Droid). When true, the proxy strips those blocks before
131
+ * flattening to a text prompt so they don't echo back to the model.
132
+ *
133
+ * Most agents (OpenCode, Crush, ForgeCode) use `<system-reminder>` to surface
134
+ * harness state the model needs to see (e.g. oh-my-opencode background task
135
+ * IDs), so the default is false — preserve them.
136
+ */
137
+ leaksCwdViaSystemReminder?(): boolean;
128
138
  /**
129
139
  * Map a client-side tool_use block to file changes (passthrough mode).
130
140
  *
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/proxy/adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAEnE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;OAGG;IACH,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IAE5C;;;OAGG;IACH,uBAAuB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAAA;IAEtD;;;OAGG;IACH,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAAA;IAEtC;;;OAGG;IACH,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAAA;IAE3C;;;;OAIG;IACH,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAAA;IAE9C;;;OAGG;IACH,gBAAgB,IAAI,MAAM,CAAA;IAE1B;;OAEG;IACH,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAAA;IAEvC;;;;OAIG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAEhF;;;OAGG;IACH,aAAa,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAA;IAE9D;;;OAGG;IACH,0BAA0B,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAA;IAE9E;;;;;;OAMG;IACH,gBAAgB,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;IAErC;;;;;;;;OAQG;IACH,eAAe,CAAC,IAAI,OAAO,CAAA;IAE3B;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAAE,CAAA;IAEtC;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CAAC,IAAI,aAAa,EAAE,CAAA;IAErC;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,OAAO,CAAA;IAE5B;;;;;;OAMG;IACH,sBAAsB,CAAC,IAAI,OAAO,CAAA;IAElC;;;;;;;;;;;;;;;OAeG;IACH,6BAA6B,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,eAAe,EAAE,UAAU,EAAE,CAAA;CAC3G"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/proxy/adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAEnE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB;;;OAGG;IACH,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;IAE5C;;;OAGG;IACH,uBAAuB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAAA;IAEtD;;;OAGG;IACH,gBAAgB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CAAA;IAEtC;;;OAGG;IACH,sBAAsB,IAAI,SAAS,MAAM,EAAE,CAAA;IAE3C;;;;OAIG;IACH,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAAA;IAE9C;;;OAGG;IACH,gBAAgB,IAAI,MAAM,CAAA;IAE1B;;OAEG;IACH,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAAA;IAEvC;;;;OAIG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAEhF;;;OAGG;IACH,aAAa,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,CAAA;IAE9D;;;OAGG;IACH,0BAA0B,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAA;IAE9E;;;;;;OAMG;IACH,gBAAgB,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAA;IAErC;;;;;;;;OAQG;IACH,eAAe,CAAC,IAAI,OAAO,CAAA;IAE3B;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAAE,CAAA;IAEtC;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CAAC,IAAI,aAAa,EAAE,CAAA;IAErC;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,OAAO,CAAA;IAE5B;;;;;;OAMG;IACH,sBAAsB,CAAC,IAAI,OAAO,CAAA;IAElC;;;;;;;;OAQG;IACH,yBAAyB,CAAC,IAAI,OAAO,CAAA;IAErC;;;;;;;;;;;;;;;OAeG;IACH,6BAA6B,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,eAAe,EAAE,UAAU,EAAE,CAAA;CAC3G"}
@@ -1 +1 @@
1
- {"version":3,"file":"droid.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/droid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAwC9C,eAAO,MAAM,YAAY,EAAE,YAuE1B,CAAA"}
1
+ {"version":3,"file":"droid.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/droid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAwC9C,eAAO,MAAM,YAAY,EAAE,YA8E1B,CAAA"}
@@ -30,6 +30,16 @@ export declare function createPassthroughMcpServer(tools: Array<{
30
30
  toolNames: string[];
31
31
  hasDeferredTools: boolean;
32
32
  };
33
+ /**
34
+ * Stable cache key for a tool set — name + input schema, sorted.
35
+ * Schema is included so silently-updated tool definitions force a rebuild
36
+ * of the cached MCP server.
37
+ */
38
+ export declare function computeToolSetKey(tools: Array<{
39
+ name: string;
40
+ input_schema?: unknown;
41
+ defer_loading?: boolean;
42
+ }>): string;
33
43
  /**
34
44
  * Strip the MCP prefix from a tool name to get the OpenCode tool name.
35
45
  * e.g., "mcp__oc__todowrite" → "todowrite"
@@ -1 +1 @@
1
- {"version":3,"file":"passthroughTools.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AA0CtE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACjG,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE;;;;EA4DlC;AAqBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD"}
1
+ {"version":3,"file":"passthroughTools.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughTools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,eAAO,MAAM,sBAAsB,cAAmC,CAAA;AA0CtE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACjG,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE;;;;EA4DlC;AAqBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAC9E,MAAM,CASR;AAUD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvD"}
@@ -16,11 +16,16 @@
16
16
  *
17
17
  * Fixes: https://github.com/rynfar/meridian/issues/167
18
18
  */
19
+ export interface SanitizeOptions {
20
+ /** Strip `<system-reminder>` blocks. Enable for adapters (Droid) that leak
21
+ * CWD/env through this tag. */
22
+ stripSystemReminder?: boolean;
23
+ }
19
24
  /**
20
25
  * Strip orchestration wrappers from a single text string.
21
26
  *
22
27
  * Designed to be called on individual content blocks (not concatenated
23
28
  * prompt strings) to eliminate cross-block regex matching risk.
24
29
  */
25
- export declare function sanitizeTextContent(text: string): string;
30
+ export declare function sanitizeTextContent(text: string, opts?: SanitizeOptions): string;
26
31
  //# sourceMappingURL=sanitize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/proxy/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA6DH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUxD"}
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/proxy/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAwEH,MAAM,WAAW,eAAe;IAC9B;oCACgC;IAChC,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,MAAM,CAapF"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AAyBvD,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAkJ7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAq4DhF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiEhG"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,CAAA;AA0BvD,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAA+B,iBAAiB,EAAE,mBAAmB,EAAsC,MAAM,iBAAiB,CAAA;AAGzI,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAA;AACjD,YAAY,EAAE,aAAa,EAAE,CAAA;AAgL7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAy6DhF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiEhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,WAAW,CAAA;AAMlB,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAqCD;kGACkG;AAClG,wBAAgB,iBAAiB,SAYhC;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,CAAC,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC/C,IAAI,CAoBN;AAUD;;uDAEuD;AACvD,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,gBAAgB,CAAC,EAAE,MAAM,GACxB,aAAa,CAuDf;AAED;;uFAEuF;AACvF,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CA2BtF;AAED;;;yFAGyF;AACzF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,EACnD,eAAe,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EACtC,YAAY,CAAC,EAAE,UAAU,QA+B1B"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/proxy/session/cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,WAAW,CAAA;AAMlB,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAqCD;kGACkG;AAClG,wBAAgB,iBAAiB,SAYhC;AAED;iFACiF;AACjF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,CAAC,EAAE,MAAM,EACzB,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,GAC/C,IAAI,CAoBN;AAUD;;uDAEuD;AACvD,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,CAAC,EAC/C,gBAAgB,CAAC,EAAE,MAAM,GACxB,aAAa,CAuDf;AAED;;uFAEuF;AACvF,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CA2BtF;AAED;;;yFAGyF;AACzF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,EACnD,eAAe,EAAE,MAAM,EACvB,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EACtC,YAAY,CAAC,EAAE,UAAU,QAoC1B"}
package/dist/server.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  getMaxSessionsLimit,
7
7
  hashMessage,
8
8
  startProxyServer
9
- } from "./cli-76s66ede.js";
9
+ } from "./cli-yahkdc3h.js";
10
10
  import"./cli-pr79d7nw.js";
11
11
  import"./cli-rtab0qa6.js";
12
12
  import"./cli-m9pfb7h9.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynfar/meridian",
3
- "version": "1.37.3",
3
+ "version": "1.37.5",
4
4
  "description": "Local Anthropic API powered by your Claude Max subscription. One subscription, every agent.",
5
5
  "type": "module",
6
6
  "main": "./dist/server.js",
@@ -25,6 +25,7 @@
25
25
  "postbuild": "node --check dist/cli.js && node --check dist/server.js && test -f dist/proxy/server.d.ts",
26
26
  "prepublishOnly": "bun run build",
27
27
  "test": "bun test --path-ignore-patterns '**/*session-store*' --path-ignore-patterns '**/*proxy-async-ops*' --path-ignore-patterns '**/*proxy-extra-usage-fallback*' --path-ignore-patterns '**/*models-auth-status*' --path-ignore-patterns '**/*proxy-context-usage-store*' --path-ignore-patterns '**/*proxy-passthrough-thinking*' --path-ignore-patterns '**/*profile-switch-integration*' --path-ignore-patterns '**/*session-recovery*' --path-ignore-patterns '**/*models.test*' --path-ignore-patterns '**/*proxy-health-degraded*' --path-ignore-patterns '**/*proxy-subagent-model-selection*' && bun test src/__tests__/profile-switch-integration.test.ts && bun test src/__tests__/proxy-extra-usage-fallback.test.ts && bun test src/__tests__/proxy-async-ops.test.ts && bun test src/__tests__/proxy-session-store.test.ts && bun test src/__tests__/session-store-pruning.test.ts && bun test src/__tests__/proxy-session-store-locking.test.ts && bun test src/__tests__/proxy-context-usage-store.test.ts && bun test src/__tests__/models-auth-status.test.ts && bun test src/__tests__/proxy-passthrough-thinking.test.ts && bun test src/__tests__/proxy-session-recovery.test.ts && bun test src/__tests__/models.test.ts && bun test src/__tests__/proxy-health-degraded.test.ts && bun test src/__tests__/proxy-subagent-model-selection.test.ts",
28
+ "nix:lock": "bun2nix -o bun.nix",
28
29
  "typecheck": "tsc --noEmit",
29
30
  "proxy:direct": "bun run ./bin/cli.ts"
30
31
  },
@@ -36,6 +37,7 @@
36
37
  "@hono/node-server": "^1.19.11",
37
38
  "@types/bun": "^1.3.11",
38
39
  "@types/node": "^22.0.0",
40
+ "bun2nix": "^2.0.8",
39
41
  "glob": "^13.0.0",
40
42
  "hono": "^4.11.4",
41
43
  "typescript": "^5.8.2"