@blockrun/clawrouter 0.12.251 → 0.12.252

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
@@ -105,7 +105,7 @@ openclaw gateway restart
105
105
 
106
106
  ### Option B — Standalone (continue.dev, Cursor, VS Code, any OpenAI-compatible client)
107
107
 
108
- > **Using Claude Code?** Check out [BRCC](https://blockrun.ai/brcc.md) — it's purpose-built for Claude Code with the same smart routing and x402 payments.
108
+ > **Using Claude Code?** Check out [BRCC](https://github.com/BlockRunAI/brcc) — it's purpose-built for Claude Code with the same smart routing and x402 payments.
109
109
  >
110
110
  > **Using NousResearch Hermes?** See [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) — a Python plugin that wires Hermes into the ClawRouter proxy. Same wallet, same <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models, same x402 USDC settlement on Base & Solana.
111
111
 
@@ -674,7 +674,7 @@ You're here. <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> mod
674
674
  </td>
675
675
  <td width="50%">
676
676
 
677
- ### 🤖 [BRCC](https://blockrun.ai/brcc.md)
677
+ ### 🤖 [BRCC](https://github.com/BlockRunAI/brcc)
678
678
 
679
679
  **BlockRun for Claude Code**
680
680
 
package/dist/cli.js CHANGED
@@ -89597,6 +89597,15 @@ function debrandSystemMessages(messages, resolvedModel) {
89597
89597
  });
89598
89598
  return hasChanges ? result : messages;
89599
89599
  }
