@rynfar/meridian 1.37.4 → 1.37.6

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.
@@ -60,14 +149,14 @@ The Claude Code SDK provides programmatic access to Claude. But your favorite co
60
149
  ## Features
61
150
 
62
151
  - **Standard Anthropic API** — drop-in compatible with any tool that supports a custom `base_url`
63
- - **OpenAI-compatible API** — `/v1/chat/completions` and `/v1/models` for tools that only speak the OpenAI protocol (Open WebUI, Continue, etc.) — no LiteLLM needed
152
+ - **OpenAI-compatible API** — `/v1/chat/completions` and `/v1/models` for tools that only speak the OpenAI protocol (Open WebUI, Continue, etc.) — no LiteLLM needed, including `image_url` support for data URLs
64
153
  - **Session management** — conversations persist across requests, survive compaction and undo, resume after proxy restarts
65
154
  - **Streaming** — full SSE streaming with MCP tool filtering
66
155
  - **Concurrent sessions** — run parent and subagent requests in parallel
67
156
  - **Subagent model selection** — primary agents get 1M context; subagents get 200k, preserving rate-limit budget
68
157
  - **Auto token refresh** — expired OAuth tokens are refreshed automatically; requests continue without interruption
69
158
  - **Passthrough mode** — forward tool calls to the client instead of executing internally
70
- - **Multimodal** — images, documents, and file attachments pass through to Claude
159
+ - **Multimodal** — images, documents, file attachments, and multimodal tool results pass through to Claude
71
160
  - **Multi-profile** — switch between Claude accounts instantly, no restart needed
72
161
  - **Telemetry dashboard** — real-time performance metrics at `/telemetry`, including token usage and prompt cache efficiency ([`MONITORING.md`](MONITORING.md))
73
162
  - **Telemetry persistence** — opt-in SQLite storage for telemetry data that survives proxy restarts, with configurable retention
@@ -320,6 +409,9 @@ Meridian speaks the OpenAI protocol natively — no LiteLLM or translation proxy
320
409
 
321
410
  **`POST /v1/chat/completions`** — accepts OpenAI chat format, returns OpenAI completion format (streaming and non-streaming)
322
411
 
412
+ - `image_url` parts are supported when provided as **data URLs** (`data:image/...;base64,...`)
413
+ - multimodal tool flows where a tool returns `tool_result.content = [text, image]` are preserved through the structured multimodal path instead of being flattened to text
414
+
323
415
  **`GET /v1/models`** — returns available Claude models in OpenAI format
324
416
 
325
417
  Point any OpenAI-compatible tool at `http://127.0.0.1:3456` with any API key value:
