@blockrun/clawrouter 0.9.37 → 0.9.39

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/dist/index.js CHANGED
@@ -1683,8 +1683,6 @@ var DEFAULT_ROUTING_CONFIG = {
1683
1683
  primary: "moonshot/kimi-k2.5",
1684
1684
  // $0.50/$2.40 - best quality/price for simple tasks
1685
1685
  fallback: [
1686
- "minimax/minimax-m2.5",
1687
- // $0.30/$1.20 - cheap with reasoning
1688
1686
  "google/gemini-2.5-flash",
1689
1687
  // 1M context, cost-effective
1690
1688
  "nvidia/gpt-oss-120b",
@@ -1696,8 +1694,6 @@ var DEFAULT_ROUTING_CONFIG = {
1696
1694
  primary: "xai/grok-code-fast-1",
1697
1695
  // Code specialist, $0.20/$1.50
1698
1696
  fallback: [
1699
- "minimax/minimax-m2.5",
1700
- // $0.30/$1.20 - cheap with reasoning
1701
1697
  "google/gemini-2.5-flash",
1702
1698
  // 1M context, cost-effective
1703
1699
  "deepseek/deepseek-chat",
@@ -1712,10 +1708,7 @@ var DEFAULT_ROUTING_CONFIG = {
1712
1708
  "google/gemini-2.5-flash",
1713
1709
  // CRITICAL: 1M context, cheap failsafe before expensive models
1714
1710
  "google/gemini-2.5-pro",
1715
- "minimax/minimax-m2.5",
1716
- // $0.30/$1.20 - cheap with reasoning
1717
1711
  "deepseek/deepseek-chat",
1718
- // Another cheap option
1719
1712
  "xai/grok-4-0709",
1720
1713
  "openai/gpt-5.2",
1721
1714
  // Newer and cheaper input than gpt-4o
@@ -1727,8 +1720,6 @@ var DEFAULT_ROUTING_CONFIG = {
1727
1720
  primary: "xai/grok-4-1-fast-reasoning",
1728
1721
  // Upgraded Grok 4.1 reasoning $0.20/$0.50
1729
1722
  fallback: [
1730
- "minimax/minimax-m2.5",
1731
- // $0.30/$1.20 - reasoning capable
1732
1723
  "deepseek/deepseek-reasoner",
1733
1724
  // Cheap reasoning model
1734
1725
  "openai/o4-mini",
@@ -1742,22 +1733,22 @@ var DEFAULT_ROUTING_CONFIG = {
1742
1733
  SIMPLE: {
1743
1734
  primary: "nvidia/gpt-oss-120b",
1744
1735
  // FREE! $0.00/$0.00
1745
- fallback: ["google/gemini-2.5-flash", "deepseek/deepseek-chat", "minimax/minimax-m2.5"]
1736
+ fallback: ["google/gemini-2.5-flash", "deepseek/deepseek-chat"]
1746
1737
  },
1747
1738
  MEDIUM: {
1748
1739
  primary: "google/gemini-2.5-flash",
1749
1740
  // $0.15/$0.60 - cheapest capable
1750
- fallback: ["deepseek/deepseek-chat", "nvidia/gpt-oss-120b", "minimax/minimax-m2.5"]
1741
+ fallback: ["deepseek/deepseek-chat", "nvidia/gpt-oss-120b"]
1751
1742
  },
1752
1743
  COMPLEX: {
1753
1744
  primary: "google/gemini-2.5-flash",
1754
1745
  // $0.15/$0.60 - 1M context handles complexity
1755
- fallback: ["deepseek/deepseek-chat", "xai/grok-4-0709", "minimax/minimax-m2.5"]
1746
+ fallback: ["deepseek/deepseek-chat", "xai/grok-4-0709"]
1756
1747
  },
1757
1748
  REASONING: {
1758
1749
  primary: "xai/grok-4-1-fast-reasoning",
1759
- // $0.20/$0.50 - was MORE expensive than AUTO!
1760
- fallback: ["deepseek/deepseek-reasoner", "minimax/minimax-m2.5"]
1750
+ // $0.20/$0.50
1751
+ fallback: ["deepseek/deepseek-reasoner"]
1761
1752
  }
1762
1753
  },
1763
1754
  // Premium tier configs - best quality (blockrun/premium)
@@ -1807,32 +1798,18 @@ var DEFAULT_ROUTING_CONFIG = {
1807
1798
  SIMPLE: {
1808
1799
  primary: "moonshot/kimi-k2.5",
1809
1800
  // Cheaper than Haiku ($0.5/$2.4 vs $1/$5), larger context
1810
- fallback: [
1811
- "minimax/minimax-m2.5",
1812
- // $0.30/$1.20 - agentic capable, cheaper than kimi
1813
- "claude-haiku-4.5",
1814
- "xai/grok-4-1-fast-non-reasoning",
1815
- "openai/gpt-4o-mini"
1816
- ]
1801
+ fallback: ["claude-haiku-4.5", "xai/grok-4-1-fast-non-reasoning", "openai/gpt-4o-mini"]
1817
1802
  },
1818
1803
  MEDIUM: {
1819
1804
  primary: "xai/grok-code-fast-1",
1820
1805
  // Code specialist for agentic coding
1821
- fallback: [
1822
- "minimax/minimax-m2.5",
1823
- // $0.30/$1.20 - agentic capable
1824
- "moonshot/kimi-k2.5",
1825
- "claude-haiku-4.5",
1826
- "claude-sonnet-4"
1827
- ]
1806
+ fallback: ["moonshot/kimi-k2.5", "claude-haiku-4.5", "claude-sonnet-4"]
1828
1807
  },
1829
1808
  COMPLEX: {
1830
1809
  primary: "claude-sonnet-4",
1831
1810
  fallback: [
1832
1811
  "claude-opus-4",
1833
1812
  // Latest Opus - best agentic
1834
- "minimax/minimax-m2.5",
1835
- // $0.30/$1.20 - cheap agentic fallback
1836
1813
  "openai/gpt-5.2",
1837
1814
  "google/gemini-3-pro-preview",
1838
1815
  "xai/grok-4-0709"
@@ -1841,13 +1818,7 @@ var DEFAULT_ROUTING_CONFIG = {
1841
1818
  REASONING: {
1842
1819
  primary: "claude-sonnet-4",
1843
1820
  // Strong tool use + reasoning for agentic tasks
1844
- fallback: [
1845
- "claude-opus-4",
1846
- "minimax/minimax-m2.5",
1847
- // $0.30/$1.20 - reasoning + agentic
1848
- "xai/grok-4-1-fast-reasoning",
1849
- "deepseek/deepseek-reasoner"
1850
- ]
1821
+ fallback: ["claude-opus-4", "xai/grok-4-1-fast-reasoning", "deepseek/deepseek-reasoner"]
1851
1822
  }
1852
1823
  },
1853
1824
  overrides: {
@@ -1951,7 +1922,33 @@ async function logUsage(entry) {
1951
1922
  }
1952
1923
 
1953
1924
  // src/stats.ts
1954
- import { readFile, readdir } from "fs/promises";
1925
+ import { readdir } from "fs/promises";
1926
+
1927
+ // src/fs-read.ts
1928
+ import { open } from "fs/promises";
1929
+ import { openSync, readSync, closeSync, fstatSync } from "fs";
1930
+ async function readTextFile(filePath) {
1931
+ const fh = await open(filePath, "r");
1932
+ try {
1933
+ const buf = Buffer.alloc((await fh.stat()).size);
1934
+ await fh.read(buf, 0, buf.length, 0);
1935
+ return buf.toString("utf-8");
1936
+ } finally {
1937
+ await fh.close();
1938
+ }
1939
+ }
1940
+ function readTextFileSync(filePath) {
1941
+ const fd = openSync(filePath, "r");
1942
+ try {
1943
+ const buf = Buffer.alloc(fstatSync(fd).size);
1944
+ readSync(fd, buf);
1945
+ return buf.toString("utf-8");
1946
+ } finally {
1947
+ closeSync(fd);
1948
+ }
1949
+ }
1950
+
1951
+ // src/stats.ts
1955
1952
  import { join as join3 } from "path";
1956
1953
  import { homedir as homedir2 } from "os";
1957
1954
 
@@ -1970,7 +1967,7 @@ var USER_AGENT = `clawrouter/${VERSION}`;
1970
1967
  var LOG_DIR2 = join3(homedir2(), ".openclaw", "blockrun", "logs");
1971
1968
  async function parseLogFile(filePath) {
1972
1969
  try {
1973
- const content = await readFile(filePath, "utf-8");
1970
+ const content = await readTextFile(filePath);
1974
1971
  const lines = content.trim().split("\n").filter(Boolean);
1975
1972
  return lines.map((line) => {
1976
1973
  const entry = JSON.parse(line);
@@ -3761,6 +3758,7 @@ var ROUTING_PROFILES = /* @__PURE__ */ new Set([
3761
3758
  ]);
3762
3759
  var FREE_MODEL = "nvidia/gpt-oss-120b";
3763
3760
  var MAX_MESSAGES = 200;
3761
+ var CONTEXT_LIMIT_KB = 5120;
3764
3762
  var HEARTBEAT_INTERVAL_MS = 2e3;
3765
3763
  var DEFAULT_REQUEST_TIMEOUT_MS = 18e4;
3766
3764
  var MAX_FALLBACK_ATTEMPTS = 5;
@@ -4147,15 +4145,28 @@ function normalizeMessagesForThinking(messages) {
4147
4145
  return hasChanges ? normalized : messages;
4148
4146
  }
4149
4147
  function truncateMessages(messages) {
4150
- if (!messages || messages.length <= MAX_MESSAGES) return messages;
4148
+ if (!messages || messages.length <= MAX_MESSAGES) {
4149
+ return {
4150
+ messages,
4151
+ wasTruncated: false,
4152
+ originalCount: messages?.length ?? 0,
4153
+ truncatedCount: messages?.length ?? 0
4154
+ };
4155
+ }
4151
4156
  const systemMsgs = messages.filter((m) => m.role === "system");
4152
4157
  const conversationMsgs = messages.filter((m) => m.role !== "system");
4153
4158
  const maxConversation = MAX_MESSAGES - systemMsgs.length;
4154
4159
  const truncatedConversation = conversationMsgs.slice(-maxConversation);
4160
+ const result = [...systemMsgs, ...truncatedConversation];
4155
4161
  console.log(
4156
- `[ClawRouter] Truncated messages: ${messages.length} \u2192 ${systemMsgs.length + truncatedConversation.length} (kept ${systemMsgs.length} system + ${truncatedConversation.length} recent)`
4162
+ `[ClawRouter] Truncated messages: ${messages.length} \u2192 ${result.length} (kept ${systemMsgs.length} system + ${truncatedConversation.length} recent)`
4157
4163
  );
4158
- return [...systemMsgs, ...truncatedConversation];
4164
+ return {
4165
+ messages: result,
4166
+ wasTruncated: true,
4167
+ originalCount: messages.length,
4168
+ truncatedCount: result.length
4169
+ };
4159
4170
  }
4160
4171
  var KIMI_BLOCK_RE = /<[||][^<>]*begin[^<>]*[||]>[\s\S]*?<[||][^<>]*end[^<>]*[||]>/gi;
4161
4172
  var KIMI_TOKEN_RE = /<[||][^<>]*[||]>/g;
@@ -4488,7 +4499,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
4488
4499
  parsed.messages = normalizeMessageRoles(parsed.messages);
4489
4500
  }
4490
4501
  if (Array.isArray(parsed.messages)) {
4491
- parsed.messages = truncateMessages(parsed.messages);
4502
+ const truncationResult = truncateMessages(parsed.messages);
4503
+ parsed.messages = truncationResult.messages;
4492
4504
  }
4493
4505
  if (Array.isArray(parsed.messages)) {
4494
4506
  parsed.messages = sanitizeToolIds(parsed.messages);
@@ -4562,6 +4574,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4562
4574
  bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
4563
4575
  }
4564
4576
  let body = Buffer.concat(bodyChunks);
4577
+ const originalContextSizeKB = Math.ceil(body.length / 1024);
4565
4578
  let routingDecision;
4566
4579
  let isStreaming = false;
4567
4580
  let modelId = "";
@@ -4669,7 +4682,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4669
4682
  const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
4670
4683
  const tools = parsed.tools;
4671
4684
  const hasTools = Array.isArray(tools) && tools.length > 0;
4672
- if (hasTools) {
4685
+ if (hasTools && tools) {
4673
4686
  console.log(
4674
4687
  `[ClawRouter] Tools detected (${tools.length}), agentic mode via keywords`
4675
4688
  );
@@ -4814,7 +4827,9 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4814
4827
  res.writeHead(200, {
4815
4828
  "content-type": "text/event-stream",
4816
4829
  "cache-control": "no-cache",
4817
- connection: "keep-alive"
4830
+ connection: "keep-alive",
4831
+ "x-context-used-kb": String(originalContextSizeKB),
4832
+ "x-context-limit-kb": String(CONTEXT_LIMIT_KB)
4818
4833
  });
4819
4834
  headersSentEarly = true;
4820
4835
  safeWrite(res, ": heartbeat\n\n");
@@ -4977,7 +4992,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4977
4992
  completedAt: Date.now()
4978
4993
  });
4979
4994
  } else {
4980
- res.writeHead(errStatus, { "Content-Type": "application/json" });
4995
+ res.writeHead(errStatus, {
4996
+ "Content-Type": "application/json",
4997
+ "x-context-used-kb": String(originalContextSizeKB),
4998
+ "x-context-limit-kb": String(CONTEXT_LIMIT_KB)
4999
+ });
4981
5000
  res.end(transformedErr);
4982
5001
  deduplicator.complete(dedupKey, {
4983
5002
  status: errStatus,
@@ -5103,6 +5122,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5103
5122
  return;
5104
5123
  responseHeaders[key] = value;
5105
5124
  });
5125
+ responseHeaders["x-context-used-kb"] = String(originalContextSizeKB);
5126
+ responseHeaders["x-context-limit-kb"] = String(CONTEXT_LIMIT_KB);
5106
5127
  res.writeHead(upstream.status, responseHeaders);
5107
5128
  if (upstream.body) {
5108
5129
  const reader = upstream.body.getReader();
@@ -5197,7 +5218,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5197
5218
  }
5198
5219
 
5199
5220
  // src/auth.ts
5200
- import { writeFile, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
5221
+ import { writeFile, mkdir as mkdir2 } from "fs/promises";
5201
5222
  import { join as join4 } from "path";
5202
5223
  import { homedir as homedir3 } from "os";
5203
5224
  import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
@@ -5205,7 +5226,7 @@ var WALLET_DIR = join4(homedir3(), ".openclaw", "blockrun");
5205
5226
  var WALLET_FILE = join4(WALLET_DIR, "wallet.key");
5206
5227
  async function loadSavedWallet() {
5207
5228
  try {
5208
- const key = (await readFile2(WALLET_FILE, "utf-8")).trim();
5229
+ const key = (await readTextFile(WALLET_FILE)).trim();
5209
5230
  if (key.startsWith("0x") && key.length === 66) {
5210
5231
  console.log(`[ClawRouter] \u2713 Loaded existing wallet from ${WALLET_FILE}`);
5211
5232
  return key;
@@ -5226,7 +5247,7 @@ async function generateAndSaveWallet() {
5226
5247
  await mkdir2(WALLET_DIR, { recursive: true });
5227
5248
  await writeFile(WALLET_FILE, key + "\n", { mode: 384 });
5228
5249
  try {
5229
- const verification = (await readFile2(WALLET_FILE, "utf-8")).trim();
5250
+ const verification = (await readTextFile(WALLET_FILE)).trim();
5230
5251
  if (verification !== key) {
5231
5252
  throw new Error("Wallet file verification failed - content mismatch");
5232
5253
  }
@@ -5255,7 +5276,6 @@ async function resolveOrGenerateWalletKey() {
5255
5276
 
5256
5277
  // src/index.ts
5257
5278
  import {
5258
- readFileSync,
5259
5279
  writeFileSync,
5260
5280
  existsSync,
5261
5281
  readdirSync,
@@ -5362,7 +5382,7 @@ function injectModelsConfig(logger) {
5362
5382
  }
5363
5383
  if (existsSync(configPath)) {
5364
5384
  try {
5365
- const content = readFileSync(configPath, "utf-8").trim();
5385
+ const content = readTextFileSync(configPath).trim();
5366
5386
  if (content) {
5367
5387
  config = JSON.parse(content);
5368
5388
  } else {
@@ -5554,7 +5574,7 @@ function injectAuthProfile(logger) {
5554
5574
  };
5555
5575
  if (existsSync(authPath)) {
5556
5576
  try {
5557
- const existing = JSON.parse(readFileSync(authPath, "utf-8"));
5577
+ const existing = JSON.parse(readTextFileSync(authPath));
5558
5578
  if (existing.version && existing.profiles) {
5559
5579
  store = existing;
5560
5580
  }
@@ -5673,7 +5693,7 @@ async function createWalletCommand() {
5673
5693
  let address;
5674
5694
  try {
5675
5695
  if (existsSync(WALLET_FILE)) {
5676
- walletKey = readFileSync(WALLET_FILE, "utf-8").trim();
5696
+ walletKey = readTextFileSync(WALLET_FILE).trim();
5677
5697
  if (walletKey.startsWith("0x") && walletKey.length === 66) {
5678
5698
  const account = privateKeyToAccount4(walletKey);
5679
5699
  address = account.address;