@blockrun/clawrouter 0.10.12 → 0.10.13

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.
@@ -0,0 +1,1095 @@
1
+ /**
2
+ * OpenClaw Plugin Types (locally defined)
3
+ *
4
+ * OpenClaw's plugin SDK uses duck typing — these match the shapes
5
+ * expected by registerProvider() and the plugin system.
6
+ * Defined locally to avoid depending on internal OpenClaw paths.
7
+ */
8
+ type ModelApi = "openai-completions" | "openai-responses" | "anthropic-messages" | "google-generative-ai" | "github-copilot" | "bedrock-converse-stream";
9
+ type ModelDefinitionConfig = {
10
+ id: string;
11
+ name: string;
12
+ api?: ModelApi;
13
+ reasoning: boolean;
14
+ input: Array<"text" | "image">;
15
+ cost: {
16
+ input: number;
17
+ output: number;
18
+ cacheRead: number;
19
+ cacheWrite: number;
20
+ };
21
+ contextWindow: number;
22
+ maxTokens: number;
23
+ headers?: Record<string, string>;
24
+ };
25
+ type ModelProviderConfig = {
26
+ baseUrl: string;
27
+ apiKey?: string;
28
+ api?: ModelApi;
29
+ headers?: Record<string, string>;
30
+ authHeader?: boolean;
31
+ models: ModelDefinitionConfig[];
32
+ };
33
+ type AuthProfileCredential = {
34
+ apiKey?: string;
35
+ type?: string;
36
+ [key: string]: unknown;
37
+ };
38
+ type ProviderAuthResult = {
39
+ profiles: Array<{
40
+ profileId: string;
41
+ credential: AuthProfileCredential;
42
+ }>;
43
+ configPatch?: Record<string, unknown>;
44
+ defaultModel?: string;
45
+ notes?: string[];
46
+ };
47
+ type WizardPrompter = {
48
+ text: (opts: {
49
+ message: string;
50
+ validate?: (value: string) => string | undefined;
51
+ }) => Promise<string | symbol>;
52
+ note: (message: string) => void;
53
+ progress: (message: string) => {
54
+ stop: (message?: string) => void;
55
+ };
56
+ };
57
+ type ProviderAuthContext = {
58
+ config: Record<string, unknown>;
59
+ agentDir?: string;
60
+ workspaceDir?: string;
61
+ prompter: WizardPrompter;
62
+ runtime: {
63
+ log: (message: string) => void;
64
+ };
65
+ isRemote: boolean;
66
+ openUrl: (url: string) => Promise<void>;
67
+ };
68
+ type ProviderAuthMethod = {
69
+ id: string;
70
+ label: string;
71
+ hint?: string;
72
+ kind: "oauth" | "api_key" | "token" | "device_code" | "custom";
73
+ run: (ctx: ProviderAuthContext) => Promise<ProviderAuthResult>;
74
+ };
75
+ type ProviderPlugin = {
76
+ id: string;
77
+ label: string;
78
+ docsPath?: string;
79
+ aliases?: string[];
80
+ envVars?: string[];
81
+ models?: ModelProviderConfig;
82
+ auth: ProviderAuthMethod[];
83
+ formatApiKey?: (cred: AuthProfileCredential) => string;
84
+ };
85
+ type PluginLogger = {
86
+ debug?: (message: string) => void;
87
+ info: (message: string) => void;
88
+ warn: (message: string) => void;
89
+ error: (message: string) => void;
90
+ };
91
+ type OpenClawPluginService = {
92
+ id: string;
93
+ start: () => void | Promise<void>;
94
+ stop?: () => void | Promise<void>;
95
+ };
96
+ type OpenClawPluginApi = {
97
+ id: string;
98
+ name: string;
99
+ version?: string;
100
+ description?: string;
101
+ source: string;
102
+ config: Record<string, unknown> & {
103
+ models?: {
104
+ providers?: Record<string, ModelProviderConfig>;
105
+ };
106
+ agents?: Record<string, unknown>;
107
+ };
108
+ pluginConfig?: Record<string, unknown>;
109
+ logger: PluginLogger;
110
+ registerProvider: (provider: ProviderPlugin) => void;
111
+ registerTool: (tool: unknown, opts?: unknown) => void;
112
+ registerHook: (events: string | string[], handler: unknown, opts?: unknown) => void;
113
+ registerHttpRoute: (params: {
114
+ path: string;
115
+ handler: unknown;
116
+ }) => void;
117
+ registerService: (service: OpenClawPluginService) => void;
118
+ registerCommand: (command: unknown) => void;
119
+ resolvePath: (input: string) => string;
120
+ on: (hookName: string, handler: unknown, opts?: unknown) => void;
121
+ };
122
+ type OpenClawPluginDefinition = {
123
+ id?: string;
124
+ name?: string;
125
+ description?: string;
126
+ version?: string;
127
+ register?: (api: OpenClawPluginApi) => void | Promise<void>;
128
+ activate?: (api: OpenClawPluginApi) => void | Promise<void>;
129
+ };
130
+
131
+ /**
132
+ * Smart Router Types
133
+ *
134
+ * Four classification tiers — REASONING is distinct from COMPLEX because
135
+ * reasoning tasks need different models (o3, gemini-pro) than general
136
+ * complex tasks (gpt-4o, sonnet-4).
137
+ *
138
+ * Scoring uses weighted float dimensions with sigmoid confidence calibration.
139
+ */
140
+ type Tier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
141
+ type RoutingDecision = {
142
+ model: string;
143
+ tier: Tier;
144
+ confidence: number;
145
+ method: "rules" | "llm";
146
+ reasoning: string;
147
+ costEstimate: number;
148
+ baselineCost: number;
149
+ savings: number;
150
+ agenticScore?: number;
151
+ };
152
+ type TierConfig = {
153
+ primary: string;
154
+ fallback: string[];
155
+ };
156
+ type ScoringConfig = {
157
+ tokenCountThresholds: {
158
+ simple: number;
159
+ complex: number;
160
+ };
161
+ codeKeywords: string[];
162
+ reasoningKeywords: string[];
163
+ simpleKeywords: string[];
164
+ technicalKeywords: string[];
165
+ creativeKeywords: string[];
166
+ imperativeVerbs: string[];
167
+ constraintIndicators: string[];
168
+ outputFormatKeywords: string[];
169
+ referenceKeywords: string[];
170
+ negationKeywords: string[];
171
+ domainSpecificKeywords: string[];
172
+ agenticTaskKeywords: string[];
173
+ dimensionWeights: Record<string, number>;
174
+ tierBoundaries: {
175
+ simpleMedium: number;
176
+ mediumComplex: number;
177
+ complexReasoning: number;
178
+ };
179
+ confidenceSteepness: number;
180
+ confidenceThreshold: number;
181
+ };
182
+ type ClassifierConfig = {
183
+ llmModel: string;
184
+ llmMaxTokens: number;
185
+ llmTemperature: number;
186
+ promptTruncationChars: number;
187
+ cacheTtlMs: number;
188
+ };
189
+ type OverridesConfig = {
190
+ maxTokensForceComplex: number;
191
+ structuredOutputMinTier: Tier;
192
+ ambiguousDefaultTier: Tier;
193
+ /**
194
+ * When enabled, prefer models optimized for agentic workflows.
195
+ * Agentic models continue autonomously with multi-step tasks
196
+ * instead of stopping and waiting for user input.
197
+ */
198
+ agenticMode?: boolean;
199
+ };
200
+ type RoutingConfig = {
201
+ version: string;
202
+ classifier: ClassifierConfig;
203
+ scoring: ScoringConfig;
204
+ tiers: Record<Tier, TierConfig>;
205
+ /** Tier configs for agentic mode - models that excel at multi-step tasks */
206
+ agenticTiers?: Record<Tier, TierConfig>;
207
+ /** Tier configs for eco profile - ultra cost-optimized (blockrun/eco) */
208
+ ecoTiers?: Record<Tier, TierConfig>;
209
+ /** Tier configs for premium profile - best quality (blockrun/premium) */
210
+ premiumTiers?: Record<Tier, TierConfig>;
211
+ overrides: OverridesConfig;
212
+ };
213
+
214
+ /**
215
+ * Tier → Model Selection
216
+ *
217
+ * Maps a classification tier to the cheapest capable model.
218
+ * Builds RoutingDecision metadata with cost estimates and savings.
219
+ */
220
+
221
+ type ModelPricing = {
222
+ inputPrice: number;
223
+ outputPrice: number;
224
+ };
225
+ /**
226
+ * Get the ordered fallback chain for a tier: [primary, ...fallbacks].
227
+ */
228
+ declare function getFallbackChain(tier: Tier, tierConfigs: Record<Tier, TierConfig>): string[];
229
+ /**
230
+ * Calculate cost for a specific model (used when fallback model is used).
231
+ * Returns updated cost fields for RoutingDecision.
232
+ */
233
+ declare function calculateModelCost(model: string, modelPricing: Map<string, ModelPricing>, estimatedInputTokens: number, maxOutputTokens: number, routingProfile?: "free" | "eco" | "auto" | "premium"): {
234
+ costEstimate: number;
235
+ baselineCost: number;
236
+ savings: number;
237
+ };
238
+ /**
239
+ * Get the fallback chain filtered by context length.
240
+ * Only returns models that can handle the estimated total context.
241
+ *
242
+ * @param tier - The tier to get fallback chain for
243
+ * @param tierConfigs - Tier configurations
244
+ * @param estimatedTotalTokens - Estimated total context (input + output)
245
+ * @param getContextWindow - Function to get context window for a model ID
246
+ * @returns Filtered list of models that can handle the context
247
+ */
248
+ declare function getFallbackChainFiltered(tier: Tier, tierConfigs: Record<Tier, TierConfig>, estimatedTotalTokens: number, getContextWindow: (modelId: string) => number | undefined): string[];
249
+
250
+ /**
251
+ * Default Routing Config
252
+ *
253
+ * All routing parameters as a TypeScript constant.
254
+ * Operators override via openclaw.yaml plugin config.
255
+ *
256
+ * Scoring uses 14 weighted dimensions with sigmoid confidence calibration.
257
+ */
258
+
259
+ declare const DEFAULT_ROUTING_CONFIG: RoutingConfig;
260
+
261
+ /**
262
+ * Smart Router Entry Point
263
+ *
264
+ * Classifies requests and routes to the cheapest capable model.
265
+ * 100% local — rules-based scoring handles all requests in <1ms.
266
+ * Ambiguous cases default to configurable tier (MEDIUM by default).
267
+ */
268
+
269
+ type RouterOptions = {
270
+ config: RoutingConfig;
271
+ modelPricing: Map<string, ModelPricing>;
272
+ routingProfile?: "free" | "eco" | "auto" | "premium";
273
+ };
274
+ /**
275
+ * Route a request to the cheapest capable model.
276
+ *
277
+ * 1. Check overrides (large context, structured output)
278
+ * 2. Run rule-based classifier (14 weighted dimensions, <1ms)
279
+ * 3. If ambiguous, default to configurable tier (no external API calls)
280
+ * 4. Select model for tier
281
+ * 5. Return RoutingDecision with metadata
282
+ */
283
+ declare function route(prompt: string, systemPrompt: string | undefined, maxOutputTokens: number, options: RouterOptions): RoutingDecision;
284
+
285
+ /**
286
+ * Response Cache for LLM Completions
287
+ *
288
+ * Caches LLM responses by request hash (model + messages + params).
289
+ * Inspired by LiteLLM's caching system. Returns cached responses for
290
+ * identical requests, saving both cost and latency.
291
+ *
292
+ * Features:
293
+ * - TTL-based expiration (default 10 minutes)
294
+ * - LRU eviction when cache is full
295
+ * - Size limits per item (1MB max)
296
+ * - Heap-based expiration tracking for efficient pruning
297
+ */
298
+ type CachedLLMResponse = {
299
+ body: Buffer;
300
+ status: number;
301
+ headers: Record<string, string>;
302
+ model: string;
303
+ cachedAt: number;
304
+ expiresAt: number;
305
+ };
306
+ type ResponseCacheConfig = {
307
+ /** Maximum number of cached responses. Default: 200 */
308
+ maxSize?: number;
309
+ /** Default TTL in seconds. Default: 600 (10 minutes) */
310
+ defaultTTL?: number;
311
+ /** Maximum size per cached item in bytes. Default: 1MB */
312
+ maxItemSize?: number;
313
+ /** Enable/disable cache. Default: true */
314
+ enabled?: boolean;
315
+ };
316
+ declare class ResponseCache {
317
+ private cache;
318
+ private expirationHeap;
319
+ private config;
320
+ private stats;
321
+ constructor(config?: ResponseCacheConfig);
322
+ /**
323
+ * Generate cache key from request body.
324
+ * Hashes: model + messages + temperature + max_tokens + other params
325
+ */
326
+ static generateKey(body: Buffer | string): string;
327
+ /**
328
+ * Check if caching is enabled for this request.
329
+ * Respects cache control headers and request params.
330
+ */
331
+ shouldCache(body: Buffer | string, headers?: Record<string, string>): boolean;
332
+ /**
333
+ * Get cached response if available and not expired.
334
+ */
335
+ get(key: string): CachedLLMResponse | undefined;
336
+ /**
337
+ * Cache a response with optional custom TTL.
338
+ */
339
+ set(key: string, response: {
340
+ body: Buffer;
341
+ status: number;
342
+ headers: Record<string, string>;
343
+ model: string;
344
+ }, ttlSeconds?: number): void;
345
+ /**
346
+ * Evict expired and oldest entries to make room.
347
+ */
348
+ private evict;
349
+ /**
350
+ * Get cache statistics.
351
+ */
352
+ getStats(): {
353
+ size: number;
354
+ maxSize: number;
355
+ hits: number;
356
+ misses: number;
357
+ evictions: number;
358
+ hitRate: string;
359
+ };
360
+ /**
361
+ * Clear all cached entries.
362
+ */
363
+ clear(): void;
364
+ /**
365
+ * Check if cache is enabled.
366
+ */
367
+ isEnabled(): boolean;
368
+ }
369
+
370
+ /**
371
+ * Balance Monitor for ClawRouter
372
+ *
373
+ * Monitors USDC balance on Base network with intelligent caching.
374
+ * Provides pre-request balance checks to prevent failed payments.
375
+ *
376
+ * Caching Strategy:
377
+ * - TTL: 30 seconds (balance is cached to avoid excessive RPC calls)
378
+ * - Optimistic deduction: after successful payment, subtract estimated cost from cache
379
+ * - Invalidation: on payment failure, immediately refresh from RPC
380
+ */
381
+ /** Balance thresholds in USDC smallest unit (6 decimals) */
382
+ declare const BALANCE_THRESHOLDS: {
383
+ /** Low balance warning threshold: $1.00 */
384
+ readonly LOW_BALANCE_MICROS: 1000000n;
385
+ /** Effectively zero threshold: $0.0001 (covers dust/rounding) */
386
+ readonly ZERO_THRESHOLD: 100n;
387
+ };
388
+ /** Balance information returned by checkBalance() */
389
+ type BalanceInfo = {
390
+ /** Raw balance in USDC smallest unit (6 decimals) */
391
+ balance: bigint;
392
+ /** Formatted balance as "$X.XX" */
393
+ balanceUSD: string;
394
+ /** True if balance < $1.00 */
395
+ isLow: boolean;
396
+ /** True if balance < $0.0001 (effectively zero) */
397
+ isEmpty: boolean;
398
+ /** Wallet address for funding instructions */
399
+ walletAddress: string;
400
+ };
401
+ /** Result from checkSufficient() */
402
+ type SufficiencyResult = {
403
+ /** True if balance >= estimated cost */
404
+ sufficient: boolean;
405
+ /** Current balance info */
406
+ info: BalanceInfo;
407
+ /** If insufficient, the shortfall as "$X.XX" */
408
+ shortfall?: string;
409
+ };
410
+ /**
411
+ * Monitors USDC balance on Base network.
412
+ *
413
+ * Usage:
414
+ * const monitor = new BalanceMonitor("0x...");
415
+ * const info = await monitor.checkBalance();
416
+ * if (info.isLow) console.warn("Low balance!");
417
+ */
418
+ declare class BalanceMonitor {
419
+ private readonly client;
420
+ private readonly walletAddress;
421
+ /** Cached balance (null = not yet fetched) */
422
+ private cachedBalance;
423
+ /** Timestamp when cache was last updated */
424
+ private cachedAt;
425
+ constructor(walletAddress: string);
426
+ /**
427
+ * Check current USDC balance.
428
+ * Uses cache if valid, otherwise fetches from RPC.
429
+ */
430
+ checkBalance(): Promise<BalanceInfo>;
431
+ /**
432
+ * Check if balance is sufficient for an estimated cost.
433
+ *
434
+ * @param estimatedCostMicros - Estimated cost in USDC smallest unit (6 decimals)
435
+ */
436
+ checkSufficient(estimatedCostMicros: bigint): Promise<SufficiencyResult>;
437
+ /**
438
+ * Optimistically deduct estimated cost from cached balance.
439
+ * Call this after a successful payment to keep cache accurate.
440
+ *
441
+ * @param amountMicros - Amount to deduct in USDC smallest unit
442
+ */
443
+ deductEstimated(amountMicros: bigint): void;
444
+ /**
445
+ * Invalidate cache, forcing next checkBalance() to fetch from RPC.
446
+ * Call this after a payment failure to get accurate balance.
447
+ */
448
+ invalidate(): void;
449
+ /**
450
+ * Force refresh balance from RPC (ignores cache).
451
+ */
452
+ refresh(): Promise<BalanceInfo>;
453
+ /**
454
+ * Format USDC amount (in micros) as "$X.XX".
455
+ */
456
+ formatUSDC(amountMicros: bigint): string;
457
+ /**
458
+ * Get the wallet address being monitored.
459
+ */
460
+ getWalletAddress(): string;
461
+ /** Fetch balance from RPC */
462
+ private fetchBalance;
463
+ /** Build BalanceInfo from raw balance */
464
+ private buildInfo;
465
+ }
466
+
467
+ /**
468
+ * Session Persistence Store
469
+ *
470
+ * Tracks model selections per session to prevent model switching mid-task.
471
+ * When a session is active, the router will continue using the same model
472
+ * instead of re-routing each request.
473
+ */
474
+ type SessionEntry = {
475
+ model: string;
476
+ tier: string;
477
+ createdAt: number;
478
+ lastUsedAt: number;
479
+ requestCount: number;
480
+ };
481
+ type SessionConfig = {
482
+ /** Enable session persistence (default: false) */
483
+ enabled: boolean;
484
+ /** Session timeout in ms (default: 30 minutes) */
485
+ timeoutMs: number;
486
+ /** Header name for session ID (default: X-Session-ID) */
487
+ headerName: string;
488
+ };
489
+ declare const DEFAULT_SESSION_CONFIG: SessionConfig;
490
+ /**
491
+ * Session persistence store for maintaining model selections.
492
+ */
493
+ declare class SessionStore {
494
+ private sessions;
495
+ private config;
496
+ private cleanupInterval;
497
+ constructor(config?: Partial<SessionConfig>);
498
+ /**
499
+ * Get the pinned model for a session, if any.
500
+ */
501
+ getSession(sessionId: string): SessionEntry | undefined;
502
+ /**
503
+ * Pin a model to a session.
504
+ */
505
+ setSession(sessionId: string, model: string, tier: string): void;
506
+ /**
507
+ * Touch a session to extend its timeout.
508
+ */
509
+ touchSession(sessionId: string): void;
510
+ /**
511
+ * Clear a specific session.
512
+ */
513
+ clearSession(sessionId: string): void;
514
+ /**
515
+ * Clear all sessions.
516
+ */
517
+ clearAll(): void;
518
+ /**
519
+ * Get session stats for debugging.
520
+ */
521
+ getStats(): {
522
+ count: number;
523
+ sessions: Array<{
524
+ id: string;
525
+ model: string;
526
+ age: number;
527
+ }>;
528
+ };
529
+ /**
530
+ * Clean up expired sessions.
531
+ */
532
+ private cleanup;
533
+ /**
534
+ * Stop the cleanup interval.
535
+ */
536
+ close(): void;
537
+ }
538
+ /**
539
+ * Generate a session ID from request headers or create a default.
540
+ */
541
+ declare function getSessionId(headers: Record<string, string | string[] | undefined>, headerName?: string): string | undefined;
542
+
543
+ /**
544
+ * Local x402 Proxy Server
545
+ *
546
+ * Sits between OpenClaw's pi-ai (which makes standard OpenAI-format requests)
547
+ * and BlockRun's API (which requires x402 micropayments).
548
+ *
549
+ * Flow:
550
+ * pi-ai → http://localhost:{port}/v1/chat/completions
551
+ * → proxy forwards to https://blockrun.ai/api/v1/chat/completions
552
+ * → gets 402 → @x402/fetch signs payment → retries
553
+ * → streams response back to pi-ai
554
+ *
555
+ * Optimizations (v0.3.0):
556
+ * - SSE heartbeat: for streaming requests, sends headers + heartbeat immediately
557
+ * before the x402 flow, preventing OpenClaw's 10-15s timeout from firing.
558
+ * - Response dedup: hashes request bodies and caches responses for 30s,
559
+ * preventing double-charging when OpenClaw retries after timeout.
560
+ * - Payment cache: after first 402, pre-signs subsequent requests to skip
561
+ * the 402 round trip (~200ms savings per request).
562
+ * - Smart routing: when model is "blockrun/auto", classify query and pick cheapest model.
563
+ * - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/
564
+ */
565
+
566
+ /**
567
+ * Get the proxy port from pre-loaded configuration.
568
+ * Port is validated at module load time, this just returns the cached value.
569
+ */
570
+ declare function getProxyPort(): number;
571
+ /** Callback info for low balance warning */
572
+ type LowBalanceInfo = {
573
+ balanceUSD: string;
574
+ walletAddress: string;
575
+ };
576
+ /** Callback info for insufficient funds error */
577
+ type InsufficientFundsInfo = {
578
+ balanceUSD: string;
579
+ requiredUSD: string;
580
+ walletAddress: string;
581
+ };
582
+ type ProxyOptions = {
583
+ walletKey: string;
584
+ apiBase?: string;
585
+ /** Port to listen on (default: 8402) */
586
+ port?: number;
587
+ routingConfig?: Partial<RoutingConfig>;
588
+ /** Request timeout in ms (default: 180000 = 3 minutes). Covers on-chain tx + LLM response. */
589
+ requestTimeoutMs?: number;
590
+ /** Skip balance checks (for testing only). Default: false */
591
+ skipBalanceCheck?: boolean;
592
+ /**
593
+ * Session persistence config. When enabled, maintains model selection
594
+ * across requests within a session to prevent mid-task model switching.
595
+ */
596
+ sessionConfig?: Partial<SessionConfig>;
597
+ /**
598
+ * Auto-compress large requests to reduce network usage.
599
+ * When enabled, requests are automatically compressed using
600
+ * LLM-safe context compression (15-40% reduction).
601
+ * Default: true
602
+ */
603
+ autoCompressRequests?: boolean;
604
+ /**
605
+ * Threshold in KB to trigger auto-compression (default: 180).
606
+ * Requests larger than this are compressed before sending.
607
+ * Set to 0 to compress all requests.
608
+ */
609
+ compressionThresholdKB?: number;
610
+ /**
611
+ * Response caching config. When enabled, identical requests return
612
+ * cached responses instead of making new API calls.
613
+ * Default: enabled with 10 minute TTL, 200 max entries.
614
+ */
615
+ cacheConfig?: ResponseCacheConfig;
616
+ onReady?: (port: number) => void;
617
+ onError?: (error: Error) => void;
618
+ onPayment?: (info: {
619
+ model: string;
620
+ amount: string;
621
+ network: string;
622
+ }) => void;
623
+ onRouted?: (decision: RoutingDecision) => void;
624
+ /** Called when balance drops below $1.00 (warning, request still proceeds) */
625
+ onLowBalance?: (info: LowBalanceInfo) => void;
626
+ /** Called when balance is insufficient for a request (request fails) */
627
+ onInsufficientFunds?: (info: InsufficientFundsInfo) => void;
628
+ };
629
+ type ProxyHandle = {
630
+ port: number;
631
+ baseUrl: string;
632
+ walletAddress: string;
633
+ balanceMonitor: BalanceMonitor;
634
+ close: () => Promise<void>;
635
+ };
636
+ /**
637
+ * Start the local x402 proxy server.
638
+ *
639
+ * If a proxy is already running on the target port, reuses it instead of failing.
640
+ * Port can be configured via BLOCKRUN_PROXY_PORT environment variable.
641
+ *
642
+ * Returns a handle with the assigned port, base URL, and a close function.
643
+ */
644
+ declare function startProxy(options: ProxyOptions): Promise<ProxyHandle>;
645
+
646
+ /**
647
+ * BlockRun ProviderPlugin for OpenClaw
648
+ *
649
+ * Registers BlockRun as an LLM provider in OpenClaw.
650
+ * Uses a local x402 proxy to handle micropayments transparently —
651
+ * pi-ai sees a standard OpenAI-compatible API at localhost.
652
+ */
653
+
654
+ /**
655
+ * BlockRun provider plugin definition.
656
+ */
657
+ declare const blockrunProvider: ProviderPlugin;
658
+
659
+ /**
660
+ * BlockRun Model Definitions for OpenClaw
661
+ *
662
+ * Maps BlockRun's 30+ AI models to OpenClaw's ModelDefinitionConfig format.
663
+ * All models use the "openai-completions" API since BlockRun is OpenAI-compatible.
664
+ *
665
+ * Pricing is in USD per 1M tokens. Operators pay these rates via x402;
666
+ * they set their own markup when reselling to end users (Phase 2).
667
+ */
668
+
669
+ /**
670
+ * Model aliases for convenient shorthand access.
671
+ * Users can type `/model claude` instead of `/model blockrun/anthropic/claude-sonnet-4-6`.
672
+ */
673
+ declare const MODEL_ALIASES: Record<string, string>;
674
+ /**
675
+ * Resolve a model alias to its full model ID.
676
+ * Also strips "blockrun/" prefix for direct model paths.
677
+ * Examples:
678
+ * - "claude" -> "anthropic/claude-sonnet-4-6" (alias)
679
+ * - "blockrun/claude" -> "anthropic/claude-sonnet-4-6" (alias with prefix)
680
+ * - "blockrun/anthropic/claude-sonnet-4-6" -> "anthropic/claude-sonnet-4-6" (prefix stripped)
681
+ * - "openai/gpt-4o" -> "openai/gpt-4o" (unchanged)
682
+ */
683
+ declare function resolveModelAlias(model: string): string;
684
+ type BlockRunModel = {
685
+ id: string;
686
+ name: string;
687
+ /** Model version (e.g., "4.6", "3.1", "5.2") for tracking updates */
688
+ version?: string;
689
+ inputPrice: number;
690
+ outputPrice: number;
691
+ contextWindow: number;
692
+ maxOutput: number;
693
+ reasoning?: boolean;
694
+ vision?: boolean;
695
+ /** Models optimized for agentic workflows (multi-step autonomous tasks) */
696
+ agentic?: boolean;
697
+ };
698
+ declare const BLOCKRUN_MODELS: BlockRunModel[];
699
+ /**
700
+ * All BlockRun models in OpenClaw format (including aliases).
701
+ */
702
+ declare const OPENCLAW_MODELS: ModelDefinitionConfig[];
703
+ /**
704
+ * Build a ModelProviderConfig for BlockRun.
705
+ *
706
+ * @param baseUrl - The proxy's local base URL (e.g., "http://127.0.0.1:12345")
707
+ */
708
+ declare function buildProviderModels(baseUrl: string): ModelProviderConfig;
709
+ /**
710
+ * Check if a model is optimized for agentic workflows.
711
+ * Agentic models continue autonomously with multi-step tasks
712
+ * instead of stopping and waiting for user input.
713
+ */
714
+ declare function isAgenticModel(modelId: string): boolean;
715
+ /**
716
+ * Get all agentic-capable models.
717
+ */
718
+ declare function getAgenticModels(): string[];
719
+ /**
720
+ * Get context window size for a model.
721
+ * Returns undefined if model not found.
722
+ */
723
+ declare function getModelContextWindow(modelId: string): number | undefined;
724
+
725
+ /**
726
+ * Usage Logger
727
+ *
728
+ * Logs every LLM request as a JSON line to a daily log file.
729
+ * Files: ~/.openclaw/blockrun/logs/usage-YYYY-MM-DD.jsonl
730
+ *
731
+ * MVP: append-only JSON lines. No rotation, no cleanup.
732
+ * Logging never breaks the request flow — all errors are swallowed.
733
+ */
734
+ type UsageEntry = {
735
+ timestamp: string;
736
+ model: string;
737
+ tier: string;
738
+ cost: number;
739
+ baselineCost: number;
740
+ savings: number;
741
+ latencyMs: number;
742
+ /** Partner service ID (e.g., "x_users_lookup") — only set for partner API calls */
743
+ partnerId?: string;
744
+ /** Partner service name (e.g., "AttentionVC") — only set for partner API calls */
745
+ service?: string;
746
+ };
747
+ /**
748
+ * Log a usage entry as a JSON line.
749
+ */
750
+ declare function logUsage(entry: UsageEntry): Promise<void>;
751
+
752
+ /**
753
+ * Request Deduplication
754
+ *
755
+ * Prevents double-charging when OpenClaw retries a request after timeout.
756
+ * Tracks in-flight requests and caches completed responses for a short TTL.
757
+ */
758
+ type CachedResponse = {
759
+ status: number;
760
+ headers: Record<string, string>;
761
+ body: Buffer;
762
+ completedAt: number;
763
+ };
764
+ declare class RequestDeduplicator {
765
+ private inflight;
766
+ private completed;
767
+ private ttlMs;
768
+ constructor(ttlMs?: number);
769
+ /** Hash request body to create a dedup key. */
770
+ static hash(body: Buffer): string;
771
+ /** Check if a response is cached for this key. */
772
+ getCached(key: string): CachedResponse | undefined;
773
+ /** Check if a request with this key is currently in-flight. Returns a promise to wait on. */
774
+ getInflight(key: string): Promise<CachedResponse> | undefined;
775
+ /** Mark a request as in-flight. */
776
+ markInflight(key: string): void;
777
+ /** Complete an in-flight request — cache result and notify waiters. */
778
+ complete(key: string, result: CachedResponse): void;
779
+ /** Remove an in-flight entry on error (don't cache failures).
780
+ * Also rejects any waiters so they can retry independently. */
781
+ removeInflight(key: string): void;
782
+ /** Prune expired completed entries. */
783
+ private prune;
784
+ }
785
+
786
+ /**
787
+ * Payment Parameter Cache
788
+ *
789
+ * Caches the 402 payment parameters (payTo, asset, network, etc.) after the first
790
+ * request to each endpoint. On subsequent requests, pre-signs the payment and
791
+ * attaches it to the first request, skipping the 402 round trip (~200ms savings).
792
+ */
793
+ type CachedPaymentParams = {
794
+ payTo: string;
795
+ asset: string;
796
+ scheme: string;
797
+ network: string;
798
+ extra?: {
799
+ name?: string;
800
+ version?: string;
801
+ };
802
+ maxTimeoutSeconds?: number;
803
+ resourceUrl?: string;
804
+ resourceDescription?: string;
805
+ cachedAt: number;
806
+ };
807
+ declare class PaymentCache {
808
+ private cache;
809
+ private ttlMs;
810
+ constructor(ttlMs?: number);
811
+ /** Get cached payment params for an endpoint path. */
812
+ get(endpointPath: string): CachedPaymentParams | undefined;
813
+ /** Cache payment params from a 402 response. */
814
+ set(endpointPath: string, params: Omit<CachedPaymentParams, "cachedAt">): void;
815
+ /** Invalidate cache for an endpoint (e.g., if payTo changed). */
816
+ invalidate(endpointPath: string): void;
817
+ }
818
+
819
+ /**
820
+ * x402 Payment Implementation
821
+ *
822
+ * Based on BlockRun's proven implementation.
823
+ * Handles 402 Payment Required responses with EIP-712 signed USDC transfers.
824
+ *
825
+ * Optimizations (v0.3.0):
826
+ * - Payment cache: after first 402, caches {payTo, asset, network} per endpoint.
827
+ * On subsequent requests, pre-signs payment and sends with first request,
828
+ * skipping the 402 round trip (~200ms savings).
829
+ * - Falls back to normal 402 flow if pre-signed payment is rejected.
830
+ */
831
+
832
+ /** Pre-auth parameters for skipping the 402 round trip. */
833
+ type PreAuthParams = {
834
+ estimatedAmount: string;
835
+ };
836
+ /** Result from createPaymentFetch — includes the fetch wrapper and payment cache. */
837
+ type PaymentFetchResult = {
838
+ fetch: (input: RequestInfo | URL, init?: RequestInit, preAuth?: PreAuthParams) => Promise<Response>;
839
+ cache: PaymentCache;
840
+ };
841
+ /**
842
+ * Create a fetch wrapper that handles x402 payment automatically.
843
+ *
844
+ * Supports pre-auth: if cached payment params + estimated amount are available,
845
+ * pre-signs and attaches payment to the first request, skipping the 402 round trip.
846
+ * Falls back to normal 402 flow if pre-signed payment is rejected.
847
+ */
848
+ declare function createPaymentFetch(privateKey: `0x${string}`): PaymentFetchResult;
849
+
850
+ /**
851
+ * Typed Error Classes for ClawRouter
852
+ *
853
+ * Provides structured errors for balance-related failures with
854
+ * all necessary information for user-friendly error messages.
855
+ */
856
+ /**
857
+ * Thrown when wallet has insufficient USDC balance for a request.
858
+ */
859
+ declare class InsufficientFundsError extends Error {
860
+ readonly code: "INSUFFICIENT_FUNDS";
861
+ readonly currentBalanceUSD: string;
862
+ readonly requiredUSD: string;
863
+ readonly walletAddress: string;
864
+ constructor(opts: {
865
+ currentBalanceUSD: string;
866
+ requiredUSD: string;
867
+ walletAddress: string;
868
+ });
869
+ }
870
+ /**
871
+ * Thrown when wallet has no USDC balance (or effectively zero).
872
+ */
873
+ declare class EmptyWalletError extends Error {
874
+ readonly code: "EMPTY_WALLET";
875
+ readonly walletAddress: string;
876
+ constructor(walletAddress: string);
877
+ }
878
+ /**
879
+ * Type guard to check if an error is InsufficientFundsError.
880
+ */
881
+ declare function isInsufficientFundsError(error: unknown): error is InsufficientFundsError;
882
+ /**
883
+ * Type guard to check if an error is EmptyWalletError.
884
+ */
885
+ declare function isEmptyWalletError(error: unknown): error is EmptyWalletError;
886
+ /**
887
+ * Type guard to check if an error is a balance-related error.
888
+ */
889
+ declare function isBalanceError(error: unknown): error is InsufficientFundsError | EmptyWalletError;
890
+ /**
891
+ * Thrown when RPC call fails (network error, node down, etc).
892
+ * Distinguishes infrastructure failures from actual empty wallets.
893
+ */
894
+ declare class RpcError extends Error {
895
+ readonly code: "RPC_ERROR";
896
+ readonly originalError: unknown;
897
+ constructor(message: string, originalError?: unknown);
898
+ }
899
+ /**
900
+ * Type guard to check if an error is RpcError.
901
+ */
902
+ declare function isRpcError(error: unknown): error is RpcError;
903
+
904
+ /**
905
+ * Retry Logic for ClawRouter
906
+ *
907
+ * Provides fetch wrapper with exponential backoff for transient errors.
908
+ * Retries on 429 (rate limit), 502, 503, 504 (server errors).
909
+ */
910
+ /** Configuration for retry behavior */
911
+ type RetryConfig = {
912
+ /** Maximum number of retries (default: 2) */
913
+ maxRetries: number;
914
+ /** Base delay in ms for exponential backoff (default: 500) */
915
+ baseDelayMs: number;
916
+ /** HTTP status codes that trigger a retry (default: [429, 502, 503, 504]) */
917
+ retryableCodes: number[];
918
+ };
919
+ /** Default retry configuration */
920
+ declare const DEFAULT_RETRY_CONFIG: RetryConfig;
921
+ /**
922
+ * Wrap a fetch-like function with retry logic and exponential backoff.
923
+ *
924
+ * @param fetchFn - The fetch function to wrap (can be standard fetch or x402 payFetch)
925
+ * @param url - URL to fetch
926
+ * @param init - Fetch init options
927
+ * @param config - Retry configuration (optional, uses defaults)
928
+ * @returns Response from successful fetch or last failed attempt
929
+ *
930
+ * @example
931
+ * ```typescript
932
+ * const response = await fetchWithRetry(
933
+ * fetch,
934
+ * "https://api.example.com/endpoint",
935
+ * { method: "POST", body: JSON.stringify(data) },
936
+ * { maxRetries: 3 }
937
+ * );
938
+ * ```
939
+ */
940
+ declare function fetchWithRetry(fetchFn: (url: string, init?: RequestInit) => Promise<Response>, url: string, init?: RequestInit, config?: Partial<RetryConfig>): Promise<Response>;
941
+ /**
942
+ * Check if an error or response indicates a retryable condition.
943
+ */
944
+ declare function isRetryable(errorOrResponse: Error | Response, config?: Partial<RetryConfig>): boolean;
945
+
946
+ /**
947
+ * Usage Statistics Aggregator
948
+ *
949
+ * Reads usage log files and aggregates statistics for terminal display.
950
+ * Supports filtering by date range and provides multiple aggregation views.
951
+ */
952
+ type DailyStats = {
953
+ date: string;
954
+ totalRequests: number;
955
+ totalCost: number;
956
+ totalBaselineCost: number;
957
+ totalSavings: number;
958
+ avgLatencyMs: number;
959
+ byTier: Record<string, {
960
+ count: number;
961
+ cost: number;
962
+ }>;
963
+ byModel: Record<string, {
964
+ count: number;
965
+ cost: number;
966
+ }>;
967
+ };
968
+ type AggregatedStats = {
969
+ period: string;
970
+ totalRequests: number;
971
+ totalCost: number;
972
+ totalBaselineCost: number;
973
+ totalSavings: number;
974
+ savingsPercentage: number;
975
+ avgLatencyMs: number;
976
+ avgCostPerRequest: number;
977
+ byTier: Record<string, {
978
+ count: number;
979
+ cost: number;
980
+ percentage: number;
981
+ }>;
982
+ byModel: Record<string, {
983
+ count: number;
984
+ cost: number;
985
+ percentage: number;
986
+ }>;
987
+ dailyBreakdown: DailyStats[];
988
+ entriesWithBaseline: number;
989
+ };
990
+ /**
991
+ * Get aggregated statistics for the last N days.
992
+ */
993
+ declare function getStats(days?: number): Promise<AggregatedStats>;
994
+ /**
995
+ * Format stats as ASCII table for terminal display.
996
+ */
997
+ declare function formatStatsAscii(stats: AggregatedStats): string;
998
+
999
+ /**
1000
+ * Partner Service Registry
1001
+ *
1002
+ * Defines available partner APIs that can be called through ClawRouter's proxy.
1003
+ * Partners provide specialized data (Twitter/X, etc.) via x402 micropayments.
1004
+ * The same wallet used for LLM calls pays for partner API calls — zero extra setup.
1005
+ */
1006
+ type PartnerServiceParam = {
1007
+ name: string;
1008
+ type: "string" | "string[]" | "number";
1009
+ description: string;
1010
+ required: boolean;
1011
+ };
1012
+ type PartnerServiceDefinition = {
1013
+ /** Unique service ID used in tool names: blockrun_{id} */
1014
+ id: string;
1015
+ /** Human-readable name */
1016
+ name: string;
1017
+ /** Partner providing this service */
1018
+ partner: string;
1019
+ /** Short description for tool listing */
1020
+ description: string;
1021
+ /** Proxy path (relative to /v1) */
1022
+ proxyPath: string;
1023
+ /** HTTP method */
1024
+ method: "GET" | "POST";
1025
+ /** Parameters for the tool's JSON Schema */
1026
+ params: PartnerServiceParam[];
1027
+ /** Pricing info for display */
1028
+ pricing: {
1029
+ perUnit: string;
1030
+ unit: string;
1031
+ minimum: string;
1032
+ maximum: string;
1033
+ };
1034
+ /** Example usage for help text */
1035
+ example: {
1036
+ input: Record<string, unknown>;
1037
+ description: string;
1038
+ };
1039
+ };
1040
+ /**
1041
+ * All registered partner services.
1042
+ * New partners are added here — the rest of the system picks them up automatically.
1043
+ */
1044
+ declare const PARTNER_SERVICES: PartnerServiceDefinition[];
1045
+ /**
1046
+ * Get a partner service by ID.
1047
+ */
1048
+ declare function getPartnerService(id: string): PartnerServiceDefinition | undefined;
1049
+
1050
+ /**
1051
+ * Partner Tool Builder
1052
+ *
1053
+ * Converts partner service definitions into OpenClaw tool definitions.
1054
+ * Each tool's execute() calls through the local proxy which handles
1055
+ * x402 payment transparently using the same wallet.
1056
+ */
1057
+ /** OpenClaw tool definition shape (duck-typed) */
1058
+ type PartnerToolDefinition = {
1059
+ name: string;
1060
+ description: string;
1061
+ parameters: {
1062
+ type: "object";
1063
+ properties: Record<string, unknown>;
1064
+ required: string[];
1065
+ };
1066
+ execute: (toolCallId: string, params: Record<string, unknown>) => Promise<unknown>;
1067
+ };
1068
+ /**
1069
+ * Build OpenClaw tool definitions for all registered partner services.
1070
+ * @param proxyBaseUrl - Local proxy base URL (e.g., "http://127.0.0.1:8402")
1071
+ */
1072
+ declare function buildPartnerTools(proxyBaseUrl: string): PartnerToolDefinition[];
1073
+
1074
+ /**
1075
+ * @blockrun/clawrouter
1076
+ *
1077
+ * Smart LLM router for OpenClaw — 30+ models, x402 micropayments, 78% cost savings.
1078
+ * Routes each request to the cheapest model that can handle it.
1079
+ *
1080
+ * Usage:
1081
+ * # Install the plugin
1082
+ * openclaw plugins install @blockrun/clawrouter
1083
+ *
1084
+ * # Fund your wallet with USDC on Base (address printed on install)
1085
+ *
1086
+ * # Use smart routing (auto-picks cheapest model)
1087
+ * openclaw models set blockrun/auto
1088
+ *
1089
+ * # Or use any specific BlockRun model
1090
+ * openclaw models set openai/gpt-5.2
1091
+ */
1092
+
1093
+ declare const plugin: OpenClawPluginDefinition;
1094
+
1095
+ 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, PARTNER_SERVICES, type PartnerServiceDefinition, type PartnerToolDefinition, 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, buildPartnerTools, buildProviderModels, calculateModelCost, createPaymentFetch, plugin as default, fetchWithRetry, formatStatsAscii, getAgenticModels, getFallbackChain, getFallbackChainFiltered, getModelContextWindow, getPartnerService, getProxyPort, getSessionId, getStats, isAgenticModel, isBalanceError, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, logUsage, resolveModelAlias, route, startProxy };