@in-the-loop-labs/pair-review 3.0.6 → 3.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 (79) hide show
  1. package/package.json +2 -1
  2. package/plugin/.claude-plugin/plugin.json +1 -1
  3. package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
  4. package/plugin-code-critic/skills/analyze/references/level1-balanced.md +8 -0
  5. package/plugin-code-critic/skills/analyze/references/level1-fast.md +7 -0
  6. package/plugin-code-critic/skills/analyze/references/level1-thorough.md +8 -0
  7. package/plugin-code-critic/skills/analyze/references/level2-balanced.md +9 -0
  8. package/plugin-code-critic/skills/analyze/references/level2-fast.md +8 -0
  9. package/plugin-code-critic/skills/analyze/references/level2-thorough.md +9 -0
  10. package/plugin-code-critic/skills/analyze/references/level3-balanced.md +9 -0
  11. package/plugin-code-critic/skills/analyze/references/level3-fast.md +8 -0
  12. package/plugin-code-critic/skills/analyze/references/level3-thorough.md +9 -0
  13. package/plugin-code-critic/skills/analyze/references/orchestration-balanced.md +9 -0
  14. package/plugin-code-critic/skills/analyze/references/orchestration-fast.md +5 -0
  15. package/plugin-code-critic/skills/analyze/references/orchestration-thorough.md +9 -0
  16. package/public/css/analysis-config.css +83 -0
  17. package/public/css/pr.css +191 -4
  18. package/public/index.html +20 -0
  19. package/public/js/components/AIPanel.js +1 -1
  20. package/public/js/components/AdvancedConfigTab.js +83 -8
  21. package/public/js/components/AnalysisConfigModal.js +155 -5
  22. package/public/js/components/ChatPanel.js +22 -5
  23. package/public/js/components/CouncilProgressModal.js +239 -22
  24. package/public/js/components/TimeoutSelect.js +2 -0
  25. package/public/js/components/VoiceCentricConfigTab.js +179 -12
  26. package/public/js/index.js +119 -1
  27. package/public/js/local.js +141 -47
  28. package/public/js/modules/suggestion-manager.js +2 -1
  29. package/public/js/pr.js +71 -12
  30. package/public/js/repo-settings.js +2 -2
  31. package/public/local.html +32 -11
  32. package/public/pr.html +2 -0
  33. package/src/ai/analyzer.js +371 -111
  34. package/src/ai/claude-provider.js +2 -0
  35. package/src/ai/codex-provider.js +1 -1
  36. package/src/ai/copilot-provider.js +2 -0
  37. package/src/ai/executable-provider.js +534 -0
  38. package/src/ai/gemini-provider.js +2 -0
  39. package/src/ai/index.js +9 -1
  40. package/src/ai/pi-provider.js +10 -8
  41. package/src/ai/prompts/baseline/consolidation/balanced.js +54 -2
  42. package/src/ai/prompts/baseline/consolidation/fast.js +31 -1
  43. package/src/ai/prompts/baseline/consolidation/thorough.js +46 -3
  44. package/src/ai/prompts/baseline/level1/balanced.js +12 -0
  45. package/src/ai/prompts/baseline/level1/fast.js +11 -0
  46. package/src/ai/prompts/baseline/level1/thorough.js +12 -0
  47. package/src/ai/prompts/baseline/level2/balanced.js +13 -0
  48. package/src/ai/prompts/baseline/level2/fast.js +12 -0
  49. package/src/ai/prompts/baseline/level2/thorough.js +13 -0
  50. package/src/ai/prompts/baseline/level3/balanced.js +13 -0
  51. package/src/ai/prompts/baseline/level3/fast.js +12 -0
  52. package/src/ai/prompts/baseline/level3/thorough.js +13 -0
  53. package/src/ai/prompts/baseline/orchestration/balanced.js +15 -0
  54. package/src/ai/prompts/baseline/orchestration/fast.js +11 -0
  55. package/src/ai/prompts/baseline/orchestration/thorough.js +15 -0
  56. package/src/ai/prompts/render-for-skill.js +3 -0
  57. package/src/ai/prompts/shared/output-schema.js +8 -0
  58. package/src/ai/provider.js +89 -4
  59. package/src/chat/prompt-builder.js +17 -1
  60. package/src/chat/session-manager.js +32 -28
  61. package/src/config.js +15 -2
  62. package/src/database.js +59 -15
  63. package/src/git/base-branch.js +113 -29
  64. package/src/local-review.js +15 -9
  65. package/src/main.js +3 -2
  66. package/src/routes/analyses.js +34 -8
  67. package/src/routes/chat.js +15 -8
  68. package/src/routes/config.js +3 -120
  69. package/src/routes/councils.js +15 -6
  70. package/src/routes/executable-analysis.js +494 -0
  71. package/src/routes/local.js +152 -15
  72. package/src/routes/mcp.js +9 -4
  73. package/src/routes/pr.js +166 -29
  74. package/src/routes/reviews.js +31 -5
  75. package/src/routes/shared.js +72 -5
  76. package/src/routes/worktrees.js +4 -2
  77. package/src/utils/comment-formatter.js +28 -11
  78. package/src/utils/instructions.js +22 -8
  79. package/src/utils/logger.js +20 -10
