polydev-ai 1.8.23 → 1.8.25

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