@@ -8904,7 +8904,67 @@ function isClosedControllerError(error) {
8904
8904
  function extractOpenAiContent(content) {
8905
8905
  if (typeof content === "string")
8906
8906
  return content;
8907
- return content.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
8907
+ return content.map((p) => {
8908
+ if (p.type === "text" && typeof p.text === "string")
8909
+ return p.text;
8910
+ if (p.type === "image_url")
8911
+ return "[Image attached]";
8912
+ return "";
8913
+ }).filter(Boolean).join("");
8914
+ }
8915
+ function parseDataUrlImage(url) {
8916
+ const match2 = /^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/s.exec(url);
8917
+ if (!match2)
8918
+ return null;
8919
+ const mediaType = match2[1];
8920
+ const data = match2[2];
8921
+ if (!mediaType || !data)
8922
+ return null;
8923
+ return {
8924
+ type: "image",
8925
+ source: {
8926
+ type: "base64",
8927
+ media_type: mediaType,
8928
+ data
8929
+ }
8930
+ };
8931
+ }
8932
+ function translateOpenAiContentToAnthropic(content) {
8933
+ if (typeof content === "string")
8934
+ return content;
8935
+ const parts = [];
8936
+ for (const part of content) {
8937
+ if (part.type === "text" && typeof part.text === "string") {
8938
+ parts.push({ type: "text", text: part.text });
8939
+ continue;
8940
+ }
8941
+ if (part.type === "image_url") {
8942
+ const url = part.image_url?.url;
8943
+ if (typeof url === "string") {
8944
+ const parsed = parseDataUrlImage(url);
8945
+ if (parsed) {
8946
+ parts.push(parsed);
8947
+ continue;
8948
+ }
8949
+ }
8950
+ parts.push({ type: "text", text: "[Unsupported image_url omitted: only data URLs are currently supported]" });
8951
+ }
8952
+ }
8953
+ if (parts.length === 1 && parts[0]?.type === "text") {
8954
+ return parts[0].text;
8955
+ }
8956
+ return parts;
8957
+ }
8958
+ function summarizeAnthropicContent(content) {
8959
+ if (typeof content === "string")
8960
+ return content;
8961
+ return content.map((part) => {
8962
+ if (part.type === "text")
8963
+ return part.text;
8964
+ if (part.type === "image")
8965
+ return "[Image attached]";
8966
+ return "";
8967
+ }).filter(Boolean).join("");
8908
8968
  }
8909
8969
  function translateOpenAiToAnthropic(body) {
8910
8970
  const messages = body.messages ?? [];
@@ -8920,7 +8980,7 @@ function translateOpenAiToAnthropic(body) {
8920
8980
  } else {
8921
8981
  turns.push({
8922
8982
  role: msg.role === "assistant" ? "assistant" : "user",
8923
- content: text
8983
+ content: translateOpenAiContentToAnthropic(msg.content ?? "")
8924
8984
  });
8925
8985
  }
8926
8986
  }
@@ -8928,7 +8988,7 @@ function translateOpenAiToAnthropic(body) {
8928
8988
  `);
8929
8989
  let messagesToSend = turns;
8930
8990
  if (turns.length > 1) {
8931
- const history = turns.slice(0, -1).map((m) => `${m.role}: ${m.content}`).join(`
8991
+ const history = turns.slice(0, -1).map((m) => `${m.role}: ${summarizeAnthropicContent(m.content)}`).join(`
8932
8992
  `);
8933
8993
  const historyBlock = `<conversation_history>
8934
8994
  ${history}
@@ -9407,7 +9467,7 @@ function fuzzyMatchAgentName(input, validAgents) {
9407
9467
  var openCodeAdapter = {
9408
9468
  name: "opencode",
9409
9469
  getSessionId(c) {
9410
- return c.req.header("x-opencode-session");
9470
+ return c.req.header("x-opencode-session") ?? c.req.header("x-session-affinity");
9411
9471
  },
9412
9472
  extractWorkingDirectory(body) {
9413
9473
  return extractClientCwd(body);
@@ -16384,7 +16444,7 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
16384
16444
  if (sessionId)
16385
16445
  sessionCache.set(sessionId, state);
16386
16446
  const fp = getConversationFingerprint(messages, workingDirectory);
16387
- if (fp)
16447
+ if (fp && !sessionId)
16388
16448
  fingerprintCache.set(fp, state);
16389
16449
  const key = sessionId || fp;
16390
16450
  if (key) {
@@ -16395,41 +16455,114 @@ function storeSession(sessionId, messages, claudeSessionId, workingDirectory, sd
16395
16455
  // src/proxy/server.ts
16396
16456
  var exec3 = promisify3(execCallback2);
16397
16457
  var claudeExecutable = "";
16398
- function buildFreshPrompt(messages, stripCacheControl, sanitizeOpts = {}) {
16399
- const MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
16400
- const hasMultimodal = messages.some((m) => Array.isArray(m.content) && m.content.some((b) => MULTIMODAL_TYPES.has(b.type)));
16458
+ var MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
16459
+ function hasMultimodalContent(content) {
16460
+ if (!Array.isArray(content))
16461
+ return false;
16462
+ return content.some((block) => {
16463
+ if (!block || typeof block !== "object")
16464
+ return false;
16465
+ if (MULTIMODAL_TYPES.has(block.type))
16466
+ return true;
16467
+ if (block.type === "tool_result")
16468
+ return hasMultimodalContent(block.content);
16469
+ return false;
16470
+ });
16471
+ }
16472
+ function stripCacheControlDeep(content) {
16473
+ if (!Array.isArray(content))
16474
+ return content;
16475
+ return content.map((block) => {
16476
+ if (!block || typeof block !== "object")
16477
+ return block;
16478
+ const { cache_control, ...rest } = block;
16479
+ if (block.type === "tool_result" && Array.isArray(block.content)) {
16480
+ return {
16481
+ ...rest,
16482
+ content: stripCacheControlDeep(block.content)
16483
+ };
16484
+ }
16485
+ return rest;
16486
+ });
16487
+ }
16488
+ function normalizeStructuredUserContent(content) {
16489
+ if (!Array.isArray(content))
16490
+ return content;
16491
+ const normalized = [];
16492
+ for (const block of content) {
16493
+ if (!block || typeof block !== "object")
16494
+ continue;
16495
+ if (block.type === "tool_result" && Array.isArray(block.content) && hasMultimodalContent(block.content)) {
16496
+ normalized.push(...normalizeStructuredUserContent(block.content));
16497
+ continue;
16498
+ }
16499
+ if (block.type === "tool_result" && Array.isArray(block.content)) {
16500
+ normalized.push({
16501
+ ...block,
16502
+ content: normalizeStructuredUserContent(block.content)
16503
+ });
16504
+ continue;
16505
+ }
16506
+ normalized.push(block);
16507
+ }
16508
+ return normalized;
16509
+ }
16510
+ function flattenAssistantContent(content) {
16511
+ if (typeof content === "string")
16512
+ return content;
16513
+ if (!Array.isArray(content))
16514
+ return String(content ?? "");
16515
+ return content.map((b) => b?.type === "text" && b.text ? b.text : "").filter(Boolean).join(`
16516
+ `);
16517
+ }
16518
+ function flattenUserContent(content, sanitizeOpts = {}) {
16519
+ if (typeof content === "string")
16520
+ return sanitizeTextContent(content, sanitizeOpts);
16521
+ if (!Array.isArray(content))
16522
+ return String(content ?? "");
16523
+ return content.map((b) => {
16524
+ if (b?.type === "text" && b.text)
16525
+ return sanitizeTextContent(b.text, sanitizeOpts);
16526
+ if (b?.type === "tool_result") {
16527
+ const inner = b.content;
16528
+ if (typeof inner === "string")
16529
+ return inner;
16530
+ if (Array.isArray(inner)) {
16531
+ return inner.map((ib) => ib?.type === "text" && ib.text ? ib.text : "").filter(Boolean).join(`
16532
+ `);
16533
+ }
16534
+ return "";
16535
+ }
16536
+ if (b?.type === "image")
16537
+ return "[Image attached]";
16538
+ if (b?.type === "document")
16539
+ return "[Document attached]";
16540
+ if (b?.type === "file")
16541
+ return "[File attached]";
16542
+ return "";
16543
+ }).filter(Boolean).join(`
16544
+ `);
16545
+ }
16546
+ function buildFreshPrompt(messages, sanitizeOpts = {}) {
16547
+ const hasMultimodal = messages.some((m) => hasMultimodalContent(m.content));
16401
16548
  if (hasMultimodal) {
16402
16549
  const structured = [];
16403
16550
  for (const m of messages) {
16404
16551
  if (m.role === "user") {
16405
16552
  structured.push({
16406
16553
  type: "user",
16407
- message: { role: "user", content: stripCacheControl(m.content) },
16554
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
16408
16555
  parent_tool_use_id: null
16409
16556
  });
16410
16557
  } else {
16411
- let text;
16412
- if (typeof m.content === "string") {
16413
- text = `[Assistant: ${m.content}]`;
16414
- } else if (Array.isArray(m.content)) {
16415
- text = m.content.map((b) => {
16416
- if (b.type === "text" && b.text)
16417
- return `[Assistant: ${b.text}]`;
16418
- if (b.type === "tool_use")
16419
- return `[Tool Use: ${b.name}(${JSON.stringify(b.input)})]`;
16420
- if (b.type === "tool_result")
16421
- return `[Tool Result: ${typeof b.content === "string" ? b.content : JSON.stringify(b.content)}]`;
16422
- return "";
16423
- }).filter(Boolean).join(`
16424
- `);
16425
- } else {
16426
- text = `[Assistant: ${String(m.content)}]`;
16558
+ const assistantText = flattenAssistantContent(m.content);
16559
+ if (assistantText) {
16560
+ structured.push({
16561
+ type: "user",
16562
+ message: { role: "user", content: `[Assistant: ${assistantText}]` },
16563
+ parent_tool_use_id: null
16564
+ });
16427
16565
  }
16428
- structured.push({
16429
- type: "user",
16430
- message: { role: "user", content: text },
16431
- parent_tool_use_id: null
16432
- });
16433
16566
  }
16434
16567
  }
16435
16568
  return async function* () {
@@ -16439,31 +16572,9 @@ function buildFreshPrompt(messages, stripCacheControl, sanitizeOpts = {}) {
16439
16572
  }
16440
16573
  return messages.map((m) => {
16441
16574
  const role = m.role === "assistant" ? "Assistant" : "Human";
16442
- let content;
16443
- if (typeof m.content === "string") {
16444
- content = sanitizeTextContent(m.content, sanitizeOpts);
16445
- } else if (Array.isArray(m.content)) {
16446
- content = m.content.map((block) => {
16447
- if (block.type === "text" && block.text)
16448
- return sanitizeTextContent(block.text, sanitizeOpts);
16449
- if (block.type === "tool_use")
16450
- return `[Tool Use: ${block.name}(${JSON.stringify(block.input)})]`;
16451
- if (block.type === "tool_result")
16452
- return `[Tool Result for ${block.tool_use_id}: ${typeof block.content === "string" ? block.content : JSON.stringify(block.content)}]`;
16453
- if (block.type === "image")
16454
- return "[Image attached]";
16455
- if (block.type === "document")
16456
- return "[Document attached]";
16457
- if (block.type === "file")
16458
- return "[File attached]";
16459
- return "";
16460
- }).filter(Boolean).join(`
16461
- `);
16462
- } else {
16463
- content = String(m.content);
16464
- }
16465
- return `${role}: ${content}`;
16466
- }).join(`
16575
+ const content = m.role === "assistant" ? flattenAssistantContent(m.content) : flattenUserContent(m.content, sanitizeOpts);
16576
+ return content ? `${role}: ${content}` : "";
16577
+ }).filter(Boolean).join(`
16467
16578
 
16468
16579
  `) || "";