package/public/css/pr.css CHANGED
@@ -501,16 +501,19 @@
501
501
  }
502
502
 
503
503
  /* Refresh button spinner animation */
504
- #refresh-pr .spinner-icon {
504
+ #refresh-pr .spinner-icon,
505
+ #local-refresh-btn .spinner-icon {
505
506
  display: none;
506
507
  animation: spin 1s linear infinite;
507
508
  }
508
509
 
509
- #refresh-pr.refreshing .refresh-icon {
510
+ #refresh-pr.refreshing .refresh-icon,
511
+ #local-refresh-btn.refreshing .refresh-icon {
510
512
  display: none !important;
511
513
  }
512
514
 
513
- #refresh-pr.refreshing .spinner-icon {
515
+ #refresh-pr.refreshing .spinner-icon,
516
+ #local-refresh-btn.refreshing .spinner-icon {
514
517
  display: inline-block !important;
515
518
  }
516
519
 
@@ -1871,6 +1874,8 @@ tr.newly-expanded .d2h-code-line-ctn {
1871
1874
  display: flex;
1872
1875
  align-items: center;
1873
1876
  gap: 8px;
1877
+ min-width: 0;
1878
+ overflow: hidden;
1874
1879
  }
1875
1880
 
1876
1881
  /* Category label badge for AI suggestions - HIDDEN in favor of unified AI Suggestion badge (issue pair_review-pqb) */
