@rynfar/meridian 1.31.1 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ---
14
14
 
15
- Meridian bridges the Claude Code SDK to the standard Anthropic API. No OAuth interception. No binary patches. No hacks. Just pure, documented SDK calls. Any tool that speaks the Anthropic or OpenAI protocol — OpenCode, Crush, Cline, Aider, Pi, Droid, Open WebUI — connects to Meridian and gets Claude, with session management, streaming, and prompt caching handled natively by the SDK.
15
+ Meridian bridges the Claude Code SDK to the standard Anthropic API. No OAuth interception. No binary patches. No hacks. Just pure, documented SDK calls. Any tool that speaks the Anthropic or OpenAI protocol — OpenCode, ForgeCode, Crush, Cline, Aider, Pi, Droid, Open WebUI — connects to Meridian and gets Claude, with session management, streaming, and prompt caching handled natively by the SDK.
16
16
 
17
17
  > [!NOTE]
18
18
  > ### How Meridian works with Anthropic
@@ -273,6 +273,45 @@ Point any OpenAI-compatible tool at `http://127.0.0.1:3456` with any API key val
273
273
 
274
274
  > **Note:** Multi-turn conversations work by packing prior turns into the system prompt. Each request is a fresh SDK session — OpenAI clients replay full history themselves and don't use Meridian's session resumption.
275
275
 
276
+ ### ForgeCode
277
+
278
+ Add a custom provider to `~/forge/.forge.toml`:
279
+
280
+ ```toml
281
+ [[providers]]
282
+ id = "meridian"
283
+ url = "http://127.0.0.1:3456/v1/messages"
284
+ models = "http://127.0.0.1:3456/v1/models"
285
+ api_key_vars = "MERIDIAN_API_KEY"
286
+ response_type = "Anthropic"
287
+ auth_methods = ["api_key"]
288
+
289
+ [session]
290
+ provider_id = "meridian"
291
+ model_id = "claude-opus-4-6"
292
+ ```
293
+
294
+ Set the API key env var (any value — Meridian authenticates through the SDK, not API keys):
295
+
296
+ ```bash
297
+ export MERIDIAN_API_KEY=unused
298
+ ```
299
+
300
+ Then log in and select the model:
301
+
302
+ ```bash
303
+ forge provider login meridian # enter any value when prompted
304
+ forge config set provider meridian --model claude-opus-4-6
305
+ ```
306
+
307
+ Start Meridian with the ForgeCode adapter:
308
+
309
+ ```bash
310
+ MERIDIAN_DEFAULT_AGENT=forgecode meridian
311
+ ```
312
+
313
+ ForgeCode uses reqwest's default User-Agent, so automatic detection isn't possible. The `MERIDIAN_DEFAULT_AGENT` env var tells Meridian to use the ForgeCode adapter for all unrecognized requests. If you run other agents alongside ForgeCode, use the `x-meridian-agent: forgecode` header instead (add `[providers.headers]` to your `.forge.toml`).
314
+
276
315
  ### Pi
277
316
 
278
317
  Pi uses the `@mariozechner/pi-ai` library which supports a configurable `baseUrl` on the model. Add a provider-level override in `~/.pi/agent/models.json`:
@@ -305,6 +344,7 @@ export ANTHROPIC_BASE_URL=http://127.0.0.1:3456
305
344
  | Agent | Status | Notes |
306
345
  |-------|--------|-------|
