agentic-flow 2.0.14 → 2.1.0

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +376 -186
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/agent-booster/index.d.ts +14 -0
  5. package/dist/agent-booster/index.d.ts.map +1 -0
  6. package/dist/agent-booster/index.js +25 -0
  7. package/dist/agent-booster/index.js.map +1 -0
  8. package/dist/harness/provenance.d.ts +72 -0
  9. package/dist/harness/provenance.d.ts.map +1 -0
  10. package/dist/harness/provenance.js +92 -0
  11. package/dist/harness/provenance.js.map +1 -0
  12. package/dist/mcp/standalone-stdio.js +4 -0
  13. package/dist/mcp/standalone-stdio.js.map +1 -1
  14. package/dist/mcp/tools/harness-tools.d.ts +28 -0
  15. package/dist/mcp/tools/harness-tools.d.ts.map +1 -0
  16. package/dist/mcp/tools/harness-tools.js +90 -0
  17. package/dist/mcp/tools/harness-tools.js.map +1 -0
  18. package/dist/optimizations/configuration-tuning.d.ts +1 -1
  19. package/dist/repair/cli.d.ts +24 -0
  20. package/dist/repair/cli.d.ts.map +1 -0
  21. package/dist/repair/cli.js +69 -0
  22. package/dist/repair/cli.js.map +1 -0
  23. package/dist/repair/darwin-repair.d.ts +80 -0
  24. package/dist/repair/darwin-repair.d.ts.map +1 -0
  25. package/dist/repair/darwin-repair.js +81 -0
  26. package/dist/repair/darwin-repair.js.map +1 -0
  27. package/dist/router/cost-optimal-router.d.ts +89 -0
  28. package/dist/router/cost-optimal-router.d.ts.map +1 -0
  29. package/dist/router/cost-optimal-router.js +94 -0
  30. package/dist/router/cost-optimal-router.js.map +1 -0
  31. package/dist/router/router.d.ts +29 -0
  32. package/dist/router/router.d.ts.map +1 -1
  33. package/dist/router/router.js +83 -0
  34. package/dist/router/router.js.map +1 -1
  35. package/dist/router/types.d.ts +1 -1
  36. package/dist/router/types.d.ts.map +1 -1
  37. package/dist/router/types.js.map +1 -1
  38. package/dist/transport/quic-loader.d.ts +33 -0
  39. package/dist/transport/quic-loader.d.ts.map +1 -1
  40. package/dist/transport/quic-loader.js +95 -0
  41. package/dist/transport/quic-loader.js.map +1 -1
  42. package/package.json +17 -3
@@ -1,8 +1,13 @@
1
1
  import { LLMProvider, RouterConfig, ChatParams, ChatResponse, StreamChunk, ProviderType, RouterMetrics } from './types.js';