@@ -1889,11 +1894,16 @@ tr.newly-expanded .d2h-code-line-ctn {
1889
1894
  font-weight: 600;
1890
1895
  background: var(--ai-subtle, rgba(245, 158, 11, 0.1));
1891
1896
  color: var(--ai-primary, #f59e0b);
1897
+ white-space: nowrap;
1898
+ flex-shrink: 1;
1899
+ min-width: 0;
1900
+ overflow: hidden;
1892
1901
  }
1893
1902
 
1894
1903
  .ai-suggestion-badge svg {
1895
1904
  width: 14px;
1896
1905
  height: 14px;
1906
+ flex-shrink: 0;
1897
1907
  opacity: 0.8;
1898
1908
  }
1899
1909
 
@@ -1924,6 +1934,7 @@ tr.newly-expanded .d2h-code-line-ctn {
1924
1934
  background: var(--ai-subtle, rgba(245, 158, 11, 0.1));
1925
1935
  border-radius: var(--radius-sm, 4px);
1926
1936
  flex-shrink: 0;
1937
+ white-space: nowrap;
1927
1938
  }
1928
1939
 
1929
1940
  /* Type-specific colors - using CSS variables for theme support */
@@ -2074,6 +2085,10 @@ tr.newly-expanded .d2h-code-line-ctn {
2074
2085
  .ai-title {
2075
2086
  font-weight: 600;
2076
2087
  color: var(--color-text-primary);
2088
+ overflow: hidden;
2089
+ text-overflow: ellipsis;
2090
+ white-space: nowrap;
2091
+ min-width: 0;
2077
2092
  }
2078
2093
 
2079
2094
  /* Confidence display removed to match cleaner prototype design (issue pair_review-7g5) */
@@ -6466,6 +6481,96 @@ body:not([data-theme="dark"]) .theme-icon-light {
6466
6481
  text-overflow: ellipsis;
6467
6482
  }
6468
6483
 
6484
+ /* Accessible visually-hidden utility */
6485
+ .sr-only {
6486
+ position: absolute;
6487
+ width: 1px;
6488
+ height: 1px;
6489
+ padding: 0;
6490
+ margin: -1px;
6491
+ overflow: hidden;
6492
+ clip: rect(0, 0, 0, 0);
6493
+ white-space: nowrap;
6494
+ border: 0;
6495
+ }
6496
+
6497
+ /* Base branch selector */
6498
+ .toolbar-base-branch-wrap {
6499
+ display: none;
6500
+ align-items: center;
6501
+ }
6502
+
6503
+ .toolbar-base-branch-wrap:not([hidden]) {
6504
+ display: inline-flex;
6505
+ }
6506
+
6507
+ .base-branch-selector-wrap {
6508
+ display: none;
6509
+ align-items: center;
6510
+ }
6511
+
6512
+ .base-branch-selector-wrap:not([hidden]) {
6513
+ display: inline-flex;
6514
+ }
6515
+
6516
+ .base-branch-vs {
6517
+ font-size: 0.75rem;
6518
+ color: var(--color-text-tertiary);
6519
+ margin: 0 6px;
6520
+ }
6521
+
6522
+ .toolbar-base-branch-static[hidden] {
6523
+ display: none;
6524
+ }
6525
+
6526
+ .toolbar-base-branch-static {
6527
+ display: inline-flex;
6528
+ align-items: center;
6529
+ gap: 4px;
6530
+ padding: 2px 8px;
6531
+ background-color: var(--color-bg-tertiary);
6532
+ border: 1px solid var(--color-border-primary);
6533
+ border-radius: 6px;
6534
+ font-family: var(--font-mono);
6535
+ font-size: 11px;
6536
+ color: var(--color-text-primary);
6537
+ white-space: nowrap;
6538
+ overflow: hidden;
6539
+ text-overflow: ellipsis;
6540
+ max-width: 260px;
6541
+ }
6542
+
6543
+ .toolbar-base-branch-static svg {
6544
+ color: var(--color-text-tertiary);
6545
+ flex-shrink: 0;
6546
+ }
6547
+
6548
+ .toolbar-base-branch-static span {
6549
+ overflow: hidden;
6550
+ text-overflow: ellipsis;
6551
+ }
6552
+
6553
+ .base-branch-select {
6554
+ font-size: 11px;
6555
+ padding: 2px 8px;
6556
+ background: var(--color-bg-tertiary);
6557
+ border: 1px solid var(--color-border-primary);
6558
+ border-radius: 6px;
6559
+ color: var(--color-text-primary);
6560
+ cursor: pointer;
6561
+ max-width: 260px;
6562
+ font-family: var(--font-mono);
6563
+ }
6564
+
6565
+ .base-branch-select:hover {
6566
+ background: var(--color-bg-secondary);
6567
+ }
6568
+
6569
+ .base-branch-select:focus {
6570
+ outline: 2px solid var(--color-accent-primary);
6571
+ outline-offset: -2px;
6572
+ }
6573
+
6469
6574
  /* Shared copy button styles for toolbar (branch + commit) */
6470
6575
  .toolbar-copy-btn {
6471
6576
  display: inline-flex;
@@ -8943,12 +9048,24 @@ body.resizing * {
8943
9048
  }
8944
9049
 
8945
9050
  /* Category label for AI findings */
9051
+ .finding-meta {
9052
+ display: flex;
9053
+ align-items: center;
9054
+ gap: 4px;
9055
+ }
9056
+
8946
9057
  .finding-category {
8947
9058
  font-size: 0.6875rem;
8948
9059
  color: var(--color-text-tertiary);
8949
9060
  text-transform: capitalize;
8950
9061
  }
8951
9062
 
9063
+ .finding-meta .severity-badge {
9064
+ margin-left: 0;
9065
+ font-size: 0.6rem;
9066
+ padding: 0 4px;
9067
+ }
9068
+
8952
9069
  /* Star icon for praise findings */
8953
9070
  .finding-star {
8954
9071
  display: flex;
@@ -9659,7 +9776,7 @@ body.resizing * {
9659
9776
  .instructions-container {
9660
9777
  display: flex;
9661
9778
  flex-direction: column;
9662
- gap: 8px;
9779
+ gap: 4px;
9663
9780
  }
9664
9781
 
9665
9782
  .repo-instructions-banner {
@@ -10186,6 +10303,18 @@ body.resizing * {
10186
10303
  min-width: 90px;
10187
10304
  }
10188
10305
 
10306
+ .executable-note {
10307
+ font-size: 11px;
10308
+ color: var(--fg-muted, #656d76);
10309
+ font-style: italic;
10310
+ white-space: nowrap;
10311
+ }
10312
+
10313
+ .vc-levels-disabled-note {
10314
+ margin-top: 12px;
10315
+ margin-bottom: 0;
10316
+ }
10317
+
10189
10318
  .btn-icon {
10190
10319
  box-sizing: border-box;
10191
10320
  width: 28px;
@@ -12758,3 +12887,61 @@ body.resizing * {
12758
12887
  opacity: 0.4;
12759
12888
  cursor: not-allowed;
12760
12889
  }
12890
+
12891
+ /* Severity badges for executable provider suggestions */
12892
+ .severity-badge {
12893
+ display: inline-flex;
12894
+ font-size: 11px;
12895
+ padding: 1px 6px;
12896
+ border-radius: 3px;
12897
+ font-weight: 500;
12898
+ margin-left: 4px;
12899
+ white-space: nowrap;
12900
+ flex-shrink: 0;
12901
+ width: fit-content;
12902
+ }
12903
+
12904
+ .severity-critical {
12905
+ background: #fde8e8;
12906
+ color: #d32f2f;
12907
+ }
12908
+
12909
+ .severity-medium {
12910
+ background: #fff3e0;
12911
+ color: #ef6c00;
12912
+ }
12913
+
12914
+ .severity-minor {
12915
+ background: #fff8e1;
12916
+ color: #f9a825;
12917
+ }
12918
+
12919
+ [data-theme="dark"] .severity-critical {
12920
+ background: #3d1c1c;
12921
+ color: #ef9a9a;
12922
+ }
12923
+
12924
+ [data-theme="dark"] .severity-medium {
12925
+ background: #3d2e1c;
12926
+ color: #ffcc80;
12927
+ }
12928
+
12929
+ [data-theme="dark"] .severity-minor {
12930
+ background: #3d3a1c;
12931
+ color: #fff176;
12932
+ }
12933
+
12934
+ /* Note shown when executable provider is selected (no level toggles) */
12935
+ .executable-provider-note {
12936
+ padding: 8px 12px;
12937
+ background: var(--bg-tertiary, #f6f8fa);
12938
+ border-radius: 6px;
12939
+ font-size: 13px;
12940
+ color: var(--fg-muted, #656d76);
12941
+ margin-top: 8px;
12942
+ }
12943
+
12944
+ [data-theme="dark"] .executable-provider-note {
12945
+ background: rgba(56, 139, 253, 0.1);
12946
+ color: var(--color-text-secondary, #8b949e);
12947
+ }
package/public/index.html CHANGED
@@ -685,6 +685,25 @@
685
685
  text-align: right;
686
686
  }
687
687
 
688
+ /* Analysis-in-progress spinner for index page rows */
689
+ .index-analysis-spinner {
690
+ display: inline-block;
691
+ width: 12px;
692
+ height: 12px;
693
+ border: 2px solid var(--ai-primary);
694
+ border-top-color: transparent;
695
+ border-radius: 50%;
696
+ animation: index-spin 0.8s linear infinite;
697
+ margin-right: 6px;
698
+ vertical-align: middle;
699
+ flex-shrink: 0;
700
+ }
701
+
702
+ @keyframes index-spin {
703
+ 0% { transform: rotate(0deg); }
704
+ 100% { transform: rotate(360deg); }
705
+ }
706
+
688
707
  .local-table .btn-repo-settings {
689
708
  margin-right: 4px;
690
709
  }
@@ -1430,6 +1449,7 @@
1430
1449
  </div>
1431
1450
  </div>
1432
1451
 
1452
+ <script src="/js/ws-client.js"></script>
1433
1453
  <script src="/js/components/Toast.js"></script>
1434
1454
  <script src="/js/index.js"></script>
1435
1455
  </body>
@@ -1219,7 +1219,7 @@ class AIPanel {
1219
1219
  ${indicator}
1220
1220
  <div class="finding-content">
1221
1221
  <span class="finding-title">${this.escapeHtml(title)}</span>
1222
- ${category ? `<span class="finding-category">${this.escapeHtml(category)}</span>` : ''}
1222
+ ${category || finding.severity ? `<span class="finding-meta">${category ? `<span class="finding-category">${this.escapeHtml(category)}</span>` : ''}${finding.severity ? `<span class="severity-badge severity-${finding.severity}">${this.escapeHtml(finding.severity.toUpperCase())}</span>` : ''}</span>` : ''}
1223
1223
  ${fileName ? `<span class="finding-location">${this.escapeHtml(fileName)}</span>` : ''}
1224
1224
  </div>
1225
1225
  </button>
@@ -289,7 +289,7 @@ class AdvancedConfigTab {
289
289
  '2': { enabled: true, voices: [] },
290
290
  '3': { enabled: true, voices: [] }
291
291
  },
292
- consolidation: { provider: this._defaultProvider || 'claude', model: this._defaultModel || 'sonnet', tier: 'balanced', timeout: AdvancedConfigTab.DEFAULT_TIMEOUT }
292
+ consolidation: { provider: this._defaultProvider || 'claude', model: this._defaultModel || 'sonnet', tier: 'balanced', timeout: this._getProviderDefaultTimeout(this._defaultProvider || 'claude') }
293
293
  };
294
294
  }
295
295
 
@@ -605,10 +605,11 @@ class AdvancedConfigTab {
605
605
  }
606
606
  });
607
607
 
608
- // Provider change -> update model dropdowns
608
+ // Provider change -> update model dropdowns + timeout default
609
609
  panel.addEventListener('change', (e) => {
610
610
  if (e.target.classList.contains('voice-provider')) {
611
611
  this._updateModelDropdown(e.target);
612
+ this._applyProviderDefaultTimeout(e.target);
612
613
  }
613
614
  // Model change -> update tier to match model's recommended tier
614
615
  if (e.target.classList.contains('voice-model')) {
@@ -805,6 +806,7 @@ class AdvancedConfigTab {
805
806
  const newProviderSelect = voiceList.querySelector(`.voice-provider[data-level="${level}"][data-index="${index}"]`);
806
807
  if (newProviderSelect) {
807
808
  this._populateProviderDropdown(newProviderSelect);
809
+ this._applyProviderDefaultTimeout(newProviderSelect);
808
810
  }
809
811
 
810
812
  // Update remove button visibility for this level
@@ -894,6 +896,16 @@ class AdvancedConfigTab {
894
896
  iconBtn.classList.toggle('has-instructions', hasContent);
895
897
  }
896
898
 
899
+ /**
900
+ * Get the default timeout for a provider, falling back to the static DEFAULT_TIMEOUT.
901
+ * @param {string} providerId - Provider ID (e.g., 'pi', 'claude')
902
+ * @returns {number} Default timeout in ms
903
+ */
904
+ _getProviderDefaultTimeout(providerId) {
905
+ const provider = this.providers[providerId];
906
+ return provider?.defaultTimeout ?? AdvancedConfigTab.DEFAULT_TIMEOUT;
907
+ }
908
+
897
909
  /**
898
910
  * Update the clock/timeout icon styling to indicate non-default timeout.
899
911
  * @param {Element} panel - The council panel element
@@ -906,7 +918,9 @@ class AdvancedConfigTab {
906
918
  const iconBtn = wrapper?.querySelector(`.toggle-timeout-icon[data-level="${level}"][data-index="${index}"]`);
907
919
  if (!iconBtn) return;
908
920
 
909
- const isNonDefault = parseInt(value, 10) !== AdvancedConfigTab.DEFAULT_TIMEOUT;
921
+ const providerId = wrapper?.querySelector('.voice-provider')?.value;
922
+ const defaultTimeout = this._getProviderDefaultTimeout(providerId);
923
+ const isNonDefault = parseInt(value, 10) !== defaultTimeout;
910
924
  iconBtn.classList.toggle('has-custom-timeout', isNonDefault);
911
925
  }
912
926
 
@@ -914,7 +928,10 @@ class AdvancedConfigTab {
914
928
  const iconBtn = panel.querySelector('#adv-orchestration-timeout-toggle');
915
929
  if (!iconBtn) return;
916
930
 
917
- const isNonDefault = parseInt(value, 10) !== AdvancedConfigTab.DEFAULT_TIMEOUT;
931
+ const orchRow = panel.querySelector('#orchestration-voice');
932
+ const providerId = orchRow?.querySelector('.voice-provider')?.value;
933
+ const defaultTimeout = this._getProviderDefaultTimeout(providerId);
934
+ const isNonDefault = parseInt(value, 10) !== defaultTimeout;
918
935
  iconBtn.classList.toggle('has-custom-timeout', isNonDefault);
919
936
  }
920
937
 
@@ -929,6 +946,48 @@ class AdvancedConfigTab {
929
946
  iconBtn.classList.toggle('has-instructions', hasContent);
930
947
  }
931
948
 
949
+ /**
950
+ * When a voice's provider changes, update its timeout to the new provider's default,
951
+ * preserving explicit user overrides via Math.max when the user had customized the value.
952
+ * @param {HTMLSelectElement} providerSelect - The provider dropdown that changed
953
+ */
954
+ _applyProviderDefaultTimeout(providerSelect) {
955
+ const panel = this.modal.querySelector('#tab-panel-advanced');
956
+ if (!panel) return;
957
+
958
+ const providerId = providerSelect.value;
959
+ const newDefault = this._getProviderDefaultTimeout(providerId);
960
+ const oldProviderId = providerSelect.dataset.previousProvider;
961
+ const oldDefault = oldProviderId ? this._getProviderDefaultTimeout(oldProviderId) : null;
962
+
963
+ const isOrchestration = providerSelect.dataset.target === 'orchestration';
964
+ if (isOrchestration) {
965
+ const timeoutEl = panel.querySelector('#adv-orchestration-timeout');
966
+ if (timeoutEl) {
967
+ const currentValue = parseInt(timeoutEl.value, 10);
968
+ const resolvedTimeout = (oldDefault !== null && currentValue !== oldDefault)
969
+ ? Math.max(currentValue, newDefault)
970
+ : newDefault;
971
+ timeoutEl.value = String(resolvedTimeout);
972
+ this._updateOrchestrationTimeoutIcon(panel, String(resolvedTimeout));
973
+ }
974
+ } else {
975
+ const { level, index } = providerSelect.dataset;
976
+ const wrapper = providerSelect.closest('.participant-wrapper');
977
+ const timeoutEl = wrapper?.querySelector('.adv-timeout');
978
+ if (timeoutEl) {
979
+ const currentValue = parseInt(timeoutEl.value, 10);
980
+ const resolvedTimeout = (oldDefault !== null && currentValue !== oldDefault)
981
+ ? Math.max(currentValue, newDefault)
982
+ : newDefault;
983
+ timeoutEl.value = String(resolvedTimeout);
984
+ this._updateTimeoutIcon(panel, level, index, String(resolvedTimeout));
985
+ }
986
+ }
987
+
988
+ providerSelect.dataset.previousProvider = providerId;
989
+ }
990
+
932
991
  // --- Dirty state tracking ---
933
992
 
934
993
  _markDirty() {
@@ -1108,6 +1167,7 @@ class AdvancedConfigTab {
1108
1167
  if (providerSelect) {
1109
1168
  this._populateProviderDropdown(providerSelect);
1110
1169
  providerSelect.value = voice.provider;
1170
+ providerSelect.dataset.previousProvider = voice.provider;
1111
1171
  this._updateModelDropdown(providerSelect);
1112
1172
  const modelSelect = row.querySelector('.voice-model');
1113
1173
  if (modelSelect) modelSelect.value = voice.model;
@@ -1121,13 +1181,17 @@ class AdvancedConfigTab {
1121
1181
  TimeoutSelect.mount(mount, { className: 'adv-timeout', title: 'Per-reviewer timeout' });
1122
1182
  }
1123
1183
  const timeoutEl = row?.querySelector('.adv-timeout');
1184
+ const providerDefaultTimeout = this._getProviderDefaultTimeout(voice.provider);
1124
1185
  if (timeoutEl && voice.timeout) {
1125
1186
  timeoutEl.value = String(voice.timeout);
1126
- // Show the dropdown if non-default
1127
- if (voice.timeout !== AdvancedConfigTab.DEFAULT_TIMEOUT) {
1187
+ // Show the dropdown if non-default for this provider
1188
+ if (voice.timeout !== providerDefaultTimeout) {
1128
1189
  timeoutEl.style.display = '';
1129
1190
  }
1130
1191
  this._updateTimeoutIcon(panel, String(level), String(i), String(voice.timeout));
1192
+ } else if (timeoutEl) {
1193
+ // No saved timeout — apply the provider's default
1194
+ timeoutEl.value = String(providerDefaultTimeout);
1131
1195
  }
1132
1196
 
1133
1197
  if (voice.customInstructions) {
@@ -1156,6 +1220,7 @@ class AdvancedConfigTab {
1156
1220
  if (providerSelect) {
1157
1221
  this._populateProviderDropdown(providerSelect);
1158
1222
  providerSelect.value = consolSection.provider;
1223
+ providerSelect.dataset.previousProvider = consolSection.provider;
1159
1224
  this._updateModelDropdown(providerSelect);
1160
1225
  const modelSelect = orchRow.querySelector('.voice-model');
1161
1226
  if (modelSelect) modelSelect.value = consolSection.model;
@@ -1166,13 +1231,18 @@ class AdvancedConfigTab {
1166
1231
 
1167
1232
  // Restore consolidation timeout
1168
1233
  const orchTimeoutSelect = panel.querySelector('#adv-orchestration-timeout');
1234
+ const orchProviderDefaultTimeout = this._getProviderDefaultTimeout(consolSection.provider);
1169
1235
  if (orchTimeoutSelect && consolSection.timeout) {
1170
1236
  orchTimeoutSelect.value = String(consolSection.timeout);
1171
- // Show the dropdown if non-default
1172
- if (consolSection.timeout !== AdvancedConfigTab.DEFAULT_TIMEOUT) {
1237
+ // Show the dropdown if non-default for this provider
1238
+ if (consolSection.timeout !== orchProviderDefaultTimeout) {
1173
1239
  orchTimeoutSelect.style.display = '';
1174
1240
  }
1175
1241
  this._updateOrchestrationTimeoutIcon(panel, String(consolSection.timeout));
1242
+ } else if (orchTimeoutSelect) {
1243
+ // No saved timeout — apply the provider's default
1244
+ orchTimeoutSelect.value = String(orchProviderDefaultTimeout);
1245
+ this._updateOrchestrationTimeoutIcon(panel, String(orchProviderDefaultTimeout));
1176
1246
  }
1177
1247
 
1178
1248
  // Restore consolidation custom instructions
@@ -1266,6 +1336,11 @@ class AdvancedConfigTab {
1266
1336
  }
1267
1337
  this._markClean();
1268
1338
  await this.loadCouncils();
1339
+ const selector = this.modal.querySelector('#council-selector');
1340
+ if (selector) {
1341
+ selector.value = this.selectedCouncilId;
1342
+ selector.classList.remove('new-council-selected');
1343
+ }
1269
1344
  }
1270
1345
 
1271
1346
  /**