89600
+ function isToolResultMessage(msg) {
89601
+ if (msg.role === "tool") return true;
89602
+ if (Array.isArray(msg.content)) {
89603
+ return msg.content.some(
89604
+ (b) => b && typeof b === "object" && b.type === "tool_result"
89605
+ );
89606
+ }
89607
+ return false;
89608
+ }
89600
89609
  function truncateMessages(messages) {
89601
89610
  if (!messages || messages.length <= MAX_MESSAGES) {
89602
89611
  return {
@@ -89609,7 +89618,11 @@ function truncateMessages(messages) {
89609
89618
  const systemMsgs = messages.filter((m) => m.role === "system");
89610
89619
  const conversationMsgs = messages.filter((m) => m.role !== "system");
89611
89620
  const maxConversation = MAX_MESSAGES - systemMsgs.length;
89612
- const truncatedConversation = conversationMsgs.slice(-maxConversation);
89621
+ let start = Math.max(0, conversationMsgs.length - maxConversation);
89622
+ while (start < conversationMsgs.length && isToolResultMessage(conversationMsgs[start])) {
89623
+ start++;
89624
+ }
89625
+ const truncatedConversation = conversationMsgs.slice(start);
89613
89626
  const result = [...systemMsgs, ...truncatedConversation];
89614
89627
  console.log(
89615
89628
  `[ClawRouter] Truncated messages: ${messages.length} \u2192 ${result.length} (kept ${systemMsgs.length} system + ${truncatedConversation.length} recent)`
@@ -90472,6 +90485,10 @@ async function startProxy(options) {
90472
90485
  }
90473
90486
  if (req.url === "/v1/images/image2image" && req.method === "POST") {
90474
90487
  const img2imgStartTime = Date.now();
90488
+ const clientAbort = new AbortController();
90489
+ res.on("close", () => {
90490
+ if (!res.writableEnded) clientAbort.abort();
90491
+ });
90475
90492
  const chunks = [];
90476
90493
  for await (const chunk of req) {
90477
90494
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -90487,7 +90504,7 @@ async function startProxy(options) {
90487
90504
  if (typeof val !== "string" || !val) continue;
90488
90505
  if (val.startsWith("data:")) {
90489
90506
  } else if (val.startsWith("https://") || val.startsWith("http://")) {
90490
- const imgResp = await fetch(val);
90507
+ const imgResp = await fetch(val, { signal: clientAbort.signal });
90491
90508
  if (!imgResp.ok)
90492
90509
  throw new Error(`Failed to download ${field} from ${val}: HTTP ${imgResp.status}`);
90493
90510
  const contentType = imgResp.headers.get("content-type") ?? "image/png";
@@ -90515,7 +90532,8 @@ async function startProxy(options) {
90515
90532
  const upstream = await payFetch(`${apiBase}/v1/images/image2image`, {
90516
90533
  method: "POST",
90517
90534
  headers: { "content-type": "application/json", "user-agent": USER_AGENT },
90518
- body: reqBody
90535
+ body: reqBody,
90536
+ signal: clientAbort.signal
90519
90537
  });
90520
90538
  const text = await upstream.text();
90521
90539
  if (!upstream.ok) {
@@ -90577,6 +90595,7 @@ async function startProxy(options) {
90577
90595
  res.writeHead(200, { "Content-Type": "application/json" });
90578
90596
  res.end(JSON.stringify(result));
90579
90597
  } catch (err) {
90598
+ if (clientAbort.signal.aborted) return;
90580
90599
  const msg = err instanceof Error ? err.message : String(err);
90581
90600
  console.error(`[ClawRouter] Image editing error: ${msg}`);
90582
90601
  if (!res.headersSent) {
package/dist/index.js CHANGED
@@ -89417,6 +89417,15 @@ function debrandSystemMessages(messages, resolvedModel) {
89417
89417
  });
89418
89418
  return hasChanges ? result : messages;
89419
89419
  }
89420
+ function isToolResultMessage(msg) {
89421
+ if (msg.role === "tool") return true;
89422
+ if (Array.isArray(msg.content)) {
89423
+ return msg.content.some(
89424
+ (b) => b && typeof b === "object" && b.type === "tool_result"
89425
+ );
89426
+ }
89427
+ return false;
89428
+ }
89420
89429
  function truncateMessages(messages) {
89421
89430
  if (!messages || messages.length <= MAX_MESSAGES) {
89422
89431
  return {
@@ -89429,7 +89438,11 @@ function truncateMessages(messages) {
89429
89438
  const systemMsgs = messages.filter((m) => m.role === "system");
89430
89439
  const conversationMsgs = messages.filter((m) => m.role !== "system");
89431
89440
  const maxConversation = MAX_MESSAGES - systemMsgs.length;
89432
- const truncatedConversation = conversationMsgs.slice(-maxConversation);
89441
+ let start = Math.max(0, conversationMsgs.length - maxConversation);
89442
+ while (start < conversationMsgs.length && isToolResultMessage(conversationMsgs[start])) {
89443
+ start++;
89444
+ }
89445
+ const truncatedConversation = conversationMsgs.slice(start);
89433
89446
  const result = [...systemMsgs, ...truncatedConversation];
89434
89447
  console.log(
89435
89448
  `[ClawRouter] Truncated messages: ${messages.length} \u2192 ${result.length} (kept ${systemMsgs.length} system + ${truncatedConversation.length} recent)`
@@ -90292,6 +90305,10 @@ async function startProxy(options) {
90292
90305
  }
90293
90306
  if (req.url === "/v1/images/image2image" && req.method === "POST") {
90294
90307
  const img2imgStartTime = Date.now();
90308
+ const clientAbort = new AbortController();
90309
+ res.on("close", () => {
90310
+ if (!res.writableEnded) clientAbort.abort();
90311
+ });
90295
90312
  const chunks = [];
90296
90313
  for await (const chunk of req) {
90297
90314
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
@@ -90307,7 +90324,7 @@ async function startProxy(options) {
90307
90324
  if (typeof val !== "string" || !val) continue;
90308
90325
  if (val.startsWith("data:")) {
90309
90326
  } else if (val.startsWith("https://") || val.startsWith("http://")) {
90310
- const imgResp = await fetch(val);
90327
+ const imgResp = await fetch(val, { signal: clientAbort.signal });
90311
90328
  if (!imgResp.ok)
90312
90329
  throw new Error(`Failed to download ${field} from ${val}: HTTP ${imgResp.status}`);
90313
90330
  const contentType = imgResp.headers.get("content-type") ?? "image/png";
@@ -90335,7 +90352,8 @@ async function startProxy(options) {
90335
90352
  const upstream = await payFetch(`${apiBase}/v1/images/image2image`, {
90336
90353
  method: "POST",
90337
90354
  headers: { "content-type": "application/json", "user-agent": USER_AGENT },
90338
- body: reqBody
90355
+ body: reqBody,
90356
+ signal: clientAbort.signal
90339
90357
  });
90340
90358
  const text = await upstream.text();
90341
90359
  if (!upstream.ok) {
@@ -90397,6 +90415,7 @@ async function startProxy(options) {
90397
90415
  res.writeHead(200, { "Content-Type": "application/json" });
90398
90416
  res.end(JSON.stringify(result));
90399
90417
  } catch (err) {
90418
+ if (clientAbort.signal.aborted) return;
90400
90419
  const msg = err instanceof Error ? err.message : String(err);
90401
90420
  console.error(`[ClawRouter] Image editing error: ${msg}`);
90402
90421
  if (!res.headersSent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/clawrouter",
3
- "version": "0.12.251",
3
+ "version": "0.12.252",
4
4
  "description": "Smart LLM router — save 88% on inference costs. 71 models (5 free), one wallet, x402 micropayments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",