polydev-ai 1.8.22 → 1.8.24

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 (2) hide show
  1. package/mcp/stdio-wrapper.js +105 -44
  2. package/package.json +1 -1
@@ -566,6 +566,7 @@ class StdioMCPWrapper {
566
566
  /**
567
567
  * Local CLI prompt sending with ALL available CLIs + remote perspectives
568
568
  * Respects user's perspectives_per_message setting for total perspectives
569
+ * Uses allProviders from dashboard - tries CLI first, falls back to API
569
570
  */
570
571
  async localSendCliPrompt(args) {
571
572
  console.error(`[Stdio Wrapper] Local CLI prompt sending with perspectives`);
@@ -587,7 +588,7 @@ class StdioMCPWrapper {
587
588
  const gracefulTimeout = Math.min(timeout_ms, 600000);
588
589
 
589
590
  // Fetch user's model preferences (cached, non-blocking on failure)
590
- // This also fetches perspectivesPerMessage setting
591
+ // This also fetches perspectivesPerMessage setting and allProviders list
591
592
  let modelPreferences = {};
592
593
  try {
593
594
  modelPreferences = await this.fetchUserModelPreferences();
@@ -613,48 +614,85 @@ class StdioMCPWrapper {
613
614
  const result = await this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout, model);
614
615
  localResults = [{ provider_id, ...result }];
615
616
  } else {
616
- // No specific provider - use providers in user's preferred order
617
- // Mix CLI (where available) + API fallback (where CLI unavailable)
618
- console.error(`[Stdio Wrapper] Using providers in user's preferred order (max: ${maxPerspectives})`);
619
- const { available: availableClis, unavailable: unavailableClis } = await this.getAllAvailableProviders();
617
+ // No specific provider - use allProviders from dashboard in order
618
+ // For each provider: try CLI first if available, otherwise use API
619
+ console.error(`[Stdio Wrapper] Using allProviders from dashboard (max: ${maxPerspectives})`);
620
620
 
621
- // Build ordered list: use CLI if available, otherwise mark for API fallback
622
- const userOrder = this.userProviderOrder || ['claude_code', 'codex_cli', 'gemini_cli'];
623
- const providersToUse = userOrder.slice(0, maxPerspectives);
621
+ // Get available CLIs for checking
622
+ const { available: availableClis } = await this.getAllAvailableProviders();
623
+ console.error(`[Stdio Wrapper] Available CLIs: ${availableClis.join(', ') || 'none'}`);
624
624
 
625
- // Separate into CLI vs API fallback
626
- const cliProviders = providersToUse.filter(p => availableClis.includes(p));
627
- const apiProviders = providersToUse.filter(p => unavailableClis.includes(p));
625
+ // Use allProviders from API (full list including API-only providers)
626
+ // Falls back to CLI-only providers if allProviders not available
627
+ const allProviders = this.allProviders || [];
628
628
 
629
- console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviders.join(', ') || 'none'}, API fallback=${apiProviders.join(', ') || 'none'}`);
630
-
631
- if (cliProviders.length === 0 && apiProviders.length === 0) {
632
- console.error(`[Stdio Wrapper] No providers available, will use remote perspectives only`);
633
- localResults = [];
634
- } else {
635
- console.error(`[Stdio Wrapper] Using ${cliProviders.length} CLIs + ${apiProviders.length} API fallbacks`);
629
+ if (allProviders.length === 0) {
630
+ // Fallback: use legacy CLI-only flow
631
+ console.error(`[Stdio Wrapper] No allProviders, using legacy CLI-only flow`);
632
+ const userOrder = this.userProviderOrder || ['claude_code', 'codex_cli', 'gemini_cli'];
633
+ const cliProviders = userOrder.slice(0, maxPerspectives).filter(p => availableClis.includes(p));
636
634
 
637
- // Run all CLI prompts concurrently
638
635
  const cliPromises = cliProviders.map(async (providerId) => {
639
636
  try {
640
637
  const model = modelPreferences[providerId] || null;
641
- if (model) {
642
- console.error(`[Stdio Wrapper] Using user's preferred model for ${providerId}: ${model}`);
643
- }
644
638
  const result = await this.cliManager.sendCliPrompt(providerId, prompt, mode, gracefulTimeout, model);
645
639
  return { provider_id: providerId, ...result };
646
640
  } catch (error) {
647
- console.error(`[Stdio Wrapper] CLI ${providerId} failed:`, error.message);
648
- return {
649
- provider_id: providerId,
650
- success: false,
651
- error: error.message,
652
- latency_ms: gracefulTimeout
653
- };
641
+ return { provider_id: providerId, success: false, error: error.message };
654
642
  }
655
643
  });
656
-
657
644
  localResults = await Promise.all(cliPromises);
