polydev-ai 1.8.17 → 1.8.19

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.
@@ -245,6 +245,7 @@ class StdioMCPWrapper {
245
245
 
246
246
  // Cache for user model preferences (provider -> model)
247
247
  this.userModelPreferences = null;
248
+ this.perspectivesPerMessage = 2; // Default to 2 perspectives
248
249
  this.modelPreferencesCacheTime = null;
249
250
  this.MODEL_PREFERENCES_CACHE_TTL = 5 * 60 * 1000; // 5 minutes cache
250
251
  }
@@ -564,6 +565,7 @@ class StdioMCPWrapper {
564
565
 
565
566
  /**
566
567
  * Local CLI prompt sending with ALL available CLIs + remote perspectives
568
+ * Respects user's perspectives_per_message setting for total perspectives
567
569
  */
568
570
  async localSendCliPrompt(args) {
569
571
  console.error(`[Stdio Wrapper] Local CLI prompt sending with perspectives`);
@@ -585,6 +587,7 @@ class StdioMCPWrapper {
585
587
  const gracefulTimeout = Math.min(timeout_ms, 600000);
586
588
 
587
589
  // Fetch user's model preferences (cached, non-blocking on failure)
590
+ // This also fetches perspectivesPerMessage setting
588
591
  let modelPreferences = {};
589
592
  try {
590
593
  modelPreferences = await this.fetchUserModelPreferences();
@@ -592,6 +595,10 @@ class StdioMCPWrapper {
592
595
  console.error('[Stdio Wrapper] Model preferences fetch failed (will use CLI defaults):', prefError.message);
593
596
  }
594
597
 
598
+ // Get the user's perspectives_per_message setting (default 2)
599
+ const maxPerspectives = this.perspectivesPerMessage || 2;
600
+ console.error(`[Stdio Wrapper] Max perspectives per message: ${maxPerspectives}`);
601
+
595
602
  let localResults = [];
596
603
 
597
604
  if (provider_id) {
@@ -606,18 +613,29 @@ class StdioMCPWrapper {
606
613
  const result = await this.cliManager.sendCliPrompt(provider_id, prompt, mode, gracefulTimeout, model);
607
614
  localResults = [{ provider_id, ...result }];
608
615
  } else {
609
- // No specific provider - use ALL available local CLIs
610
- console.error(`[Stdio Wrapper] Using all available local CLIs`);
611
- const availableProviders = await this.getAllAvailableProviders();
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);
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));
628
+
629
+ console.error(`[Stdio Wrapper] Provider breakdown: CLI=${cliProviders.join(', ') || 'none'}, API fallback=${apiProviders.join(', ') || 'none'}`);
612
630
 
613
- if (availableProviders.length === 0) {
614
- console.error(`[Stdio Wrapper] No local CLIs available, will use remote perspectives only`);
631
+ if (cliProviders.length === 0 && apiProviders.length === 0) {
632
+ console.error(`[Stdio Wrapper] No providers available, will use remote perspectives only`);
615
633
  localResults = [];
616
634
  } else {
617
- console.error(`[Stdio Wrapper] Found ${availableProviders.length} available CLIs: ${availableProviders.join(', ')}`);
635
+ console.error(`[Stdio Wrapper] Using ${cliProviders.length} CLIs + ${apiProviders.length} API fallbacks`);
618
636
 
619
637
  // Run all CLI prompts concurrently
620
- const cliPromises = availableProviders.map(async (providerId) => {
638
+ const cliPromises = cliProviders.map(async (providerId) => {
621
639
  try {
622
640
  const model = modelPreferences[providerId] || null;
623
641
  if (model) {
@@ -645,8 +663,25 @@ class StdioMCPWrapper {
645
663
  console.error('[Stdio Wrapper] CLI results reporting failed (non-critical):', err.message);
646
664
  });
647
665
 
648
- // Get remote perspectives (only for models not covered by local CLIs)
649
- const perspectivesResult = await this.callPerspectivesForCli(args, localResults);
666
+ // Calculate how many successful local perspectives we got
667
+ const successfulLocalCount = localResults.filter(r => r.success).length;
668
+ const remainingPerspectives = maxPerspectives - successfulLocalCount;
669
+
670
+ // Get remote perspectives only if we need more perspectives to reach maxPerspectives
671
+ let perspectivesResult;
672
+ if (remainingPerspectives > 0) {
673
+ console.error(`[Stdio Wrapper] Need ${remainingPerspectives} more perspectives from remote`);
674
+ perspectivesResult = await this.callPerspectivesForCli(args, localResults, remainingPerspectives);
675
+ } else {
676
+ console.error(`[Stdio Wrapper] Already have ${successfulLocalCount} perspectives, skipping remote call`);
677
+ perspectivesResult = {
678
+ success: true,
679
+ content: '',
680
+ skipped: true,
681
+ reason: `Already have ${successfulLocalCount} perspectives (max: ${maxPerspectives})`,
682
+ timestamp: new Date().toISOString()
683
+ };
684
+ }
650
685
 
651
686
  // Record usage for all CLI responses
652
687
  for (const localResult of localResults) {
@@ -673,14 +708,19 @@ class StdioMCPWrapper {
673
708
 
674
709
  /**
675
710
  * Get all available and authenticated CLI providers
711
+ * Uses user's provider order from dashboard (display_order) instead of hardcoded order
712
+ * Falls back to API for providers where CLI is not available/authenticated
676
713
  */
677
714
  async getAllAvailableProviders() {
678
715
  try {
679
716
  const results = await this.cliManager.forceCliDetection();
680
717
  const availableProviders = [];
718
+ const unavailableProviders = [];
681
719
 
682
- // Priority order: claude_code > codex_cli > gemini_cli
683
- const priorityOrder = ['claude_code', 'codex_cli', 'gemini_cli'];
720
+ // Use user's provider order from dashboard (fetched via model-preferences API)
721
+ // Falls back to default order if not yet loaded
722
+ const priorityOrder = this.userProviderOrder || ['claude_code', 'codex_cli', 'gemini_cli'];
723
+ console.error(`[Stdio Wrapper] Using provider order: ${priorityOrder.join(' > ')}`);
684
724
 
685
725
  for (const providerId of priorityOrder) {
686
726
  const status = results[providerId];
@@ -688,17 +728,25 @@ class StdioMCPWrapper {
688
728
  // Skip providers with quota exhausted - they'll use API fallback
689
729
  if (status.quota_exhausted) {
690
730
  console.error(`[Stdio Wrapper] Skipping ${providerId} - quota exhausted, will use API fallback`);
731
+ unavailableProviders.push(providerId);
691
732
  continue;
692
733
  }
693
734
  availableProviders.push(providerId);
735
+ } else {
736
+ // CLI not available - will fall back to API for this provider
737
+ const reason = !status ? 'not detected' : (!status.available ? 'not installed' : 'not authenticated');
738
+ console.error(`[Stdio Wrapper] CLI ${providerId} ${reason}, will use API fallback`);
739
+ unavailableProviders.push(providerId);
694
740
  }
695
741
  }
696
742
 
697
- return availableProviders;
743
+ // Return available CLIs first, then unavailable ones (for API fallback)
744
+ // The caller will handle mixing CLI + API based on availability
745
+ return { available: availableProviders, unavailable: unavailableProviders };
698
746
 
699
747
  } catch (error) {
700
748
  console.error('[Stdio Wrapper] Failed to get available providers:', error);
701
- return [];
749
+ return { available: [], unavailable: [] };
702
750
  }
703
751
  }
704
752
 
@@ -782,8 +830,11 @@ class StdioMCPWrapper {
782
830
  /**
783
831
  * Call remote perspectives for CLI prompts
784
832
  * Only calls remote APIs for providers NOT covered by successful local CLIs
833
+ * @param {Object} args - Original request arguments
834
+ * @param {Array} localResults - Results from local CLIs
835
+ * @param {number} maxPerspectives - Maximum number of remote perspectives to fetch
785
836
  */
786
- async callPerspectivesForCli(args, localResults) {
837
+ async callPerspectivesForCli(args, localResults, maxPerspectives = 2) {
787
838
  // Determine which providers succeeded locally
788
839
  const successfulLocalProviders = localResults
789
840
  .filter(r => r.success)
@@ -800,20 +851,21 @@ class StdioMCPWrapper {
800
851
  .map(cli => cliToApiProvider[cli])
801
852
  .filter(Boolean);
802
853
 
803
- // If all major providers are covered locally, skip remote call entirely
804
- if (excludeProviders.length >= 3 ||
854
+ // If all major providers are covered locally OR we don't need more perspectives, skip remote call
855
+ if (maxPerspectives <= 0 ||
856
+ excludeProviders.length >= 3 ||
805
857
  (excludeProviders.includes('anthropic') && excludeProviders.includes('openai') && excludeProviders.includes('google'))) {
806
- console.error(`[Stdio Wrapper] All providers covered by local CLIs, skipping remote perspectives`);
858
+ console.error(`[Stdio Wrapper] All providers covered by local CLIs or max perspectives reached, skipping remote perspectives`);
807
859
  return {
808
860
  success: true,
809
861
  content: '',
810
862
  skipped: true,
811
- reason: 'All providers covered by local CLIs',
863
+ reason: 'All providers covered by local CLIs or max perspectives reached',
812
864
  timestamp: new Date().toISOString()
813
865
  };
814
866
  }
815
867
 
816
- console.error(`[Stdio Wrapper] Calling remote perspectives (excluding: ${excludeProviders.join(', ') || 'none'})`);
868
+ console.error(`[Stdio Wrapper] Calling remote perspectives (excluding: ${excludeProviders.join(', ') || 'none'}, max: ${maxPerspectives})`);
817
869
 
818
870
  // Format CLI responses for logging on the server
819
871
  const cliResponses = localResults.map(result => ({
@@ -839,6 +891,8 @@ class StdioMCPWrapper {
839
891
  exclude_providers: excludeProviders,
840
892
  // Pass CLI responses for dashboard logging
841
893
  cli_responses: cliResponses,
894
+ // Limit remote perspectives to what we need
895
+ max_perspectives: maxPerspectives,
842
896
  project_memory: 'none',
843
897
  temperature: 0.7,
844
898
  max_tokens: 20000
@@ -1307,6 +1361,7 @@ class StdioMCPWrapper {
1307
1361
  /**
1308
1362
  * Fetch user's model preferences from API keys
1309
1363
  * Returns a map of CLI provider -> default_model
1364
+ * Also fetches and caches perspectivesPerMessage setting
1310
1365
  */
1311
1366
  async fetchUserModelPreferences() {
1312
1367
  // Check cache first
@@ -1342,7 +1397,16 @@ class StdioMCPWrapper {
1342
1397
  this.userModelPreferences = result.modelPreferences;
1343
1398
  this.modelPreferencesCacheTime = Date.now();
1344
1399
 
1400
+ // Also cache perspectives_per_message setting (default 2)
1401
+ this.perspectivesPerMessage = result.perspectivesPerMessage || 2;
1402
+
1403
+ // Cache provider order from user's dashboard (respects display_order)
1404
+ // This determines which CLIs/APIs to use first
1405
+ this.userProviderOrder = result.providerOrder || ['claude_code', 'codex_cli', 'gemini_cli'];
1406
+
1345
1407
  console.error('[Stdio Wrapper] Model preferences loaded:', JSON.stringify(result.modelPreferences));
1408
+ console.error('[Stdio Wrapper] Provider order:', JSON.stringify(this.userProviderOrder));
1409
+ console.error('[Stdio Wrapper] Perspectives per message:', this.perspectivesPerMessage);
1346
1410
  return result.modelPreferences;
1347
1411
  } else {
1348
1412
  console.error('[Stdio Wrapper] No model preferences in response');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polydev-ai",
3
- "version": "1.8.17",
3
+ "version": "1.8.19",
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",