@blockrun/clawrouter 0.9.3 → 0.9.5

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
@@ -509,8 +509,8 @@ type ProxyOptions = {
509
509
  */
510
510
  sessionConfig?: Partial<SessionConfig>;
511
511
  /**
512
- * Auto-compress large requests to fit within API limits.
513
- * When enabled, requests approaching 200KB are automatically compressed using
512
+ * Auto-compress large requests to reduce network usage.
513
+ * When enabled, requests are automatically compressed using
514
514
  * LLM-safe context compression (15-40% reduction).
515
515
  * Default: true
516
516
  */
@@ -521,11 +521,6 @@ type ProxyOptions = {
521
521
  * Set to 0 to compress all requests.
522
522
  */
523
523
  compressionThresholdKB?: number;
524
- /**
525
- * Maximum request size in KB after compression (default: 200).
526
- * Hard limit enforced by BlockRun API.
527
- */
528
- maxRequestSizeKB?: number;
529
524
  onReady?: (port: number) => void;
530
525
  onError?: (error: Error) => void;
531
526
  onPayment?: (info: {
@@ -586,7 +581,12 @@ declare const blockrunProvider: ProviderPlugin;
586
581
  declare const MODEL_ALIASES: Record<string, string>;
587
582
  /**
588
583
  * Resolve a model alias to its full model ID.
589
- * Returns the original model if not an alias.
584
+ * Also strips "blockrun/" prefix for direct model paths.
585
+ * Examples:
586
+ * - "claude" -> "anthropic/claude-sonnet-4" (alias)
587
+ * - "blockrun/claude" -> "anthropic/claude-sonnet-4" (alias with prefix)
588
+ * - "blockrun/anthropic/claude-sonnet-4" -> "anthropic/claude-sonnet-4" (prefix stripped)
589
+ * - "openai/gpt-4o" -> "openai/gpt-4o" (unchanged)
590
590
  */
591
591
  declare function resolveModelAlias(model: string): string;
592
592
  type BlockRunModel = {
@@ -678,7 +678,8 @@ declare class RequestDeduplicator {
678
678
  markInflight(key: string): void;
679
679
  /** Complete an in-flight request — cache result and notify waiters. */
680
680
  complete(key: string, result: CachedResponse): void;
681
- /** Remove an in-flight entry on error (don't cache failures). */
681
+ /** Remove an in-flight entry on error (don't cache failures).
682
+ * Also rejects any waiters so they can retry independently. */
682
683
  removeInflight(key: string): void;
683
684
  /** Prune expired completed entries. */
684
685
  private prune;
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ function resolveModelAlias(model) {
37
37
  const withoutPrefix = normalized.slice("blockrun/".length);
38
38
  const resolvedWithoutPrefix = MODEL_ALIASES[withoutPrefix];
39
39
  if (resolvedWithoutPrefix) return resolvedWithoutPrefix;
40
+ return withoutPrefix;
40
41
  }
41
42
  return model;
42
43
  }
@@ -2082,26 +2083,14 @@ var RequestDeduplicator = class {
2082
2083
  getInflight(key) {
2083
2084
  const entry = this.inflight.get(key);
2084
2085
  if (!entry) return void 0;
2085
- const promise = new Promise((resolve) => {
2086
- entry.waiters.push(
2087
- new Promise((r) => {
2088
- const orig = entry.resolve;
2089
- entry.resolve = (result) => {
2090
- orig(result);
2091
- resolve(result);
2092
- r(result);
2093
- };
2094
- })
2095
- );
2086
+ return new Promise((resolve) => {
2087
+ entry.resolvers.push(resolve);
2096
2088
  });
2097
- return promise;
2098
2089
  }
2099
2090
  /** Mark a request as in-flight. */
2100
2091
  markInflight(key) {
2101
2092
  this.inflight.set(key, {
2102
- resolve: () => {
2103
- },
2104
- waiters: []
2093
+ resolvers: []
2105
2094
  });
2106
2095
  }
2107
2096
  /** Complete an in-flight request — cache result and notify waiters. */
@@ -2111,14 +2100,33 @@ var RequestDeduplicator = class {
2111
2100
  }
2112
2101
  const entry = this.inflight.get(key);
2113
2102
  if (entry) {
2114
- entry.resolve(result);
2103
+ for (const resolve of entry.resolvers) {
2104
+ resolve(result);
2105
+ }
2115
2106
  this.inflight.delete(key);
2116
2107
  }
2117
2108
  this.prune();
2118
2109
  }
2119
- /** Remove an in-flight entry on error (don't cache failures). */
2110
+ /** Remove an in-flight entry on error (don't cache failures).
2111
+ * Also rejects any waiters so they can retry independently. */
2120
2112
  removeInflight(key) {
2121
- this.inflight.delete(key);
2113
+ const entry = this.inflight.get(key);
2114
+ if (entry) {
2115
+ const errorBody = Buffer.from(
2116
+ JSON.stringify({
2117
+ error: { message: "Original request failed, please retry", type: "dedup_origin_failed" }
2118
+ })
2119
+ );
2120
+ for (const resolve of entry.resolvers) {
2121
+ resolve({
2122
+ status: 503,
2123
+ headers: { "content-type": "application/json" },
2124
+ body: errorBody,
2125
+ completedAt: Date.now()
2126
+ });
2127
+ }
2128
+ this.inflight.delete(key);
2129
+ }
2122
2130
  }
2123
2131
  /** Prune expired completed entries. */
2124
2132
  prune() {
@@ -4026,7 +4034,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4026
4034
  }
4027
4035
  const autoCompress = options.autoCompressRequests ?? true;
4028
4036
  const compressionThreshold = options.compressionThresholdKB ?? 180;
4029
- const sizeLimit = options.maxRequestSizeKB ?? 200;
4030
4037
  const requestSizeKB = Math.ceil(body.length / 1024);
4031
4038
  if (autoCompress && requestSizeKB > compressionThreshold) {
4032
4039
  try {
@@ -4068,21 +4075,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4068
4075
  );
4069
4076
  parsed.messages = compressionResult.messages;
4070
4077
  body = Buffer.from(JSON.stringify(parsed));
4071
- if (compressedSizeKB > sizeLimit) {
4072
- const errorMsg = {
4073
- error: {
4074
- message: `Request size ${compressedSizeKB}KB still exceeds limit after compression (original: ${requestSizeKB}KB). Please reduce context size.`,
4075
- type: "request_too_large",
4076
- original_size_kb: requestSizeKB,
4077
- compressed_size_kb: compressedSizeKB,
4078
- limit_kb: sizeLimit,
4079
- help: "Try: 1) Remove old messages from history, 2) Summarize large tool results, 3) Use direct API for very large contexts"
4080
- }
4081
- };
4082
- res.writeHead(413, { "Content-Type": "application/json" });
4083
- res.end(JSON.stringify(errorMsg));
4084
- return;
4085
- }
4086
4078
  }
4087
4079
  } catch (err) {
4088
4080
  console.warn(
@@ -4090,21 +4082,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
4090
4082
  );
4091
4083
  }
4092
4084
  }
4093
- const finalSizeKB = Math.ceil(body.length / 1024);
4094
- if (finalSizeKB > sizeLimit) {
4095
- const errorMsg = {
4096
- error: {
4097
- message: `Request size ${finalSizeKB}KB exceeds limit ${sizeLimit}KB. Please reduce context size.`,
4098
- type: "request_too_large",
4099
- size_kb: finalSizeKB,
4100
- limit_kb: sizeLimit,
4101
- help: "Try: 1) Remove old messages from history, 2) Summarize large tool results, 3) Enable compression (autoCompressRequests: true)"
4102
- }
4103
- };
4104
- res.writeHead(413, { "Content-Type": "application/json" });
4105
- res.end(JSON.stringify(errorMsg));
4106
- return;
4107
- }
4108
4085
  const dedupKey = RequestDeduplicator.hash(body);
4109
4086
  const cached = deduplicator.getCached(dedupKey);
4110
4087
  if (cached) {