2
+ import { CostOptimalRouter } from './cost-optimal-router.js';
2
3
  export declare class ModelRouter {
3
4
  private config;
4
5
  private providers;
5
6
  private metrics;
7
+ private costOptimalRouter?;
8
+ private embedQuery?;
9
+ private embedCache;
10
+ private static readonly EMBED_CACHE_MAX;
6
11
  constructor(configPath?: string);
7
12
  private loadConfig;
8
13
  private createConfigFromEnv;
@@ -16,6 +21,30 @@ export declare class ModelRouter {
16
21
  private selectByRules;
17
22
  private matchesRule;
18
23
  private selectByCost;
24
+ /**
25
+ * Enable learned cost-optimal routing (ADR-073). Attaches a
26
+ * {@link CostOptimalRouter} plus an embedder and switches the routing mode to
27
+ * `'cost-optimal'`. Opt-in: until called, routing behaves exactly as before.
28
+ */
29
+ enableCostOptimalRouting(opts: {
30
+ router: CostOptimalRouter;
31
+ embed: (text: string) => Promise<number[]> | number[];
32
+ }): void;
33
+ /**
34
+ * Embed `text`, caching the result. Bounded LRU: a cache hit refreshes
35
+ * recency; the oldest entry is evicted past {@link EMBED_CACHE_MAX}.
36
+ */
37
+ private embedCached;
38
+ /** Extract the most recent user-message text to embed for routing. */
39
+ private lastUserText;
40
+ /**
41
+ * Learned cost-optimal selection (ADR-073): embed the query, route to the
42
+ * cheapest model predicted to clear the quality bar, steer params.model to it,
43
+ * and return the matching provider. Falls back to the {@link selectByCost}
44
+ * heuristic when the learned router/embedder isn't configured or errors —
45
+ * routing never hard-fails on the cost-optimal path.
46
+ */
47
+ private selectByCostOptimal;
19
48
  private selectByPerformance;
20
49
  private handleProviderError;
21
50
  private updateMetrics;
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EAEd,MAAM,YAAY,CAAC;AAOpB,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,OAAO,CAAgB;gBAEnB,UAAU,CAAC,EAAE,MAAM;IAM/B,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,mBAAmB;IAiD3B,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,mBAAmB;IAkE3B,OAAO,CAAC,iBAAiB;IAUnB,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAuBlE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC;YAapE,cAAc;IAgC5B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,mBAAmB;YAqBb,mBAAmB;IA4BjC,OAAO,CAAC,aAAa;IA8CrB,UAAU,IAAI,aAAa;IAI3B,SAAS,IAAI,YAAY;IAIzB,YAAY,IAAI,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC;CAG/C"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EAGd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAO7D,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,OAAO,CAAgB;IAE/B,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAC9C,OAAO,CAAC,UAAU,CAAC,CAAiD;IAGpE,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAO;gBAElC,UAAU,CAAC,EAAE,MAAM;IAM/B,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,mBAAmB;IAiD3B,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,mBAAmB;IAkE3B,OAAO,CAAC,iBAAiB;IAUnB,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAuBlE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC;YAapE,cAAc;IAmC5B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,YAAY;IAgBpB;;;;OAIG;IACH,wBAAwB,CAAC,IAAI,EAAE;QAC7B,MAAM,EAAE,iBAAiB,CAAC;QAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;KACvD,GAAG,IAAI;IAMR;;;OAGG;YACW,WAAW;IAgBzB,sEAAsE;IACtE,OAAO,CAAC,YAAY;IAepB;;;;;;OAMG;YACW,mBAAmB;IAyBjC,OAAO,CAAC,mBAAmB;YAqBb,mBAAmB;IA4BjC,OAAO,CAAC,aAAa;IA8CrB,UAAU,IAAI,aAAa;IAI3B,SAAS,IAAI,YAAY;IAIzB,YAAY,IAAI,GAAG,CAAC,YAAY,EAAE,WAAW,CAAC;CAG/C"}
@@ -11,6 +11,13 @@ export class ModelRouter {
11
11
  config;
12
12
  providers = new Map();
13
13
  metrics;
14
+ // ADR-073: learned cost-optimal routing (opt-in via enableCostOptimalRouting).
15
+ costOptimalRouter;
16
+ embedQuery;
17
+ // Bounded LRU embedding cache — embedding dominates the cost-optimal hot path
18
+ // (≈ms) vs the µs-scale k-NN, and recurring prompts are common.
19
+ embedCache = new Map();
20
+ static EMBED_CACHE_MAX = 512;
14
21
  constructor(configPath) {
15
22
  this.config = this.loadConfig(configPath);
16
23
  this.initializeProviders();
@@ -227,6 +234,8 @@ export class ModelRouter {
227
234
  return this.selectByRules(params, agentType);
228
235
  case 'cost-optimized':
229
236
  return this.selectByCost(params);
237
+ case 'cost-optimal':
238
+ return this.selectByCostOptimal(params);
230
239
  case 'performance-optimized':
231
240
  return this.selectByPerformance(params);
232
241
  default:
@@ -280,6 +289,80 @@ export class ModelRouter {
280
289
  }
281
290
  return this.getDefaultProvider();
282
291
  }
292
+ /**
293
+ * Enable learned cost-optimal routing (ADR-073). Attaches a
294
+ * {@link CostOptimalRouter} plus an embedder and switches the routing mode to
295
+ * `'cost-optimal'`. Opt-in: until called, routing behaves exactly as before.
296
+ */
297
+ enableCostOptimalRouting(opts) {
298
+ this.costOptimalRouter = opts.router;
299
+ this.embedQuery = opts.embed;
300
+ this.config.routing = { ...(this.config.routing ?? { mode: 'cost-optimal' }), mode: 'cost-optimal' };
301
+ }
302
+ /**
303
+ * Embed `text`, caching the result. Bounded LRU: a cache hit refreshes
304
+ * recency; the oldest entry is evicted past {@link EMBED_CACHE_MAX}.
305
+ */
306
+ async embedCached(text) {
307
+ const hit = this.embedCache.get(text);
308
+ if (hit) {
309
+ this.embedCache.delete(text); // move to most-recently-used
310
+ this.embedCache.set(text, hit);
311
+ return hit;
312
+ }
313
+ const embedding = await this.embedQuery(text);
314
+ this.embedCache.set(text, embedding);
315
+ if (this.embedCache.size > ModelRouter.EMBED_CACHE_MAX) {
316
+ const oldest = this.embedCache.keys().next().value;
317
+ if (oldest !== undefined)
318
+ this.embedCache.delete(oldest);
319
+ }
320
+ return embedding;
321
+ }
322
+ /** Extract the most recent user-message text to embed for routing. */
323
+ lastUserText(messages) {
324
+ for (let i = messages.length - 1; i >= 0; i--) {
325
+ const m = messages[i];
326
+ if (m.role !== 'user')
327
+ continue;
328
+ return typeof m.content === 'string'
329
+ ? m.content
330
+ : m.content.map((b) => b.text ?? '').join(' ');
331
+ }
332
+ // No user turn — fall back to all text content.
333
+ return messages
334
+ .map((m) => (typeof m.content === 'string' ? m.content : ''))
335
+ .join(' ')
336
+ .trim();
337
+ }
338
+ /**
339
+ * Learned cost-optimal selection (ADR-073): embed the query, route to the
340
+ * cheapest model predicted to clear the quality bar, steer params.model to it,
341
+ * and return the matching provider. Falls back to the {@link selectByCost}
342
+ * heuristic when the learned router/embedder isn't configured or errors —
343
+ * routing never hard-fails on the cost-optimal path.
344
+ */
345
+ async selectByCostOptimal(params) {
346
+ if (this.costOptimalRouter && this.embedQuery) {
347
+ try {
348
+ const text = this.lastUserText(params.messages);
349
+ const embedding = await this.embedCached(text);
350
+ const decision = this.costOptimalRouter.route(embedding);
351
+ const provider = this.providers.get(decision.provider);
352
+ if (provider) {
353
+ params.model = decision.model; // steer model, not just provider
354
+ console.log(`💡 Cost-optimal routing → ${decision.id} ` +
355
+ `(q̂=${decision.predictedQuality.toFixed(2)}, $${decision.costPerMTok}/Mtok, metBar=${decision.metBar})`);
356
+ return provider;
357
+ }
358
+ console.warn(`⚠️ Cost-optimal pick '${decision.provider}' not initialized; falling back to heuristic`);
359
+ }
360
+ catch (err) {
361
+ console.warn('⚠️ Cost-optimal routing failed; falling back to heuristic', err);
362
+ }
363
+ }
364
+ return this.selectByCost(params);
365
+ }
283
366
  selectByPerformance(params) {
284
367
  // For now, use metrics to select fastest provider
285
368
  let fastestProvider = null;
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"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","sourcesContent":["// Multi-model router core implementation\nimport { readFileSync, existsSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport {\n LLMProvider,\n RouterConfig,\n ChatParams,\n ChatResponse,\n StreamChunk,\n ProviderType,\n RouterMetrics,\n ProviderError,\n} from './types.js';\nimport { OpenRouterProvider } from './providers/openrouter.js';\nimport { AnthropicProvider } from './providers/anthropic.js';\nimport { ONNXLocalProvider } from './providers/onnx-local.js';\nimport { GeminiProvider } from './providers/gemini.js';\nimport { OllamaProvider } from './providers/ollama.js';\n\nexport class ModelRouter {\n private config: RouterConfig;\n private providers: Map<ProviderType, LLMProvider> = new Map();\n private metrics: RouterMetrics;\n\n constructor(configPath?: string) {\n this.config = this.loadConfig(configPath);\n this.initializeProviders();\n this.metrics = this.initializeMetrics();\n }\n\n private loadConfig(configPath?: string): RouterConfig {\n const paths = [\n configPath,\n process.env.AGENTIC_FLOW_ROUTER_CONFIG,\n join(homedir(), '.agentic-flow', 'router.config.json'),\n join(process.cwd(), 'router.config.json'),\n join(process.cwd(), 'config', 'router.config.json'),\n join(process.cwd(), 'router.config.example.json'),\n ].filter(Boolean) as string[];\n\n for (const path of paths) {\n if (existsSync(path)) {\n const content = readFileSync(path, 'utf-8');\n const config = JSON.parse(content);\n\n // Substitute environment variables\n return this.substituteEnvVars(config);\n }\n }\n\n // If no config file found, create config from environment variables\n return this.createConfigFromEnv();\n }\n\n private createConfigFromEnv(): RouterConfig {\n // Create minimal config from environment variables\n const config: any = {\n version: '1.0',\n defaultProvider: (process.env.PROVIDER as any) || 'anthropic',\n routing: { mode: 'manual' as const },\n providers: {} as any,\n };\n\n // Add Anthropic if API key exists\n if (process.env.ANTHROPIC_API_KEY) {\n config.providers.anthropic = {\n apiKey: process.env.ANTHROPIC_API_KEY,\n baseUrl: process.env.ANTHROPIC_BASE_URL,\n };\n }\n\n // Add OpenRouter if API key exists\n if (process.env.OPENROUTER_API_KEY) {\n config.providers.openrouter = {\n apiKey: process.env.OPENROUTER_API_KEY,\n baseUrl: process.env.OPENROUTER_BASE_URL,\n };\n }\n\n // Add Gemini if API key exists\n if (process.env.GOOGLE_GEMINI_API_KEY) {\n config.providers.gemini = {\n apiKey: process.env.GOOGLE_GEMINI_API_KEY,\n };\n }\n\n // Add Ollama if API key OR explicit base URL is set (self-hosted needs no key)\n if (process.env.OLLAMA_API_KEY || process.env.OLLAMA_BASE_URL) {\n config.providers.ollama = {\n apiKey: process.env.OLLAMA_API_KEY,\n baseUrl: process.env.OLLAMA_BASE_URL,\n };\n }\n\n // ONNX is always available (no API key needed)\n config.providers.onnx = {\n modelPath: process.env.ONNX_MODEL_PATH,\n executionProviders: ['cpu'],\n };\n\n return config as RouterConfig;\n }\n\n private substituteEnvVars(obj: any): any {\n if (typeof obj === 'string') {\n // Replace ${VAR_NAME} with environment variable value\n return obj.replace(/\\$\\{([^}]+)\\}/g, (_, key) => {\n const [varName, defaultValue] = key.split(':-');\n return process.env[varName] || defaultValue || '';\n });\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => this.substituteEnvVars(item));\n }\n\n if (obj && typeof obj === 'object') {\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = this.substituteEnvVars(value);\n }\n return result;\n }\n\n return obj;\n }\n\n private initializeProviders(): void {\n const verbose = process.env.ROUTER_VERBOSE === 'true';\n\n // Initialize Anthropic\n if (this.config.providers.anthropic) {\n try {\n const provider = new AnthropicProvider(this.config.providers.anthropic);\n this.providers.set('anthropic', provider);\n if (verbose) console.log('✅ Anthropic provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize Anthropic:', error);\n }\n }\n\n // Initialize OpenRouter\n if (this.config.providers.openrouter) {\n try {\n const provider = new OpenRouterProvider(this.config.providers.openrouter);\n this.providers.set('openrouter', provider);\n if (verbose) console.log('✅ OpenRouter provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize OpenRouter:', error);\n }\n }\n\n // Initialize ONNX Local\n if (this.config.providers.onnx) {\n try {\n const provider = new ONNXLocalProvider({\n modelPath:\n this.config.providers.onnx.modelPath ||\n './models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',\n executionProviders: this.config.providers.onnx.executionProviders || ['cpu'],\n maxTokens: this.config.providers.onnx.maxTokens || 100,\n temperature: this.config.providers.onnx.temperature || 0.7,\n });\n this.providers.set('onnx', provider);\n if (verbose) console.log('✅ ONNX Local provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize ONNX:', error);\n }\n }\n\n // Initialize Gemini\n if (this.config.providers.gemini) {\n try {\n const provider = new GeminiProvider(this.config.providers.gemini);\n this.providers.set('gemini', provider);\n if (verbose) console.log('✅ Gemini provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize Gemini:', error);\n }\n }\n\n // Initialize Ollama (OpenAI-compat, self-hosted or ollama.com Cloud)\n if (this.config.providers.ollama) {\n try {\n const provider = new OllamaProvider(this.config.providers.ollama);\n this.providers.set('ollama', provider);\n if (verbose) console.log('✅ Ollama provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize Ollama:', error);\n }\n }\n }\n\n private initializeMetrics(): RouterMetrics {\n return {\n totalRequests: 0,\n totalCost: 0,\n totalTokens: { input: 0, output: 0 },\n providerBreakdown: {},\n agentBreakdown: {},\n };\n }\n\n async chat(params: ChatParams, agentType?: string): Promise<ChatResponse> {\n const startTime = Date.now();\n const provider = await this.selectProvider(params, agentType);\n\n try {\n const response = await provider.chat(params);\n\n // Update metrics\n this.updateMetrics(provider.name, response, Date.now() - startTime, agentType);\n\n // Add metadata\n response.metadata = {\n ...response.metadata,\n provider: provider.name,\n latency: Date.now() - startTime,\n };\n\n return response;\n } catch (error) {\n return this.handleProviderError(error as ProviderError, params, agentType);\n }\n }\n\n async *stream(params: ChatParams, agentType?: string): AsyncGenerator<StreamChunk> {\n const provider = await this.selectProvider(params, agentType);\n\n if (!provider.stream) {\n throw new Error(`Provider ${provider.name} does not support streaming`);\n }\n\n const iterator = provider.stream(params);\n for await (const chunk of iterator) {\n yield chunk;\n }\n }\n\n private async selectProvider(params: ChatParams, agentType?: string): Promise<LLMProvider> {\n // If provider is explicitly specified in params, use it\n if (params.provider) {\n const forcedProvider = this.providers.get(params.provider as ProviderType);\n if (forcedProvider) {\n return forcedProvider;\n }\n console.warn(\n `⚠️ Requested provider '${params.provider}' not available, falling back to routing logic`\n );\n }\n\n const routingMode = this.config.routing?.mode || 'manual';\n\n switch (routingMode) {\n case 'manual':\n return this.getDefaultProvider();\n\n case 'rule-based':\n return this.selectByRules(params, agentType);\n\n case 'cost-optimized':\n return this.selectByCost(params);\n\n case 'performance-optimized':\n return this.selectByPerformance(params);\n\n default:\n return this.getDefaultProvider();\n }\n }\n\n private getDefaultProvider(): LLMProvider {\n const provider = this.providers.get(this.config.defaultProvider);\n if (!provider) {\n throw new Error(`Default provider ${this.config.defaultProvider} not initialized`);\n }\n return provider;\n }\n\n private selectByRules(params: ChatParams, agentType?: string): LLMProvider {\n const rules = this.config.routing?.rules || [];\n\n for (const rule of rules) {\n if (this.matchesRule(rule.condition, params, agentType)) {\n const provider = this.providers.get(rule.action.provider);\n if (provider) {\n console.log(`🎯 Routing via rule: ${rule.reason || 'matched condition'}`);\n return provider;\n }\n }\n }\n\n return this.getDefaultProvider();\n }\n\n private matchesRule(condition: any, params: ChatParams, agentType?: string): boolean {\n if (condition.agentType && agentType) {\n if (!condition.agentType.includes(agentType)) {\n return false;\n }\n }\n\n if (condition.requiresTools !== undefined) {\n if (condition.requiresTools && (!params.tools || params.tools.length === 0)) {\n return false;\n }\n }\n\n // TODO: Add more condition matching logic\n\n return true;\n }\n\n private selectByCost(params: ChatParams): LLMProvider {\n // For now, prefer cheaper providers\n // TODO: Implement actual cost calculation\n const providerOrder: ProviderType[] = ['openrouter', 'anthropic', 'openai'];\n\n for (const providerType of providerOrder) {\n const provider = this.providers.get(providerType);\n if (provider) {\n console.log(`💰 Cost-optimized routing: selected ${provider.name}`);\n return provider;\n }\n }\n\n return this.getDefaultProvider();\n }\n\n private selectByPerformance(params: ChatParams): LLMProvider {\n // For now, use metrics to select fastest provider\n let fastestProvider: LLMProvider | null = null;\n let lowestLatency = Infinity;\n\n for (const [providerType, provider] of this.providers) {\n const breakdown = this.metrics.providerBreakdown[providerType];\n if (breakdown && breakdown.avgLatency < lowestLatency) {\n lowestLatency = breakdown.avgLatency;\n fastestProvider = provider;\n }\n }\n\n if (fastestProvider) {\n console.log(`⚡ Performance-optimized routing: selected ${fastestProvider.name}`);\n return fastestProvider;\n }\n\n return this.getDefaultProvider();\n }\n\n private async handleProviderError(\n error: ProviderError,\n params: ChatParams,\n agentType?: string\n ): Promise<ChatResponse> {\n console.error(`❌ Provider error from ${error.provider}:`, error.message);\n\n // Try fallback chain\n const fallbackChain = this.config.fallbackChain || [];\n\n for (const providerType of fallbackChain) {\n if (providerType === error.provider) continue; // Skip failed provider\n\n const provider = this.providers.get(providerType);\n if (provider) {\n console.log(`🔄 Falling back to ${provider.name}`);\n try {\n return await provider.chat(params);\n } catch (fallbackError) {\n console.error(`❌ Fallback provider ${provider.name} also failed`);\n continue;\n }\n }\n }\n\n throw error; // No fallback succeeded\n }\n\n private updateMetrics(\n providerName: string,\n response: ChatResponse,\n latency: number,\n agentType?: string\n ): void {\n this.metrics.totalRequests++;\n\n if (response.usage) {\n this.metrics.totalTokens.input += response.usage.inputTokens;\n this.metrics.totalTokens.output += response.usage.outputTokens;\n }\n\n if (response.metadata?.cost) {\n this.metrics.totalCost += response.metadata.cost;\n }\n\n // Provider breakdown\n if (!this.metrics.providerBreakdown[providerName]) {\n this.metrics.providerBreakdown[providerName] = {\n requests: 0,\n cost: 0,\n avgLatency: 0,\n errors: 0,\n };\n }\n\n const breakdown = this.metrics.providerBreakdown[providerName];\n breakdown.requests++;\n breakdown.cost += response.metadata?.cost || 0;\n breakdown.avgLatency =\n (breakdown.avgLatency * (breakdown.requests - 1) + latency) / breakdown.requests;\n\n // Agent breakdown\n if (agentType) {\n if (!this.metrics.agentBreakdown) {\n this.metrics.agentBreakdown = {};\n }\n if (!this.metrics.agentBreakdown[agentType]) {\n this.metrics.agentBreakdown[agentType] = { requests: 0, cost: 0 };\n }\n this.metrics.agentBreakdown[agentType].requests++;\n this.metrics.agentBreakdown[agentType].cost += response.metadata?.cost || 0;\n }\n }\n\n getMetrics(): RouterMetrics {\n return { ...this.metrics };\n }\n\n getConfig(): RouterConfig {\n return { ...this.config };\n }\n\n getProviders(): Map<ProviderType, LLMProvider> {\n return new Map(this.providers);\n }\n}\n"]}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"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","sourcesContent":["// Multi-model router core implementation\nimport { readFileSync, existsSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport {\n LLMProvider,\n RouterConfig,\n ChatParams,\n ChatResponse,\n StreamChunk,\n ProviderType,\n RouterMetrics,\n ProviderError,\n Message,\n} from './types.js';\nimport { CostOptimalRouter } from './cost-optimal-router.js';\nimport { OpenRouterProvider } from './providers/openrouter.js';\nimport { AnthropicProvider } from './providers/anthropic.js';\nimport { ONNXLocalProvider } from './providers/onnx-local.js';\nimport { GeminiProvider } from './providers/gemini.js';\nimport { OllamaProvider } from './providers/ollama.js';\n\nexport class ModelRouter {\n private config: RouterConfig;\n private providers: Map<ProviderType, LLMProvider> = new Map();\n private metrics: RouterMetrics;\n // ADR-073: learned cost-optimal routing (opt-in via enableCostOptimalRouting).\n private costOptimalRouter?: CostOptimalRouter;\n private embedQuery?: (text: string) => Promise<number[]> | number[];\n // Bounded LRU embedding cache — embedding dominates the cost-optimal hot path\n // (≈ms) vs the µs-scale k-NN, and recurring prompts are common.\n private embedCache = new Map<string, number[]>();\n private static readonly EMBED_CACHE_MAX = 512;\n\n constructor(configPath?: string) {\n this.config = this.loadConfig(configPath);\n this.initializeProviders();\n this.metrics = this.initializeMetrics();\n }\n\n private loadConfig(configPath?: string): RouterConfig {\n const paths = [\n configPath,\n process.env.AGENTIC_FLOW_ROUTER_CONFIG,\n join(homedir(), '.agentic-flow', 'router.config.json'),\n join(process.cwd(), 'router.config.json'),\n join(process.cwd(), 'config', 'router.config.json'),\n join(process.cwd(), 'router.config.example.json'),\n ].filter(Boolean) as string[];\n\n for (const path of paths) {\n if (existsSync(path)) {\n const content = readFileSync(path, 'utf-8');\n const config = JSON.parse(content);\n\n // Substitute environment variables\n return this.substituteEnvVars(config);\n }\n }\n\n // If no config file found, create config from environment variables\n return this.createConfigFromEnv();\n }\n\n private createConfigFromEnv(): RouterConfig {\n // Create minimal config from environment variables\n const config: any = {\n version: '1.0',\n defaultProvider: (process.env.PROVIDER as any) || 'anthropic',\n routing: { mode: 'manual' as const },\n providers: {} as any,\n };\n\n // Add Anthropic if API key exists\n if (process.env.ANTHROPIC_API_KEY) {\n config.providers.anthropic = {\n apiKey: process.env.ANTHROPIC_API_KEY,\n baseUrl: process.env.ANTHROPIC_BASE_URL,\n };\n }\n\n // Add OpenRouter if API key exists\n if (process.env.OPENROUTER_API_KEY) {\n config.providers.openrouter = {\n apiKey: process.env.OPENROUTER_API_KEY,\n baseUrl: process.env.OPENROUTER_BASE_URL,\n };\n }\n\n // Add Gemini if API key exists\n if (process.env.GOOGLE_GEMINI_API_KEY) {\n config.providers.gemini = {\n apiKey: process.env.GOOGLE_GEMINI_API_KEY,\n };\n }\n\n // Add Ollama if API key OR explicit base URL is set (self-hosted needs no key)\n if (process.env.OLLAMA_API_KEY || process.env.OLLAMA_BASE_URL) {\n config.providers.ollama = {\n apiKey: process.env.OLLAMA_API_KEY,\n baseUrl: process.env.OLLAMA_BASE_URL,\n };\n }\n\n // ONNX is always available (no API key needed)\n config.providers.onnx = {\n modelPath: process.env.ONNX_MODEL_PATH,\n executionProviders: ['cpu'],\n };\n\n return config as RouterConfig;\n }\n\n private substituteEnvVars(obj: any): any {\n if (typeof obj === 'string') {\n // Replace ${VAR_NAME} with environment variable value\n return obj.replace(/\\$\\{([^}]+)\\}/g, (_, key) => {\n const [varName, defaultValue] = key.split(':-');\n return process.env[varName] || defaultValue || '';\n });\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => this.substituteEnvVars(item));\n }\n\n if (obj && typeof obj === 'object') {\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = this.substituteEnvVars(value);\n }\n return result;\n }\n\n return obj;\n }\n\n private initializeProviders(): void {\n const verbose = process.env.ROUTER_VERBOSE === 'true';\n\n // Initialize Anthropic\n if (this.config.providers.anthropic) {\n try {\n const provider = new AnthropicProvider(this.config.providers.anthropic);\n this.providers.set('anthropic', provider);\n if (verbose) console.log('✅ Anthropic provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize Anthropic:', error);\n }\n }\n\n // Initialize OpenRouter\n if (this.config.providers.openrouter) {\n try {\n const provider = new OpenRouterProvider(this.config.providers.openrouter);\n this.providers.set('openrouter', provider);\n if (verbose) console.log('✅ OpenRouter provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize OpenRouter:', error);\n }\n }\n\n // Initialize ONNX Local\n if (this.config.providers.onnx) {\n try {\n const provider = new ONNXLocalProvider({\n modelPath:\n this.config.providers.onnx.modelPath ||\n './models/phi-4/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/model.onnx',\n executionProviders: this.config.providers.onnx.executionProviders || ['cpu'],\n maxTokens: this.config.providers.onnx.maxTokens || 100,\n temperature: this.config.providers.onnx.temperature || 0.7,\n });\n this.providers.set('onnx', provider);\n if (verbose) console.log('✅ ONNX Local provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize ONNX:', error);\n }\n }\n\n // Initialize Gemini\n if (this.config.providers.gemini) {\n try {\n const provider = new GeminiProvider(this.config.providers.gemini);\n this.providers.set('gemini', provider);\n if (verbose) console.log('✅ Gemini provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize Gemini:', error);\n }\n }\n\n // Initialize Ollama (OpenAI-compat, self-hosted or ollama.com Cloud)\n if (this.config.providers.ollama) {\n try {\n const provider = new OllamaProvider(this.config.providers.ollama);\n this.providers.set('ollama', provider);\n if (verbose) console.log('✅ Ollama provider initialized');\n } catch (error) {\n if (verbose) console.error('❌ Failed to initialize Ollama:', error);\n }\n }\n }\n\n private initializeMetrics(): RouterMetrics {\n return {\n totalRequests: 0,\n totalCost: 0,\n totalTokens: { input: 0, output: 0 },\n providerBreakdown: {},\n agentBreakdown: {},\n };\n }\n\n async chat(params: ChatParams, agentType?: string): Promise<ChatResponse> {\n const startTime = Date.now();\n const provider = await this.selectProvider(params, agentType);\n\n try {\n const response = await provider.chat(params);\n\n // Update metrics\n this.updateMetrics(provider.name, response, Date.now() - startTime, agentType);\n\n // Add metadata\n response.metadata = {\n ...response.metadata,\n provider: provider.name,\n latency: Date.now() - startTime,\n };\n\n return response;\n } catch (error) {\n return this.handleProviderError(error as ProviderError, params, agentType);\n }\n }\n\n async *stream(params: ChatParams, agentType?: string): AsyncGenerator<StreamChunk> {\n const provider = await this.selectProvider(params, agentType);\n\n if (!provider.stream) {\n throw new Error(`Provider ${provider.name} does not support streaming`);\n }\n\n const iterator = provider.stream(params);\n for await (const chunk of iterator) {\n yield chunk;\n }\n }\n\n private async selectProvider(params: ChatParams, agentType?: string): Promise<LLMProvider> {\n // If provider is explicitly specified in params, use it\n if (params.provider) {\n const forcedProvider = this.providers.get(params.provider as ProviderType);\n if (forcedProvider) {\n return forcedProvider;\n }\n console.warn(\n `⚠️ Requested provider '${params.provider}' not available, falling back to routing logic`\n );\n }\n\n const routingMode = this.config.routing?.mode || 'manual';\n\n switch (routingMode) {\n case 'manual':\n return this.getDefaultProvider();\n\n case 'rule-based':\n return this.selectByRules(params, agentType);\n\n case 'cost-optimized':\n return this.selectByCost(params);\n\n case 'cost-optimal':\n return this.selectByCostOptimal(params);\n\n case 'performance-optimized':\n return this.selectByPerformance(params);\n\n default:\n return this.getDefaultProvider();\n }\n }\n\n private getDefaultProvider(): LLMProvider {\n const provider = this.providers.get(this.config.defaultProvider);\n if (!provider) {\n throw new Error(`Default provider ${this.config.defaultProvider} not initialized`);\n }\n return provider;\n }\n\n private selectByRules(params: ChatParams, agentType?: string): LLMProvider {\n const rules = this.config.routing?.rules || [];\n\n for (const rule of rules) {\n if (this.matchesRule(rule.condition, params, agentType)) {\n const provider = this.providers.get(rule.action.provider);\n if (provider) {\n console.log(`🎯 Routing via rule: ${rule.reason || 'matched condition'}`);\n return provider;\n }\n }\n }\n\n return this.getDefaultProvider();\n }\n\n private matchesRule(condition: any, params: ChatParams, agentType?: string): boolean {\n if (condition.agentType && agentType) {\n if (!condition.agentType.includes(agentType)) {\n return false;\n }\n }\n\n if (condition.requiresTools !== undefined) {\n if (condition.requiresTools && (!params.tools || params.tools.length === 0)) {\n return false;\n }\n }\n\n // TODO: Add more condition matching logic\n\n return true;\n }\n\n private selectByCost(params: ChatParams): LLMProvider {\n // For now, prefer cheaper providers\n // TODO: Implement actual cost calculation\n const providerOrder: ProviderType[] = ['openrouter', 'anthropic', 'openai'];\n\n for (const providerType of providerOrder) {\n const provider = this.providers.get(providerType);\n if (provider) {\n console.log(`💰 Cost-optimized routing: selected ${provider.name}`);\n return provider;\n }\n }\n\n return this.getDefaultProvider();\n }\n\n /**\n * Enable learned cost-optimal routing (ADR-073). Attaches a\n * {@link CostOptimalRouter} plus an embedder and switches the routing mode to\n * `'cost-optimal'`. Opt-in: until called, routing behaves exactly as before.\n */\n enableCostOptimalRouting(opts: {\n router: CostOptimalRouter;\n embed: (text: string) => Promise<number[]> | number[];\n }): void {\n this.costOptimalRouter = opts.router;\n this.embedQuery = opts.embed;\n this.config.routing = { ...(this.config.routing ?? { mode: 'cost-optimal' }), mode: 'cost-optimal' };\n }\n\n /**\n * Embed `text`, caching the result. Bounded LRU: a cache hit refreshes\n * recency; the oldest entry is evicted past {@link EMBED_CACHE_MAX}.\n */\n private async embedCached(text: string): Promise<number[]> {\n const hit = this.embedCache.get(text);\n if (hit) {\n this.embedCache.delete(text); // move to most-recently-used\n this.embedCache.set(text, hit);\n return hit;\n }\n const embedding = await this.embedQuery!(text);\n this.embedCache.set(text, embedding);\n if (this.embedCache.size > ModelRouter.EMBED_CACHE_MAX) {\n const oldest = this.embedCache.keys().next().value;\n if (oldest !== undefined) this.embedCache.delete(oldest);\n }\n return embedding;\n }\n\n /** Extract the most recent user-message text to embed for routing. */\n private lastUserText(messages: Message[]): string {\n for (let i = messages.length - 1; i >= 0; i--) {\n const m = messages[i];\n if (m.role !== 'user') continue;\n return typeof m.content === 'string'\n ? m.content\n : m.content.map((b) => b.text ?? '').join(' ');\n }\n // No user turn — fall back to all text content.\n return messages\n .map((m) => (typeof m.content === 'string' ? m.content : ''))\n .join(' ')\n .trim();\n }\n\n /**\n * Learned cost-optimal selection (ADR-073): embed the query, route to the\n * cheapest model predicted to clear the quality bar, steer params.model to it,\n * and return the matching provider. Falls back to the {@link selectByCost}\n * heuristic when the learned router/embedder isn't configured or errors —\n * routing never hard-fails on the cost-optimal path.\n */\n private async selectByCostOptimal(params: ChatParams): Promise<LLMProvider> {\n if (this.costOptimalRouter && this.embedQuery) {\n try {\n const text = this.lastUserText(params.messages);\n const embedding = await this.embedCached(text);\n const decision = this.costOptimalRouter.route(embedding);\n const provider = this.providers.get(decision.provider);\n if (provider) {\n params.model = decision.model; // steer model, not just provider\n console.log(\n `💡 Cost-optimal routing → ${decision.id} ` +\n `(q̂=${decision.predictedQuality.toFixed(2)}, $${decision.costPerMTok}/Mtok, metBar=${decision.metBar})`,\n );\n return provider;\n }\n console.warn(\n `⚠️ Cost-optimal pick '${decision.provider}' not initialized; falling back to heuristic`,\n );\n } catch (err) {\n console.warn('⚠️ Cost-optimal routing failed; falling back to heuristic', err);\n }\n }\n return this.selectByCost(params);\n }\n\n private selectByPerformance(params: ChatParams): LLMProvider {\n // For now, use metrics to select fastest provider\n let fastestProvider: LLMProvider | null = null;\n let lowestLatency = Infinity;\n\n for (const [providerType, provider] of this.providers) {\n const breakdown = this.metrics.providerBreakdown[providerType];\n if (breakdown && breakdown.avgLatency < lowestLatency) {\n lowestLatency = breakdown.avgLatency;\n fastestProvider = provider;\n }\n }\n\n if (fastestProvider) {\n console.log(`⚡ Performance-optimized routing: selected ${fastestProvider.name}`);\n return fastestProvider;\n }\n\n return this.getDefaultProvider();\n }\n\n private async handleProviderError(\n error: ProviderError,\n params: ChatParams,\n agentType?: string\n ): Promise<ChatResponse> {\n console.error(`❌ Provider error from ${error.provider}:`, error.message);\n\n // Try fallback chain\n const fallbackChain = this.config.fallbackChain || [];\n\n for (const providerType of fallbackChain) {\n if (providerType === error.provider) continue; // Skip failed provider\n\n const provider = this.providers.get(providerType);\n if (provider) {\n console.log(`🔄 Falling back to ${provider.name}`);\n try {\n return await provider.chat(params);\n } catch (fallbackError) {\n console.error(`❌ Fallback provider ${provider.name} also failed`);\n continue;\n }\n }\n }\n\n throw error; // No fallback succeeded\n }\n\n private updateMetrics(\n providerName: string,\n response: ChatResponse,\n latency: number,\n agentType?: string\n ): void {\n this.metrics.totalRequests++;\n\n if (response.usage) {\n this.metrics.totalTokens.input += response.usage.inputTokens;\n this.metrics.totalTokens.output += response.usage.outputTokens;\n }\n\n if (response.metadata?.cost) {\n this.metrics.totalCost += response.metadata.cost;\n }\n\n // Provider breakdown\n if (!this.metrics.providerBreakdown[providerName]) {\n this.metrics.providerBreakdown[providerName] = {\n requests: 0,\n cost: 0,\n avgLatency: 0,\n errors: 0,\n };\n }\n\n const breakdown = this.metrics.providerBreakdown[providerName];\n breakdown.requests++;\n breakdown.cost += response.metadata?.cost || 0;\n breakdown.avgLatency =\n (breakdown.avgLatency * (breakdown.requests - 1) + latency) / breakdown.requests;\n\n // Agent breakdown\n if (agentType) {\n if (!this.metrics.agentBreakdown) {\n this.metrics.agentBreakdown = {};\n }\n if (!this.metrics.agentBreakdown[agentType]) {\n this.metrics.agentBreakdown[agentType] = { requests: 0, cost: 0 };\n }\n this.metrics.agentBreakdown[agentType].requests++;\n this.metrics.agentBreakdown[agentType].cost += response.metadata?.cost || 0;\n }\n }\n\n getMetrics(): RouterMetrics {\n return { ...this.metrics };\n }\n\n getConfig(): RouterConfig {\n return { ...this.config };\n }\n\n getProviders(): Map<ProviderType, LLMProvider> {\n return new Map(this.providers);\n }\n}\n"]}
@@ -113,7 +113,7 @@ export interface RouterConfig {
113
113
  cache?: CacheConfig;
114
114
  }
115
115
  export interface RoutingConfig {
116
- mode: 'manual' | 'cost-optimized' | 'performance-optimized' | 'quality-optimized' | 'rule-based';
116
+ mode: 'manual' | 'cost-optimized' | 'cost-optimal' | 'performance-optimized' | 'quality-optimized' | 'rule-based';
117
117
  rules?: RoutingRule[];
118
118
  costOptimization?: {
119
119
  enabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/router/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/router/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/router/types.ts"],"names":[],"mappings":"AAAA,oCAAoC","sourcesContent":["// Core types for multi-model router\n\nexport interface LLMProvider {\n name: string;\n type: ProviderType;\n supportsStreaming: boolean;\n supportsTools: boolean;\n supportsMCP: boolean;\n chat(params: ChatParams): Promise<ChatResponse>;\n stream?(params: ChatParams): AsyncGenerator<StreamChunk>;\n validateCapabilities(features: string[]): boolean;\n}\n\nexport type ProviderType = 'anthropic' | 'openai' | 'openrouter' | 'ollama' | 'litellm' | 'onnx' | 'gemini' | 'custom';\n\nexport interface ChatParams {\n model: string;\n messages: Message[];\n temperature?: number;\n maxTokens?: number;\n tools?: Tool[];\n toolChoice?: 'auto' | 'any' | 'none' | { type: 'tool'; name: string };\n stream?: boolean;\n metadata?: Record<string, any>;\n provider?: string; // Force specific provider (gemini, openrouter, anthropic, etc.)\n}\n\nexport interface Message {\n role: 'user' | 'assistant' | 'system';\n content: string | ContentBlock[];\n}\n\nexport interface ContentBlock {\n type: 'text' | 'tool_use' | 'tool_result';\n text?: string;\n id?: string;\n name?: string;\n input?: any;\n content?: any;\n is_error?: boolean;\n}\n\nexport interface Tool {\n name: string;\n description: string;\n input_schema: {\n type: 'object';\n properties: Record<string, any>;\n required?: string[];\n };\n}\n\nexport interface ChatResponse {\n id: string;\n model: string;\n content: ContentBlock[];\n stopReason?: 'end_turn' | 'max_tokens' | 'tool_use' | 'stop_sequence';\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n metadata?: {\n provider: string;\n model?: string;\n cost?: number;\n latency?: number;\n executionProviders?: string[];\n [key: string]: any;\n };\n}\n\nexport interface StreamChunk {\n type: 'content_block_start' | 'content_block_delta' | 'content_block_stop' | 'message_start' | 'message_delta' | 'message_stop';\n delta?: {\n type: 'text_delta' | 'input_json_delta';\n text?: string;\n partial_json?: string;\n };\n content_block?: ContentBlock;\n message?: Partial<ChatResponse>;\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n}\n\nexport interface ProviderConfig {\n apiKey?: string;\n baseUrl?: string;\n organization?: string;\n models?: {\n default: string;\n fast?: string;\n advanced?: string;\n [key: string]: string | undefined;\n };\n timeout?: number;\n maxRetries?: number;\n retryDelay?: number;\n rateLimit?: {\n requestsPerMinute?: number;\n tokensPerMinute?: number;\n };\n preferences?: Record<string, any>;\n // ONNX-specific config\n modelPath?: string;\n executionProviders?: string[];\n maxTokens?: number;\n temperature?: number;\n localInference?: boolean;\n gpuAcceleration?: boolean;\n}\n\nexport interface RouterConfig {\n version: string;\n defaultProvider: ProviderType;\n fallbackChain?: ProviderType[];\n providers: Record<ProviderType, ProviderConfig>;\n routing?: RoutingConfig;\n toolCalling?: ToolCallingConfig;\n monitoring?: MonitoringConfig;\n cache?: CacheConfig;\n}\n\nexport interface RoutingConfig {\n mode: 'manual' | 'cost-optimized' | 'performance-optimized' | 'quality-optimized' | 'rule-based';\n rules?: RoutingRule[];\n costOptimization?: {\n enabled: boolean;\n maxCostPerRequest?: number;\n budgetAlerts?: {\n daily?: number;\n monthly?: number;\n };\n preferCheaper?: boolean;\n costThreshold?: number;\n };\n performance?: {\n timeout?: number;\n concurrentRequests?: number;\n circuitBreaker?: {\n enabled: boolean;\n threshold: number;\n timeout: number;\n resetTimeout?: number;\n };\n };\n}\n\nexport interface RoutingRule {\n condition: {\n agentType?: string[];\n requiresTools?: boolean;\n complexity?: 'low' | 'medium' | 'high';\n privacy?: 'low' | 'medium' | 'high';\n localOnly?: boolean;\n requiresReasoning?: boolean;\n };\n action: {\n provider: ProviderType;\n model: string;\n temperature?: number;\n maxTokens?: number;\n };\n reason?: string;\n}\n\nexport interface ToolCallingConfig {\n translationEnabled: boolean;\n defaultFormat: 'anthropic' | 'openai' | 'auto-detect';\n formatMapping?: Record<string, string>;\n fallbackStrategy?: 'disable-tools' | 'use-text' | 'fail';\n}\n\nexport interface MonitoringConfig {\n enabled: boolean;\n logLevel: 'debug' | 'info' | 'warn' | 'error';\n metrics?: {\n trackCost?: boolean;\n trackLatency?: boolean;\n trackTokens?: boolean;\n trackErrors?: boolean;\n };\n alerts?: {\n costThreshold?: number;\n errorRate?: number;\n latencyThreshold?: number;\n };\n}\n\nexport interface CacheConfig {\n enabled: boolean;\n ttl?: number;\n maxSize?: number;\n strategy?: 'lru' | 'fifo' | 'lfu';\n providers?: Record<string, { ttl?: number }>;\n}\n\nexport interface RouterMetrics {\n totalRequests: number;\n totalCost: number;\n totalTokens: {\n input: number;\n output: number;\n };\n providerBreakdown: Record<string, {\n requests: number;\n cost: number;\n avgLatency: number;\n errors: number;\n }>;\n agentBreakdown?: Record<string, {\n requests: number;\n cost: number;\n }>;\n}\n\nexport interface ProviderError extends Error {\n provider: string;\n statusCode?: number;\n retryable: boolean;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/router/types.ts"],"names":[],"mappings":"AAAA,oCAAoC","sourcesContent":["// Core types for multi-model router\n\nexport interface LLMProvider {\n name: string;\n type: ProviderType;\n supportsStreaming: boolean;\n supportsTools: boolean;\n supportsMCP: boolean;\n chat(params: ChatParams): Promise<ChatResponse>;\n stream?(params: ChatParams): AsyncGenerator<StreamChunk>;\n validateCapabilities(features: string[]): boolean;\n}\n\nexport type ProviderType = 'anthropic' | 'openai' | 'openrouter' | 'ollama' | 'litellm' | 'onnx' | 'gemini' | 'custom';\n\nexport interface ChatParams {\n model: string;\n messages: Message[];\n temperature?: number;\n maxTokens?: number;\n tools?: Tool[];\n toolChoice?: 'auto' | 'any' | 'none' | { type: 'tool'; name: string };\n stream?: boolean;\n metadata?: Record<string, any>;\n provider?: string; // Force specific provider (gemini, openrouter, anthropic, etc.)\n}\n\nexport interface Message {\n role: 'user' | 'assistant' | 'system';\n content: string | ContentBlock[];\n}\n\nexport interface ContentBlock {\n type: 'text' | 'tool_use' | 'tool_result';\n text?: string;\n id?: string;\n name?: string;\n input?: any;\n content?: any;\n is_error?: boolean;\n}\n\nexport interface Tool {\n name: string;\n description: string;\n input_schema: {\n type: 'object';\n properties: Record<string, any>;\n required?: string[];\n };\n}\n\nexport interface ChatResponse {\n id: string;\n model: string;\n content: ContentBlock[];\n stopReason?: 'end_turn' | 'max_tokens' | 'tool_use' | 'stop_sequence';\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n metadata?: {\n provider: string;\n model?: string;\n cost?: number;\n latency?: number;\n executionProviders?: string[];\n [key: string]: any;\n };\n}\n\nexport interface StreamChunk {\n type: 'content_block_start' | 'content_block_delta' | 'content_block_stop' | 'message_start' | 'message_delta' | 'message_stop';\n delta?: {\n type: 'text_delta' | 'input_json_delta';\n text?: string;\n partial_json?: string;\n };\n content_block?: ContentBlock;\n message?: Partial<ChatResponse>;\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n}\n\nexport interface ProviderConfig {\n apiKey?: string;\n baseUrl?: string;\n organization?: string;\n models?: {\n default: string;\n fast?: string;\n advanced?: string;\n [key: string]: string | undefined;\n };\n timeout?: number;\n maxRetries?: number;\n retryDelay?: number;\n rateLimit?: {\n requestsPerMinute?: number;\n tokensPerMinute?: number;\n };\n preferences?: Record<string, any>;\n // ONNX-specific config\n modelPath?: string;\n executionProviders?: string[];\n maxTokens?: number;\n temperature?: number;\n localInference?: boolean;\n gpuAcceleration?: boolean;\n}\n\nexport interface RouterConfig {\n version: string;\n defaultProvider: ProviderType;\n fallbackChain?: ProviderType[];\n providers: Record<ProviderType, ProviderConfig>;\n routing?: RoutingConfig;\n toolCalling?: ToolCallingConfig;\n monitoring?: MonitoringConfig;\n cache?: CacheConfig;\n}\n\nexport interface RoutingConfig {\n // 'cost-optimized' = legacy heuristic provider ordering;\n // 'cost-optimal' = learned predicted-quality-per-cost routing (ADR-073)\n mode: 'manual' | 'cost-optimized' | 'cost-optimal' | 'performance-optimized' | 'quality-optimized' | 'rule-based';\n rules?: RoutingRule[];\n costOptimization?: {\n enabled: boolean;\n maxCostPerRequest?: number;\n budgetAlerts?: {\n daily?: number;\n monthly?: number;\n };\n preferCheaper?: boolean;\n costThreshold?: number;\n };\n performance?: {\n timeout?: number;\n concurrentRequests?: number;\n circuitBreaker?: {\n enabled: boolean;\n threshold: number;\n timeout: number;\n resetTimeout?: number;\n };\n };\n}\n\nexport interface RoutingRule {\n condition: {\n agentType?: string[];\n requiresTools?: boolean;\n complexity?: 'low' | 'medium' | 'high';\n privacy?: 'low' | 'medium' | 'high';\n localOnly?: boolean;\n requiresReasoning?: boolean;\n };\n action: {\n provider: ProviderType;\n model: string;\n temperature?: number;\n maxTokens?: number;\n };\n reason?: string;\n}\n\nexport interface ToolCallingConfig {\n translationEnabled: boolean;\n defaultFormat: 'anthropic' | 'openai' | 'auto-detect';\n formatMapping?: Record<string, string>;\n fallbackStrategy?: 'disable-tools' | 'use-text' | 'fail';\n}\n\nexport interface MonitoringConfig {\n enabled: boolean;\n logLevel: 'debug' | 'info' | 'warn' | 'error';\n metrics?: {\n trackCost?: boolean;\n trackLatency?: boolean;\n trackTokens?: boolean;\n trackErrors?: boolean;\n };\n alerts?: {\n costThreshold?: number;\n errorRate?: number;\n latencyThreshold?: number;\n };\n}\n\nexport interface CacheConfig {\n enabled: boolean;\n ttl?: number;\n maxSize?: number;\n strategy?: 'lru' | 'fifo' | 'lfu';\n providers?: Record<string, { ttl?: number }>;\n}\n\nexport interface RouterMetrics {\n totalRequests: number;\n totalCost: number;\n totalTokens: {\n input: number;\n output: number;\n };\n providerBreakdown: Record<string, {\n requests: number;\n cost: number;\n avgLatency: number;\n errors: number;\n }>;\n agentBreakdown?: Record<string, {\n requests: number;\n cost: number;\n }>;\n}\n\nexport interface ProviderError extends Error {\n provider: string;\n statusCode?: number;\n retryable: boolean;\n}\n"]}
@@ -137,6 +137,39 @@ declare class WebSocketFallbackTransport implements AgentTransport {
137
137
  * streamId. Lets callers register both per-stream + catch-all.
138
138
  */
139
139
  private inboundHandlers;
140
+ /**
141
+ * Server-accepted (inbound) sockets indexed by peer host (#162). A
142
+ * peer that already dialed us has a live, full-duplex socket here;
143
+ * {@link getOrCreateConnection} reuses it for replies instead of
144
+ * opening a fresh outbound connection — which, on a direct
145
+ * point-to-point link, can hit a per-process stale scoped route
146
+ * (`EHOSTUNREACH`) even while the inbound direction is healthy.
147
+ */
148
+ private inboundByHost;
149
+ /** Active liveness-probe intervals, cleared on close (#162). */
150
+ private heartbeats;
151
+ /**
152
+ * Liveness ping cadence, derived from `maxIdleTimeoutMs` (#162 — this
153
+ * config was previously accepted but never used). Pings at 1/6 of the
154
+ * idle timeout so a dead socket is caught within ~1/3 of it. Default
155
+ * 30000ms → 5000ms.
156
+ */
157
+ private get pingIntervalMs();
158
+ /**
159
+ * Normalize an address or socket `remoteAddress` to a bare host key
160
+ * for {@link inboundByHost} lookups. Strips `ws(s)://` scheme, an
161
+ * IPv6-mapped IPv4 prefix (`::ffff:`), bracketed IPv6 (`[::1]:port`),
162
+ * and a trailing `:port` on plain `host:port` forms. Bare IPv6
163
+ * (multiple colons, no brackets) is returned unchanged.
164
+ */
165
+ private hostOf;
166
+ /**
167
+ * Liveness probe for a socket (#162). Pings on {@link pingIntervalMs};
168
+ * a socket that misses a ping/pong window is terminated so a stale
169
+ * connection can't silently black-hole sends. The interval is unref'd
170
+ * (never keeps the process alive on its own) and cleared on close/error.
171
+ */
172
+ private startHeartbeat;
140
173
  /** Compose the per-(address, streamId) queue key. */
141
174
  private queueKey;
142
175
  /** Resolve the streamId for a message — defaults to DEFAULT_STREAM_ID. */
@@ -1 +1 @@
1
- {"version":3,"file":"quic-loader.d.ts","sourceRoot":"","sources":["../../src/transport/quic-loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"quic-loader.d.ts","sourceRoot":"","sources":["../../src/transport/quic-loader.ts"],"names":[],"mappings":"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"}
@@ -61,6 +61,74 @@ class WebSocketFallbackTransport {
61
61
  * streamId. Lets callers register both per-stream + catch-all.
62
62
  */
63
63
  inboundHandlers = new Set();
64
+ /**
65
+ * Server-accepted (inbound) sockets indexed by peer host (#162). A
66
+ * peer that already dialed us has a live, full-duplex socket here;
67
+ * {@link getOrCreateConnection} reuses it for replies instead of
68
+ * opening a fresh outbound connection — which, on a direct
69
+ * point-to-point link, can hit a per-process stale scoped route
70
+ * (`EHOSTUNREACH`) even while the inbound direction is healthy.
71
+ */
72
+ inboundByHost = new Map();
73
+ /** Active liveness-probe intervals, cleared on close (#162). */
74
+ heartbeats = new Set();
75
+ /**
76
+ * Liveness ping cadence, derived from `maxIdleTimeoutMs` (#162 — this
77
+ * config was previously accepted but never used). Pings at 1/6 of the
78
+ * idle timeout so a dead socket is caught within ~1/3 of it. Default
79
+ * 30000ms → 5000ms.
80
+ */
81
+ get pingIntervalMs() {
82
+ return Math.max(1000, Math.floor(this.config.maxIdleTimeoutMs / 6));
83
+ }
84
+ /**
85
+ * Normalize an address or socket `remoteAddress` to a bare host key
86
+ * for {@link inboundByHost} lookups. Strips `ws(s)://` scheme, an
87
+ * IPv6-mapped IPv4 prefix (`::ffff:`), bracketed IPv6 (`[::1]:port`),
88
+ * and a trailing `:port` on plain `host:port` forms. Bare IPv6
89
+ * (multiple colons, no brackets) is returned unchanged.
90
+ */
91
+ hostOf(addr) {
92
+ let s = addr.replace(/^wss?:\/\//i, '');
93
+ const bracket = s.match(/^\[([^\]]+)\](?::\d+)?$/);
94
+ if (bracket)
95
+ s = bracket[1];
96
+ else if (/^[^:]+:\d+$/.test(s))
97
+ s = s.slice(0, s.lastIndexOf(':'));
98
+ return s.replace(/^::ffff:/i, '');
99
+ }
100
+ /**
101
+ * Liveness probe for a socket (#162). Pings on {@link pingIntervalMs};
102
+ * a socket that misses a ping/pong window is terminated so a stale
103
+ * connection can't silently black-hole sends. The interval is unref'd
104
+ * (never keeps the process alive on its own) and cleared on close/error.
105
+ */
106
+ startHeartbeat(ws) {
107
+ let alive = true;
108
+ ws.on('pong', () => { alive = true; });
109
+ const interval = setInterval(() => {
110
+ if (!alive) {
111
+ try {
112
+ ws.terminate();
113
+ }
114
+ catch { /* already gone */ }
115
+ return; // 'close' fires → stop() clears this interval
116
+ }
117
+ alive = false;
118
+ try {
119
+ ws.ping();
120
+ }
121
+ catch { /* socket is closing */ }
122
+ }, this.pingIntervalMs);
123
+ interval.unref?.();
124
+ this.heartbeats.add(interval);
125
+ const stop = () => {
126
+ clearInterval(interval);
127
+ this.heartbeats.delete(interval);
128
+ };
129
+ ws.on('close', stop);
130
+ ws.on('error', stop);
131
+ }
64
132
  /** Compose the per-(address, streamId) queue key. */
65
133
  queueKey(address, streamId) {
66
134
  return `${address}#${streamId}`;
@@ -140,6 +208,18 @@ class WebSocketFallbackTransport {
140
208
  attachServerHandlers(wss) {
141
209
  wss.on('connection', (ws, req) => {
142
210
  const remoteAddr = `${req.socket.remoteAddress}:${req.socket.remotePort}`;
211
+ // #162: register this accepted socket for outbound reuse, and probe
212
+ // it for liveness. Reusing it for replies avoids a fresh (stale-route
213
+ // prone) outbound dial back to the same peer.
214
+ const host = this.hostOf(req.socket.remoteAddress ?? remoteAddr);
215
+ this.inboundByHost.set(host, ws);
216
+ this.startHeartbeat(ws);
217
+ const dropInbound = () => {
218
+ if (this.inboundByHost.get(host) === ws)
219
+ this.inboundByHost.delete(host);
220
+ };
221
+ ws.on('close', dropInbound);
222
+ ws.on('error', dropInbound);
143
223
  ws.on('message', (raw) => {
144
224
  try {
145
225
  const message = JSON.parse(raw.toString());
@@ -163,6 +243,15 @@ class WebSocketFallbackTransport {
163
243
  resolve(existing);
164
244
  return;
165
245
  }
246
+ // #162: prefer an already-open INBOUND socket from the same peer host
247
+ // before dialing. WebSocket is full-duplex, so the server-accepted
248
+ // socket can carry our replies — and the inbound direction stays
249
+ // healthy when a fresh outbound dial would hit a stale scoped route.
250
+ const inbound = this.inboundByHost.get(this.hostOf(address));
251
+ if (inbound && inbound.readyState === WebSocket.OPEN) {
252
+ resolve(inbound);
253
+ return;
254
+ }
166
255
  const url = address.startsWith('ws://') || address.startsWith('wss://')
167
256
  ? address
168
257
  : `ws://${address}`;
@@ -216,6 +305,7 @@ class WebSocketFallbackTransport {
216
305
  ws.on('open', () => {
217
306
  this.connections.set(address, ws);
218
307
  this.connectionsCreated++;
308
+ this.startHeartbeat(ws); // #162: keep the outbound link probed
219
309
  resolve(ws);
220
310
  });
221
311
  ws.on('error', (error) => {
@@ -325,6 +415,11 @@ class WebSocketFallbackTransport {
325
415
  }
326
416
  this.connections.clear();
327
417
  this.messageQueue.clear();
418
+ // Stop all liveness probes and drop the inbound index (#162).
419
+ for (const interval of this.heartbeats)
420
+ clearInterval(interval);
421
+ this.heartbeats.clear();
422
+ this.inboundByHost.clear();
328
423
  // Inbound: WebSocketServer.close() blocks until every accepted
329
424
  // socket disconnects. Forcibly terminate them so the close
330
425
  // callback fires within the test/CI timeout window.