307
346
  | [OpenCode](https://github.com/anomalyco/opencode) | ✅ Verified | Requires `meridian setup` — full tool support, session resume, streaming, subagents |
347
+ | [ForgeCode](https://forgecode.dev) | ✅ Verified | Provider config (see above) — passthrough tool execution, session resume, streaming |
308
348
  | [Droid (Factory AI)](https://factory.ai/product/ide) | ✅ Verified | BYOK config (see above) — full tool support, session resume, streaming |
309
349
  | [Crush](https://github.com/charmbracelet/crush) | ✅ Verified | Provider config (see above) — full tool support, session resume, headless `crush run` |
310
350
  | [Cline](https://github.com/cline/cline) | ✅ Verified | Config (see above) — full tool support, file read/write/edit, bash, session resume |
@@ -324,6 +364,7 @@ src/proxy/
324
364
  ├── adapters/
325
365
  │ ├── detect.ts ← Agent detection from request headers
326
366
  │ ├── opencode.ts ← OpenCode adapter
367
+ │ ├── forgecode.ts ← ForgeCode adapter
327
368
  │ ├── crush.ts ← Crush adapter
328
369
  │ ├── droid.ts ← Droid adapter
329
370
  │ ├── pi.ts ← Pi adapter
@@ -394,7 +435,7 @@ Implement the `AgentAdapter` interface in `src/proxy/adapters/`. See [`adapters/
394
435
  | `MERIDIAN_TELEMETRY_SIZE` | `CLAUDE_PROXY_TELEMETRY_SIZE` | `1000` | Telemetry ring buffer size |
395
436
  | `MERIDIAN_NO_FILE_CHANGES` | `CLAUDE_PROXY_NO_FILE_CHANGES` | unset | Disable "Files changed" summary in responses |
396
437
  | `MERIDIAN_SONNET_MODEL` | `CLAUDE_PROXY_SONNET_MODEL` | `sonnet` | Sonnet context tier: `sonnet` (200k, default) or `sonnet[1m]` (1M, requires Extra Usage†) |
397
- | `MERIDIAN_DEFAULT_AGENT` | — | `opencode` | Default adapter for unrecognized agents: `opencode`, `pi`, `crush`, `droid`, `passthrough`. Requires restart. |
438
+ | `MERIDIAN_DEFAULT_AGENT` | — | `opencode` | Default adapter for unrecognized agents: `opencode`, `forgecode`, `pi`, `crush`, `droid`, `passthrough`. Requires restart. |
398
439
  | `MERIDIAN_PROFILES` | — | unset | JSON array of profile configs (overrides disk discovery). See [Multi-Profile Support](#multi-profile-support). |
399
440
  | `MERIDIAN_DEFAULT_PROFILE` | — | *(first profile)* | Default profile ID when no header is sent |
400
441
 
@@ -7413,7 +7413,6 @@ function translateAnthropicSseEvent(event, completionId, model, created) {
7413
7413
  return null;
7414
7414
  }
7415
7415
  function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000)) {
7416
- const extendedContext = isMaxSubscription ? 1e6 : 200000;
7417
7416
  return [
7418
7417
  {
7419
7418
  id: "claude-sonnet-4-6",
@@ -7421,7 +7420,7 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
7421
7420
  created: now,
7422
7421
  owned_by: "anthropic",
7423
7422
  display_name: "Claude Sonnet 4.6",
7424
- context_window: extendedContext
7423
+ context_window: 200000
7425
7424
  },
7426
7425
  {
7427
7426
  id: "claude-opus-4-6",
@@ -7429,7 +7428,7 @@ function buildModelList(isMaxSubscription, now = Math.floor(Date.now() / 1000))
7429
7428
  created: now,
7430
7429
  owned_by: "anthropic",
7431
7430
  display_name: "Claude Opus 4.6",
7432
- context_window: extendedContext
7431
+ context_window: isMaxSubscription ? 1e6 : 200000
7433
7432
  },
7434
7433
  {
7435
7434
  id: "claude-haiku-4-5-20251001",
@@ -8132,13 +8131,85 @@ var piAdapter = {
8132
8131
  }
8133
8132
  };
8134
8133
 
8134
+ // src/proxy/adapters/forgecode.ts
8135
+ var FORGECODE_MCP_SERVER_NAME = "forgecode";
8136
+ var FORGECODE_ALLOWED_MCP_TOOLS = [
8137
+ `mcp__${FORGECODE_MCP_SERVER_NAME}__read`,
8138
+ `mcp__${FORGECODE_MCP_SERVER_NAME}__write`,
8139
+ `mcp__${FORGECODE_MCP_SERVER_NAME}__edit`,
8140
+ `mcp__${FORGECODE_MCP_SERVER_NAME}__bash`,
8141
+ `mcp__${FORGECODE_MCP_SERVER_NAME}__glob`,
8142
+ `mcp__${FORGECODE_MCP_SERVER_NAME}__grep`
8143
+ ];
8144
+ function extractForgeCodeCwd(body) {
8145
+ let systemText = "";
8146
+ if (typeof body.system === "string") {
8147
+ systemText = body.system;
8148
+ } else if (Array.isArray(body.system)) {
8149
+ systemText = body.system.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(`
8150
+ `);
8151
+ }
8152
+ if (!systemText)
8153
+ return;
8154
+ const match2 = systemText.match(/<current_working_directory>\s*([^<]+?)\s*<\/current_working_directory>/i);
8155
+ return match2?.[1]?.trim() || undefined;
8156
+ }
8157
+ var forgeCodeAdapter = {
8158
+ name: "forgecode",
8159
+ getSessionId(_c) {
8160
+ return;
8161
+ },
8162
+ extractWorkingDirectory(body) {
8163
+ return extractForgeCodeCwd(body);
8164
+ },
8165
+ normalizeContent(content) {
8166
+ return normalizeContent(content);
8167
+ },
8168
+ getBlockedBuiltinTools() {
8169
+ return BLOCKED_BUILTIN_TOOLS;
8170
+ },
8171
+ getAgentIncompatibleTools() {
8172
+ return CLAUDE_CODE_ONLY_TOOLS;
8173
+ },
8174
+ getMcpServerName() {
8175
+ return FORGECODE_MCP_SERVER_NAME;
8176
+ },
8177
+ getAllowedMcpTools() {
8178
+ return FORGECODE_ALLOWED_MCP_TOOLS;
8179
+ },
8180
+ buildSdkAgents(_body, _mcpToolNames) {
8181
+ return {};
8182
+ },
8183
+ buildSdkHooks(_body, _sdkAgents) {
8184
+ return;
8185
+ },
8186
+ buildSystemContextAddendum(_body, _sdkAgents) {
8187
+ return "";
8188
+ },
8189
+ extractFileChangesFromToolUse(toolName, toolInput) {
8190
+ const input = toolInput;
8191
+ const filePath = input?.file_path ?? input?.filePath ?? input?.path;
8192
+ if (toolName === "write" && filePath) {
8193
+ return [{ operation: "wrote", path: String(filePath) }];
8194
+ }
8195
+ if ((toolName === "patch" || toolName === "multi_patch") && filePath) {
8196
+ return [{ operation: "edited", path: String(filePath) }];
8197
+ }
8198
+ if (toolName === "shell" && input?.command) {
8199
+ return extractFileChangesFromBash(String(input.command));
8200
+ }
8201
+ return [];
8202
+ }
8203
+ };
8204
+
8135
8205
  // src/proxy/adapters/detect.ts
8136
8206
  var ADAPTER_MAP = {
8137
8207
  opencode: openCodeAdapter,
8138
8208
  droid: droidAdapter,
8139
8209
  crush: crushAdapter,
8140
8210
  passthrough: passthroughAdapter,
8141
- pi: piAdapter
8211
+ pi: piAdapter,
8212
+ forgecode: forgeCodeAdapter
8142
8213
  };
8143
8214
  var envDefault = process.env.MERIDIAN_DEFAULT_AGENT || "";
8144
8215
  if (envDefault && !ADAPTER_MAP[envDefault]) {
@@ -14028,6 +14099,33 @@ function formatAnomalyAlerts(requestId, anomalies) {
14028
14099
  });
14029
14100
  }
14030
14101
 
14102
+ // src/proxy/tokenUsage.ts
14103
+ function computeCacheHitRate(usage) {
14104
+ if (!usage)
14105
+ return;
14106
+ const read = usage.cache_read_input_tokens ?? 0;
14107
+ const creation = usage.cache_creation_input_tokens ?? 0;
14108
+ const uncached = usage.input_tokens ?? 0;
14109
+ const total = uncached + read + creation;
14110
+ if (total === 0)
14111
+ return;
14112
+ return read / total;
14113
+ }
14114
+ function formatTokenCount(n) {
14115
+ return n > 1000 ? `${Math.round(n / 1000)}k` : String(n);
14116
+ }
14117
+ function formatUsageSummary(usage) {
14118
+ const parts = [
14119
+ `input=${formatTokenCount(usage.input_tokens ?? 0)}`,
14120
+ `output=${formatTokenCount(usage.output_tokens ?? 0)}`,
14121
+ ...usage.cache_read_input_tokens ? [`cache_read=${formatTokenCount(usage.cache_read_input_tokens)}`] : [],
14122
+ ...usage.cache_creation_input_tokens ? [`cache_write=${formatTokenCount(usage.cache_creation_input_tokens)}`] : []
14123
+ ];
14124
+ const rate = computeCacheHitRate(usage);
14125
+ const cacheTag = rate !== undefined ? ` cache=${Math.round(rate * 100)}%` : "";
14126
+ return `${parts.join(" ")}${cacheTag}`;
14127
+ }
14128
+
14031
14129
  // src/proxy/session/lineage.ts
14032
14130
  import { createHash as createHash2 } from "crypto";
14033
14131
  var MIN_SUFFIX_FOR_COMPACTION = 2;
@@ -14706,29 +14804,7 @@ function buildFreshPrompt(messages, stripCacheControl) {
14706
14804
  `) || "";
14707
14805
  }
14708
14806
  function logUsage(requestId, usage) {
14709
- const fmt2 = (n) => n > 1000 ? `${Math.round(n / 1000)}k` : String(n);
14710
- const cacheRead = usage.cache_read_input_tokens ?? 0;
14711
- const totalInput = usage.input_tokens ?? 0;
14712
- const cacheRate = totalInput > 0 ? Math.round(cacheRead / totalInput * 100) : 0;
14713
- const cacheTag = totalInput > 0 ? ` cache=${cacheRate}%` : "";
14714
- const parts = [
14715
- `input=${fmt2(usage.input_tokens ?? 0)}`,
14716
- `output=${fmt2(usage.output_tokens ?? 0)}`,
14717
- ...usage.cache_read_input_tokens ? [`cache_read=${fmt2(usage.cache_read_input_tokens)}`] : [],
14718
- ...usage.cache_creation_input_tokens ? [`cache_write=${fmt2(usage.cache_creation_input_tokens)}`] : []
14719
- ];
14720
- console.error(`[PROXY] ${requestId} usage: ${parts.join(" ")}${cacheTag}`);
14721
- }
14722
- function computeCacheHitRate(usage) {
14723
- if (!usage)
14724
- return;
14725
- const read = usage.cache_read_input_tokens ?? 0;
14726
- const creation = usage.cache_creation_input_tokens ?? 0;
14727
- const uncached = usage.input_tokens ?? 0;
14728
- const total = uncached + read + creation;
14729
- if (total === 0)
14730
- return;
14731
- return read / total;
14807
+ console.error(`[PROXY] ${requestId} usage: ${formatUsageSummary(usage)}`);
14732
14808
  }
14733
14809
  function checkTokenHealth(requestId, sdkSessionId, usage, turnNumber, isResume, isPassthrough) {
14734
14810
  if (!usage || !sdkSessionId)
@@ -14881,6 +14957,13 @@ function createProxyServer(config = {}) {
14881
14957
  console.error(`[PROXY] ${requestMeta.requestId} ignoring malformed x-opencode-thinking header: ${e instanceof Error ? e.message : String(e)}`);
14882
14958
  }
14883
14959
  }
14960
+ const thinkingBetaStripped = betaFilter.stripped.some((b) => b.startsWith("interleaved-thinking"));
14961
+ if (thinkingBetaStripped) {
14962
+ thinking = { type: "disabled" };
14963
+ if (betaFilter.stripped.length > 0) {
14964
+ console.error(`[PROXY] ${requestMeta.requestId} thinking disabled (thinking beta stripped by ${getBetaPolicyFromEnv()} policy)`);
14965
+ }
14966
+ }
14884
14967
  const parsedBudget = taskBudgetHeader ? Number.parseInt(taskBudgetHeader, 10) : NaN;
14885
14968
  const taskBudget = Number.isFinite(parsedBudget) ? { total: parsedBudget } : body.task_budget ? { total: body.task_budget.total ?? body.task_budget } : undefined;
14886
14969
  const betas = betaFilter.forwarded;
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startProxyServer
4
- } from "./cli-7hathpbb.js";
4
+ } from "./cli-1qv75efv.js";
5
5
  import"./cli-g9ypdz51.js";
6
6
  import"./cli-rtab0qa6.js";
7
7
  import"./cli-m9pfb7h9.js";
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAqC9C;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CA+BtD"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAuC9C;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,CA+BtD"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ForgeCode agent adapter.
3
+ *
4
+ * Provides ForgeCode-specific behavior for session tracking, working directory
5
+ * extraction, content normalization, and tool configuration.
6
+ *
7
+ * ForgeCode (forgecode.dev) is a Rust-based terminal coding agent by Antinomy
8
+ * that makes standard Anthropic Messages API calls. When using a custom provider
9
+ * URL (pointing at Meridian), ForgeCode operates in passthrough mode with its
10
+ * own tool execution loop.
11
+ *
12
+ * Key characteristics:
13
+ * - User-Agent: reqwest default (no distinctive prefix) — use x-meridian-agent or env var
14
+ * - No session header: relies on fingerprint-based session cache
15
+ * - CWD in system prompt: <current_working_directory>/path</current_working_directory>
16
+ * - Snake_case tools: read, write, patch, multi_patch, shell, fs_search, etc.
17
+ * - Always streams (stream: true by default)
18
+ * - Manages its own tool execution loop: passthrough mode is appropriate
19
+ * - Subagent routing handled client-side via Task tool — invisible to proxy
20
+ *
21
+ * Detection: ForgeCode uses reqwest's default User-Agent, so automatic detection
22
+ * is unreliable. Use one of:
23
+ * - x-meridian-agent: forgecode header (per-request)
24
+ * - MERIDIAN_DEFAULT_AGENT=forgecode env var (global default)
25
+ */
26
+ import type { AgentAdapter } from "../adapter";
27
+ export declare const forgeCodeAdapter: AgentAdapter;
28
+ //# sourceMappingURL=forgecode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forgecode.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/forgecode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAwC9C,eAAO,MAAM,gBAAgB,EAAE,YA6F9B,CAAA"}
@@ -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;AAuBvD,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;AAyK7B,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CA0vDhF;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;AAwBvD,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,CAqwDhF;AAED,wBAAsB,gBAAgB,CAAC,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAiEhG"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Token usage formatting and cache-hit-rate calculations.
3
+ *
4
+ * Pure functions shared between stderr request logging and telemetry storage.
5
+ * Extracting these out of server.ts guarantees a single source of truth for
6
+ * the cache-hit-rate formula — stderr logs and the telemetry dashboard now
7
+ * agree.
8
+ *
9
+ * Previously `logUsage` computed `cache_read / input_tokens`, but the SDK
10
+ * reports `input_tokens` as *only* the non-cached portion, so the ratio blew
11
+ * past 100% (e.g. `cache=2600000%`) whenever a cache hit actually happened.
12
+ * `computeCacheHitRate` already had the correct formula; this module moves
13
+ * `logUsage` onto the same helper.
14
+ *
15
+ * This module is pure — no I/O, no imports from server.ts or session/.
16
+ */
17
+ import type { TokenUsage } from "./session/lineage";
18
+ /**
19
+ * Compute the fraction of input tokens served from cache for a single turn.
20
+ *
21
+ * The SDK's `input_tokens` field reports only the non-cached portion, so the
22
+ * true total input size is `input_tokens + cache_read_input_tokens +
23
+ * cache_creation_input_tokens`. Returns a value in [0, 1], or `undefined` if
24
+ * there is no input to measure against.
25
+ */
26
+ export declare function computeCacheHitRate(usage: TokenUsage | undefined): number | undefined;
27
+ /**
28
+ * Format an integer token count for compact display.
29
+ *
30
+ * - values ≤ 1000 → exact integer (e.g. `"847"`)
31
+ * - values > 1000 → rounded to nearest thousand with "k" suffix (e.g. `"71k"`)
32
+ */
33
+ export declare function formatTokenCount(n: number): string;
34
+ /**
35
+ * Build the single-line stderr summary for a request's token usage.
36
+ *
37
+ * Example outputs:
38
+ * "input=3 output=416 cache_read=71k cache_write=2k cache=97%"
39
+ * "input=10 output=8 cache_write=59k cache=0%"
40
+ * "input=1000 output=50" (no caching activity)
41
+ *
42
+ * The `cache=XX%` tag is emitted whenever `computeCacheHitRate` returns a
43
+ * defined value. The tag is suppressed only when there is literally no input
44
+ * data to compute a ratio from (neither uncached nor cached).
45
+ */
46
+ export declare function formatUsageSummary(usage: TokenUsage): string;
47
+ //# sourceMappingURL=tokenUsage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenUsage.d.ts","sourceRoot":"","sources":["../../src/proxy/tokenUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAQrF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAU5D"}
package/dist/server.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  getMaxSessionsLimit,
7
7
  hashMessage,
8
8
  startProxyServer
9
- } from "./cli-7hathpbb.js";
9
+ } from "./cli-1qv75efv.js";
10
10
  import"./cli-g9ypdz51.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.31.1",
3
+ "version": "1.34.0",
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",