16469
16580
  }
@@ -16567,17 +16678,7 @@ function createProxyServer(config = {}) {
16567
16678
  return withClaudeLogContext({ requestId: requestMeta.requestId, endpoint: requestMeta.endpoint }, async () => {
16568
16679
  const adapter = detectAdapter(c);
16569
16680
  try {
16570
- let stripCacheControl = function(content) {
16571
- if (!Array.isArray(content))
16572
- return content;
16573
- return content.map((block) => {
16574
- if (block.cache_control) {
16575
- const { cache_control, ...rest } = block;
16576
- return rest;
16577
- }
16578
- return block;
16579
- });
16580
- }, makePrompt = function() {
16681
+ let makePrompt = function() {
16581
16682
  if (structuredMessages) {
16582
16683
  const msgs = structuredMessages;
16583
16684
  return async function* () {
@@ -16594,6 +16695,7 @@ function createProxyServer(config = {}) {
16594
16695
  const profile = resolveProfile(finalConfig.profiles, finalConfig.defaultProfile, c.req.header("x-meridian-profile") || undefined);
16595
16696
  const authStatus = await getClaudeAuthStatusAsync(profile.id !== "default" ? profile.id : undefined, Object.keys(profile.env).length > 0 ? profile.env : undefined);
16596
16697
  const agentMode = c.req.header("x-opencode-agent-mode") ?? null;
16698
+ const requestSource = c.req.header("x-meridian-source")?.slice(0, 64) || undefined;
16597
16699
  let model = mapModelToClaudeModel(body.model || "sonnet", authStatus?.subscriptionType, agentMode);
16598
16700
  const adapterStreamPref = adapter.prefersStreaming?.(body);
16599
16701
  const stream2 = adapterStreamPref !== undefined ? adapterStreamPref : body.stream ?? false;
@@ -16653,7 +16755,11 @@ function createProxyServer(config = {}) {
16653
16755
  const agentSessionId = adapter.getSessionId(c);
16654
16756
  const profileSessionId = profile.id !== "default" && agentSessionId ? `${profile.id}:${agentSessionId}` : agentSessionId;
16655
16757
  const profileScopedCwd = profile.id !== "default" ? `${workingDirectory}::profile=${profile.id}` : workingDirectory;
16656
- const lineageResult = lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
16758
+ const isIndependentSession = requestSource?.startsWith("fork-") || requestSource?.startsWith("subagent-") || false;
16759
+ let lineageResult = isIndependentSession ? { type: "diverged" } : lookupSession(profileSessionId, body.messages || [], profileScopedCwd);
16760
+ if (lineageResult.type === "undo" && adapter.name === "opencode" && !agentSessionId) {
16761
+ lineageResult = { type: "diverged" };
16762
+ }
16657
16763
  const isResume = lineageResult.type === "continuation" || lineageResult.type === "compaction";
16658
16764
  const isUndo = lineageResult.type === "undo";
16659
16765
  const cachedSession = lineageResult.type !== "diverged" ? lineageResult.session : undefined;
@@ -16666,10 +16772,10 @@ function createProxyServer(config = {}) {
16666
16772
  const lineageType = lineageResult.type === "diverged" && !cachedSession ? "new" : lineageResult.type;
16667
16773
  const msgCount = Array.isArray(body.messages) ? body.messages.length : 0;
16668
16774
  const toolCount = body.tools?.length ?? 0;
16669
- 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}`;
16775
+ 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}`;
16670
16776
  console.error(`[PROXY] ${requestLogLine} msgs=${msgSummary}`);
16671
16777
  diagnosticLog2.session(`${requestLogLine}`, requestMeta.requestId);
16672
- if (lineageResult.type === "diverged" && profileSessionId) {
16778
+ if (lineageResult.type === "diverged" && profileSessionId && !isIndependentSession) {
16673
16779
  const recovery = lookupSessionRecovery(profileSessionId);
16674
16780
  if (recovery) {
16675
16781
  const prevId = recovery.previousClaudeSessionId || recovery.claudeSessionId;
@@ -16712,8 +16818,7 @@ function createProxyServer(config = {}) {
16712
16818
  } else {
16713
16819
  messagesToConvert = allMessages;
16714
16820
  }
16715
- const MULTIMODAL_TYPES = new Set(["image", "document", "file"]);
16716
- const hasMultimodal = messagesToConvert?.some((m) => Array.isArray(m.content) && m.content.some((b) => MULTIMODAL_TYPES.has(b.type)));
16821
+ const hasMultimodal = messagesToConvert?.some((m) => hasMultimodalContent(m.content));
16717
16822
  let structuredMessages;
16718
16823
  let textPrompt;
16719
16824
  if (hasMultimodal) {
@@ -16723,7 +16828,7 @@ function createProxyServer(config = {}) {
16723
16828
  if (m.role === "user") {
16724
16829
  structuredMessages.push({
16725
16830
  type: "user",
16726
- message: { role: "user", content: stripCacheControl(m.content) },
16831
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
16727
16832
  parent_tool_use_id: null
16728
16833
  });
16729
16834
  }
@@ -16733,63 +16838,27 @@ function createProxyServer(config = {}) {
16733
16838
  if (m.role === "user") {
16734
16839
  structuredMessages.push({
16735
16840
  type: "user",
16736
- message: { role: "user", content: stripCacheControl(m.content) },
16841
+ message: { role: "user", content: normalizeStructuredUserContent(stripCacheControlDeep(m.content)) },
16737
16842
  parent_tool_use_id: null
16738
16843
  });
16739
16844
  } else {
16740
- let text;
16741
- if (typeof m.content === "string") {
16742
- text = `[Assistant: ${m.content}]`;
16743
- } else if (Array.isArray(m.content)) {
16744
- text = m.content.map((b) => {
16745
- if (b.type === "text" && b.text)
16746
- return `[Assistant: ${b.text}]`;
16747
- if (b.type === "tool_use")
16748
- return `[Tool Use: ${b.name}(${JSON.stringify(b.input)})]`;
16749
- if (b.type === "tool_result")
16750
- return `[Tool Result: ${typeof b.content === "string" ? b.content : JSON.stringify(b.content)}]`;
16751
- return "";
16752
- }).filter(Boolean).join(`
16753
- `);
16754
- } else {
16755
- text = `[Assistant: ${String(m.content)}]`;
16845
+ const assistantText = flattenAssistantContent(m.content);
16846
+ if (assistantText) {
16847
+ structuredMessages.push({
16848
+ type: "user",
16849
+ message: { role: "user", content: `[Assistant: ${assistantText}]` },
16850
+ parent_tool_use_id: null
16851
+ });
16756
16852
  }
16757
- structuredMessages.push({
16758
- type: "user",
16759
- message: { role: "user", content: text },
16760
- parent_tool_use_id: null
16761
- });
16762
16853
  }
16763
16854
  }
16764
16855
  }
16765
16856
  } else {
16766
16857
  textPrompt = messagesToConvert?.map((m) => {
16767
16858
  const role = m.role === "assistant" ? "Assistant" : "Human";
16768
- let content;
16769
- if (typeof m.content === "string") {
16770
- content = sanitizeTextContent(m.content, sanitizeOpts);
16771
- } else if (Array.isArray(m.content)) {
16772
- content = m.content.map((block) => {
16773
- if (block.type === "text" && block.text)
16774
- return sanitizeTextContent(block.text, sanitizeOpts);
16775
- if (block.type === "tool_use")
16776
- return `[Tool Use: ${block.name}(${JSON.stringify(block.input)})]`;
16777
- if (block.type === "tool_result")
16778
- return `[Tool Result for ${block.tool_use_id}: ${typeof block.content === "string" ? block.content : JSON.stringify(block.content)}]`;
16779
- if (block.type === "image")
16780
- return "[Image attached]";
16781
- if (block.type === "document")
16782
- return "[Document attached]";
16783
- if (block.type === "file")
16784
- return "[File attached]";
16785
- return "";
16786
- }).filter(Boolean).join(`
16787
- `);
16788
- } else {
16789
- content = String(m.content);
16790
- }
16791
- return `${role}: ${content}`;
16792
- }).join(`
16859
+ const content = m.role === "assistant" ? flattenAssistantContent(m.content) : flattenUserContent(m.content, sanitizeOpts);
16860
+ return content ? `${role}: ${content}` : "";
16861
+ }).filter(Boolean).join(`
16793
16862
 
16794
16863
  `) || "";
16795
16864
  }
@@ -16943,7 +17012,7 @@ function createProxyServer(config = {}) {
16943
17012
  for (let i = 0;i < allMessages.length; i++)
16944
17013
  sdkUuidMap.push(null);
16945
17014
  yield* query(buildQueryOptions({
16946
- prompt: buildFreshPrompt(allMessages, stripCacheControl, sanitizeOpts),
17015
+ prompt: buildFreshPrompt(allMessages, sanitizeOpts),
16947
17016
  model,
16948
17017
  workingDirectory,
16949
17018
  systemContext,
@@ -17186,7 +17255,7 @@ Subprocess stderr: ${stderrOutput}`;
17186
17255
  cacheCreationInputTokens: lastUsage?.cache_creation_input_tokens,
17187
17256
  cacheHitRate: computeCacheHitRate(lastUsage)
17188
17257
  });
17189
- if (currentSessionId) {
17258
+ if (currentSessionId && !isIndependentSession) {
17190
17259
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
17191
17260
  }
17192
17261
  const responseSessionId = currentSessionId || resumeSessionId || `session_${Date.now()}`;
@@ -17312,7 +17381,7 @@ Subprocess stderr: ${stderrOutput}`;
17312
17381
  for (let i = 0;i < allMessages.length; i++)
17313
17382
  sdkUuidMap.push(null);
17314
17383
  yield* query(buildQueryOptions({
17315
- prompt: buildFreshPrompt(allMessages, stripCacheControl, sanitizeOpts),
17384
+ prompt: buildFreshPrompt(allMessages, sanitizeOpts),
17316
17385
  model,
17317
17386
  workingDirectory,
17318
17387
  systemContext,
@@ -17570,7 +17639,7 @@ data: ${JSON.stringify({ type: "message_stop" })}
17570
17639
  const allNames = [...sessionDiscoveredTools.get(sessId)];
17571
17640
  console.error(`[PROXY] ${requestMeta.requestId} discovered=${discoveredTools.size} (${newNames}) session_total=${allNames.length}`);
17572
17641
  }
17573
- if (currentSessionId) {
17642
+ if (currentSessionId && !isIndependentSession) {
17574
17643
  storeSession(profileSessionId, body.messages || [], currentSessionId, profileScopedCwd, sdkUuidMap, lastUsage);
17575
17644
  }
17576
17645
  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-g6ndy8jh.js";
4
+ } from "./cli-z5r7ptsm.js";
5
5
  import"./cli-pr79d7nw.js";
6
6
  import"./cli-rtab0qa6.js";
7
7
  import"./cli-m9pfb7h9.js";
@@ -16,9 +16,22 @@
16
16
  * and don't benefit from Meridian's session resumption.
17
17
  */
18
18
  export type OpenAiRole = "system" | "user" | "assistant";
19
+ export interface OpenAiTextPart {
20
+ type: "text";
21
+ text?: string;
22
+ }
23
+ export interface OpenAiImageUrlPart {
24
+ type: "image_url";
25
+ image_url?: {
26
+ url?: string;
27
+ };
28
+ }
19
29
  export interface OpenAiContentPart {
20
30
  type: string;
21
31
  text?: string;
32
+ image_url?: {
33
+ url?: string;
34
+ };
22
35
  }
23
36
  export interface OpenAiMessage {
24
37
  role: OpenAiRole;
@@ -33,9 +46,22 @@ export interface OpenAiChatRequest {
33
46
  temperature?: number;
34
47
  top_p?: number;
35
48
  }
49
+ export interface AnthropicTextBlock {
50
+ type: "text";
51
+ text: string;
52
+ }
53
+ export interface AnthropicImageBlock {
54
+ type: "image";
55
+ source: {
56
+ type: "base64";
57
+ media_type: string;
58
+ data: string;
59
+ };
60
+ }
61
+ export type AnthropicInputContentBlock = AnthropicTextBlock | AnthropicImageBlock;
36
62
  export interface AnthropicMessage {
37
63
  role: "user" | "assistant";
38
- content: string;
64
+ content: string | AnthropicInputContentBlock[];
39
65
  }
40
66
  export interface AnthropicRequestBody {
41
67
  model: string;
@@ -103,6 +129,8 @@ export interface OpenAiModel {
103
129
  /**
104
130
  * Normalise an OpenAI message content field to a plain string.
105
131
  * Handles both string content and structured content arrays.
132
+ * Non-text blocks are summarized so history packing does not silently erase
133
+ * multimodal context.
106
134
  */
107
135
  export declare function extractOpenAiContent(content: string | OpenAiContentPart[]): string;
108
136
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/proxy/openai.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/proxy/openai.ts"],"names":[],"mappings":"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"}
@@ -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;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;AAmJ7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA65DhF;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;AA8N7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAy5DhF;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-g6ndy8jh.js";
9
+ } from "./cli-z5r7ptsm.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.4",
3
+ "version": "1.37.6",
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"