polydev-ai 1.9.0 → 1.9.1
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/mcp/stdio-wrapper.js +106 -70
- package/package.json +1 -1
package/mcp/stdio-wrapper.js
CHANGED
|
@@ -1406,93 +1406,129 @@ Error: ${error.message}`
|
|
|
1406
1406
|
const result = await this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout, model);
|
|
1407
1407
|
localResults = [{ provider_id, ...result }];
|
|
1408
1408
|
} else {
|
|
1409
|
-
// No specific provider -
|
|
1410
|
-
//
|
|
1411
|
-
|
|
1409
|
+
// No specific provider - SMART ROUTING: CLIs first (free), then API/credits
|
|
1410
|
+
// Priority order: Available CLIs > User's API keys > Credits tier models
|
|
1411
|
+
// If a configured API/credits model can be accessed through an available CLI, use CLI
|
|
1412
|
+
console.error(`[Stdio Wrapper] Smart routing: CLIs first, then API/credits (max: ${maxPerspectives})`);
|
|
1412
1413
|
|
|
1413
1414
|
// Get available CLIs for checking
|
|
1414
1415
|
const { available: availableClis } = await this.getAllAvailableProviders();
|
|
1415
1416
|
console.error(`[Stdio Wrapper] Available CLIs: ${availableClis.join(', ') || 'none'}`);
|
|
1416
1417
|
|
|
1417
|
-
// Use allProviders from API (
|
|
1418
|
-
// Falls back to CLI-only providers if allProviders not available
|
|
1418
|
+
// Use allProviders from API (user's configured providers from dashboard)
|
|
1419
1419
|
const allProviders = this.allProviders || [];
|
|
1420
1420
|
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1421
|
+
// CLI to provider name mapping
|
|
1422
|
+
const cliToProviderMap = {
|
|
1423
|
+
'claude_code': 'anthropic',
|
|
1424
|
+
'gemini_cli': 'google',
|
|
1425
|
+
'codex_cli': 'openai'
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1428
|
+
// Provider name normalization (matches model-preferences API)
|
|
1429
|
+
const normalizeProvider = (provider) => {
|
|
1430
|
+
const map = {
|
|
1431
|
+
'gemini': 'google',
|
|
1432
|
+
'google-ai': 'google',
|
|
1433
|
+
'x-ai': 'xai',
|
|
1434
|
+
'open-ai': 'openai',
|
|
1435
|
+
'anthropic-ai': 'anthropic'
|
|
1436
|
+
};
|
|
1437
|
+
const lower = provider.toLowerCase();
|
|
1438
|
+
return map[lower] || lower;
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// CLI priority order: Claude Code first, then Gemini, then Codex
|
|
1442
|
+
const cliPriorityOrder = ['claude_code', 'gemini_cli', 'codex_cli'];
|
|
1443
|
+
|
|
1444
|
+
// Build merged provider list: CLIs first, then API-only
|
|
1445
|
+
const finalProviders = [];
|
|
1446
|
+
const usedProviderNames = new Set();
|
|
1447
|
+
|
|
1448
|
+
// STEP 1: Add ALL available CLIs first (in priority order) - they're FREE
|
|
1449
|
+
for (const cliId of cliPriorityOrder) {
|
|
1450
|
+
if (finalProviders.length >= maxPerspectives) break;
|
|
1451
|
+
if (!availableClis.includes(cliId)) continue;
|
|
1452
|
+
|
|
1453
|
+
const providerName = cliToProviderMap[cliId];
|
|
1454
|
+
usedProviderNames.add(providerName);
|
|
1429
1455
|
|
|
1430
|
-
|
|
1456
|
+
// Check if user has a configured model for this provider (from API keys or credits)
|
|
1457
|
+
// If so, use that model through CLI instead of burning API credits
|
|
1458
|
+
const configuredProvider = allProviders.find(p => {
|
|
1459
|
+
const normalized = normalizeProvider(p.provider);
|
|
1460
|
+
return normalized === providerName;
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
finalProviders.push({
|
|
1464
|
+
provider: providerName,
|
|
1465
|
+
model: configuredProvider?.model || null,
|
|
1466
|
+
cliId: cliId,
|
|
1467
|
+
source: 'cli',
|
|
1468
|
+
tier: configuredProvider?.tier || null
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
console.error(`[Stdio Wrapper] [CLI-FIRST] Added CLI: ${cliId} (${providerName})${configuredProvider ? ` with configured model: ${configuredProvider.model}` : ' with CLI default'}`);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// STEP 2: Fill remaining slots with API/credits providers (skip those already covered by CLI)
|
|
1475
|
+
for (const p of allProviders) {
|
|
1476
|
+
if (finalProviders.length >= maxPerspectives) break;
|
|
1477
|
+
|
|
1478
|
+
const normalizedProvider = normalizeProvider(p.provider);
|
|
1479
|
+
if (usedProviderNames.has(normalizedProvider)) {
|
|
1480
|
+
console.error(`[Stdio Wrapper] [CLI-FIRST] Skipping ${normalizedProvider} (already covered by CLI)`);
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
usedProviderNames.add(normalizedProvider);
|
|
1485
|
+
finalProviders.push({
|
|
1486
|
+
...p,
|
|
1487
|
+
provider: normalizedProvider,
|
|
1488
|
+
source: 'api'
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
console.error(`[Stdio Wrapper] [CLI-FIRST] Added API/credits: ${normalizedProvider} (${p.model})${p.tier ? ` [${p.tier}]` : ''}`);
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
console.error(`[Stdio Wrapper] Final provider list (${finalProviders.length}/${maxPerspectives}): ${finalProviders.map(p => `${p.provider}[${p.source}]`).join(', ')}`);
|
|
1495
|
+
|
|
1496
|
+
// Separate into CLI entries (local execution) vs API entries (remote execution)
|
|
1497
|
+
const cliProviderEntries = finalProviders.filter(p => p.source === 'cli');
|
|
1498
|
+
const apiOnlyProviders = finalProviders.filter(p => p.source === 'api');
|
|
1499
|
+
|
|
1500
|
+
console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviderEntries.map(p => p.cliId).join(', ') || 'none'}, API-only=${apiOnlyProviders.map(p => p.provider).join(', ') || 'none'}`);
|
|
1501
|
+
|
|
1502
|
+
// Run all CLI prompts concurrently
|
|
1503
|
+
if (cliProviderEntries.length > 0) {
|
|
1504
|
+
const cliPromises = cliProviderEntries.map(async (providerEntry) => {
|
|
1431
1505
|
try {
|
|
1432
|
-
const model = modelPreferences[
|
|
1433
|
-
|
|
1506
|
+
const model = providerEntry.model || modelPreferences[providerEntry.cliId] || null;
|
|
1507
|
+
if (model) {
|
|
1508
|
+
console.error(`[Stdio Wrapper] Using model for ${providerEntry.cliId}: ${model}`);
|
|
1509
|
+
}
|
|
1510
|
+
const result = await this.cliManager.sendCliPrompt(providerEntry.cliId, prompt, mode, gracefulTimeout, model);
|
|
1434
1511
|
return {
|
|
1435
|
-
provider_id:
|
|
1436
|
-
original_provider:
|
|
1512
|
+
provider_id: providerEntry.cliId,
|
|
1513
|
+
original_provider: providerEntry.provider,
|
|
1437
1514
|
...result
|
|
1438
1515
|
};
|
|
1439
1516
|
} catch (error) {
|
|
1440
|
-
|
|
1517
|
+
console.error(`[Stdio Wrapper] CLI ${providerEntry.cliId} failed:`, error.message);
|
|
1518
|
+
return {
|
|
1519
|
+
provider_id: providerEntry.cliId,
|
|
1520
|
+
original_provider: providerEntry.provider,
|
|
1521
|
+
success: false,
|
|
1522
|
+
error: error.message,
|
|
1523
|
+
latency_ms: gracefulTimeout
|
|
1524
|
+
};
|
|
1441
1525
|
}
|
|
1442
1526
|
});
|
|
1443
1527
|
localResults = await Promise.all(cliPromises);
|
|
1444
|
-
} else {
|
|
1445
|
-
// NEW: Use allProviders list (includes CLI + API-only providers)
|
|
1446
|
-
const providersToUse = allProviders.slice(0, maxPerspectives);
|
|
1447
|
-
console.error(`[Stdio Wrapper] Using ${providersToUse.length} providers from dashboard`);
|
|
1448
|
-
|
|
1449
|
-
// Separate into CLI providers vs API-only providers
|
|
1450
|
-
const cliProviderEntries = [];
|
|
1451
|
-
const apiOnlyProviders = [];
|
|
1452
|
-
|
|
1453
|
-
for (const p of providersToUse) {
|
|
1454
|
-
if (p.cliId && availableClis.includes(p.cliId)) {
|
|
1455
|
-
// Has CLI and CLI is available
|
|
1456
|
-
cliProviderEntries.push(p);
|
|
1457
|
-
} else {
|
|
1458
|
-
// No CLI or CLI unavailable - needs API
|
|
1459
|
-
apiOnlyProviders.push(p);
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviderEntries.map(p => p.cliId).join(', ') || 'none'}, API-only=${apiOnlyProviders.map(p => p.provider).join(', ') || 'none'}`);
|
|
1464
|
-
|
|
1465
|
-
// Run all CLI prompts concurrently
|
|
1466
|
-
if (cliProviderEntries.length > 0) {
|
|
1467
|
-
const cliPromises = cliProviderEntries.map(async (providerEntry) => {
|
|
1468
|
-
try {
|
|
1469
|
-
const model = providerEntry.model || modelPreferences[providerEntry.cliId] || null;
|
|
1470
|
-
if (model) {
|
|
1471
|
-
console.error(`[Stdio Wrapper] Using model for ${providerEntry.cliId}: ${model}`);
|
|
1472
|
-
}
|
|
1473
|
-
const result = await this.cliManager.sendCliPrompt(providerEntry.cliId, prompt, mode, gracefulTimeout, model);
|
|
1474
|
-
return {
|
|
1475
|
-
provider_id: providerEntry.cliId,
|
|
1476
|
-
original_provider: providerEntry.provider,
|
|
1477
|
-
...result
|
|
1478
|
-
};
|
|
1479
|
-
} catch (error) {
|
|
1480
|
-
console.error(`[Stdio Wrapper] CLI ${providerEntry.cliId} failed:`, error.message);
|
|
1481
|
-
return {
|
|
1482
|
-
provider_id: providerEntry.cliId,
|
|
1483
|
-
original_provider: providerEntry.provider,
|
|
1484
|
-
success: false,
|
|
1485
|
-
error: error.message,
|
|
1486
|
-
latency_ms: gracefulTimeout
|
|
1487
|
-
};
|
|
1488
|
-
}
|
|
1489
|
-
});
|
|
1490
|
-
localResults = await Promise.all(cliPromises);
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
// Store API-only providers info for remote API call
|
|
1494
|
-
this._apiOnlyProviders = apiOnlyProviders;
|
|
1495
1528
|
}
|
|
1529
|
+
|
|
1530
|
+
// Store API-only providers info for remote API call
|
|
1531
|
+
this._apiOnlyProviders = apiOnlyProviders;
|
|
1496
1532
|
}
|
|
1497
1533
|
|
|
1498
1534
|
// Calculate how many successful local perspectives we got
|