@blockrun/clawrouter 0.12.241 → 0.12.242

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
@@ -34777,7 +34777,7 @@ var init_esm5 = __esm({
34777
34777
  }
34778
34778
  });
34779
34779
 
34780
- // src/router/rules.ts
34780
+ // node_modules/@blockrun/router-core/dist/index.js
34781
34781
  function scoreTokenCount(estimatedTokens, thresholds) {
34782
34782
  if (estimatedTokens < thresholds.simple) {
34783
34783
  return { name: "tokenCount", score: -1, signal: `short (${estimatedTokens} tokens)` };
@@ -35017,13 +35017,6 @@ function classifyByRules(prompt, systemPrompt, estimatedTokens, config) {
35017
35017
  function calibrateConfidence(distance, steepness) {
35018
35018
  return 1 / (1 + Math.exp(-steepness * distance));
35019
35019
  }
35020
- var init_rules = __esm({
35021
- "src/router/rules.ts"() {
35022
- "use strict";
35023
- }
35024
- });
35025
-
35026
- // src/router/selector.ts
35027
35020
  function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile, agenticScore) {
35028
35021
  const tierConfig = tierConfigs[tier];
35029
35022
  const model = tierConfig.primary;
@@ -35112,19 +35105,14 @@ function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getCo
35112
35105
  }
35113
35106
  return filtered;
35114
35107
  }
35115
- var BASELINE_MODEL_ID, BASELINE_INPUT_PRICE, BASELINE_OUTPUT_PRICE, SERVER_MARGIN_PERCENT, MIN_PAYMENT_USD;
35116
- var init_selector = __esm({
35117
- "src/router/selector.ts"() {
35118
- "use strict";
35119
- BASELINE_MODEL_ID = "anthropic/claude-opus-4.7";
35120
- BASELINE_INPUT_PRICE = 5;
35121
- BASELINE_OUTPUT_PRICE = 25;
35122
- SERVER_MARGIN_PERCENT = 5;
35123
- MIN_PAYMENT_USD = 1e-3;
35124
- }
35125
- });
35126
-
35127
- // src/router/strategy.ts
35108
+ function filterCandidatesByCapacity(models, estimatedInputTokens, requestedOutputTokens, getCapabilities2) {
35109
+ const filtered = models.filter((modelId) => {
35110
+ const capabilities = getCapabilities2(modelId);
35111
+ if (!capabilities) return true;
35112
+ return capabilities.contextWindow >= (estimatedInputTokens + requestedOutputTokens) * 1.1 && capabilities.maxOutput >= requestedOutputTokens;
35113
+ });
35114
+ return filtered;
35115
+ }
35128
35116
  function applyPromotions(tierConfigs, promotions, profile, now2 = /* @__PURE__ */ new Date()) {
35129
35117
  if (!promotions || promotions.length === 0) return tierConfigs;
35130
35118
  let result = tierConfigs;
@@ -35154,19 +35142,666 @@ function getStrategy(name) {
35154
35142
  }
35155
35143
  return strategy;
35156
35144
  }
35157
- var RulesStrategy, registry;
35158
- var init_strategy = __esm({
35159
- "src/router/strategy.ts"() {
35145
+ function registerStrategy(strategy) {
35146
+ registry.set(strategy.name, strategy);
35147
+ }
35148
+ function inferToolRequirement(prompt, _systemPrompt, toolChoice) {
35149
+ if (toolChoice === "none") return false;
35150
+ if (toolChoice === "required") return true;
35151
+ if (typeof toolChoice === "object" && toolChoice !== null && toolChoice.type === "function") {
35152
+ return true;
35153
+ }
35154
+ const text = prompt;
35155
+ const explicitTool = /\b(?:use|call|invoke)\s+(?:the\s+)?[\w.-]+\s+(?:tool|function|api)\b|\btool[_ -]?call\b|使用.{0,20}(?:工具|函数|接口)|调用.{0,20}(?:工具|函数|接口)/i;
35156
+ const codeEnvironment = /\b(?:run|execute)\s+(?:the\s+)?(?:tests?|command|script|build|linter)|\b(?:edit|modify|patch|create|write|save|delete|rename|move|inspect|read)\b.{0,60}\b(?:file|repository|repo|codebase|directory|folder)\b|\b(?:terminal|shell|bash|zsh|pytest|npm test|pnpm test|git\s+(?:status|diff|commit)|docker)\b|(?:运行|执行).{0,20}(?:测试|命令|脚本|构建)|(?:修改|编辑|修复|创建|读取|检查|保存).{0,30}(?:文件|仓库|代码库|目录)/i;
35157
+ const webAction = /\b(?:browse|search|look up|fetch|open)\b.{0,80}\b(?:web|website|url|online|documentation|docs|news|weather|price)\b|(?:浏览|搜索|查询|打开).{0,30}(?:网页|网站|链接|文档|新闻|天气|价格)/i;
35158
+ const statefulAction = /\b(?:refund|cancel|book|reserve|purchase|buy|return|exchange|transfer|update|change)\b.{0,80}\b(?:order|booking|reservation|account|address|payment|subscription|ticket|flight|item)\b|(?:退款|取消|预订|购买|退货|换货|转账|更新|修改).{0,30}(?:订单|预订|账户|地址|付款|订阅|票|航班|商品)/i;
35159
+ return explicitTool.test(text) || codeEnvironment.test(text) || webAction.test(text) || statefulAction.test(text);
35160
+ }
35161
+ function likelyNeedsParallelToolCalls(prompt, needsTools, toolCount, toolNames) {
35162
+ if (!needsTools || toolCount === void 0 || toolCount < 1) return false;
35163
+ const text = prompt.trim();
35164
+ const explicitRepeat = /\b(?:in parallel|simultaneously|concurrently|for each|each of|every one|both|(?:two|three|multiple|several)\s+(?:cities|locations|items|tasks|orders|users|files))\b|并行|同时|分别|每个|各自|(?:两个|三个|多个)(?:城市|地点|项目|任务|订单|用户|文件)|cada uno|para cada|simult[aá]neamente/i.test(
35165
+ text
35166
+ );
35167
+ if (explicitRepeat) return true;
35168
+ const sentenceClauses = text.split(/[.!?。!?]+/).map((part) => part.trim()).filter((part) => part.length >= 8);
35169
+ if (/\b(?:also|additionally|furthermore)\b|另外|此外|그리고/i.test(text) && sentenceClauses.length >= 2 || /\band\s+(?:also|for the)\b/i.test(text))
35170
+ return true;
35171
+ const pairedQuantity = /\b\d+(?:\.\d+)?\s+(?:and|or)\s+\d+(?:\.\d+)?\s*(?:gb|mb|tb|kg|g|ml|oz|cups?|cores?|cpus?)\b/i.test(
35172
+ text
35173
+ );
35174
+ if (pairedQuantity) return true;
35175
+ const operationTokens = /* @__PURE__ */ new Set([
35176
+ "add",
35177
+ "delete",
35178
+ "remove",
35179
+ "cancel",
35180
+ "return",
35181
+ "exchange",
35182
+ "modify",
35183
+ "book",
35184
+ "transfer",
35185
+ "send",
35186
+ "upload",
35187
+ "download",
35188
+ "create",
35189
+ "close"
35190
+ ]);
35191
+ const lowered = text.toLowerCase();
35192
+ const matchedOperationTokens = new Set(
35193
+ (toolNames ?? []).flatMap((name) => name.toLowerCase().split(/[^a-z0-9\u3400-\u9fff]+/)).filter((token) => operationTokens.has(token) && lowered.includes(token))
35194
+ );
35195
+ if (matchedOperationTokens.size >= 2) return true;
35196
+ const nonEmptyLines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
35197
+ const quantityMentions = text.match(
35198
+ /\b(?:\d+(?:\.\d+)?|one|two|three|four|five|six|seven|eight|nine|ten)\s*(?:oz|ounce|ounces|g|gram|grams|kg|ml|cups?|pieces?|tablespoons?)\b/gi
35199
+ ) ?? [];
35200
+ if (nonEmptyLines.length >= 2 && quantityMentions.length >= 2) return true;
35201
+ const repeatedLookup = /\b(?:weather|climate|clima|tiempo|temperature|snow|news|report)\b|天气|气象|温度|降雪|新闻|报告/i.test(
35202
+ text
35203
+ );
35204
+ const multiLocationConnector = /\b(?:and also|both|y|e)\b|还有|以及|和|、/i.test(text);
35205
+ const commaSeparatedLocations = (text.match(/[,,]/g) ?? []).length >= 2;
35206
+ if (repeatedLookup && (multiLocationConnector || commaSeparatedLocations)) return true;
35207
+ const distinctOrderParts = /\b(?:food|meal)\b[\s\S]*\bdrink\b|\bdrink\b[\s\S]*\b(?:food|meal)\b/i.test(text);
35208
+ const koreanParallelClauses = (text.match(/,/g) ?? []).length >= 3 && /하고|그리고/.test(text);
35209
+ return distinctOrderParts || koreanParallelClauses;
35210
+ }
35211
+ function classifyTask(prompt, systemPrompt, options) {
35212
+ const fullText = `${systemPrompt ?? ""} ${prompt}`;
35213
+ const estimatedInputTokens = Math.ceil(fullText.length / 4);
35214
+ const scanLimit = Math.max(1, Math.min(8e3, options.config.classifier.promptTruncationChars));
35215
+ const sample = (value) => {
35216
+ if (value.length <= scanLimit) return value;
35217
+ const prefixLength = Math.ceil(scanLimit / 2);
35218
+ return `${value.slice(0, prefixLength)}
35219
+ ${value.slice(-(scanLimit - prefixLength))}`;
35220
+ };
35221
+ const scannedPrompt = sample(prompt);
35222
+ const scannedSystemPrompt = sample(systemPrompt ?? "");
35223
+ const scannedFullText = `${scannedSystemPrompt} ${scannedPrompt}`;
35224
+ const text = scannedPrompt.toLowerCase();
35225
+ const explicitCodeSignal = /```|\b(?:typescript|javascript|python|rust|java|sql|stack trace|traceback|exception)\b|\.(?:ts|tsx|js|py|go|rs)\b/i.test(
35226
+ scannedPrompt
35227
+ );
35228
+ const codeConstructSignal = /\b(?:implement|refactor|debug|write|edit|modify|create|define|review|fix)\b[\s\S]{0,48}\b(?:api|function|class|method)\b|\b(?:api|function|class|method)\b[\s\S]{0,48}\b(?:code|implementation|typescript|javascript|python|rust|java)\b/i.test(
35229
+ scannedPrompt
35230
+ );
35231
+ const nativeCodeSignal = /\b(?:programmed|written|implemented?|code)\s+(?:in|using)\s+(?:c\+\+|c|rust|go)\b/i.test(
35232
+ scannedPrompt
35233
+ );
35234
+ const hasCode = explicitCodeSignal || codeConstructSignal || nativeCodeSignal;
35235
+ const toolsAvailable = options.hasTools ?? false;
35236
+ const needsTools = options.requiresTools ?? (toolsAvailable && inferToolRequirement(scannedPrompt, scannedSystemPrompt));
35237
+ const likelyParallelToolCalls = likelyNeedsParallelToolCalls(
35238
+ scannedPrompt,
35239
+ needsTools,
35240
+ options.toolCount,
35241
+ options.toolNames
35242
+ );
35243
+ const normalizedToolNames = (options.toolNames ?? []).map((name) => name.toLowerCase());
35244
+ const airlineToolSignal = normalizedToolNames.some(
35245
+ (name) => /(?:flight|reservation|airport|baggage|passenger)/.test(name)
35246
+ );
35247
+ const retailToolSignal = normalizedToolNames.some(
35248
+ (name) => /(?:order|product|item|return|exchange|address)/.test(name)
35249
+ );
35250
+ const webResearchToolSignal = normalizedToolNames.some(
35251
+ (name) => /^(?:web_?search|web_?fetch)$/.test(name)
35252
+ );
35253
+ const agentDomain = airlineToolSignal && !retailToolSignal ? "airline" : retailToolSignal && !airlineToolSignal ? "retail" : webResearchToolSignal ? "web_research" : "other";
35254
+ const clueConnectors = scannedFullText.match(
35255
+ /\b(?:after|before|while|where|whose|which|in \d{4}|as of|over \d+|another|also|furthermore)\b|(?:之后|之前|其中|截至|超过|另一个|此外)/gi
35256
+ ) ?? [];
35257
+ const entityResolutionSignal = /\b(?:identify|who (?:is|was)|what (?:is|was) the name|which (?:person|player|company|country|city)|find the (?:person|player|name|entity))\b|(?:找出|识别|是谁|哪位|名称是什么)/i.test(
35258
+ scannedFullText
35259
+ );
35260
+ const exactAnswerSignal = /\b(?:exact answer|single best-supported answer|following clues|multiple public sources)\b|(?:精确答案|根据.*线索|多个公开来源)/i.test(
35261
+ scannedFullText
35262
+ );
35263
+ const deepWebResearch = agentDomain === "web_research" && (exactAnswerSignal || entityResolutionSignal && (clueConnectors.length >= 3 || prompt.length >= 320));
35264
+ const globalOptimizationSignal = /\b(?:cheapest|lowest[- ]price|least expensive|most expensive|highest(?:[- ]priced)?|largest|smallest|maximum|minimum|best available|closest|not (?:cost|exceed))\b|最便宜|最低价|最贵|最高价|最大|最小/i.test(
35265
+ scannedPrompt
35266
+ );
35267
+ const globalScopeSignal = /\b(?:everything|all (?:(?:my|your|their|the) )?(?:future |upcoming )?(?:items|orders|passengers|flights|reservations|bookings)|every (?:item|order|passenger|flight|reservation|booking))\b|全部|所有|每个/i.test(
35268
+ scannedPrompt
35269
+ );
35270
+ const globalChoiceSignal = globalOptimizationSignal || globalScopeSignal;
35271
+ const crossRecordSignal = /\b(?:another|other|different|previous)\s+(?:order|reservation|booking|account|address)\b|另一(?:个)?(?:订单|预订|账户|地址)|其他(?:订单|预订|账户|地址)/i.test(
35272
+ scannedPrompt
35273
+ );
35274
+ const reservationIds = scannedPrompt.match(/\b[A-Z0-9]{6}\b/g) ?? [];
35275
+ const crossReservationBatchSignal = agentDomain === "airline" && (/\b(?:two|three|multiple|several)(?:\s+of\s+(?:my|our|the))?\s+(?:upcoming\s+)?(?:reservations?|bookings?)\b|\b(?:a\s+)?(?:second|third)\s+(?:reservation|booking)\b/i.test(
35276
+ scannedPrompt
35277
+ ) || new Set(reservationIds).size >= 2);
35278
+ const conditionalGlobalWorkflowSignal = agentDomain === "airline" && globalScopeSignal && /\b(?:if|that (?:contain|have)|longer than|shorter than|under|over|at (?:most|least)|wherever possible)\b|如果|超过|少于|不超过|尽可能/i.test(
35279
+ scannedPrompt
35280
+ ) && /\b(?:cancel|change|upgrade|move|book)\b[\s\S]*\b(?:cancel|change|upgrade|move|book)\b|取消[\s\S]*(?:升级|更改)|升级[\s\S]*(?:取消|更改)/i.test(
35281
+ scannedPrompt
35282
+ );
35283
+ const policyExceptionSignal = agentDomain === "retail" && /\b(?:return|refund|send back|get (?:my |the )?money back)\b|退货|退款|退回/i.test(
35284
+ scannedPrompt
35285
+ ) && /\b(?:amex|american express|visa|mastercard|credit card|debit card|different card|another card|other card)\b|信用卡|借记卡|其他卡|另一张卡/i.test(
35286
+ scannedPrompt
35287
+ );
35288
+ const singleSelectedPolicyException = policyExceptionSignal && /\b(?:return|refund|send back)\b[^.!?。!?]{0,96}\b(?:the )?(?:pricier|cheaper|more expensive|less expensive|costlier|one)\b/i.test(
35289
+ scannedPrompt
35290
+ );
35291
+ const negotiatedWorkflowSignal = agentDomain === "retail" && /\b(?:return|exchange)\b|退货|退回|换货|交换/i.test(scannedPrompt);
35292
+ const numberedSteps = (scannedPrompt.match(/(?:^|\s)\d+(?:\.\d+)*[.)]\s+/g) ?? []).length;
35293
+ const complexMultiToolPlan = likelyParallelToolCalls && ((options.toolCount ?? 0) >= 6 || numberedSteps >= 3 || prompt.length > 1200);
35294
+ let agentRisk = needsTools && singleSelectedPolicyException ? "policy_exception_simple" : needsTools && policyExceptionSignal ? "policy_exception" : (
35295
+ // Airline prompts that require a global optimum (for example the
35296
+ // cheapest itinerary across several candidates) are materially harder
35297
+ // than applying one change to every passenger in a known reservation.
35298
+ // Full-session evidence supports Sonnet for the former, while upgrading
35299
+ // the latter merely because it says "all passengers" caused a large cost
35300
+ // increase without a quality gain.
35301
+ needsTools && agentDomain === "airline" && (globalOptimizationSignal || conditionalGlobalWorkflowSignal) ? "complex_high" : needsTools && (likelyParallelToolCalls || globalChoiceSignal || crossRecordSignal || crossReservationBatchSignal || negotiatedWorkflowSignal) ? "high" : "standard"
35302
+ );
35303
+ const needsVision = options.hasVision ?? false;
35304
+ const needsStructuredOutput = options.requiresStructuredOutput ?? false;
35305
+ const latencySensitive = /\b(?:urgent|asap|fast|quick|low latency|real[- ]time)\b|尽快|马上|快速|低延迟/i.test(
35306
+ scannedFullText
35307
+ );
35308
+ const highStakes = /\b(?:production|security|payment|legal|medical|financial|audit)\b|生产|安全|支付|法律|医疗|财务|审计/i.test(
35309
+ scannedFullText
35310
+ );
35311
+ const terminalToolSignal = normalizedToolNames.some(
35312
+ (name) => /^(?:terminalexec|terminalinspect|terminalsendkeys)$/.test(name)
35313
+ );
35314
+ const simpleTerminalArtifact = /\b(?:create|write|convert|generate|build|implement|run|fix|repair|debug|make)\b[\s\S]{0,120}\b(?:file|script|csv|parquet|json|txt|server|endpoint)\b/i.test(
35315
+ scannedPrompt
35316
+ );
35317
+ const terminalComplexRepair = terminalToolSignal && /\b(?:multiple|several)\s+(?:scripts?|files?|components?)\b|\b(?:pipeline|dependencies)\b[\s\S]{0,100}\b(?:fail|issue|fix|repair|run|execute)\b|\b(?:identify|find|fix|repair)\s+(?:and\s+)?(?:fix\s+)?all\s+(?:the\s+)?issues\b/i.test(
35318
+ scannedPrompt
35319
+ );
35320
+ const mentionedTerminalRuntimes = new Set(
35321
+ (scannedPrompt.match(/\b(?:gcc|clang|rustc|javac|go\s+build|node|python)\b/gi) ?? []).map(
35322
+ (name) => name.toLowerCase().replace(/\s+/g, " ")
35323
+ )
35324
+ );
35325
+ const terminalCrossRuntimeArtifact = terminalToolSignal && (/\bpolyglot\b/i.test(scannedPrompt) || /\b(?:both|each)\b[\s\S]{0,120}\b(?:compilers?|runtimes?|toolchains?)\b/i.test(
35326
+ scannedPrompt
35327
+ ) || mentionedTerminalRuntimes.size >= 2 && /\b(?:compile|build|run|execute)\b/i.test(scannedPrompt));
35328
+ const terminalFrameworkToNativeArtifact = terminalToolSignal && /\b(?:pytorch|tensorflow|jax|onnx|state[_ -]?dict|checkpoint|safetensors?)\b|\.(?:pth|pt|onnx)\b/i.test(
35329
+ scannedPrompt
35330
+ ) && /\b(?:pure|native|programmed|written|implemented?)\s+(?:in|using)\s+(?:c\+\+|c|rust|go)\b|\b(?:c\+\+|c|rust|go)\s+(?:program|binary|executable|cli|tool|implementation)\b/i.test(
35331
+ scannedPrompt
35332
+ ) && /\b(?:inference|model|weights?|tensor|export|convert|load)\b/i.test(scannedPrompt);
35333
+ if (needsTools && (terminalComplexRepair || terminalCrossRuntimeArtifact || terminalFrameworkToNativeArtifact) && (agentRisk === "standard" || agentRisk === "high"))
35334
+ agentRisk = "complex_high";
35335
+ const complexTerminalOperation = /\b(?:git|ssh|nginx|https|certificate|authentication|credential|deploy|production|encrypt|gpg|shred|securely delete|decommission|benchmark|evaluate|embedding|chess|image|search the web|schema|statistical|statistics|aggregate|join|multiple inputs?)\b/i.test(
35336
+ scannedPrompt
35337
+ );
35338
+ const terminalCredentialSignal = /\b(?:ssh|nginx|certificate|authentication|credentials?|passwords?|api keys?|deploy|production|encrypt|gpg|shred|securely delete|decommission)\b/i.test(
35339
+ scannedPrompt
35340
+ ) || /\b(?:access|auth|authentication|bearer|secret|api)\s+tokens?\b|\btokens?\s+(?:secret|credential|authentication)\b/i.test(
35341
+ scannedPrompt
35342
+ );
35343
+ const terminalSafetySensitive = terminalToolSignal && (highStakes || terminalCredentialSignal);
35344
+ const implicitTerminalCode = needsTools && terminalToolSignal && agentRisk === "standard" && !highStakes && !complexTerminalOperation && numberedSteps < 3 && prompt.length <= 1e3 && simpleTerminalArtifact;
35345
+ const language = /[\u3400-\u9fff]/.test(scannedFullText) ? "zh" : "other";
35346
+ const multipleChoiceSignals = (scannedPrompt.match(/(?:^|\n)\s*[A-D][.)]\s+/gim) ?? []).length;
35347
+ const numericSignals = (scannedPrompt.match(/-?\d+(?:[.,]\d+)?/g) ?? []).length;
35348
+ const compactMathProblem = !hasCode && prompt.length < 2500 && numericSignals >= 2 && (/[+×÷=%$€£¥]|\b(?:total|each|per|times|half|twice|percent|how many|how much|calculate)\b/i.test(
35349
+ scannedPrompt
35350
+ ) || /[??]\s*$/.test(scannedPrompt.trim()) || numericSignals >= 3);
35351
+ let taskType = "chat";
35352
+ if (needsVision) taskType = "vision";
35353
+ else if (estimatedInputTokens > 8e4) taskType = "long_context";
35354
+ else if (needsTools && (hasCode || implicitTerminalCode)) taskType = "code_agent";
35355
+ else if (needsTools && likelyParallelToolCalls && !complexMultiToolPlan)
35356
+ taskType = "tool_agent_parallel";
35357
+ else if (needsTools) taskType = "tool_agent";
35358
+ else if (multipleChoiceSignals >= 3) taskType = "reasoning_mcq";
35359
+ else if (compactMathProblem) taskType = "reasoning_math";
35360
+ else if (/\b(?:bug|debug|error|failure|failing|regression|crash|修复|报错|错误|调试)\b/i.test(text))
35361
+ taskType = "debug";
35362
+ else if (hasCode || /\b(?:refactor|implement|patch|edit|rewrite|重构|实现|修改)\b/i.test(text))
35363
+ taskType = "code_edit";
35364
+ else if (needsStructuredOutput || /\b(?:extract|json|schema|csv|字段|提取)\b/i.test(text))
35365
+ taskType = "extraction";
35366
+ else if (/\b(?:prove|derive|theorem|formal|mathematical|reasoning|证明|推导|定理|数学)\b/i.test(text))
35367
+ taskType = "reasoning";
35368
+ return {
35369
+ taskType,
35370
+ estimatedInputTokens,
35371
+ hasCode,
35372
+ needsTools,
35373
+ toolsAvailable,
35374
+ needsVision,
35375
+ needsStructuredOutput,
35376
+ latencySensitive,
35377
+ highStakes,
35378
+ language,
35379
+ likelyParallelToolCalls,
35380
+ complexMultiToolPlan,
35381
+ agentDomain,
35382
+ deepWebResearch,
35383
+ agentRisk,
35384
+ terminalToolSignal,
35385
+ terminalSafetySensitive,
35386
+ implicitTerminalCode
35387
+ };
35388
+ }
35389
+ function affinity(modelId, task, language = "other", agentDomain = "other", deepWebResearch = false, agentRisk = "standard", terminalToolSignal = false, terminalSafetySensitive = false) {
35390
+ const id2 = modelId.toLowerCase();
35391
+ const modelName = id2.slice(id2.indexOf("/") + 1);
35392
+ const match = (values, score) => values.some((value) => modelName === value) ? score : 0;
35393
+ const base3 = 0.68;
35394
+ switch (task) {
35395
+ case "code_agent":
35396
+ if (terminalToolSignal && agentRisk === "complex_high") {
35397
+ return Math.max(
35398
+ base3,
35399
+ match(["claude-sonnet-5"], 1),
35400
+ match(["gpt-5.3-codex"], 0.87),
35401
+ match(["gpt-5-mini"], 0.78),
35402
+ match(["gemini-3.5-flash"], 0.76)
35403
+ );
35404
+ }
35405
+ return Math.max(
35406
+ base3,
35407
+ match(["gpt-5.3-codex"], 1),
35408
+ match(["claude-sonnet-5"], 0.98),
35409
+ match(["gpt-5-mini"], 0.96),
35410
+ match(["gemini-3.5-flash"], 0.92),
35411
+ match(["kimi-k3"], 0.9),
35412
+ match(["deepseek-v4-pro", "glm-5.2"], 0.88)
35413
+ );
35414
+ case "tool_agent":
35415
+ if (terminalToolSignal && agentRisk === "complex_high") {
35416
+ return Math.max(
35417
+ base3,
35418
+ match(["claude-sonnet-5"], 1),
35419
+ match(["gpt-5.3-codex"], 0.87),
35420
+ match(["gpt-5-mini"], 0.78),
35421
+ match(["gemini-3.5-flash"], 0.76)
35422
+ );
35423
+ }
35424
+ if (terminalToolSignal && !terminalSafetySensitive) {
35425
+ return Math.max(
35426
+ base3,
35427
+ match(["gpt-5-mini"], 1),
35428
+ match(["gpt-5.3-codex"], 0.98),
35429
+ match(["claude-sonnet-5"], 0.9),
35430
+ match(["gemini-3.5-flash"], 0.89)
35431
+ );
35432
+ }
35433
+ if (terminalToolSignal && terminalSafetySensitive) {
35434
+ return Math.max(
35435
+ base3,
35436
+ match(["claude-sonnet-5"], 1),
35437
+ match(["claude-opus-4.8"], 0.9),
35438
+ match(["gpt-5.3-codex"], 0.84)
35439
+ );
35440
+ }
35441
+ if (agentDomain === "web_research") {
35442
+ return deepWebResearch ? Math.max(
35443
+ base3,
35444
+ match(["claude-sonnet-5"], 1),
35445
+ match(["gpt-5-mini"], 0.88),
35446
+ match(["gemini-3.5-flash"], 0.84),
35447
+ match(["claude-opus-5"], 0.8),
35448
+ match(["claude-opus-4.8"], 0.78)
35449
+ ) : Math.max(
35450
+ base3,
35451
+ match(["claude-sonnet-5"], 1),
35452
+ match(["gpt-5-mini"], 0.88),
35453
+ match(["gemini-3.5-flash"], 0.86),
35454
+ match(["claude-opus-5"], 0.84),
35455
+ match(["claude-opus-4.8"], 0.82)
35456
+ );
35457
+ }
35458
+ if (agentDomain === "retail") {
35459
+ if (agentRisk === "standard") {
35460
+ return Math.max(
35461
+ base3,
35462
+ match(["gpt-5-mini"], 1),
35463
+ match(["claude-sonnet-5"], 0.88),
35464
+ match(["gemini-3.5-flash"], 0.82),
35465
+ match(["gpt-5.3-codex"], 0.81),
35466
+ match(["kimi-k3"], 0.78),
35467
+ match(["deepseek-v4-pro"], 0.76)
35468
+ );
35469
+ }
35470
+ if (agentRisk === "policy_exception") {
35471
+ return Math.max(
35472
+ base3,
35473
+ match(["gpt-4.1"], 1),
35474
+ match(["claude-sonnet-5"], 0.9),
35475
+ match(["deepseek-v4-pro"], 0.82),
35476
+ match(["gpt-5-mini"], 0.8),
35477
+ match(["gpt-4o-mini"], 0.76)
35478
+ );
35479
+ }
35480
+ if (agentRisk === "policy_exception_simple") {
35481
+ return Math.max(
35482
+ base3,
35483
+ match(["gpt-5-mini"], 1),
35484
+ match(["gpt-4.1"], 0.86),
35485
+ match(["deepseek-v4-pro"], 0.82),
35486
+ match(["gpt-4o-mini"], 0.8)
35487
+ );
35488
+ }
35489
+ return Math.max(
35490
+ base3,
35491
+ match(["deepseek-v4-pro"], 1),
35492
+ match(["claude-sonnet-5"], 0.88),
35493
+ match(["gemini-3.5-flash"], 0.82),
35494
+ match(["gpt-5.3-codex"], 0.81),
35495
+ match(["kimi-k3"], 0.78),
35496
+ match(["gpt-5-mini"], 0.76)
35497
+ );
35498
+ }
35499
+ if (agentDomain === "airline") {
35500
+ if (agentRisk === "complex_high") {
35501
+ return Math.max(
35502
+ base3,
35503
+ match(["claude-sonnet-5"], 1),
35504
+ match(["gpt-5-mini"], 0.78),
35505
+ match(["gemini-3.5-flash"], 0.76),
35506
+ match(["deepseek-v4-pro"], 0.74)
35507
+ );
35508
+ }
35509
+ return Math.max(
35510
+ base3,
35511
+ match(["gpt-5-mini"], 1),
35512
+ match(["claude-sonnet-5"], 0.9),
35513
+ match(["gemini-3.5-flash"], 0.8),
35514
+ match(["deepseek-v4-pro"], 0.76)
35515
+ );
35516
+ }
35517
+ return Math.max(
35518
+ base3,
35519
+ match(["claude-sonnet-5"], 1),
35520
+ match(["gemini-3.5-flash"], 0.88),
35521
+ match(["gpt-5.3-codex"], 0.87),
35522
+ match(["gpt-5-mini"], 0.84),
35523
+ match(["kimi-k3"], 0.85),
35524
+ match(["deepseek-v4-pro"], 0.82)
35525
+ );
35526
+ case "tool_agent_parallel":
35527
+ if (terminalToolSignal) {
35528
+ return terminalSafetySensitive ? Math.max(
35529
+ base3,
35530
+ match(["claude-sonnet-5"], 1),
35531
+ match(["claude-opus-4.8"], 0.9),
35532
+ match(["gpt-5.3-codex"], 0.86)
35533
+ ) : Math.max(
35534
+ base3,
35535
+ match(["gpt-5-mini"], 1),
35536
+ match(["gpt-5.3-codex"], 0.98),
35537
+ match(["claude-sonnet-5"], 0.92),
35538
+ match(["gemini-3.5-flash"], 0.88)
35539
+ );
35540
+ }
35541
+ if (agentDomain === "web_research") {
35542
+ return deepWebResearch ? Math.max(
35543
+ base3,
35544
+ match(["claude-sonnet-5"], 1),
35545
+ match(["gpt-5-mini"], 0.88),
35546
+ match(["gemini-3.5-flash"], 0.84),
35547
+ match(["claude-opus-5"], 0.8),
35548
+ match(["claude-opus-4.8"], 0.78)
35549
+ ) : Math.max(
35550
+ base3,
35551
+ match(["claude-sonnet-5"], 1),
35552
+ match(["gpt-5-mini"], 0.88),
35553
+ match(["gemini-3.5-flash"], 0.86),
35554
+ match(["claude-opus-5"], 0.84),
35555
+ match(["claude-opus-4.8"], 0.82)
35556
+ );
35557
+ }
35558
+ if (agentDomain === "retail") {
35559
+ if (agentRisk === "policy_exception") {
35560
+ return Math.max(
35561
+ base3,
35562
+ match(["gpt-4.1"], 1),
35563
+ match(["claude-sonnet-5"], 0.9),
35564
+ match(["deepseek-v4-pro"], 0.82),
35565
+ match(["gpt-5-mini"], 0.8),
35566
+ match(["gpt-4o-mini"], 0.76)
35567
+ );
35568
+ }
35569
+ if (agentRisk === "policy_exception_simple") {
35570
+ return Math.max(
35571
+ base3,
35572
+ match(["gpt-5-mini"], 1),
35573
+ match(["gpt-4.1"], 0.86),
35574
+ match(["deepseek-v4-pro"], 0.82),
35575
+ match(["gpt-4o-mini"], 0.8)
35576
+ );
35577
+ }
35578
+ return Math.max(
35579
+ base3,
35580
+ match(["deepseek-v4-pro"], 1),
35581
+ match(["claude-sonnet-5"], 0.88),
35582
+ match(["claude-opus-4.8"], 0.84),
35583
+ match(["gpt-5-mini"], 0.78),
35584
+ match(["gemini-3.5-flash"], 0.76)
35585
+ );
35586
+ }
35587
+ if (agentDomain === "airline") {
35588
+ return agentRisk === "complex_high" ? Math.max(
35589
+ base3,
35590
+ match(["claude-sonnet-5"], 1),
35591
+ match(["gpt-5-mini"], 0.78),
35592
+ match(["claude-opus-4.8"], 0.76),
35593
+ match(["gemini-3.5-flash"], 0.74)
35594
+ ) : Math.max(
35595
+ base3,
35596
+ match(["gpt-5-mini"], 1),
35597
+ match(["claude-sonnet-5"], 0.9),
35598
+ match(["gemini-3.5-flash"], 0.8)
35599
+ );
35600
+ }
35601
+ return Math.max(
35602
+ base3,
35603
+ match(["claude-opus-4.8"], 1),
35604
+ match(["claude-sonnet-5"], 0.84),
35605
+ match(["grok-4.5"], 0.82),
35606
+ match(["gemini-3.5-flash"], 0.8),
35607
+ match(["deepseek-v4-pro"], 0.78)
35608
+ );
35609
+ case "code_edit":
35610
+ case "debug":
35611
+ return Math.max(
35612
+ base3,
35613
+ match(["gpt-5.3-codex"], 1),
35614
+ match(["claude-sonnet-4.6"], 0.94),
35615
+ match(["glm-5.2"], 0.9),
35616
+ match(["kimi-k2.7", "deepseek-v4-pro"], 0.86)
35617
+ );
35618
+ case "reasoning":
35619
+ return Math.max(
35620
+ base3,
35621
+ match(["claude-sonnet-5", "claude-sonnet-4.6"], 0.98),
35622
+ match(["deepseek-v4-pro"], 0.95),
35623
+ match(["grok-4.5"], 0.94),
35624
+ match(["gemini-3.1-pro", "gemini-3.5-flash"], 0.92)
35625
+ );
35626
+ case "reasoning_mcq":
35627
+ return Math.max(
35628
+ base3,
35629
+ match(["gemini-3-flash-preview"], 1),
35630
+ match(["gemini-3.5-flash"], 0.91),
35631
+ match(["grok-4.5"], 0.9),
35632
+ match(["claude-sonnet-5"], 0.88),
35633
+ match(["deepseek-v4-pro"], 0.84)
35634
+ );
35635
+ case "reasoning_math":
35636
+ return Math.max(
35637
+ base3,
35638
+ match(["gemini-3.5-flash"], 1),
35639
+ match(["grok-4.5"], 0.93),
35640
+ match(["claude-sonnet-5", "deepseek-v4-pro", "kimi-k3"], 0.9),
35641
+ match(["kimi-k2.7"], 0.84)
35642
+ );
35643
+ case "vision":
35644
+ return Math.max(
35645
+ base3,
35646
+ match(["gemini-3.1-pro"], 0.96),
35647
+ match(["qwen3.7-max", "claude-sonnet-4.6", "kimi-k2.7", "grok-4.3"], 0.9)
35648
+ );
35649
+ case "long_context":
35650
+ return Math.max(
35651
+ base3,
35652
+ match(["gemini-3.1-pro"], 1),
35653
+ match(["qwen3.7-max", "glm-5.2"], 0.89),
35654
+ match(["gemini-3.5-flash"], 0.88),
35655
+ match(["deepseek-v4-pro"], 0.85)
35656
+ );
35657
+ case "extraction": {
35658
+ const kimiExtractionAffinity = language === "zh" ? 1 : 0.9;
35659
+ return Math.max(
35660
+ base3,
35661
+ match(["gemini-3.5-flash", "gemini-2.5-flash", "gpt-4o-mini"], 0.9),
35662
+ match(["claude-sonnet-5", "claude-sonnet-4.6"], 0.9),
35663
+ match(["kimi-k3", "kimi-k2.7"], kimiExtractionAffinity)
35664
+ );
35665
+ }
35666
+ default:
35667
+ return Math.max(
35668
+ base3,
35669
+ match(["gemini-3.5-flash", "gemini-2.5-flash", "kimi-k3", "kimi-k2.7"], 0.86)
35670
+ );
35671
+ }
35672
+ }
35673
+ function evidenceCandidates(task) {
35674
+ if (task === "code_agent") {
35675
+ return [
35676
+ "openai/gpt-5.3-codex",
35677
+ "anthropic/claude-sonnet-5",
35678
+ "openai/gpt-5-mini",
35679
+ "google/gemini-3.5-flash",
35680
+ "moonshot/kimi-k3",
35681
+ "deepseek/deepseek-v4-pro"
35682
+ ];
35683
+ }
35684
+ if (task === "tool_agent") {
35685
+ return [
35686
+ "anthropic/claude-sonnet-5",
35687
+ "anthropic/claude-opus-5",
35688
+ "openai/gpt-5-mini",
35689
+ "openai/gpt-4.1",
35690
+ "openai/gpt-4o-mini",
35691
+ "google/gemini-3.5-flash",
35692
+ "openai/gpt-5.3-codex",
35693
+ "moonshot/kimi-k3",
35694
+ "deepseek/deepseek-v4-pro"
35695
+ ];
35696
+ }
35697
+ if (task === "tool_agent_parallel") {
35698
+ return [
35699
+ "anthropic/claude-opus-5",
35700
+ "anthropic/claude-opus-4.8",
35701
+ "anthropic/claude-sonnet-5",
35702
+ "openai/gpt-5-mini",
35703
+ "openai/gpt-4.1",
35704
+ "openai/gpt-4o-mini",
35705
+ "xai/grok-4.5",
35706
+ "google/gemini-3.5-flash",
35707
+ "deepseek/deepseek-v4-pro"
35708
+ ];
35709
+ }
35710
+ if (task === "long_context") {
35711
+ return [
35712
+ "google/gemini-3.1-pro",
35713
+ "deepseek/deepseek-v4-pro",
35714
+ "qwen/qwen3.7-max",
35715
+ "zai/glm-5.2",
35716
+ "google/gemini-3.5-flash"
35717
+ ];
35718
+ }
35719
+ if (task === "reasoning_mcq") {
35720
+ return [
35721
+ "google/gemini-3-flash-preview",
35722
+ "google/gemini-3.5-flash",
35723
+ "xai/grok-4.5",
35724
+ "anthropic/claude-sonnet-5",
35725
+ "deepseek/deepseek-v4-pro"
35726
+ ];
35727
+ }
35728
+ if (task === "reasoning_math") {
35729
+ return [
35730
+ "google/gemini-3.5-flash",
35731
+ "xai/grok-4.5",
35732
+ "anthropic/claude-sonnet-5",
35733
+ "deepseek/deepseek-v4-pro",
35734
+ "moonshot/kimi-k3"
35735
+ ];
35736
+ }
35737
+ return [];
35738
+ }
35739
+ function isEligible(modelId, features, maxOutputTokens, options) {
35740
+ const model = options.modelCapabilities?.[modelId] ?? DEFAULT_MODEL_CAPABILITIES[modelId];
35741
+ if (!model) return true;
35742
+ if (features.needsTools && !model.supportsTools) return false;
35743
+ if (features.needsVision && !model.supportsVision) return false;
35744
+ if (features.needsStructuredOutput && !model.supportsTools) return false;
35745
+ if (model.maxOutputTokens < maxOutputTokens) return false;
35746
+ return model.contextWindow >= (features.estimatedInputTokens + maxOutputTokens) * 1.1;
35747
+ }
35748
+ function estimatedCost(modelId, options, inputTokens, outputTokens) {
35749
+ const price = options.modelPricing.get(modelId);
35750
+ if (!price) return Number.POSITIVE_INFINITY;
35751
+ if (price.flatPrice !== void 0) return price.flatPrice;
35752
+ return (inputTokens * price.inputPrice + outputTokens * price.outputPrice) / 1e6;
35753
+ }
35754
+ function profileScore(modelId, options, now2) {
35755
+ const profile = options.modelPerformance?.[modelId] ?? LIVE_MODEL_PROFILES[modelId] ?? HISTORICAL_MODEL_PROFILES[modelId];
35756
+ if (!profile) return void 0;
35757
+ const measuredAt = Date.parse(profile.measuredAt);
35758
+ if (!Number.isFinite(measuredAt)) return void 0;
35759
+ const ageDays = Math.max(0, (now2.getTime() - measuredAt) / 864e5);
35760
+ const sampleConfidence = profile.samples === void 0 ? 1 : Math.min(1, Math.max(0, profile.samples) / 10);
35761
+ const freshness = Math.pow(0.5, ageDays / 30) * sampleConfidence;
35762
+ const quality = profile.intelligenceIndex === void 0 ? void 0 : Math.min(1, profile.intelligenceIndex / 50);
35763
+ const speed = Math.min(
35764
+ 1,
35765
+ (2e3 / Math.max(500, profile.latencyMs) + profile.outputTokensPerSecond / 250) / 2
35766
+ );
35767
+ const tailSpeed = Math.min(1, 3e3 / Math.max(750, profile.p95LatencyMs ?? profile.latencyMs));
35768
+ const reliability = Math.max(0, 1 - (profile.errorRate ?? 0));
35769
+ return { quality, speed, tailSpeed, reliability, freshness };
35770
+ }
35771
+ function route(prompt, systemPrompt, maxOutputTokens, options) {
35772
+ const strategy = getStrategy(options.config.strategy ?? "portfolio");
35773
+ return strategy.route(prompt, systemPrompt, maxOutputTokens, options);
35774
+ }
35775
+ var BASELINE_MODEL_ID, BASELINE_INPUT_PRICE, BASELINE_OUTPUT_PRICE, SERVER_MARGIN_PERCENT, MIN_PAYMENT_USD, RulesStrategy, registry, DEFAULT_MODEL_CAPABILITIES, model_profiles_generated_default, LIVE_MODEL_PROFILES, HISTORICAL_MODEL_PROFILES, DEFAULT_PORTFOLIO_WEIGHTS, PortfolioStrategy, DEFAULT_ROUTING_CONFIG;
35776
+ var init_dist = __esm({
35777
+ "node_modules/@blockrun/router-core/dist/index.js"() {
35160
35778
  "use strict";
35161
- init_rules();
35162
- init_selector();
35779
+ BASELINE_MODEL_ID = "anthropic/claude-opus-4.7";
35780
+ BASELINE_INPUT_PRICE = 5;
35781
+ BASELINE_OUTPUT_PRICE = 25;
35782
+ SERVER_MARGIN_PERCENT = 5;
35783
+ MIN_PAYMENT_USD = 1e-3;
35163
35784
  RulesStrategy = class {
35164
35785
  name = "rules";
35165
35786
  route(prompt, systemPrompt, maxOutputTokens, options) {
35166
35787
  const { config, modelPricing } = options;
35167
35788
  const fullText = `${systemPrompt ?? ""} ${prompt}`;
35168
35789
  const estimatedTokens = Math.ceil(fullText.length / 4);
35169
- const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
35790
+ const scanLimit = Math.max(1, Math.min(8e3, config.classifier.promptTruncationChars));
35791
+ const sample = (value) => {
35792
+ if (value.length <= scanLimit) return value;
35793
+ const prefixLength = Math.ceil(scanLimit / 2);
35794
+ return `${value.slice(0, prefixLength)}
35795
+ ${value.slice(-(scanLimit - prefixLength))}`;
35796
+ };
35797
+ const scannedPrompt = sample(prompt);
35798
+ const scannedSystemPrompt = systemPrompt ? sample(systemPrompt) : void 0;
35799
+ const ruleResult = classifyByRules(
35800
+ scannedPrompt,
35801
+ scannedSystemPrompt,
35802
+ estimatedTokens,
35803
+ config.scoring
35804
+ );
35170
35805
  const { routingProfile } = options;
35171
35806
  let tierConfigs;
35172
35807
  let profileSuffix;
@@ -35183,7 +35818,7 @@ var init_strategy = __esm({
35183
35818
  const agenticScore = ruleResult.agenticScore ?? 0;
35184
35819
  const isAutoAgentic = agenticScore >= 0.5;
35185
35820
  const agenticModeSetting = config.overrides.agenticMode;
35186
- const hasToolsInRequest = options.hasTools ?? false;
35821
+ const hasToolsInRequest = options.requiresTools ?? options.hasTools ?? false;
35187
35822
  let useAgenticTiers;
35188
35823
  if (agenticModeSetting === false) {
35189
35824
  useAgenticTiers = false;
@@ -35213,7 +35848,7 @@ var init_strategy = __esm({
35213
35848
  );
35214
35849
  return { ...decision2, tierConfigs, profile };
35215
35850
  }
35216
- const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
35851
+ const hasStructuredOutput = options.requiresStructuredOutput === true || (scannedSystemPrompt ? /json|structured|schema/i.test(scannedSystemPrompt) : false);
35217
35852
  let tier;
35218
35853
  let confidence;
35219
35854
  const method = "rules";
@@ -35252,16 +35887,789 @@ var init_strategy = __esm({
35252
35887
  };
35253
35888
  registry = /* @__PURE__ */ new Map();
35254
35889
  registry.set("rules", new RulesStrategy());
35255
- }
35256
- });
35257
-
35258
- // src/router/config.ts
35259
- var DEFAULT_ROUTING_CONFIG;
35260
- var init_config = __esm({
35261
- "src/router/config.ts"() {
35262
- "use strict";
35890
+ DEFAULT_MODEL_CAPABILITIES = Object.freeze({
35891
+ "anthropic/claude-fable-5": {
35892
+ contextWindow: 1e6,
35893
+ maxOutputTokens: 128e3,
35894
+ supportsTools: true,
35895
+ supportsVision: true
35896
+ },
35897
+ "anthropic/claude-haiku-4.5": {
35898
+ contextWindow: 2e5,
35899
+ maxOutputTokens: 8192,
35900
+ supportsTools: true,
35901
+ supportsVision: true
35902
+ },
35903
+ "anthropic/claude-opus-4.6": {
35904
+ contextWindow: 1e6,
35905
+ maxOutputTokens: 128e3,
35906
+ supportsTools: true,
35907
+ supportsVision: true
35908
+ },
35909
+ "anthropic/claude-opus-4.7": {
35910
+ contextWindow: 1e6,
35911
+ maxOutputTokens: 128e3,
35912
+ supportsTools: true,
35913
+ supportsVision: true
35914
+ },
35915
+ "anthropic/claude-opus-4.8": {
35916
+ contextWindow: 1e6,
35917
+ maxOutputTokens: 128e3,
35918
+ supportsTools: true,
35919
+ supportsVision: true
35920
+ },
35921
+ "anthropic/claude-opus-5": {
35922
+ contextWindow: 1e6,
35923
+ maxOutputTokens: 128e3,
35924
+ supportsTools: true,
35925
+ supportsVision: true
35926
+ },
35927
+ "anthropic/claude-sonnet-4.6": {
35928
+ contextWindow: 2e5,
35929
+ maxOutputTokens: 64e3,
35930
+ supportsTools: true,
35931
+ supportsVision: true
35932
+ },
35933
+ "anthropic/claude-sonnet-5": {
35934
+ contextWindow: 1e6,
35935
+ maxOutputTokens: 128e3,
35936
+ supportsTools: true,
35937
+ supportsVision: true
35938
+ },
35939
+ "deepseek/deepseek-chat": {
35940
+ contextWindow: 1e6,
35941
+ maxOutputTokens: 8192,
35942
+ supportsTools: true,
35943
+ supportsVision: false
35944
+ },
35945
+ "deepseek/deepseek-reasoner": {
35946
+ contextWindow: 1e6,
35947
+ maxOutputTokens: 8192,
35948
+ supportsTools: true,
35949
+ supportsVision: false
35950
+ },
35951
+ "deepseek/deepseek-v4-pro": {
35952
+ contextWindow: 1048576,
35953
+ maxOutputTokens: 65536,
35954
+ supportsTools: true,
35955
+ supportsVision: false
35956
+ },
35957
+ "free/deepseek-v4-flash": {
35958
+ contextWindow: 1e6,
35959
+ maxOutputTokens: 16384,
35960
+ supportsTools: false,
35961
+ supportsVision: false
35962
+ },
35963
+ "free/gpt-oss-120b": {
35964
+ contextWindow: 128e3,
35965
+ maxOutputTokens: 16384,
35966
+ supportsTools: false,
35967
+ supportsVision: false
35968
+ },
35969
+ "free/gpt-oss-20b": {
35970
+ contextWindow: 128e3,
35971
+ maxOutputTokens: 16384,
35972
+ supportsTools: false,
35973
+ supportsVision: false
35974
+ },
35975
+ "free/seed-oss-36b": {
35976
+ contextWindow: 131072,
35977
+ maxOutputTokens: 16384,
35978
+ supportsTools: false,
35979
+ supportsVision: false
35980
+ },
35981
+ "google/gemini-2.5-flash": {
35982
+ contextWindow: 1e6,
35983
+ maxOutputTokens: 65536,
35984
+ supportsTools: true,
35985
+ supportsVision: true
35986
+ },
35987
+ "google/gemini-2.5-flash-lite": {
35988
+ contextWindow: 1e6,
35989
+ maxOutputTokens: 65536,
35990
+ supportsTools: true,
35991
+ supportsVision: false
35992
+ },
35993
+ "google/gemini-2.5-pro": {
35994
+ contextWindow: 105e4,
35995
+ maxOutputTokens: 65536,
35996
+ supportsTools: true,
35997
+ supportsVision: true
35998
+ },
35999
+ "google/gemini-3-flash-preview": {
36000
+ contextWindow: 1e6,
36001
+ maxOutputTokens: 65536,
36002
+ supportsTools: false,
36003
+ supportsVision: true
36004
+ },
36005
+ "google/gemini-3.1-flash-lite": {
36006
+ contextWindow: 1e6,
36007
+ maxOutputTokens: 8192,
36008
+ supportsTools: true,
36009
+ supportsVision: false
36010
+ },
36011
+ "google/gemini-3.1-pro": {
36012
+ contextWindow: 105e4,
36013
+ maxOutputTokens: 65536,
36014
+ supportsTools: true,
36015
+ supportsVision: true
36016
+ },
36017
+ "google/gemini-3.5-flash": {
36018
+ contextWindow: 1048576,
36019
+ maxOutputTokens: 65536,
36020
+ supportsTools: true,
36021
+ supportsVision: true
36022
+ },
36023
+ "moonshot/kimi-k2.5": {
36024
+ contextWindow: 262144,
36025
+ maxOutputTokens: 16384,
36026
+ supportsTools: true,
36027
+ supportsVision: true
36028
+ },
36029
+ "moonshot/kimi-k2.6": {
36030
+ contextWindow: 262144,
36031
+ maxOutputTokens: 65536,
36032
+ supportsTools: true,
36033
+ supportsVision: true
36034
+ },
36035
+ "moonshot/kimi-k2.7": {
36036
+ contextWindow: 262144,
36037
+ maxOutputTokens: 65536,
36038
+ supportsTools: true,
36039
+ supportsVision: true
36040
+ },
36041
+ "moonshot/kimi-k3": {
36042
+ contextWindow: 1048576,
36043
+ maxOutputTokens: 65536,
36044
+ supportsTools: true,
36045
+ supportsVision: true
36046
+ },
36047
+ "openai/gpt-4.1": {
36048
+ contextWindow: 128e3,
36049
+ maxOutputTokens: 16384,
36050
+ supportsTools: true,
36051
+ supportsVision: true
36052
+ },
36053
+ "openai/gpt-4o-mini": {
36054
+ contextWindow: 128e3,
36055
+ maxOutputTokens: 16384,
36056
+ supportsTools: true,
36057
+ supportsVision: false
36058
+ },
36059
+ "openai/gpt-5-mini": {
36060
+ contextWindow: 2e5,
36061
+ maxOutputTokens: 65536,
36062
+ supportsTools: true,
36063
+ supportsVision: false
36064
+ },
36065
+ "openai/gpt-5.3-codex": {
36066
+ contextWindow: 4e5,
36067
+ maxOutputTokens: 128e3,
36068
+ supportsTools: true,
36069
+ supportsVision: false
36070
+ },
36071
+ "openai/gpt-5.4": {
36072
+ contextWindow: 4e5,
36073
+ maxOutputTokens: 128e3,
36074
+ supportsTools: true,
36075
+ supportsVision: true
36076
+ },
36077
+ "openai/gpt-5.4-nano": {
36078
+ contextWindow: 105e4,
36079
+ maxOutputTokens: 32768,
36080
+ supportsTools: true,
36081
+ supportsVision: false
36082
+ },
36083
+ "openai/gpt-5.5": {
36084
+ contextWindow: 105e4,
36085
+ maxOutputTokens: 128e3,
36086
+ supportsTools: true,
36087
+ supportsVision: true
36088
+ },
36089
+ "openai/gpt-5.6-terra": {
36090
+ contextWindow: 105e4,
36091
+ maxOutputTokens: 128e3,
36092
+ supportsTools: true,
36093
+ supportsVision: true
36094
+ },
36095
+ "openai/o3": {
36096
+ contextWindow: 2e5,
36097
+ maxOutputTokens: 1e5,
36098
+ supportsTools: true,
36099
+ supportsVision: false
36100
+ },
36101
+ "openai/o4-mini": {
36102
+ contextWindow: 128e3,
36103
+ maxOutputTokens: 65536,
36104
+ supportsTools: true,
36105
+ supportsVision: false
36106
+ },
36107
+ "qwen/qwen3.7-max": {
36108
+ contextWindow: 1e6,
36109
+ maxOutputTokens: 65536,
36110
+ supportsTools: true,
36111
+ supportsVision: false
36112
+ },
36113
+ "xai/grok-3-mini": {
36114
+ contextWindow: 131072,
36115
+ maxOutputTokens: 16384,
36116
+ supportsTools: true,
36117
+ supportsVision: false
36118
+ },
36119
+ "xai/grok-4-0709": {
36120
+ contextWindow: 131072,
36121
+ maxOutputTokens: 16384,
36122
+ supportsTools: true,
36123
+ supportsVision: false
36124
+ },
36125
+ "xai/grok-4-1-fast-non-reasoning": {
36126
+ contextWindow: 131072,
36127
+ maxOutputTokens: 16384,
36128
+ supportsTools: true,
36129
+ supportsVision: false
36130
+ },
36131
+ "xai/grok-4-1-fast-reasoning": {
36132
+ contextWindow: 131072,
36133
+ maxOutputTokens: 16384,
36134
+ supportsTools: true,
36135
+ supportsVision: false
36136
+ },
36137
+ "xai/grok-4-fast-non-reasoning": {
36138
+ contextWindow: 131072,
36139
+ maxOutputTokens: 16384,
36140
+ supportsTools: true,
36141
+ supportsVision: false
36142
+ },
36143
+ "xai/grok-4-fast-reasoning": {
36144
+ contextWindow: 131072,
36145
+ maxOutputTokens: 16384,
36146
+ supportsTools: true,
36147
+ supportsVision: false
36148
+ },
36149
+ "xai/grok-4.5": {
36150
+ contextWindow: 5e5,
36151
+ maxOutputTokens: 16384,
36152
+ supportsTools: true,
36153
+ supportsVision: true
36154
+ },
36155
+ "zai/glm-5.1": {
36156
+ contextWindow: 2e5,
36157
+ maxOutputTokens: 128e3,
36158
+ supportsTools: true,
36159
+ supportsVision: false
36160
+ },
36161
+ "zai/glm-5.2": {
36162
+ contextWindow: 1e6,
36163
+ maxOutputTokens: 262144,
36164
+ supportsTools: true,
36165
+ supportsVision: false
36166
+ }
36167
+ });
36168
+ model_profiles_generated_default = {
36169
+ "openai/gpt-5.5": {
36170
+ measuredAt: "2026-07-21T10:21:31Z",
36171
+ latencyMs: 6243.1,
36172
+ p95LatencyMs: 9865,
36173
+ outputTokensPerSecond: 12.53,
36174
+ errorRate: 0,
36175
+ samples: 3
36176
+ },
36177
+ "openai/gpt-5.4-pro": {
36178
+ measuredAt: "2026-07-21T10:21:31Z",
36179
+ latencyMs: 13015.5,
36180
+ p95LatencyMs: 23976.4,
36181
+ outputTokensPerSecond: 6.42,
36182
+ errorRate: 0,
36183
+ samples: 3
36184
+ },
36185
+ "openai/gpt-5.4-mini": {
36186
+ measuredAt: "2026-07-21T10:21:31Z",
36187
+ latencyMs: 5550,
36188
+ p95LatencyMs: 6595.7,
36189
+ outputTokensPerSecond: 11.96,
36190
+ errorRate: 0.3333,
36191
+ samples: 3
36192
+ },
36193
+ "openai/gpt-5.3-codex": {
36194
+ measuredAt: "2026-07-21T10:21:31Z",
36195
+ latencyMs: 4617.1,
36196
+ p95LatencyMs: 5800.7,
36197
+ outputTokensPerSecond: 12.48,
36198
+ errorRate: 0,
36199
+ samples: 3
36200
+ },
36201
+ "anthropic/claude-opus-4.8": {
36202
+ measuredAt: "2026-07-21T10:21:31Z",
36203
+ latencyMs: 3915.1,
36204
+ p95LatencyMs: 6130.8,
36205
+ outputTokensPerSecond: 16.33,
36206
+ errorRate: 0,
36207
+ samples: 3
36208
+ },
36209
+ "anthropic/claude-opus-4.6": {
36210
+ measuredAt: "2026-07-21T10:21:31Z",
36211
+ latencyMs: 3765.5,
36212
+ p95LatencyMs: 4257.2,
36213
+ outputTokensPerSecond: 14.18,
36214
+ errorRate: 0,
36215
+ samples: 3
36216
+ },
36217
+ "anthropic/claude-sonnet-4.6": {
36218
+ measuredAt: "2026-07-21T10:21:31Z",
36219
+ latencyMs: 3860.6,
36220
+ p95LatencyMs: 5093.5,
36221
+ outputTokensPerSecond: 13.85,
36222
+ errorRate: 0,
36223
+ samples: 3
36224
+ },
36225
+ "anthropic/claude-haiku-4.5": {
36226
+ measuredAt: "2026-07-21T10:21:31Z",
36227
+ latencyMs: 2734.9,
36228
+ p95LatencyMs: 3181.6,
36229
+ outputTokensPerSecond: 19.58,
36230
+ errorRate: 0,
36231
+ samples: 3
36232
+ },
36233
+ "google/gemini-3.1-pro": {
36234
+ measuredAt: "2026-07-21T10:21:31Z",
36235
+ latencyMs: 13935.7,
36236
+ p95LatencyMs: 26675.3,
36237
+ outputTokensPerSecond: 77.47,
36238
+ errorRate: 0,
36239
+ samples: 3
36240
+ },
36241
+ "google/gemini-3.5-flash": {
36242
+ measuredAt: "2026-07-21T10:21:31Z",
36243
+ latencyMs: 4608.7,
36244
+ p95LatencyMs: 8420.9,
36245
+ outputTokensPerSecond: 57.88,
36246
+ errorRate: 0,
36247
+ samples: 3
36248
+ },
36249
+ "google/gemini-3.1-flash-lite": {
36250
+ measuredAt: "2026-07-21T10:21:31Z",
36251
+ latencyMs: 4619.7,
36252
+ p95LatencyMs: 9927.1,
36253
+ outputTokensPerSecond: 42.01,
36254
+ errorRate: 0,
36255
+ samples: 3
36256
+ },
36257
+ "google/gemini-2.5-flash": {
36258
+ measuredAt: "2026-07-21T10:21:31Z",
36259
+ latencyMs: 5506.9,
36260
+ p95LatencyMs: 11462.5,
36261
+ outputTokensPerSecond: 65.19,
36262
+ errorRate: 0,
36263
+ samples: 3
36264
+ },
36265
+ "deepseek/deepseek-v4-pro": {
36266
+ measuredAt: "2026-07-21T10:21:31Z",
36267
+ latencyMs: 6044.8,
36268
+ p95LatencyMs: 10782.3,
36269
+ outputTokensPerSecond: 22.47,
36270
+ errorRate: 0,
36271
+ samples: 3
36272
+ },
36273
+ "deepseek/deepseek-reasoner": {
36274
+ measuredAt: "2026-07-21T10:21:31Z",
36275
+ latencyMs: 4111.9,
36276
+ p95LatencyMs: 5305.7,
36277
+ outputTokensPerSecond: 16.46,
36278
+ errorRate: 0,
36279
+ samples: 3
36280
+ },
36281
+ "deepseek/deepseek-chat": {
36282
+ measuredAt: "2026-07-21T10:21:31Z",
36283
+ latencyMs: 2648.6,
36284
+ p95LatencyMs: 3524.1,
36285
+ outputTokensPerSecond: 16.73,
36286
+ errorRate: 0,
36287
+ samples: 3
36288
+ },
36289
+ "moonshot/kimi-k2.7": {
36290
+ measuredAt: "2026-07-21T10:21:31Z",
36291
+ latencyMs: 4295.4,
36292
+ p95LatencyMs: 6153.8,
36293
+ outputTokensPerSecond: 18.54,
36294
+ errorRate: 0,
36295
+ samples: 3
36296
+ },
36297
+ "qwen/qwen3.7-max": {
36298
+ measuredAt: "2026-07-21T10:21:31Z",
36299
+ latencyMs: 30729.4,
36300
+ p95LatencyMs: 39622,
36301
+ outputTokensPerSecond: 36.89,
36302
+ errorRate: 0.3333,
36303
+ samples: 3
36304
+ },
36305
+ "xai/grok-4.3": {
36306
+ measuredAt: "2026-07-21T10:21:31Z",
36307
+ latencyMs: 6946.1,
36308
+ p95LatencyMs: 9495.4,
36309
+ outputTokensPerSecond: 65.3,
36310
+ errorRate: 0,
36311
+ samples: 3
36312
+ },
36313
+ "xai/grok-4.20-reasoning": {
36314
+ measuredAt: "2026-07-21T10:21:31Z",
36315
+ latencyMs: 3472.4,
36316
+ p95LatencyMs: 5332.4,
36317
+ outputTokensPerSecond: 13.27,
36318
+ errorRate: 0,
36319
+ samples: 3
36320
+ },
36321
+ "xai/grok-4.20-non-reasoning": {
36322
+ measuredAt: "2026-07-21T10:21:31Z",
36323
+ latencyMs: 5174.4,
36324
+ p95LatencyMs: 6081.7,
36325
+ outputTokensPerSecond: 10.21,
36326
+ errorRate: 0.3333,
36327
+ samples: 3
36328
+ },
36329
+ "xai/grok-4-1-fast-reasoning": {
36330
+ measuredAt: "2026-07-21T10:21:31Z",
36331
+ latencyMs: 13148.2,
36332
+ p95LatencyMs: 19104.2,
36333
+ outputTokensPerSecond: 4.28,
36334
+ errorRate: 0,
36335
+ samples: 3
36336
+ },
36337
+ "minimax/minimax-m3": {
36338
+ measuredAt: "2026-07-21T10:21:31Z",
36339
+ latencyMs: 3385,
36340
+ p95LatencyMs: 4247.2,
36341
+ outputTokensPerSecond: 15.16,
36342
+ errorRate: 0,
36343
+ samples: 3
36344
+ },
36345
+ "minimax/minimax-m2.7": {
36346
+ measuredAt: "2026-07-21T10:21:31Z",
36347
+ latencyMs: 4596.7,
36348
+ p95LatencyMs: 6884.6,
36349
+ outputTokensPerSecond: 17.03,
36350
+ errorRate: 0,
36351
+ samples: 3
36352
+ },
36353
+ "zai/glm-5.2": {
36354
+ measuredAt: "2026-07-21T10:21:31Z",
36355
+ latencyMs: 4406.3,
36356
+ p95LatencyMs: 6139.7,
36357
+ outputTokensPerSecond: 10.41,
36358
+ errorRate: 0,
36359
+ samples: 3
36360
+ },
36361
+ "zai/glm-5.1": {
36362
+ measuredAt: "2026-07-21T10:21:31Z",
36363
+ latencyMs: 7775.4,
36364
+ p95LatencyMs: 9182.1,
36365
+ outputTokensPerSecond: 6.08,
36366
+ errorRate: 0,
36367
+ samples: 3
36368
+ },
36369
+ "zai/glm-5": {
36370
+ measuredAt: "2026-07-21T10:21:31Z",
36371
+ latencyMs: 4159.4,
36372
+ p95LatencyMs: 4992.7,
36373
+ outputTokensPerSecond: 10.28,
36374
+ errorRate: 0,
36375
+ samples: 3
36376
+ },
36377
+ "free/qwen3-coder-480b": {
36378
+ measuredAt: "2026-07-21T10:21:31Z",
36379
+ latencyMs: 2063.9,
36380
+ p95LatencyMs: 3646.3,
36381
+ outputTokensPerSecond: 39.8,
36382
+ errorRate: 0,
36383
+ samples: 3
36384
+ },
36385
+ "free/mistral-large-3-675b": {
36386
+ measuredAt: "2026-07-21T10:21:31Z",
36387
+ latencyMs: 3147.5,
36388
+ p95LatencyMs: 5555.3,
36389
+ outputTokensPerSecond: 27.76,
36390
+ errorRate: 0,
36391
+ samples: 3
36392
+ },
36393
+ "free/nemotron-3-nano-omni-30b-a3b-reasoning": {
36394
+ measuredAt: "2026-07-21T10:21:31Z",
36395
+ latencyMs: 6508.4,
36396
+ p95LatencyMs: 14252.7,
36397
+ outputTokensPerSecond: 68.26,
36398
+ errorRate: 0,
36399
+ samples: 3
36400
+ },
36401
+ "free/glm-4.7": {
36402
+ measuredAt: "2026-07-21T10:21:31Z",
36403
+ latencyMs: 2014.8,
36404
+ p95LatencyMs: 3039.9,
36405
+ outputTokensPerSecond: 39.92,
36406
+ errorRate: 0,
36407
+ samples: 3
36408
+ }
36409
+ };
36410
+ LIVE_MODEL_PROFILES = Object.freeze(
36411
+ model_profiles_generated_default
36412
+ );
36413
+ HISTORICAL_MODEL_PROFILES = Object.freeze({
36414
+ "anthropic/claude-haiku-4.5": {
36415
+ measuredAt: "2026-03-16T13:50:48Z",
36416
+ latencyMs: 2305,
36417
+ outputTokensPerSecond: 140.6
36418
+ },
36419
+ "anthropic/claude-opus-4.6": {
36420
+ measuredAt: "2026-03-16T13:50:48Z",
36421
+ latencyMs: 2139,
36422
+ outputTokensPerSecond: 119.7
36423
+ },
36424
+ "anthropic/claude-sonnet-4.6": {
36425
+ measuredAt: "2026-03-16T13:50:48Z",
36426
+ latencyMs: 2110,
36427
+ outputTokensPerSecond: 121.3
36428
+ },
36429
+ "deepseek/deepseek-chat": {
36430
+ measuredAt: "2026-03-16T13:50:48Z",
36431
+ latencyMs: 1431,
36432
+ outputTokensPerSecond: 179.2,
36433
+ intelligenceIndex: 32
36434
+ },
36435
+ "google/gemini-2.5-flash": {
36436
+ measuredAt: "2026-03-16T13:50:48Z",
36437
+ latencyMs: 1238,
36438
+ outputTokensPerSecond: 207.6,
36439
+ intelligenceIndex: 20
36440
+ },
36441
+ "google/gemini-2.5-flash-lite": {
36442
+ measuredAt: "2026-03-16T13:50:48Z",
36443
+ latencyMs: 1353,
36444
+ outputTokensPerSecond: 192.5,
36445
+ intelligenceIndex: 20
36446
+ },
36447
+ "google/gemini-2.5-pro": {
36448
+ measuredAt: "2026-03-16T13:50:48Z",
36449
+ latencyMs: 1294,
36450
+ outputTokensPerSecond: 197.8
36451
+ },
36452
+ "google/gemini-3.1-pro": {
36453
+ measuredAt: "2026-03-16T13:50:48Z",
36454
+ latencyMs: 1609,
36455
+ outputTokensPerSecond: 167.2
36456
+ },
36457
+ "moonshot/kimi-k2.5": {
36458
+ measuredAt: "2026-03-16T13:50:48Z",
36459
+ latencyMs: 1646,
36460
+ outputTokensPerSecond: 155.7
36461
+ },
36462
+ "openai/gpt-4o-mini": {
36463
+ measuredAt: "2026-03-16T13:50:48Z",
36464
+ latencyMs: 2764,
36465
+ outputTokensPerSecond: 92.8
36466
+ },
36467
+ "openai/gpt-5.3-codex": {
36468
+ measuredAt: "2026-03-16T13:50:48Z",
36469
+ latencyMs: 7935,
36470
+ outputTokensPerSecond: 32.3
36471
+ },
36472
+ "xai/grok-4-1-fast-non-reasoning": {
36473
+ measuredAt: "2026-03-16T13:50:48Z",
36474
+ latencyMs: 1244,
36475
+ outputTokensPerSecond: 205.8,
36476
+ intelligenceIndex: 41
36477
+ },
36478
+ "xai/grok-4-1-fast-reasoning": {
36479
+ measuredAt: "2026-03-16T13:50:48Z",
36480
+ latencyMs: 1454,
36481
+ outputTokensPerSecond: 176.2,
36482
+ intelligenceIndex: 41
36483
+ }
36484
+ });
36485
+ DEFAULT_PORTFOLIO_WEIGHTS = {
36486
+ auto: {
36487
+ quality: 0.47,
36488
+ capability: 0.2,
36489
+ cost: 0.18,
36490
+ speed: 0.07,
36491
+ reliability: 0.03,
36492
+ legacy: 0.05
36493
+ },
36494
+ eco: { quality: 0.36, capability: 0.2, cost: 0.28, speed: 0.1, reliability: 0.04, legacy: 0.02 },
36495
+ premium: {
36496
+ quality: 0.58,
36497
+ capability: 0.2,
36498
+ cost: 0.08,
36499
+ speed: 0.06,
36500
+ reliability: 0.06,
36501
+ legacy: 0.02
36502
+ },
36503
+ highStakesBoost: { quality: 0.08, reliability: 0.05 },
36504
+ latencySensitiveSpeedBoost: 0.08,
36505
+ affinityFloorGap: { auto: 0.1, eco: 0.22, premium: 0.05 }
36506
+ };
36507
+ PortfolioStrategy = class {
36508
+ name = "portfolio";
36509
+ route(prompt, systemPrompt, maxOutputTokens, options) {
36510
+ const features = classifyTask(prompt, systemPrompt, options);
36511
+ const base3 = new RulesStrategy().route(prompt, systemPrompt, maxOutputTokens, {
36512
+ ...options,
36513
+ requiresTools: features.needsTools
36514
+ });
36515
+ const tierConfigs = base3.tierConfigs;
36516
+ if (!tierConfigs) return base3;
36517
+ const targetTier = (features.taskType === "reasoning_mcq" || features.taskType === "reasoning_math") && (base3.tier === "SIMPLE" || base3.tier === "MEDIUM") ? "REASONING" : base3.tier;
36518
+ const tierConfig = tierConfigs[targetTier];
36519
+ const configuredCandidates = tierConfig ? getFallbackChain(targetTier, tierConfigs) : [];
36520
+ const chain3 = [
36521
+ .../* @__PURE__ */ new Set([...configuredCandidates, ...evidenceCandidates(features.taskType)])
36522
+ ].filter((model2) => typeof model2 === "string" && model2.length > 0);
36523
+ const eligible = chain3.filter(
36524
+ (model2) => isEligible(model2, features, maxOutputTokens, options)
36525
+ );
36526
+ const eligibleCandidates = eligible.length > 0 ? eligible : chain3;
36527
+ if (eligibleCandidates.length === 0) return base3;
36528
+ const profileName = options.routingProfile === "eco" ? "eco" : options.routingProfile === "premium" ? "premium" : "auto";
36529
+ const portfolio = options.config.portfolio ?? DEFAULT_PORTFOLIO_WEIGHTS;
36530
+ const getAffinity = (model2) => affinity(
36531
+ model2,
36532
+ features.taskType,
36533
+ features.language,
36534
+ features.agentDomain,
36535
+ features.deepWebResearch,
36536
+ features.agentRisk,
36537
+ features.terminalToolSignal,
36538
+ features.terminalSafetySensitive
36539
+ );
36540
+ const bestAffinity = Math.max(...eligibleCandidates.map(getAffinity));
36541
+ const specificAffinity = eligibleCandidates.filter((model2) => getAffinity(model2) > 0.68);
36542
+ const affinityPool = specificAffinity.length > 0 ? specificAffinity : [eligibleCandidates[0]];
36543
+ const affinityFloorGap = features.terminalToolSignal ? Math.max(
36544
+ portfolio.affinityFloorGap[profileName],
36545
+ features.terminalSafetySensitive ? 0.15 : 0.12
36546
+ ) : portfolio.affinityFloorGap[profileName];
36547
+ const candidates = affinityPool.filter(
36548
+ (model2) => getAffinity(model2) >= bestAffinity - affinityFloorGap
36549
+ );
36550
+ const costs = candidates.map(
36551
+ (model2) => estimatedCost(model2, options, features.estimatedInputTokens, maxOutputTokens)
36552
+ );
36553
+ const finiteCosts = costs.filter(Number.isFinite);
36554
+ const minCost = finiteCosts.length > 0 ? Math.min(...finiteCosts) : 0;
36555
+ const maxCost = finiteCosts.length > 0 ? Math.max(...finiteCosts) : 1;
36556
+ const now2 = options.now ?? /* @__PURE__ */ new Date();
36557
+ const profileWeights = portfolio[profileName];
36558
+ const rankedEntries = candidates.map((model2, index2) => {
36559
+ const cost = estimatedCost(model2, options, features.estimatedInputTokens, maxOutputTokens);
36560
+ const costScore = Number.isFinite(cost) && maxCost > minCost ? 1 - (cost - minCost) / (maxCost - minCost) : 0.5;
36561
+ const capabilityScore = isEligible(model2, features, maxOutputTokens, options) ? 1 : 0;
36562
+ const profile = profileScore(model2, options, now2);
36563
+ const observedQuality = profile?.quality === void 0 ? getAffinity(model2) : getAffinity(model2) * (1 - profile.freshness) + profile.quality * profile.freshness;
36564
+ const observedSpeed = profile ? profile.speed * profile.freshness : 0.5;
36565
+ const observedTailSpeed = profile ? profile.tailSpeed * profile.freshness : 0.5;
36566
+ const observedReliability = profile ? profile.reliability * profile.freshness + (1 - profile.freshness) : 1;
36567
+ const legacyScore = 1 - index2 / Math.max(1, candidates.length - 1);
36568
+ const qualityWeight = profileWeights.quality + (features.highStakes ? portfolio.highStakesBoost.quality : 0);
36569
+ const speedScore = features.latencySensitive ? observedTailSpeed : observedSpeed;
36570
+ const speedWeight = profileWeights.speed + (features.latencySensitive ? portfolio.latencySensitiveSpeedBoost : 0);
36571
+ const reliabilityWeight = profileWeights.reliability + (features.highStakes ? portfolio.highStakesBoost.reliability : 0);
36572
+ const score = observedQuality * qualityWeight + capabilityScore * profileWeights.capability + costScore * profileWeights.cost + speedScore * speedWeight + observedReliability * reliabilityWeight + legacyScore * profileWeights.legacy;
36573
+ return {
36574
+ model: model2,
36575
+ score,
36576
+ quality: observedQuality,
36577
+ cost: costScore,
36578
+ speed: speedScore,
36579
+ reliability: observedReliability
36580
+ };
36581
+ }).sort((a, b) => b.score - a.score);
36582
+ const scoredModels = rankedEntries.map((item) => item.model);
36583
+ const webResearchFallbackOrder = [
36584
+ "anthropic/claude-sonnet-5",
36585
+ "openai/gpt-5-mini",
36586
+ "google/gemini-3.5-flash",
36587
+ "anthropic/claude-opus-5",
36588
+ "anthropic/claude-opus-4.8",
36589
+ "openai/gpt-5.3-codex"
36590
+ ];
36591
+ const ranked = features.agentDomain === "web_research" ? [
36592
+ ...scoredModels,
36593
+ ...webResearchFallbackOrder.filter(
36594
+ (model2) => eligibleCandidates.includes(model2) && !scoredModels.includes(model2)
36595
+ ),
36596
+ ...eligibleCandidates.filter(
36597
+ (model2) => !scoredModels.includes(model2) && !webResearchFallbackOrder.includes(model2)
36598
+ )
36599
+ ] : features.taskType === "tool_agent" || features.taskType === "tool_agent_parallel" && features.agentDomain !== "other" ? [
36600
+ ...scoredModels,
36601
+ ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))
36602
+ ] : [
36603
+ ...scoredModels,
36604
+ ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))
36605
+ ];
36606
+ const model = ranked[0] ?? base3.model;
36607
+ const selectedTierConfigs = {
36608
+ ...tierConfigs,
36609
+ [targetTier]: { primary: model, fallback: ranked.slice(1) }
36610
+ };
36611
+ const decision = selectModel(
36612
+ targetTier,
36613
+ base3.confidence,
36614
+ "portfolio",
36615
+ `${base3.reasoning} | v3 task=${features.taskType} agentRisk=${features.agentRisk} deepWebResearch=${features.deepWebResearch} terminalCode=${features.implicitTerminalCode} terminalSafety=${features.terminalSafetySensitive} candidates=${ranked.length}`,
36616
+ selectedTierConfigs,
36617
+ options.modelPricing,
36618
+ features.estimatedInputTokens,
36619
+ maxOutputTokens,
36620
+ options.routingProfile,
36621
+ base3.agenticScore
36622
+ );
36623
+ return {
36624
+ ...decision,
36625
+ tierConfigs: selectedTierConfigs,
36626
+ profile: base3.profile,
36627
+ candidates: ranked,
36628
+ candidateScores: rankedEntries.map(({ model: model2, score, quality, cost, speed, reliability }) => ({
36629
+ model: model2,
36630
+ score,
36631
+ quality,
36632
+ cost,
36633
+ speed,
36634
+ reliability
36635
+ })),
36636
+ taskType: features.taskType,
36637
+ routerVersion: "v3-portfolio"
36638
+ };
36639
+ }
36640
+ };
35263
36641
  DEFAULT_ROUTING_CONFIG = {
35264
- version: "2.0",
36642
+ version: "3.4",
36643
+ strategy: "portfolio",
36644
+ portfolio: {
36645
+ auto: {
36646
+ quality: 0.47,
36647
+ capability: 0.2,
36648
+ cost: 0.18,
36649
+ speed: 0.07,
36650
+ reliability: 0.03,
36651
+ legacy: 0.05
36652
+ },
36653
+ eco: {
36654
+ quality: 0.36,
36655
+ capability: 0.2,
36656
+ cost: 0.28,
36657
+ speed: 0.1,
36658
+ reliability: 0.04,
36659
+ legacy: 0.02
36660
+ },
36661
+ premium: {
36662
+ quality: 0.58,
36663
+ capability: 0.2,
36664
+ cost: 0.08,
36665
+ speed: 0.06,
36666
+ reliability: 0.06,
36667
+ legacy: 0.02
36668
+ },
36669
+ highStakesBoost: { quality: 0.08, reliability: 0.05 },
36670
+ latencySensitiveSpeedBoost: 0.08,
36671
+ affinityFloorGap: { auto: 0.1, eco: 0.22, premium: 0.05 }
36672
+ },
35265
36673
  classifier: {
35266
36674
  llmModel: "google/gemini-2.5-flash",
35267
36675
  llmMaxTokens: 10,
@@ -36641,21 +38049,15 @@ var init_config = __esm({
36641
38049
  // Set to `true` to force agentic tiers; `false` to disable them entirely.
36642
38050
  }
36643
38051
  };
38052
+ registerStrategy(new PortfolioStrategy());
36644
38053
  }
36645
38054
  });
36646
38055
 
36647
38056
  // src/router/index.ts
36648
- function route(prompt, systemPrompt, maxOutputTokens, options) {
36649
- const strategy = getStrategy("rules");
36650
- return strategy.route(prompt, systemPrompt, maxOutputTokens, options);
36651
- }
36652
38057
  var init_router = __esm({
36653
38058
  "src/router/index.ts"() {
36654
38059
  "use strict";
36655
- init_strategy();
36656
- init_strategy();
36657
- init_selector();
36658
- init_config();
38060
+ init_dist();
36659
38061
  }
36660
38062
  });
36661
38063
 
@@ -56616,7 +58018,7 @@ var init_exclude_models = __esm({
56616
58018
 
56617
58019
  // src/config.ts
56618
58020
  var DEFAULT_PORT, PROXY_PORT;
56619
- var init_config2 = __esm({
58021
+ var init_config = __esm({
56620
58022
  "src/config.ts"() {
56621
58023
  "use strict";
56622
58024
  DEFAULT_PORT = 8402;
@@ -73014,27 +74416,27 @@ var require_dns = __commonJS({
73014
74416
  }
73015
74417
  );
73016
74418
  }
73017
- #defaultPick(origin2, hostnameRecords, affinity) {
74419
+ #defaultPick(origin2, hostnameRecords, affinity2) {
73018
74420
  let ip = null;
73019
74421
  const { records, offset } = hostnameRecords;
73020
74422
  let family;
73021
74423
  if (this.dualStack) {
73022
- if (affinity == null) {
74424
+ if (affinity2 == null) {
73023
74425
  if (offset == null || offset === maxInt) {
73024
74426
  hostnameRecords.offset = 0;
73025
- affinity = 4;
74427
+ affinity2 = 4;
73026
74428
  } else {
73027
74429
  hostnameRecords.offset++;
73028
- affinity = (hostnameRecords.offset & 1) === 1 ? 6 : 4;
74430
+ affinity2 = (hostnameRecords.offset & 1) === 1 ? 6 : 4;
73029
74431
  }
73030
74432
  }
73031
- if (records[affinity] != null && records[affinity].ips.length > 0) {
73032
- family = records[affinity];
74433
+ if (records[affinity2] != null && records[affinity2].ips.length > 0) {
74434
+ family = records[affinity2];
73033
74435
  } else {
73034
- family = records[affinity === 4 ? 6 : 4];
74436
+ family = records[affinity2 === 4 ? 6 : 4];
73035
74437
  }
73036
74438
  } else {
73037
- family = records[affinity];
74439
+ family = records[affinity2];
73038
74440
  }
73039
74441
  if (family == null || family.ips.length === 0) {
73040
74442
  return ip;
@@ -73051,7 +74453,7 @@ var require_dns = __commonJS({
73051
74453
  }
73052
74454
  if (Date.now() - ip.timestamp > ip.ttl) {
73053
74455
  family.ips.splice(position, 1);
73054
- return this.pick(origin2, hostnameRecords, affinity);
74456
+ return this.pick(origin2, hostnameRecords, affinity2);
73055
74457
  }
73056
74458
  return ip;
73057
74459
  }
@@ -73190,11 +74592,11 @@ var require_dns = __commonJS({
73190
74592
  throw new InvalidArgumentError("Invalid storage. Must be a object with methods: { get, set, full, delete }");
73191
74593
  }
73192
74594
  const dualStack = interceptorOpts?.dualStack ?? true;
73193
- let affinity;
74595
+ let affinity2;
73194
74596
  if (dualStack) {
73195
- affinity = interceptorOpts?.affinity ?? null;
74597
+ affinity2 = interceptorOpts?.affinity ?? null;
73196
74598
  } else {
73197
- affinity = interceptorOpts?.affinity ?? 4;
74599
+ affinity2 = interceptorOpts?.affinity ?? 4;
73198
74600
  }
73199
74601
  const opts = {
73200
74602
  maxTTL: interceptorOpts?.maxTTL ?? 1e4,
@@ -73202,7 +74604,7 @@ var require_dns = __commonJS({
73202
74604
  lookup: interceptorOpts?.lookup ?? null,
73203
74605
  pick: interceptorOpts?.pick ?? null,
73204
74606
  dualStack,
73205
- affinity,
74607
+ affinity: affinity2,
73206
74608
  maxItems: interceptorOpts?.maxItems ?? Infinity,
73207
74609
  storage: interceptorOpts?.storage
73208
74610
  };
@@ -86497,6 +87899,19 @@ function buildModelPricing() {
86497
87899
  }
86498
87900
  return map;
86499
87901
  }
87902
+ function buildModelCapabilities() {
87903
+ return Object.fromEntries(
87904
+ BLOCKRUN_MODELS.map((model) => [
87905
+ model.id,
87906
+ {
87907
+ contextWindow: model.contextWindow,
87908
+ maxOutputTokens: model.maxOutput,
87909
+ supportsTools: model.toolCalling === true,
87910
+ supportsVision: model.vision === true
87911
+ }
87912
+ ])
87913
+ );
87914
+ }
86500
87915
  function buildProxyModelList(createdAt = Math.floor(Date.now() / 1e3)) {
86501
87916
  const seen = /* @__PURE__ */ new Set();
86502
87917
  return OPENCLAW_MODELS.filter((model) => {
@@ -86842,7 +88257,8 @@ async function startProxy(options) {
86842
88257
  const modelPricing = buildModelPricing();
86843
88258
  const routerOpts = {
86844
88259
  config: routingConfig,
86845
- modelPricing
88260
+ modelPricing,
88261
+ modelCapabilities: buildModelCapabilities()
86846
88262
  };
86847
88263
  const deduplicator = new RequestDeduplicator();
86848
88264
  const responseCache2 = new ResponseCache(options.cacheConfig);
@@ -87988,6 +89404,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
87988
89404
  let maxTokens = DEFAULT_MAX_TOKENS;
87989
89405
  let routingProfile = null;
87990
89406
  let stickyExplicitModel;
89407
+ let pendingShadowComparison;
87991
89408
  let balanceFallbackNotice;
87992
89409
  let budgetDowngradeNotice;
87993
89410
  let budgetDowngradeHeaderMode;
@@ -88583,8 +90000,16 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
88583
90000
  const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
88584
90001
  const tools = parsed.tools;
88585
90002
  hasTools = Array.isArray(tools) && tools.length > 0;
90003
+ const requiresTools = hasTools && inferToolRequirement(prompt, systemPrompt, parsed.tool_choice);
90004
+ const toolNames = (tools ?? []).map((tool) => {
90005
+ if (typeof tool !== "object" || tool === null) return void 0;
90006
+ const fn = tool.function;
90007
+ if (typeof fn !== "object" || fn === null) return void 0;
90008
+ const name = fn.name;
90009
+ return typeof name === "string" ? name : void 0;
90010
+ }).filter((name) => name !== void 0);
88586
90011
  if (hasTools && tools) {
88587
- console.log(`[ClawRouter] Tools detected (${tools.length}), forcing agentic tiers`);
90012
+ console.log(`[ClawRouter] Tools detected (${tools.length}), required=${requiresTools}`);
88588
90013
  }
88589
90014
  hasVision = parsedMessages.some((m) => {
88590
90015
  if (Array.isArray(m.content)) {
@@ -88598,8 +90023,36 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
88598
90023
  routingDecision = route(prompt, systemPrompt, maxTokens, {
88599
90024
  ...routerOpts,
88600
90025
  routingProfile: routingProfile ?? void 0,
88601
- hasTools
90026
+ hasTools,
90027
+ toolCount: tools?.length ?? 0,
90028
+ toolNames,
90029
+ requiresTools,
90030
+ hasVision,
90031
+ requiresStructuredOutput: typeof parsed.response_format === "object" && parsed.response_format !== null
88602
90032
  });
90033
+ const shadow = routerOpts.config.shadow;
90034
+ const activeStrategy = routerOpts.config.strategy ?? "portfolio";
90035
+ const shadowRate = Math.max(0, Math.min(1, shadow?.sampleRate ?? 1));
90036
+ if (shadow && shadow.strategy !== activeStrategy && Math.random() < shadowRate) {
90037
+ const requiresStructuredOutput = typeof parsed.response_format === "object" && parsed.response_format !== null;
90038
+ const shadowDecision = route(prompt, systemPrompt, maxTokens, {
90039
+ ...routerOpts,
90040
+ config: { ...routerOpts.config, strategy: shadow.strategy },
90041
+ routingProfile: routingProfile ?? void 0,
90042
+ hasTools,
90043
+ toolCount: tools?.length ?? 0,
90044
+ toolNames,
90045
+ requiresTools,
90046
+ hasVision,
90047
+ requiresStructuredOutput
90048
+ });
90049
+ pendingShadowComparison = {
90050
+ shadow: shadowDecision,
90051
+ hasTools,
90052
+ hasVision,
90053
+ requiresStructuredOutput
90054
+ };
90055
+ }
88603
90056
  if (hasTools && routingDecision.tier === "SIMPLE") {
88604
90057
  console.log(
88605
90058
  `[ClawRouter] SIMPLE+tools: keeping agentic model ${routingDecision.model} (tools need reliable function-call support)`
@@ -88708,6 +90161,21 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
88708
90161
  );
88709
90162
  }
88710
90163
  }
90164
+ if (pendingShadowComparison) {
90165
+ try {
90166
+ options.onShadowRouted?.({
90167
+ executed: routingDecision,
90168
+ shadow: pendingShadowComparison.shadow,
90169
+ sameModel: routingDecision.model === pendingShadowComparison.shadow.model,
90170
+ hasTools: pendingShadowComparison.hasTools,
90171
+ hasVision: pendingShadowComparison.hasVision,
90172
+ requiresStructuredOutput: pendingShadowComparison.requiresStructuredOutput
90173
+ });
90174
+ } catch (err) {
90175
+ const message = err instanceof Error ? err.message : String(err);
90176
+ console.warn(`[ClawRouter] Shadow routing callback failed: ${message}`);
90177
+ }
90178
+ }
88711
90179
  options.onRouted?.(routingDecision);
88712
90180
  }
88713
90181
  }
@@ -89055,35 +90523,64 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
89055
90523
  const estimatedInputTokens = Math.ceil(body.length / 4);
89056
90524
  const estimatedTotalTokens = estimatedInputTokens + maxTokens;
89057
90525
  const tierConfigs = routingDecision.tierConfigs ?? routerOpts.config.tiers;
89058
- const fullChain = prependStickyExplicitModel(
89059
- getFallbackChain(routingDecision.tier, tierConfigs)
89060
- );
89061
- const contextFiltered = prependStickyExplicitModel(
89062
- getFallbackChainFiltered(
89063
- routingDecision.tier,
89064
- tierConfigs,
89065
- estimatedTotalTokens,
89066
- getModelContextWindow
89067
- )
90526
+ const rankedCandidates = routingDecision.candidates ?? getFallbackChain(routingDecision.tier, tierConfigs);
90527
+ const selectedModel = routingDecision.model;
90528
+ const fullChain = prependStickyExplicitModel([
90529
+ selectedModel,
90530
+ ...rankedCandidates.filter((model) => model !== selectedModel)
90531
+ ]);
90532
+ const contextFiltered = filterCandidatesByCapacity(
90533
+ fullChain,
90534
+ estimatedInputTokens,
90535
+ maxTokens,
90536
+ (modelId2) => {
90537
+ const model = BLOCKRUN_MODEL_BY_ID.get(modelId2);
90538
+ return model ? { contextWindow: model.contextWindow, maxOutput: model.maxOutput } : void 0;
90539
+ }
89068
90540
  );
90541
+ if (contextFiltered.length === 0) {
90542
+ const errPayload = JSON.stringify({
90543
+ error: {
90544
+ message: `No routed model can satisfy the requested context and output capacity (~${estimatedTotalTokens} tokens). Reduce the input or max_tokens and retry.`,
90545
+ type: "invalid_request_error",
90546
+ code: "context_capacity_exceeded"
90547
+ }
90548
+ });
90549
+ if (heartbeatInterval) {
90550
+ clearInterval(heartbeatInterval);
90551
+ heartbeatInterval = void 0;
90552
+ }
90553
+ if (headersSentEarly) {
90554
+ safeWrite(res, `data: ${errPayload}
90555
+
90556
+ data: [DONE]
90557
+
90558
+ `);
90559
+ res.end();
90560
+ } else {
90561
+ res.writeHead(400, { "Content-Type": "application/json" });
90562
+ res.end(errPayload);
90563
+ }
90564
+ completed = true;
90565
+ deduplicator.removeInflight(dedupKey);
90566
+ clearTimeout(timeoutId);
90567
+ req.removeListener("close", onClientClose);
90568
+ return;
90569
+ }
89069
90570
  const contextExcluded = fullChain.filter((m) => !contextFiltered.includes(m));
89070
90571
  if (contextExcluded.length > 0) {
89071
90572
  console.log(
89072
90573
  `[ClawRouter] Context filter (~${estimatedTotalTokens} tokens): excluded ${contextExcluded.join(", ")}`
89073
90574
  );
89074
90575
  }
89075
- const excludeFiltered = prependStickyExplicitModel(
89076
- filterByExcludeList(contextFiltered, excludeList)
89077
- );
90576
+ const excludeFiltered = filterByExcludeList(contextFiltered, excludeList);
89078
90577
  const excludeExcluded = contextFiltered.filter((m) => !excludeFiltered.includes(m));
89079
90578
  if (excludeExcluded.length > 0) {
89080
90579
  console.log(
89081
90580
  `[ClawRouter] Exclude filter: excluded ${excludeExcluded.join(", ")} (user preference)`
89082
90581
  );
89083
90582
  }
89084
- let toolFiltered = prependStickyExplicitModel(
89085
- filterByToolCalling(excludeFiltered, hasTools, supportsToolCalling)
89086
- );
90583
+ let toolFiltered = filterByToolCalling(excludeFiltered, hasTools, supportsToolCalling);
89087
90584
  const toolExcluded = excludeFiltered.filter((m) => !toolFiltered.includes(m));
89088
90585
  if (toolExcluded.length > 0) {
89089
90586
  console.log(
@@ -89102,19 +90599,17 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
89102
90599
  console.log(
89103
90600
  `[ClawRouter] Tool-compliance filter: excluded ${dropped.join(", ")} (unreliable tool schema handling)`
89104
90601
  );
89105
- toolFiltered = prependStickyExplicitModel(compliant);
90602
+ toolFiltered = compliant;
89106
90603
  }
89107
90604
  }
89108
- const visionFiltered = prependStickyExplicitModel(
89109
- filterByVision(toolFiltered, hasVision, supportsVision)
89110
- );
90605
+ const visionFiltered = filterByVision(toolFiltered, hasVision, supportsVision);
89111
90606
  const visionExcluded = toolFiltered.filter((m) => !visionFiltered.includes(m));
89112
90607
  if (visionExcluded.length > 0) {
89113
90608
  console.log(
89114
90609
  `[ClawRouter] Vision filter: excluded ${visionExcluded.join(", ")} (no vision support)`
89115
90610
  );
89116
90611
  }
89117
- modelsToTry = prependStickyExplicitModel(visionFiltered).slice(0, MAX_FALLBACK_ATTEMPTS);
90612
+ modelsToTry = visionFiltered.slice(0, MAX_FALLBACK_ATTEMPTS);
89118
90613
  modelsToTry = prioritizeNonRateLimited(modelsToTry);
89119
90614
  } else {
89120
90615
  modelsToTry = modelId ? [modelId] : [];
@@ -90004,7 +91499,7 @@ var init_proxy = __esm({
90004
91499
  init_client2();
90005
91500
  init_esm5();
90006
91501
  init_router();
90007
- init_rules();
91502
+ init_router();
90008
91503
  init_models();
90009
91504
  init_max_tokens();
90010
91505
  init_logger();
@@ -90018,7 +91513,7 @@ var init_proxy = __esm({
90018
91513
  init_session();
90019
91514
  init_updater();
90020
91515
  init_exclude_models();
90021
- init_config2();
91516
+ init_config();
90022
91517
  init_journal();
90023
91518
  init_upstream_proxy();
90024
91519
  init_textual_tool_calls();
@@ -91650,7 +93145,7 @@ var init_headers = __esm({
91650
93145
 
91651
93146
  // node_modules/@polymarket/clob-client-v2/dist/config.js
91652
93147
  var AMOY_CONTRACTS, MATIC_CONTRACTS, COLLATERAL_TOKEN_DECIMALS, getContractConfig;
91653
- var init_config3 = __esm({
93148
+ var init_config2 = __esm({
91654
93149
  "node_modules/@polymarket/clob-client-v2/dist/config.js"() {
91655
93150
  "use strict";
91656
93151
  AMOY_CONTRACTS = {
@@ -91950,7 +93445,7 @@ var init_buildMarketOrderCreationArgs = __esm({
91950
93445
  "node_modules/@polymarket/clob-client-v2/dist/order-builder/helpers/buildMarketOrderCreationArgs.js"() {
91951
93446
  "use strict";
91952
93447
  init_constants2();
91953
- init_config3();
93448
+ init_config2();
91954
93449
  init_getMarketOrderRawAmounts();
91955
93450
  init_esm();
91956
93451
  }
@@ -92511,7 +94006,7 @@ var init_createMarketOrder = __esm({
92511
94006
  "node_modules/@polymarket/clob-client-v2/dist/order-builder/helpers/createMarketOrder.js"() {
92512
94007
  "use strict";
92513
94008
  init_signer();
92514
- init_config3();
94009
+ init_config2();
92515
94010
  init_buildMarketOrderCreationArgs();
92516
94011
  init_signatureTypeV2();
92517
94012
  init_buildOrder();
@@ -92619,7 +94114,7 @@ var init_buildOrderCreationArgs = __esm({
92619
94114
  "node_modules/@polymarket/clob-client-v2/dist/order-builder/helpers/buildOrderCreationArgs.js"() {
92620
94115
  "use strict";
92621
94116
  init_constants2();
92622
- init_config3();
94117
+ init_config2();
92623
94118
  init_getOrderRawAmounts();
92624
94119
  init_esm();
92625
94120
  }
@@ -92631,7 +94126,7 @@ var init_createOrder = __esm({
92631
94126
  "node_modules/@polymarket/clob-client-v2/dist/order-builder/helpers/createOrder.js"() {
92632
94127
  "use strict";
92633
94128
  init_signer();
92634
- init_config3();
94129
+ init_config2();
92635
94130
  init_signatureTypeV2();
92636
94131
  init_buildOrder();
92637
94132
  init_buildOrderCreationArgs();
@@ -92669,7 +94164,7 @@ var init_createExchangeV3OrderFromAmounts = __esm({
92669
94164
  "use strict";
92670
94165
  init_constants2();
92671
94166
  init_signer();
92672
- init_config3();
94167
+ init_config2();
92673
94168
  init_signatureTypeV2();
92674
94169
  init_buildOrder();
92675
94170
  createExchangeV3OrderFromAmounts = async (eoaSigner, chainId, signatureType, funderAddress, userOrder) => {
@@ -110471,7 +111966,7 @@ var init_axios2 = __esm({
110471
111966
 
110472
111967
  // node_modules/browser-or-node/dist/index.mjs
110473
111968
  var isBrowser, isNode, isWebWorker, isJsDom, isDeno, isBun;
110474
- var init_dist = __esm({
111969
+ var init_dist2 = __esm({
110475
111970
  "node_modules/browser-or-node/dist/index.mjs"() {
110476
111971
  "use strict";
110477
111972
  isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
@@ -110495,7 +111990,7 @@ var init_http_helpers = __esm({
110495
111990
  "node_modules/@polymarket/clob-client-v2/dist/http-helpers/index.js"() {
110496
111991
  "use strict";
110497
111992
  init_axios2();
110498
- init_dist();
111993
+ init_dist2();
110499
111994
  GET = "GET";
110500
111995
  POST = "POST";
110501
111996
  DELETE = "DELETE";
@@ -111527,10 +113022,10 @@ var init_client4 = __esm({
111527
113022
  });
111528
113023
 
111529
113024
  // node_modules/@polymarket/clob-client-v2/dist/index.js
111530
- var init_dist2 = __esm({
113025
+ var init_dist3 = __esm({
111531
113026
  "node_modules/@polymarket/clob-client-v2/dist/index.js"() {
111532
113027
  "use strict";
111533
- init_config3();
113028
+ init_config2();
111534
113029
  init_signatureTypeV2();
111535
113030
  init_side();
111536
113031
  init_clob();
@@ -111550,7 +113045,7 @@ import * as net from "net";
111550
113045
  import * as http4 from "http";
111551
113046
  import { Agent as HttpsAgent } from "https";
111552
113047
  var INTERNAL, Agent2;
111553
- var init_dist3 = __esm({
113048
+ var init_dist4 = __esm({
111554
113049
  "node_modules/agent-base/dist/index.js"() {
111555
113050
  "use strict";
111556
113051
  init_helpers();
@@ -111789,7 +113284,7 @@ function createNegotiateAuth() {
111789
113284
  };
111790
113285
  };
111791
113286
  }
111792
- var init_dist4 = __esm({
113287
+ var init_dist5 = __esm({
111793
113288
  "node_modules/proxy-agent-negotiate/dist/index.js"() {
111794
113289
  "use strict";
111795
113290
  }
@@ -111816,13 +113311,13 @@ function omit(obj, ...keys) {
111816
113311
  return ret;
111817
113312
  }
111818
113313
  var import_debug2, debug2, setServernameFromNonIpHost, HttpsProxyAgent2;
111819
- var init_dist5 = __esm({
113314
+ var init_dist6 = __esm({
111820
113315
  "node_modules/https-proxy-agent/dist/index.js"() {
111821
113316
  "use strict";
111822
113317
  import_debug2 = __toESM(require_src(), 1);
111823
- init_dist3();
111824
- init_parse_proxy_response();
111825
113318
  init_dist4();
113319
+ init_parse_proxy_response();
113320
+ init_dist5();
111826
113321
  debug2 = (0, import_debug2.default)("https-proxy-agent");
111827
113322
  setServernameFromNonIpHost = (options) => {
111828
113323
  if (options.servername === void 0 && options.host && !net2.isIP(options.host)) {
@@ -112035,7 +113530,7 @@ var POLYGON_CHAIN_ID, CTF_EXCHANGE_V2, NEG_RISK_CTF_EXCHANGE_V2, NEG_RISK_ADAPTE
112035
113530
  var init_constants3 = __esm({
112036
113531
  "src/polymarket/constants.ts"() {
112037
113532
  "use strict";
112038
- init_dist2();
113533
+ init_dist3();
112039
113534
  POLYGON_CHAIN_ID = 137;
112040
113535
  CTF_EXCHANGE_V2 = "0xE111180000d2663C0091e4f400237545B87B996B";
112041
113536
  NEG_RISK_CTF_EXCHANGE_V2 = "0xe2222d279d744050d28e00520010520000310F59";
@@ -112572,11 +114067,11 @@ var init_client5 = __esm({
112572
114067
  "src/polymarket/client.ts"() {
112573
114068
  "use strict";
112574
114069
  init_axios2();
112575
- init_dist5();
114070
+ init_dist6();
112576
114071
  init_esm();
112577
114072
  init_accounts();
112578
114073
  init_chains();
112579
- init_dist2();
114074
+ init_dist3();
112580
114075
  init_wallet_adapter();
112581
114076
  init_constants3();
112582
114077
  init_creds();
@@ -112946,7 +114441,7 @@ var ledger;
112946
114441
  var init_orders = __esm({
112947
114442
  "src/polymarket/orders.ts"() {
112948
114443
  "use strict";
112949
- init_dist2();
114444
+ init_dist3();
112950
114445
  init_client5();
112951
114446
  init_constants3();
112952
114447
  init_creds();
@@ -220128,7 +221623,7 @@ var init_relayer = __esm({
220128
221623
  "use strict";
220129
221624
  import_builder_relayer_client = __toESM(require_dist3(), 1);
220130
221625
  import_builder_signing_sdk = __toESM(require_dist4(), 1);
220131
- init_dist2();
221626
+ init_dist3();
220132
221627
  init_esm();
220133
221628
  init_chains();
220134
221629
  init_client5();
@@ -220468,7 +221963,7 @@ var init_setup = __esm({
220468
221963
  "use strict";
220469
221964
  init_esm();
220470
221965
  init_chains();
220471
- init_dist2();
221966
+ init_dist3();
220472
221967
  init_client5();
220473
221968
  init_constants3();
220474
221969
  init_creds();
@@ -220922,7 +222417,7 @@ function sleep2(ms) {
220922
222417
  return new Promise((r2) => setTimeout(r2, ms));
220923
222418
  }
220924
222419
  var BlockrunError, PaymentError, APIError, BASE_CHAIN_ID2, USDC_BASE2, USDC_DOMAIN, TRANSFER_TYPES, BLOCKRUN_SERVICE_CODE2, LOCALHOST_DOMAINS, BLOCKRUN_DIR2, COST_LOG_FILE, SDK_VERSION, USER_AGENT2, DEFAULT_TIMEOUT, PHONE_PRICES, DEFAULT_API_URL13, DEFAULT_TIMEOUT13, DEFAULT_POLL_INTERVAL_MS, DEFAULT_POLL_BUDGET_MS, MAX_SIGNED_AUTH_SECONDS, BlockrunClient, WALLET_DIR2, WALLET_FILE3, WALLET_DIR22, SOLANA_WALLET_FILE, CACHE_DIR, DATA_DIR, COST_LOG_FILE2, DEFAULT_TTL;
220925
- var init_dist6 = __esm({
222420
+ var init_dist7 = __esm({
220926
222421
  "node_modules/@blockrun/llm/dist/index.js"() {
220927
222422
  "use strict";
220928
222423
  init_accounts();
@@ -221490,7 +222985,7 @@ var init_fund = __esm({
221490
222985
  "src/polymarket/fund.ts"() {
221491
222986
  "use strict";
221492
222987
  init_axios2();
221493
- init_dist6();
222988
+ init_dist7();
221494
222989
  init_wallet_adapter();
221495
222990
  init_client5();
221496
222991
  init_constants3();
@@ -221969,29 +223464,29 @@ var init_spend_control = __esm({
221969
223464
  getLimits() {
221970
223465
  return { ...this.limits };
221971
223466
  }
221972
- check(estimatedCost) {
223467
+ check(estimatedCost2) {
221973
223468
  const now2 = this.now();
221974
223469
  if (this.limits.perRequest !== void 0) {
221975
- if (estimatedCost > this.limits.perRequest) {
223470
+ if (estimatedCost2 > this.limits.perRequest) {
221976
223471
  return {
221977
223472
  allowed: false,
221978
223473
  blockedBy: "perRequest",
221979
223474
  remaining: this.limits.perRequest,
221980
- reason: `Per-request limit exceeded: $${estimatedCost.toFixed(4)} > $${this.limits.perRequest.toFixed(2)} max`
223475
+ reason: `Per-request limit exceeded: $${estimatedCost2.toFixed(4)} > $${this.limits.perRequest.toFixed(2)} max`
221981
223476
  };
221982
223477
  }
221983
223478
  }
221984
223479
  if (this.limits.hourly !== void 0) {
221985
223480
  const hourlySpent = this.getSpendingInWindow(now2 - HOUR_MS, now2);
221986
223481
  const remaining = this.limits.hourly - hourlySpent;
221987
- if (estimatedCost > remaining) {
223482
+ if (estimatedCost2 > remaining) {
221988
223483
  const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - HOUR_MS);
221989
223484
  const resetIn = oldestInWindow ? Math.ceil((oldestInWindow.timestamp + HOUR_MS - now2) / 1e3) : 0;
221990
223485
  return {
221991
223486
  allowed: false,
221992
223487
  blockedBy: "hourly",
221993
223488
  remaining,
221994
- reason: `Hourly limit exceeded: $${(hourlySpent + estimatedCost).toFixed(2)} > $${this.limits.hourly.toFixed(2)} max`,
223489
+ reason: `Hourly limit exceeded: $${(hourlySpent + estimatedCost2).toFixed(2)} > $${this.limits.hourly.toFixed(2)} max`,
221995
223490
  resetIn
221996
223491
  };
221997
223492
  }
@@ -221999,26 +223494,26 @@ var init_spend_control = __esm({
221999
223494
  if (this.limits.daily !== void 0) {
222000
223495
  const dailySpent = this.getSpendingInWindow(now2 - DAY_MS, now2);
222001
223496
  const remaining = this.limits.daily - dailySpent;
222002
- if (estimatedCost > remaining) {
223497
+ if (estimatedCost2 > remaining) {
222003
223498
  const oldestInWindow = this.history.find((r2) => r2.timestamp >= now2 - DAY_MS);
222004
223499
  const resetIn = oldestInWindow ? Math.ceil((oldestInWindow.timestamp + DAY_MS - now2) / 1e3) : 0;
222005
223500
  return {
222006
223501
  allowed: false,
222007
223502
  blockedBy: "daily",
222008
223503
  remaining,
222009
- reason: `Daily limit exceeded: $${(dailySpent + estimatedCost).toFixed(2)} > $${this.limits.daily.toFixed(2)} max`,
223504
+ reason: `Daily limit exceeded: $${(dailySpent + estimatedCost2).toFixed(2)} > $${this.limits.daily.toFixed(2)} max`,
222010
223505
  resetIn
222011
223506
  };
222012
223507
  }
222013
223508
  }
222014
223509
  if (this.limits.session !== void 0) {
222015
223510
  const remaining = this.limits.session - this.sessionSpent;
222016
- if (estimatedCost > remaining) {
223511
+ if (estimatedCost2 > remaining) {
222017
223512
  return {
222018
223513
  allowed: false,
222019
223514
  blockedBy: "session",
222020
223515
  remaining,
222021
- reason: `Session limit exceeded: $${(this.sessionSpent + estimatedCost).toFixed(2)} > $${this.limits.session.toFixed(2)} max`
223516
+ reason: `Session limit exceeded: $${(this.sessionSpent + estimatedCost2).toFixed(2)} > $${this.limits.session.toFixed(2)} max`
222022
223517
  };
222023
223518
  }
222024
223519
  }
@@ -222694,6 +224189,11 @@ async function startProxyInBackground(api, startupGeneration) {
222694
224189
  `[${decision.tier}] ${decision.model} $${cost} (saved ${saved}%) | ${decision.reasoning}`
222695
224190
  );
222696
224191
  },
224192
+ onShadowRouted: (comparison) => {
224193
+ api.logger.info(
224194
+ `[router-shadow] executed=${comparison.executed.model} (${comparison.executed.method}) candidate=${comparison.shadow.model} (${comparison.shadow.method}) same=${comparison.sameModel} tools=${comparison.hasTools} vision=${comparison.hasVision} structured=${comparison.requiresStructuredOutput}`
224195
+ );
224196
+ },
222697
224197
  onLowBalance: (info) => {
222698
224198
  api.logger.warn(`[!] Low balance: ${info.balanceUSD}. Fund wallet: ${info.walletAddress}`);
222699
224199
  },
@@ -223891,6 +225391,7 @@ export {
223891
225391
  deriveEvmKey,
223892
225392
  deriveSolanaKeyBytes,
223893
225393
  fetchWithRetry,
225394
+ filterCandidatesByCapacity,
223894
225395
  formatDuration,
223895
225396
  formatStatsAscii,
223896
225397
  generateWalletMnemonic,
@@ -223903,6 +225404,7 @@ export {
223903
225404
  getSessionId,
223904
225405
  getStats,
223905
225406
  hashRequestContent,
225407
+ inferToolRequirement,
223906
225408
  injectAuthProfile,
223907
225409
  injectModelsConfig,
223908
225410
  isAgenticModel,