@blockrun/clawrouter 0.9.8 → 0.9.10

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.d.ts CHANGED
@@ -281,6 +281,91 @@ type RouterOptions = {
281
281
  */
282
282
  declare function route(prompt: string, systemPrompt: string | undefined, maxOutputTokens: number, options: RouterOptions): RoutingDecision;
283
283
 
284
+ /**
285
+ * Response Cache for LLM Completions
286
+ *
287
+ * Caches LLM responses by request hash (model + messages + params).
288
+ * Inspired by LiteLLM's caching system. Returns cached responses for
289
+ * identical requests, saving both cost and latency.
290
+ *
291
+ * Features:
292
+ * - TTL-based expiration (default 10 minutes)
293
+ * - LRU eviction when cache is full
294
+ * - Size limits per item (1MB max)
295
+ * - Heap-based expiration tracking for efficient pruning
296
+ */
297
+ type CachedLLMResponse = {
298
+ body: Buffer;
299
+ status: number;
300
+ headers: Record<string, string>;
301
+ model: string;
302
+ cachedAt: number;
303
+ expiresAt: number;
304
+ };
305
+ type ResponseCacheConfig = {
306
+ /** Maximum number of cached responses. Default: 200 */
307
+ maxSize?: number;
308
+ /** Default TTL in seconds. Default: 600 (10 minutes) */
309
+ defaultTTL?: number;
310
+ /** Maximum size per cached item in bytes. Default: 1MB */
311
+ maxItemSize?: number;
312
+ /** Enable/disable cache. Default: true */
313
+ enabled?: boolean;
314
+ };
315
+ declare class ResponseCache {
316
+ private cache;
317
+ private expirationHeap;
318
+ private config;
319
+ private stats;
320
+ constructor(config?: ResponseCacheConfig);
321
+ /**
322
+ * Generate cache key from request body.
323
+ * Hashes: model + messages + temperature + max_tokens + other params
324
+ */
325
+ static generateKey(body: Buffer | string): string;
326
+ /**
327
+ * Check if caching is enabled for this request.
328
+ * Respects cache control headers and request params.
329
+ */
330
+ shouldCache(body: Buffer | string, headers?: Record<string, string>): boolean;
331
+ /**
332
+ * Get cached response if available and not expired.
333
+ */
334
+ get(key: string): CachedLLMResponse | undefined;
335
+ /**
336
+ * Cache a response with optional custom TTL.
337
+ */
338
+ set(key: string, response: {
339
+ body: Buffer;
340
+ status: number;
341
+ headers: Record<string, string>;
342
+ model: string;
343
+ }, ttlSeconds?: number): void;
344
+ /**
345
+ * Evict expired and oldest entries to make room.
346
+ */
347
+ private evict;
348
+ /**
349
+ * Get cache statistics.
350
+ */
351
+ getStats(): {
352
+ size: number;
353
+ maxSize: number;
354
+ hits: number;
355
+ misses: number;
356
+ evictions: number;
357
+ hitRate: string;
358
+ };
359
+ /**
360
+ * Clear all cached entries.
361
+ */
362
+ clear(): void;
363
+ /**
364
+ * Check if cache is enabled.
365
+ */
366
+ isEnabled(): boolean;
367
+ }
368
+
284
369
  /**
285
370
  * Balance Monitor for ClawRouter
286
371
  *
@@ -521,6 +606,12 @@ type ProxyOptions = {
521
606
  * Set to 0 to compress all requests.
522
607
  */
523
608
  compressionThresholdKB?: number;
609
+ /**
610
+ * Response caching config. When enabled, identical requests return
611
+ * cached responses instead of making new API calls.
612
+ * Default: enabled with 10 minute TTL, 200 max entries.
613
+ */
614
+ cacheConfig?: ResponseCacheConfig;
524
615
  onReady?: (port: number) => void;
525
616
  onError?: (error: Error) => void;
526
617
  onPayment?: (info: {
@@ -917,4 +1008,4 @@ declare function formatStatsAscii(stats: AggregatedStats): string;
917
1008
 
918
1009
  declare const plugin: OpenClawPluginDefinition;
919
1010
 
920
- export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, type BalanceInfo, BalanceMonitor, type CachedPaymentParams, type CachedResponse, DEFAULT_RETRY_CONFIG, DEFAULT_ROUTING_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, EmptyWalletError, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, OPENCLAW_MODELS, PaymentCache, type PaymentFetchResult, type PreAuthParams, type ProxyHandle, type ProxyOptions, RequestDeduplicator, type RetryConfig, type RoutingConfig, type RoutingDecision, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SufficiencyResult, type Tier, type UsageEntry, blockrunProvider, buildProviderModels, calculateModelCost, createPaymentFetch, plugin as default, fetchWithRetry, formatStatsAscii, getAgenticModels, getFallbackChain, getFallbackChainFiltered, getModelContextWindow, getProxyPort, getSessionId, getStats, isAgenticModel, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, logUsage, resolveModelAlias, route, startProxy };
1011
+ export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, type BalanceInfo, BalanceMonitor, type CachedLLMResponse, type CachedPaymentParams, type CachedResponse, DEFAULT_RETRY_CONFIG, DEFAULT_ROUTING_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, EmptyWalletError, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, OPENCLAW_MODELS, PaymentCache, type PaymentFetchResult, type PreAuthParams, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, type RoutingConfig, type RoutingDecision, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SufficiencyResult, type Tier, type UsageEntry, blockrunProvider, buildProviderModels, calculateModelCost, createPaymentFetch, plugin as default, fetchWithRetry, formatStatsAscii, getAgenticModels, getFallbackChain, getFallbackChainFiltered, getModelContextWindow, getProxyPort, getSessionId, getStats, isAgenticModel, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, logUsage, resolveModelAlias, route, startProxy };
package/dist/index.js CHANGED
@@ -3,12 +3,16 @@ var MODEL_ALIASES = {
3
3
  // Claude
4
4
  claude: "anthropic/claude-sonnet-4",
5
5
  sonnet: "anthropic/claude-sonnet-4",
6
- opus: "anthropic/claude-opus-4",
6
+ opus: "anthropic/claude-opus-4.6",
7
+ // Updated to latest Opus 4.6
8
+ "opus-46": "anthropic/claude-opus-4.6",
9
+ "opus-45": "anthropic/claude-opus-4.5",
7
10
  haiku: "anthropic/claude-haiku-4.5",
8
11
  // OpenAI
9
12
  gpt: "openai/gpt-4o",
10
13
  gpt4: "openai/gpt-4o",
11
14
  gpt5: "openai/gpt-5.2",
15
+ codex: "openai/gpt-5.2-codex",
12
16
  mini: "openai/gpt-4o-mini",
13
17
  o3: "openai/o3",
14
18
  // DeepSeek
@@ -113,6 +117,16 @@ var BLOCKRUN_MODELS = [
113
117
  maxOutput: 128e3,
114
118
  reasoning: true
115
119
  },
120
+ // OpenAI Codex Family
121
+ {
122
+ id: "openai/gpt-5.2-codex",
123
+ name: "GPT-5.2 Codex",
124
+ inputPrice: 2.5,
125
+ outputPrice: 12,
126
+ contextWindow: 128e3,
127
+ maxOutput: 32e3,
128
+ agentic: true
129
+ },
116
130
  // OpenAI GPT-4 Family
117
131
  {
118
132
  id: "openai/gpt-4.1",
@@ -218,6 +232,17 @@ var BLOCKRUN_MODELS = [
218
232
  reasoning: true,
219
233
  agentic: true
220
234
  },
235
+ {
236
+ id: "anthropic/claude-opus-4.6",
237
+ name: "Claude Opus 4.6",
238
+ inputPrice: 5,
239
+ outputPrice: 25,
240
+ contextWindow: 2e5,
241
+ maxOutput: 64e3,
242
+ reasoning: true,
243
+ vision: true,
244
+ agentic: true
245
+ },
221
246
  // Google
222
247
  {
223
248
  id: "google/gemini-3-pro-preview",
@@ -1645,27 +1670,43 @@ var DEFAULT_ROUTING_CONFIG = {
1645
1670
  }
1646
1671
  },
1647
1672
  // Premium tier configs - best quality (blockrun/premium)
1648
- // kimi=coding, sonnet=reasoning/instructions, opus=heavy lifting/architecture/audits
1673
+ // codex=complex coding, kimi=simple coding, sonnet=reasoning/instructions, opus=architecture/PM/audits
1649
1674
  premiumTiers: {
1650
1675
  SIMPLE: {
1651
1676
  primary: "moonshot/kimi-k2.5",
1652
- // $0.50/$2.40 - good for coding
1677
+ // $0.50/$2.40 - good for simple coding
1653
1678
  fallback: ["anthropic/claude-haiku-4.5", "google/gemini-2.5-flash", "xai/grok-code-fast-1"]
1654
1679
  },
1655
1680
  MEDIUM: {
1656
1681
  primary: "anthropic/claude-sonnet-4",
1657
1682
  // $3/$15 - reasoning/instructions
1658
- fallback: ["moonshot/kimi-k2.5", "google/gemini-2.5-pro", "xai/grok-4-0709"]
1683
+ fallback: [
1684
+ "openai/gpt-5.2-codex",
1685
+ "moonshot/kimi-k2.5",
1686
+ "google/gemini-2.5-pro",
1687
+ "xai/grok-4-0709"
1688
+ ]
1659
1689
  },
1660
1690
  COMPLEX: {
1661
- primary: "anthropic/claude-opus-4.5",
1662
- // $5/$25 - architecture, audits, heavy lifting
1663
- fallback: ["anthropic/claude-sonnet-4", "google/gemini-3-pro-preview", "moonshot/kimi-k2.5"]
1691
+ primary: "openai/gpt-5.2-codex",
1692
+ // $2.50/$10 - complex coding (78% cost savings vs Opus)
1693
+ fallback: [
1694
+ "anthropic/claude-opus-4.6",
1695
+ "anthropic/claude-opus-4.5",
1696
+ "anthropic/claude-sonnet-4",
1697
+ "google/gemini-3-pro-preview",
1698
+ "moonshot/kimi-k2.5"
1699
+ ]
1664
1700
  },
1665
1701
  REASONING: {
1666
1702
  primary: "anthropic/claude-sonnet-4",
1667
1703
  // $3/$15 - best for reasoning/instructions
1668
- fallback: ["anthropic/claude-opus-4.5", "openai/o3", "xai/grok-4-1-fast-reasoning"]
1704
+ fallback: [
1705
+ "anthropic/claude-opus-4.6",
1706
+ "anthropic/claude-opus-4.5",
1707
+ "openai/o3",
1708
+ "xai/grok-4-1-fast-reasoning"
1709
+ ]
1669
1710
  }
1670
1711
  },
1671
1712
  // Agentic tier configs - models that excel at multi-step autonomous tasks
@@ -1687,7 +1728,7 @@ var DEFAULT_ROUTING_CONFIG = {
1687
1728
  COMPLEX: {
1688
1729
  primary: "anthropic/claude-sonnet-4",
1689
1730
  fallback: [
1690
- "anthropic/claude-opus-4.5",
1731
+ "anthropic/claude-opus-4.6",
1691
1732
  // Latest Opus - best agentic
1692
1733
  "openai/gpt-5.2",
1693
1734
  "google/gemini-3-pro-preview",
@@ -1698,7 +1739,7 @@ var DEFAULT_ROUTING_CONFIG = {
1698
1739
  primary: "anthropic/claude-sonnet-4",
1699
1740
  // Strong tool use + reasoning for agentic tasks
1700
1741
  fallback: [
1701
- "anthropic/claude-opus-4.5",
1742
+ "anthropic/claude-opus-4.6",
1702
1743
  "xai/grok-4-fast-reasoning",
1703
1744
  "moonshot/kimi-k2.5",
1704
1745
  "deepseek/deepseek-reasoner"
@@ -2128,6 +2169,203 @@ var RequestDeduplicator = class {
2128
2169
  }
2129
2170
  };
2130
2171
 
2172
+ // src/response-cache.ts
2173
+ import { createHash as createHash2 } from "crypto";
2174
+ var DEFAULT_CONFIG = {
2175
+ maxSize: 200,
2176
+ defaultTTL: 600,
2177
+ maxItemSize: 1048576,
2178
+ // 1MB
2179
+ enabled: true
2180
+ };
2181
+ function canonicalize2(obj) {
2182
+ if (obj === null || typeof obj !== "object") {
2183
+ return obj;
2184
+ }
2185
+ if (Array.isArray(obj)) {
2186
+ return obj.map(canonicalize2);
2187
+ }
2188
+ const sorted = {};
2189
+ for (const key of Object.keys(obj).sort()) {
2190
+ sorted[key] = canonicalize2(obj[key]);
2191
+ }
2192
+ return sorted;
2193
+ }
2194
+ var TIMESTAMP_PATTERN2 = /^\[\w{3}\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+\w+\]\s*/;
2195
+ function normalizeForCache(obj) {
2196
+ const result = {};
2197
+ for (const [key, value] of Object.entries(obj)) {
2198
+ if (["stream", "user", "request_id", "x-request-id"].includes(key)) {
2199
+ continue;
2200
+ }
2201
+ if (key === "messages" && Array.isArray(value)) {
2202
+ result[key] = value.map((msg) => {
2203
+ if (typeof msg === "object" && msg !== null) {
2204
+ const m = msg;
2205
+ if (typeof m.content === "string") {
2206
+ return { ...m, content: m.content.replace(TIMESTAMP_PATTERN2, "") };
2207
+ }
2208
+ }
2209
+ return msg;
2210
+ });
2211
+ } else {
2212
+ result[key] = value;
2213
+ }
2214
+ }
2215
+ return result;
2216
+ }
2217
+ var ResponseCache = class {
2218
+ cache = /* @__PURE__ */ new Map();
2219
+ expirationHeap = [];
2220
+ config;
2221
+ // Stats for monitoring
2222
+ stats = {
2223
+ hits: 0,
2224
+ misses: 0,
2225
+ evictions: 0
2226
+ };
2227
+ constructor(config = {}) {
2228
+ const filtered = Object.fromEntries(
2229
+ Object.entries(config).filter(([, v]) => v !== void 0)
2230
+ );
2231
+ this.config = { ...DEFAULT_CONFIG, ...filtered };
2232
+ }
2233
+ /**
2234
+ * Generate cache key from request body.
2235
+ * Hashes: model + messages + temperature + max_tokens + other params
2236
+ */
2237
+ static generateKey(body) {
2238
+ try {
2239
+ const parsed = JSON.parse(typeof body === "string" ? body : body.toString());
2240
+ const normalized = normalizeForCache(parsed);
2241
+ const canonical = canonicalize2(normalized);
2242
+ const keyContent = JSON.stringify(canonical);
2243
+ return createHash2("sha256").update(keyContent).digest("hex").slice(0, 32);
2244
+ } catch {
2245
+ const content = typeof body === "string" ? body : body.toString();
2246
+ return createHash2("sha256").update(content).digest("hex").slice(0, 32);
2247
+ }
2248
+ }
2249
+ /**
2250
+ * Check if caching is enabled for this request.
2251
+ * Respects cache control headers and request params.
2252
+ */
2253
+ shouldCache(body, headers) {
2254
+ if (!this.config.enabled) return false;
2255
+ if (headers?.["cache-control"]?.includes("no-cache")) {
2256
+ return false;
2257
+ }
2258
+ try {
2259
+ const parsed = JSON.parse(typeof body === "string" ? body : body.toString());
2260
+ if (parsed.cache === false || parsed.no_cache === true) {
2261
+ return false;
2262
+ }
2263
+ } catch {
2264
+ }
2265
+ return true;
2266
+ }
2267
+ /**
2268
+ * Get cached response if available and not expired.
2269
+ */
2270
+ get(key) {
2271
+ const entry = this.cache.get(key);
2272
+ if (!entry) {
2273
+ this.stats.misses++;
2274
+ return void 0;
2275
+ }
2276
+ if (Date.now() > entry.expiresAt) {
2277
+ this.cache.delete(key);
2278
+ this.stats.misses++;
2279
+ return void 0;
2280
+ }
2281
+ this.stats.hits++;
2282
+ return entry;
2283
+ }
2284
+ /**
2285
+ * Cache a response with optional custom TTL.
2286
+ */
2287
+ set(key, response, ttlSeconds) {
2288
+ if (!this.config.enabled || this.config.maxSize <= 0) return;
2289
+ if (response.body.length > this.config.maxItemSize) {
2290
+ console.log(`[ResponseCache] Skipping cache - item too large: ${response.body.length} bytes`);
2291
+ return;
2292
+ }
2293
+ if (response.status >= 400) {
2294
+ return;
2295
+ }
2296
+ if (this.cache.size >= this.config.maxSize) {
2297
+ this.evict();
2298
+ }
2299
+ const now = Date.now();
2300
+ const ttl = ttlSeconds ?? this.config.defaultTTL;
2301
+ const expiresAt = now + ttl * 1e3;
2302
+ const entry = {
2303
+ ...response,
2304
+ cachedAt: now,
2305
+ expiresAt
2306
+ };
2307
+ this.cache.set(key, entry);
2308
+ this.expirationHeap.push({ expiresAt, key });
2309
+ }
2310
+ /**
2311
+ * Evict expired and oldest entries to make room.
2312
+ */
2313
+ evict() {
2314
+ const now = Date.now();
2315
+ this.expirationHeap.sort((a, b) => a.expiresAt - b.expiresAt);
2316
+ while (this.expirationHeap.length > 0) {
2317
+ const oldest = this.expirationHeap[0];
2318
+ const entry = this.cache.get(oldest.key);
2319
+ if (!entry || entry.expiresAt !== oldest.expiresAt) {
2320
+ this.expirationHeap.shift();
2321
+ continue;
2322
+ }
2323
+ if (oldest.expiresAt <= now) {
2324
+ this.cache.delete(oldest.key);
2325
+ this.expirationHeap.shift();
2326
+ this.stats.evictions++;
2327
+ } else {
2328
+ break;
2329
+ }
2330
+ }
2331
+ while (this.cache.size >= this.config.maxSize && this.expirationHeap.length > 0) {
2332
+ const oldest = this.expirationHeap.shift();
2333
+ if (this.cache.has(oldest.key)) {
2334
+ this.cache.delete(oldest.key);
2335
+ this.stats.evictions++;
2336
+ }
2337
+ }
2338
+ }
2339
+ /**
2340
+ * Get cache statistics.
2341
+ */
2342
+ getStats() {
2343
+ const total = this.stats.hits + this.stats.misses;
2344
+ const hitRate = total > 0 ? (this.stats.hits / total * 100).toFixed(1) + "%" : "0%";
2345
+ return {
2346
+ size: this.cache.size,
2347
+ maxSize: this.config.maxSize,
2348
+ hits: this.stats.hits,
2349
+ misses: this.stats.misses,
2350
+ evictions: this.stats.evictions,
2351
+ hitRate
2352
+ };
2353
+ }
2354
+ /**
2355
+ * Clear all cached entries.
2356
+ */
2357
+ clear() {
2358
+ this.cache.clear();
2359
+ this.expirationHeap = [];
2360
+ }
2361
+ /**
2362
+ * Check if cache is enabled.
2363
+ */
2364
+ isEnabled() {
2365
+ return this.config.enabled;
2366
+ }
2367
+ };
2368
+
2131
2369
  // src/balance.ts
2132
2370
  import { createPublicClient, http, erc20Abi } from "viem";
2133
2371
  import { base } from "viem/chains";
@@ -3631,6 +3869,7 @@ async function startProxy(options) {
3631
3869
  modelPricing
3632
3870
  };
3633
3871
  const deduplicator = new RequestDeduplicator();
3872
+ const responseCache = new ResponseCache(options.cacheConfig);
3634
3873
  const sessionStore = new SessionStore(options.sessionConfig);
3635
3874
  const connections = /* @__PURE__ */ new Set();
3636
3875
  const server = createServer(async (req, res) => {
@@ -3671,6 +3910,15 @@ async function startProxy(options) {
3671
3910
  res.end(JSON.stringify(response));
3672
3911
  return;
3673
3912
  }
3913
+ if (req.url === "/cache" || req.url?.startsWith("/cache?")) {
3914
+ const stats = responseCache.getStats();
3915
+ res.writeHead(200, {
3916
+ "Content-Type": "application/json",
3917
+ "Cache-Control": "no-cache"
3918
+ });
3919
+ res.end(JSON.stringify(stats, null, 2));
3920
+ return;
3921
+ }
3674
3922
  if (req.url === "/stats" || req.url?.startsWith("/stats?")) {
3675
3923
  try {
3676
3924
  const url = new URL(req.url, "http://localhost");
@@ -3717,7 +3965,8 @@ async function startProxy(options) {
3717
3965
  routerOpts,
3718
3966
  deduplicator,
3719
3967
  balanceMonitor,
3720
- sessionStore
3968
+ sessionStore,
3969
+ responseCache
3721
3970
  );
3722
3971
  } catch (err) {
3723
3972
  const error = err instanceof Error ? err : new Error(String(err));
@@ -3918,7 +4167,7 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
3918
4167
  };
3919
4168
  }
3920
4169
  }
3921
- async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator, balanceMonitor, sessionStore) {
4170
+ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator, balanceMonitor, sessionStore, responseCache) {
3922
4171
  const startTime = Date.now();
3923
4172
  const upstreamUrl = `${apiBase}${req.url}`;
3924
4173
  const bodyChunks = [];
@@ -4086,6 +4335,20 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4086
4335
  );
4087
4336
  }
4088
4337
  }
4338
+ const cacheKey = ResponseCache.generateKey(body);
4339
+ const reqHeaders = {};
4340
+ for (const [key, value] of Object.entries(req.headers)) {
4341
+ if (typeof value === "string") reqHeaders[key] = value;
4342
+ }
4343
+ if (responseCache.shouldCache(body, reqHeaders)) {
4344
+ const cachedResponse = responseCache.get(cacheKey);
4345
+ if (cachedResponse) {
4346
+ console.log(`[ClawRouter] Cache HIT for ${cachedResponse.model} (saved API call)`);
4347
+ res.writeHead(cachedResponse.status, cachedResponse.headers);
4348
+ res.end(cachedResponse.body);
4349
+ return;
4350
+ }
4351
+ }
4089
4352
  const dedupKey = RequestDeduplicator.hash(body);
4090
4353
  const cached = deduplicator.getCached(dedupKey);
4091
4354
  if (cached) {
@@ -4438,12 +4701,22 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4438
4701
  }
4439
4702
  }
4440
4703
  res.end();
4704
+ const responseBody = Buffer.concat(responseChunks);
4441
4705
  deduplicator.complete(dedupKey, {
4442
4706
  status: upstream.status,
4443
4707
  headers: responseHeaders,
4444
- body: Buffer.concat(responseChunks),
4708
+ body: responseBody,
4445
4709
  completedAt: Date.now()
4446
4710
  });
4711
+ if (upstream.status === 200 && responseCache.shouldCache(body)) {
4712
+ responseCache.set(cacheKey, {
4713
+ body: responseBody,
4714
+ status: upstream.status,
4715
+ headers: responseHeaders,
4716
+ model: modelId
4717
+ });
4718
+ console.log(`[ClawRouter] Cached response for ${modelId} (${responseBody.length} bytes)`);
4719
+ }
4447
4720
  }
4448
4721
  if (estimatedCostMicros !== void 0) {
4449
4722
  balanceMonitor.deductEstimated(estimatedCostMicros);
@@ -4750,9 +5023,9 @@ function injectModelsConfig(logger) {
4750
5023
  { id: "eco", alias: "eco" },
4751
5024
  { id: "premium", alias: "premium" },
4752
5025
  { id: "free", alias: "free" },
4753
- { id: "sonnet", alias: "sonnet" },
4754
- { id: "opus", alias: "opus" },
4755
- { id: "haiku", alias: "haiku" },
5026
+ { id: "sonnet", alias: "br-sonnet" },
5027
+ { id: "opus", alias: "br-opus" },
5028
+ { id: "haiku", alias: "br-haiku" },
4756
5029
  { id: "gpt5", alias: "gpt5" },
4757
5030
  { id: "mini", alias: "mini" },
4758
5031
  { id: "grok-fast", alias: "grok-fast" },
@@ -4778,9 +5051,13 @@ function injectModelsConfig(logger) {
4778
5051
  }
4779
5052
  for (const m of KEY_MODEL_ALIASES) {
4780
5053
  const fullId = `blockrun/${m.id}`;
4781
- if (!allowlist[fullId]) {
5054
+ const existing = allowlist[fullId];
5055
+ if (!existing) {
4782
5056
  allowlist[fullId] = { alias: m.alias };
4783
5057
  needsWrite = true;
5058
+ } else if (existing.alias !== m.alias) {
5059
+ existing.alias = m.alias;
5060
+ needsWrite = true;
4784
5061
  }
4785
5062
  }
4786
5063
  if (needsWrite) {
@@ -5114,6 +5391,7 @@ export {
5114
5391
  OPENCLAW_MODELS,
5115
5392
  PaymentCache,
5116
5393
  RequestDeduplicator,
5394
+ ResponseCache,
5117
5395
  RpcError,
5118
5396
  SessionStore,
5119
5397
  blockrunProvider,