645
+ } else {
646
+ // NEW: Use allProviders list (includes CLI + API-only providers)
647
+ const providersToUse = allProviders.slice(0, maxPerspectives);
648
+ console.error(`[Stdio Wrapper] Using ${providersToUse.length} providers from dashboard`);
649
+
650
+ // Separate into CLI providers vs API-only providers
651
+ const cliProviderEntries = [];
652
+ const apiOnlyProviders = [];
653
+
654
+ for (const p of providersToUse) {
655
+ if (p.cliId && availableClis.includes(p.cliId)) {
656
+ // Has CLI and CLI is available
657
+ cliProviderEntries.push(p);
658
+ } else {
659
+ // No CLI or CLI unavailable - needs API
660
+ apiOnlyProviders.push(p);
661
+ }
662
+ }
663
+
664
+ console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviderEntries.map(p => p.cliId).join(', ') || 'none'}, API-only=${apiOnlyProviders.map(p => p.provider).join(', ') || 'none'}`);
665
+
666
+ // Run all CLI prompts concurrently
667
+ if (cliProviderEntries.length > 0) {
668
+ const cliPromises = cliProviderEntries.map(async (providerEntry) => {
669
+ try {
670
+ const model = providerEntry.model || modelPreferences[providerEntry.cliId] || null;
671
+ if (model) {
672
+ console.error(`[Stdio Wrapper] Using model for ${providerEntry.cliId}: ${model}`);
673
+ }
674
+ const result = await this.cliManager.sendCliPrompt(providerEntry.cliId, prompt, mode, gracefulTimeout, model);
675
+ return {
676
+ provider_id: providerEntry.cliId,
677
+ original_provider: providerEntry.provider,
678
+ ...result
679
+ };
680
+ } catch (error) {
681
+ console.error(`[Stdio Wrapper] CLI ${providerEntry.cliId} failed:`, error.message);
682
+ return {
683
+ provider_id: providerEntry.cliId,
684
+ original_provider: providerEntry.provider,
685
+ success: false,
686
+ error: error.message,
687
+ latency_ms: gracefulTimeout
688
+ };
689
+ }
690
+ });
691
+ localResults = await Promise.all(cliPromises);
692
+ }
693
+
694
+ // Store API-only providers info for remote API call
695
+ this._apiOnlyProviders = apiOnlyProviders;
658
696
  }
659
697
  }
660
698
 
@@ -665,15 +703,26 @@ class StdioMCPWrapper {
665
703
 
666
704
  // Calculate how many successful local perspectives we got
667
705
  const successfulLocalCount = localResults.filter(r => r.success).length;
668
- const remainingPerspectives = maxPerspectives - successfulLocalCount;
706
+ const failedCliCount = localResults.filter(r => !r.success).length;
707
+
708
+ // Need API for: API-only providers + failed CLIs
709
+ const apiOnlyCount = (this._apiOnlyProviders || []).length;
710
+ const remainingPerspectives = apiOnlyCount + failedCliCount;
669
711
 
670
- // Get remote perspectives only if we need more perspectives to reach maxPerspectives
712
+ // Get remote perspectives for API-only providers and failed CLIs
671
713
  let perspectivesResult;
672
714
  if (remainingPerspectives > 0) {
673
- console.error(`[Stdio Wrapper] Need ${remainingPerspectives} more perspectives from remote`);
674
- perspectivesResult = await this.callPerspectivesForCli(args, localResults, remainingPerspectives);
715
+ console.error(`[Stdio Wrapper] Need ${remainingPerspectives} perspectives from remote API (${apiOnlyCount} API-only + ${failedCliCount} failed CLIs)`);
716
+
717
+ // Pass API-only provider info to help remote API choose correct models
718
+ const apiProvidersInfo = (this._apiOnlyProviders || []).map(p => ({
719
+ provider: p.provider,
720
+ model: p.model
721
+ }));
722
+
723
+ perspectivesResult = await this.callPerspectivesForCli(args, localResults, remainingPerspectives, apiProvidersInfo);
675
724
  } else {
676
- console.error(`[Stdio Wrapper] Already have ${successfulLocalCount} perspectives, skipping remote call`);
725
+ console.error(`[Stdio Wrapper] Already have ${successfulLocalCount} perspectives from CLIs, skipping remote call`);
677
726
  perspectivesResult = {
678
727
  success: true,
679
728
  content: '',
@@ -859,8 +908,9 @@ class StdioMCPWrapper {
859
908
  * @param {Object} args - Original request arguments
860
909
  * @param {Array} localResults - Results from local CLIs
861
910
  * @param {number} maxPerspectives - Maximum number of remote perspectives to fetch
911
+ * @param {Array} apiProvidersInfo - Optional array of API-only providers to request (from allProviders)
862
912
  */
863
- async callPerspectivesForCli(args, localResults, maxPerspectives = 2) {
913
+ async callPerspectivesForCli(args, localResults, maxPerspectives = 2, apiProvidersInfo = []) {
864
914
  // Determine which providers succeeded locally
865
915
  const successfulLocalProviders = localResults
866
916
  .filter(r => r.success)
@@ -877,21 +927,25 @@ class StdioMCPWrapper {
877
927
  .map(cli => cliToApiProvider[cli])
878
928
  .filter(Boolean);
879
929
 
880
- // If all major providers are covered locally OR we don't need more perspectives, skip remote call
881
- if (maxPerspectives <= 0 ||
882
- excludeProviders.length >= 3 ||
883
- (excludeProviders.includes('anthropic') && excludeProviders.includes('openai') && excludeProviders.includes('google'))) {
884
- console.error(`[Stdio Wrapper] All providers covered by local CLIs or max perspectives reached, skipping remote perspectives`);
930
+ // If we don't need any perspectives, skip remote call
931
+ if (maxPerspectives <= 0) {
932
+ console.error(`[Stdio Wrapper] Max perspectives is 0, skipping remote perspectives`);
885
933
  return {
886
934
  success: true,
887
935
  content: '',
888
936
  skipped: true,
889
- reason: 'All providers covered by local CLIs or max perspectives reached',
937
+ reason: 'No perspectives needed',
890
938
  timestamp: new Date().toISOString()
891
939
  };
892
940
  }
893
941
 
894
- console.error(`[Stdio Wrapper] Calling remote perspectives (excluding: ${excludeProviders.join(', ') || 'none'}, max: ${maxPerspectives})`);
942
+ // Build list of specific providers to request (from API-only providers)
943
+ const requestProviders = apiProvidersInfo.map(p => ({
944
+ provider: p.provider,
945
+ model: p.model
946
+ }));
947
+
948
+ console.error(`[Stdio Wrapper] Calling remote perspectives (excluding: ${excludeProviders.join(', ') || 'none'}, requesting: ${requestProviders.map(p => p.provider).join(', ') || 'any'}, max: ${maxPerspectives})`);
895
949
 
896
950
  // Format CLI responses for logging on the server
897
951
  const cliResponses = localResults.map(result => ({
@@ -915,6 +969,8 @@ class StdioMCPWrapper {
915
969
  user_token: this.userToken,
916
970
  // Exclude providers that succeeded locally
917
971
  exclude_providers: excludeProviders,
972
+ // NEW: Specific providers to request (from API-only list)
973
+ request_providers: requestProviders.length > 0 ? requestProviders : undefined,
918
974
  // Pass CLI responses for dashboard logging
919
975
  cli_responses: cliResponses,
920
976
  // Limit remote perspectives to what we need
@@ -1387,7 +1443,7 @@ class StdioMCPWrapper {
1387
1443
  /**
1388
1444
  * Fetch user's model preferences from API keys
1389
1445
  * Returns a map of CLI provider -> default_model
1390
- * Also fetches and caches perspectivesPerMessage setting
1446
+ * Also fetches and caches perspectivesPerMessage setting and allProviders list
1391
1447
  */
1392
1448
  async fetchUserModelPreferences() {
1393
1449
  // Check cache first
@@ -1433,8 +1489,13 @@ class StdioMCPWrapper {
1433
1489
  // IMPORTANT: This is cached alongside modelPreferences and restored when cache is used
1434
1490
  this.userProviderOrder = result.providerOrder || ['claude_code', 'codex_cli', 'gemini_cli'];
1435
1491
 
1492
+ // NEW: Cache full list of all providers (CLI + API-only) from dashboard
1493
+ // Format: [{ provider: 'openai', model: 'gpt-52-codex', cliId: 'codex_cli' }, { provider: 'x-ai', model: 'grok-4', cliId: null }, ...]
1494
+ this.allProviders = result.allProviders || [];
1495
+
1436
1496
  console.error('[Stdio Wrapper] Model preferences loaded:', JSON.stringify(result.modelPreferences));
1437
1497
  console.error('[Stdio Wrapper] Provider order:', JSON.stringify(this.userProviderOrder));
1498
+ console.error('[Stdio Wrapper] All providers:', JSON.stringify(this.allProviders));
1438
1499
  console.error('[Stdio Wrapper] Perspectives per message:', this.perspectivesPerMessage);
1439
1500
  return result.modelPreferences;
1440
1501
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.8.22",
3
+ "version": "1.8.24",
4
4
  "description": "Agentic workflow assistant with CLI integration - get diverse perspectives from multiple LLMs when stuck or need enhanced reasoning",
5
5
  "keywords": [
6
6
  "mcp",