@in-the-loop-labs/pair-review 3.0.6 → 3.1.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/package.json +2 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/skills/analyze/references/level1-balanced.md +8 -0
- package/plugin-code-critic/skills/analyze/references/level1-fast.md +7 -0
- package/plugin-code-critic/skills/analyze/references/level1-thorough.md +8 -0
- package/plugin-code-critic/skills/analyze/references/level2-balanced.md +9 -0
- package/plugin-code-critic/skills/analyze/references/level2-fast.md +8 -0
- package/plugin-code-critic/skills/analyze/references/level2-thorough.md +9 -0
- package/plugin-code-critic/skills/analyze/references/level3-balanced.md +9 -0
- package/plugin-code-critic/skills/analyze/references/level3-fast.md +8 -0
- package/plugin-code-critic/skills/analyze/references/level3-thorough.md +9 -0
- package/plugin-code-critic/skills/analyze/references/orchestration-balanced.md +9 -0
- package/plugin-code-critic/skills/analyze/references/orchestration-fast.md +5 -0
- package/plugin-code-critic/skills/analyze/references/orchestration-thorough.md +9 -0
- package/public/css/analysis-config.css +103 -0
- package/public/css/pr.css +191 -4
- package/public/index.html +20 -0
- package/public/js/components/AIPanel.js +1 -1
- package/public/js/components/AdvancedConfigTab.js +87 -9
- package/public/js/components/AnalysisConfigModal.js +206 -5
- package/public/js/components/ChatPanel.js +22 -5
- package/public/js/components/CouncilProgressModal.js +241 -23
- package/public/js/components/TimeoutSelect.js +2 -0
- package/public/js/components/VoiceCentricConfigTab.js +183 -13
- package/public/js/index.js +119 -1
- package/public/js/local.js +166 -51
- package/public/js/modules/suggestion-manager.js +2 -1
- package/public/js/pr.js +71 -12
- package/public/js/repo-settings.js +2 -2
- package/public/local.html +32 -11
- package/public/pr.html +2 -0
- package/src/ai/analyzer.js +371 -111
- package/src/ai/claude-provider.js +2 -0
- package/src/ai/codex-provider.js +1 -1
- package/src/ai/copilot-provider.js +2 -0
- package/src/ai/executable-provider.js +538 -0
- package/src/ai/gemini-provider.js +2 -0
- package/src/ai/index.js +9 -1
- package/src/ai/pi-provider.js +10 -8
- package/src/ai/prompts/baseline/consolidation/balanced.js +54 -2
- package/src/ai/prompts/baseline/consolidation/fast.js +31 -1
- package/src/ai/prompts/baseline/consolidation/thorough.js +46 -3
- package/src/ai/prompts/baseline/level1/balanced.js +12 -0
- package/src/ai/prompts/baseline/level1/fast.js +11 -0
- package/src/ai/prompts/baseline/level1/thorough.js +12 -0
- package/src/ai/prompts/baseline/level2/balanced.js +13 -0
- package/src/ai/prompts/baseline/level2/fast.js +12 -0
- package/src/ai/prompts/baseline/level2/thorough.js +13 -0
- package/src/ai/prompts/baseline/level3/balanced.js +13 -0
- package/src/ai/prompts/baseline/level3/fast.js +12 -0
- package/src/ai/prompts/baseline/level3/thorough.js +13 -0
- package/src/ai/prompts/baseline/orchestration/balanced.js +15 -0
- package/src/ai/prompts/baseline/orchestration/fast.js +11 -0
- package/src/ai/prompts/baseline/orchestration/thorough.js +15 -0
- package/src/ai/prompts/render-for-skill.js +3 -0
- package/src/ai/prompts/shared/output-schema.js +8 -0
- package/src/ai/provider.js +91 -4
- package/src/chat/prompt-builder.js +39 -4
- package/src/chat/session-manager.js +32 -28
- package/src/config.js +15 -2
- package/src/database.js +59 -15
- package/src/git/base-branch.js +113 -29
- package/src/github/parser.js +1 -1
- package/src/local-review.js +15 -9
- package/src/local-scope.js +83 -0
- package/src/main.js +3 -2
- package/src/routes/analyses.js +34 -8
- package/src/routes/chat.js +15 -8
- package/src/routes/config.js +3 -120
- package/src/routes/councils.js +15 -6
- package/src/routes/executable-analysis.js +494 -0
- package/src/routes/local.js +152 -15
- package/src/routes/mcp.js +9 -4
- package/src/routes/pr.js +166 -29
- package/src/routes/reviews.js +31 -5
- package/src/routes/shared.js +72 -5
- package/src/routes/worktrees.js +4 -2
- package/src/utils/comment-formatter.js +28 -11
- package/src/utils/instructions.js +22 -8
- package/src/utils/logger.js +20 -10
|
@@ -23,6 +23,11 @@ class CouncilProgressModal {
|
|
|
23
23
|
// Track per-voice completion state
|
|
24
24
|
this._voiceStates = {};
|
|
25
25
|
|
|
26
|
+
// Track which voice keys are executable providers.
|
|
27
|
+
// Populated from progress events (levels.exec.voices) and persisted
|
|
28
|
+
// across show()/hide() cycles so reopened modals render correctly.
|
|
29
|
+
this._executableVoices = new Set();
|
|
30
|
+
|
|
26
31
|
this._createModal();
|
|
27
32
|
this._setupEventListeners();
|
|
28
33
|
}
|
|
@@ -54,9 +59,12 @@ class CouncilProgressModal {
|
|
|
54
59
|
// Detect rendering mode
|
|
55
60
|
const configType = options.configType || (councilConfig ? 'advanced' : 'single');
|
|
56
61
|
this._renderMode = configType;
|
|
62
|
+
this._noLevels = options.noLevels || false;
|
|
57
63
|
|
|
58
64
|
// Rebuild DOM based on mode
|
|
59
|
-
if (configType === 'single') {
|
|
65
|
+
if (configType === 'single' && this._noLevels) {
|
|
66
|
+
this._rebuildBodyNoLevels();
|
|
67
|
+
} else if (configType === 'single') {
|
|
60
68
|
const enabledLevels = options.enabledLevels || [1, 2, 3];
|
|
61
69
|
this._rebuildBodySingleModel(enabledLevels);
|
|
62
70
|
} else if (configType === 'council') {
|
|
@@ -239,11 +247,34 @@ class CouncilProgressModal {
|
|
|
239
247
|
* @param {Object} status
|
|
240
248
|
*/
|
|
241
249
|
updateProgress(status) {
|
|
250
|
+
// No-levels mode: update the single row based on overall status
|
|
251
|
+
if (this._noLevels) {
|
|
252
|
+
this._updateNoLevelsProgress(status);
|
|
253
|
+
// Terminal states
|
|
254
|
+
if (status.status === 'completed') {
|
|
255
|
+
this._handleCompletion(status);
|
|
256
|
+
} else if (status.status === 'failed') {
|
|
257
|
+
this._handleFailure(status);
|
|
258
|
+
} else if (status.status === 'cancelled') {
|
|
259
|
+
this._handleCancellation(status);
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
242
264
|
if (!status.levels || typeof status.levels !== 'object') {
|
|
243
265
|
console.warn('Council progress: invalid status structure', status);
|
|
244
266
|
return;
|
|
245
267
|
}
|
|
246
268
|
|
|
269
|
+
// Track executable voice keys from exec level data so that reopened
|
|
270
|
+
// modals can render executable reviewers correctly without relying on
|
|
271
|
+
// the config modal's provider cache (which may be empty on reopen).
|
|
272
|
+
if (status.levels.exec?.voices) {
|
|
273
|
+
for (const voiceKey of Object.keys(status.levels.exec.voices)) {
|
|
274
|
+
this._executableVoices.add(voiceKey);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
247
278
|
if (this._renderMode === 'single') {
|
|
248
279
|
// Single-model: update level headers directly
|
|
249
280
|
for (let level = 1; level <= 3; level++) {
|
|
@@ -292,6 +323,63 @@ class CouncilProgressModal {
|
|
|
292
323
|
}
|
|
293
324
|
}
|
|
294
325
|
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// No-levels progress
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Update the single row for no-levels providers.
|
|
332
|
+
* Maps overall analysis status to the single "Running analysis..." row.
|
|
333
|
+
*/
|
|
334
|
+
_updateNoLevelsProgress(status) {
|
|
335
|
+
const header = this.modal.querySelector('.council-level-header[data-level="analysis"]');
|
|
336
|
+
if (!header) return;
|
|
337
|
+
|
|
338
|
+
const iconEl = header.querySelector('.council-level-icon');
|
|
339
|
+
const statusEl = header.querySelector('.council-level-status');
|
|
340
|
+
if (!iconEl || !statusEl) return;
|
|
341
|
+
|
|
342
|
+
// Derive state from overall status or from any level status
|
|
343
|
+
let state = 'pending';
|
|
344
|
+
if (status.status === 'running' || status.status === 'in_progress') {
|
|
345
|
+
state = 'running';
|
|
346
|
+
} else if (status.status === 'completed') {
|
|
347
|
+
state = 'completed';
|
|
348
|
+
} else if (status.status === 'failed') {
|
|
349
|
+
state = 'failed';
|
|
350
|
+
} else if (status.status === 'cancelled') {
|
|
351
|
+
state = 'cancelled';
|
|
352
|
+
} else if (status.levels) {
|
|
353
|
+
// Fall back to checking individual levels
|
|
354
|
+
const levelValues = Object.values(status.levels);
|
|
355
|
+
if (levelValues.some(l => l.status === 'running')) state = 'running';
|
|
356
|
+
else if (levelValues.some(l => l.status === 'completed')) state = 'running';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this._renderState(iconEl, statusEl, state, 'council-level');
|
|
360
|
+
|
|
361
|
+
// Show stream event text in the snippet element
|
|
362
|
+
const levelEl = header.closest('.council-level');
|
|
363
|
+
const snippetEl = levelEl?.querySelector('.council-level-snippet');
|
|
364
|
+
if (snippetEl) {
|
|
365
|
+
// Check for stream events in any level
|
|
366
|
+
let streamText = null;
|
|
367
|
+
if (status.levels) {
|
|
368
|
+
for (const lvl of Object.values(status.levels)) {
|
|
369
|
+
if (lvl.streamEvent?.text) {
|
|
370
|
+
streamText = lvl.streamEvent.text;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (state === 'running' && streamText) {
|
|
375
|
+
snippetEl.textContent = streamText;
|
|
376
|
+
snippetEl.style.display = 'block';
|
|
377
|
+
} else if (state !== 'running') {
|
|
378
|
+
snippetEl.style.display = 'none';
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
295
383
|
// ---------------------------------------------------------------------------
|
|
296
384
|
// Per-voice progress
|
|
297
385
|
// ---------------------------------------------------------------------------
|
|
@@ -518,6 +606,55 @@ class CouncilProgressModal {
|
|
|
518
606
|
}
|
|
519
607
|
}
|
|
520
608
|
|
|
609
|
+
// Handle executable voice updates (level 'exec'):
|
|
610
|
+
// Executable voices emit progress with level: 'exec' instead of numeric 1/2/3.
|
|
611
|
+
// The single "Running analysis..." row uses data-vc-level="exec".
|
|
612
|
+
const execLevel = status.levels?.exec;
|
|
613
|
+
if (execLevel) {
|
|
614
|
+
if (execLevel.voices) {
|
|
615
|
+
// Check if any exec voice was rendered with native L1/L2/L3 structure
|
|
616
|
+
// instead of the single exec row. This happens when the modal was opened
|
|
617
|
+
// before _executableVoices was populated. If so, re-render the full body.
|
|
618
|
+
let needsRerender = false;
|
|
619
|
+
for (const voiceId of Object.keys(execLevel.voices)) {
|
|
620
|
+
const execEl = this.modal.querySelector(`[data-vc-voice="${voiceId}"][data-vc-level="exec"]`);
|
|
621
|
+
if (!execEl) {
|
|
622
|
+
// Voice exists in exec progress but has no exec DOM element — was likely
|
|
623
|
+
// rendered with native structure. Re-render to fix.
|
|
624
|
+
needsRerender = true;
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (needsRerender && this.councilConfig) {
|
|
629
|
+
this._rebuildBodyVoiceCentric(this.councilConfig);
|
|
630
|
+
// Re-apply all progress to the fresh DOM
|
|
631
|
+
this._updateVoiceCentric(status);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
for (const [voiceId, vStatus] of Object.entries(execLevel.voices)) {
|
|
636
|
+
this._setVoiceCentricLevelState(voiceId, 'exec', vStatus.status || 'running', vStatus);
|
|
637
|
+
}
|
|
638
|
+
} else if (execLevel.status && execLevel.status !== 'pending') {
|
|
639
|
+
// Aggregate terminal update without per-voice breakdown (completion/cancellation
|
|
640
|
+
// paths may collapse exec progress to just { status, progress }). Propagate the
|
|
641
|
+
// status to every rendered exec row, mirroring the pattern used for numeric levels
|
|
642
|
+
// with shared terminal state in _updateVoiceFromLevelStatus.
|
|
643
|
+
const terminalState = execLevel.status;
|
|
644
|
+
const execEls = this.modal.querySelectorAll('[data-vc-level="exec"]');
|
|
645
|
+
execEls.forEach(el => {
|
|
646
|
+
const voiceKey = el.dataset.vcVoice;
|
|
647
|
+
const stateKey = `${voiceKey}:exec`;
|
|
648
|
+
if (this._voiceStates[stateKey] !== 'completed' && this._voiceStates[stateKey] !== 'failed') {
|
|
649
|
+
this._setVoiceCentricLevelState(voiceKey, 'exec', terminalState, execLevel);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
if (execLevel.streamEvent?.text && execLevel.voiceId) {
|
|
654
|
+
this._setVoiceCentricStreamText(execLevel.voiceId, 'exec', execLevel.streamEvent.text);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
521
658
|
// Handle per-voice orchestration updates (level 4):
|
|
522
659
|
// In voice-centric mode, each reviewer has a consolidation child at data-vc-level="4".
|
|
523
660
|
// The backend tracks per-voice orchestration state in levels[4].voices, including
|
|
@@ -1073,6 +1210,26 @@ class CouncilProgressModal {
|
|
|
1073
1210
|
}
|
|
1074
1211
|
}
|
|
1075
1212
|
|
|
1213
|
+
// Executable voices don't require enabled levels — add any not already captured
|
|
1214
|
+
// (handles all-executable councils where no levels are enabled).
|
|
1215
|
+
// Use _executableVoices (populated from progress events) as primary source,
|
|
1216
|
+
// falling back to the config modal's provider cache. This ensures executable
|
|
1217
|
+
// voices render correctly even when the modal is reopened for an already-running
|
|
1218
|
+
// analysis where the config modal cache may be empty.
|
|
1219
|
+
if (config.voices) {
|
|
1220
|
+
const providersInfo = window.analysisConfigModal?.providers || {};
|
|
1221
|
+
for (const voice of config.voices) {
|
|
1222
|
+
const isExec = providersInfo[voice.provider]?.isExecutable || false;
|
|
1223
|
+
if (isExec) {
|
|
1224
|
+
const sig = `${voice.provider}|${voice.model}|${voice.tier || 'balanced'}|${voice.customInstructions || ''}`;
|
|
1225
|
+
if (!seenSignatures.has(sig)) {
|
|
1226
|
+
seenSignatures.add(sig);
|
|
1227
|
+
uniqueVoices.push(voice);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1076
1233
|
// Build voiceMap: voiceKey -> { voice, levels } using deduplicated array indices
|
|
1077
1234
|
const voiceMap = new Map();
|
|
1078
1235
|
uniqueVoices.forEach((voice, idx) => {
|
|
@@ -1080,11 +1237,33 @@ class CouncilProgressModal {
|
|
|
1080
1237
|
voiceMap.set(voiceKey, { voice, levels: enabledLevelNums });
|
|
1081
1238
|
});
|
|
1082
1239
|
|
|
1240
|
+
// Also add any executable voices known from progress events but not yet
|
|
1241
|
+
// in the voiceMap (covers the case where config modal providers cache is
|
|
1242
|
+
// empty but we've already seen exec-level progress data).
|
|
1243
|
+
for (const execVoiceKey of this._executableVoices) {
|
|
1244
|
+
if (!voiceMap.has(execVoiceKey)) {
|
|
1245
|
+
// Find the matching voice in config.voices by key pattern
|
|
1246
|
+
const configVoices = config.voices || [];
|
|
1247
|
+
const matchingVoice = configVoices.find((v, idx) => {
|
|
1248
|
+
const candidateKey = `${v.provider}-${v.model}${idx > 0 ? `-${idx}` : ''}`;
|
|
1249
|
+
return candidateKey === execVoiceKey;
|
|
1250
|
+
});
|
|
1251
|
+
if (matchingVoice) {
|
|
1252
|
+
voiceMap.set(execVoiceKey, { voice: matchingVoice, levels: enabledLevelNums });
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1083
1257
|
let html = '<div class="council-progress-tree">';
|
|
1084
1258
|
|
|
1085
|
-
// Build a parent row for each unique voice
|
|
1259
|
+
// Build a parent row for each unique voice.
|
|
1260
|
+
// Determine executable status from: (1) _executableVoices set (populated from
|
|
1261
|
+
// progress events), or (2) config modal provider cache as fallback.
|
|
1262
|
+
const providersMap = window.analysisConfigModal?.providers || {};
|
|
1263
|
+
|
|
1086
1264
|
for (const [voiceKey, { voice, levels }] of voiceMap) {
|
|
1087
|
-
const
|
|
1265
|
+
const isExecutable = this._executableVoices.has(voiceKey) || (providersMap[voice.provider]?.isExecutable || false);
|
|
1266
|
+
const label = this._formatVoiceLabel(voice, { isExecutable });
|
|
1088
1267
|
|
|
1089
1268
|
html += `
|
|
1090
1269
|
<div class="council-level" data-voice-key="${voiceKey}">
|
|
@@ -1096,31 +1275,46 @@ class CouncilProgressModal {
|
|
|
1096
1275
|
<div class="council-level-children">
|
|
1097
1276
|
`;
|
|
1098
1277
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
const connectorClass = 'connector-mid';
|
|
1278
|
+
if (isExecutable) {
|
|
1279
|
+
// Executable voices: single "Running analysis..." row instead of L1/L2/L3
|
|
1102
1280
|
html += `
|
|
1103
|
-
<div class="council-voice
|
|
1104
|
-
<span class="council-voice-connector
|
|
1105
|
-
<span class="council-voice-icon
|
|
1106
|
-
<span class="council-voice-label">
|
|
1107
|
-
<span class="council-voice-status
|
|
1281
|
+
<div class="council-voice connector-last" data-vc-voice="${voiceKey}" data-vc-level="exec">
|
|
1282
|
+
<span class="council-voice-connector connector-last"></span>
|
|
1283
|
+
<span class="council-voice-icon pending">\u25CB</span>
|
|
1284
|
+
<span class="council-voice-label">Running analysis\u2026</span>
|
|
1285
|
+
<span class="council-voice-status pending">Pending</span>
|
|
1108
1286
|
<div class="council-voice-detail">
|
|
1109
1287
|
<div class="council-voice-snippet" style="display: none;"></div>
|
|
1110
1288
|
</div>
|
|
1111
1289
|
</div>
|
|
1112
1290
|
`;
|
|
1113
|
-
}
|
|
1291
|
+
} else {
|
|
1292
|
+
// Native voices: L1/L2/L3 children + orchestration
|
|
1293
|
+
levels.forEach((levelNum) => {
|
|
1294
|
+
const connectorClass = 'connector-mid';
|
|
1295
|
+
html += `
|
|
1296
|
+
<div class="council-voice ${connectorClass}" data-vc-voice="${voiceKey}" data-vc-level="${levelNum}">
|
|
1297
|
+
<span class="council-voice-connector ${connectorClass}"></span>
|
|
1298
|
+
<span class="council-voice-icon running"><span class="council-spinner"></span></span>
|
|
1299
|
+
<span class="council-voice-label">Level ${levelNum} \u2014 ${levelNames[levelNum]}</span>
|
|
1300
|
+
<span class="council-voice-status running">Running...</span>
|
|
1301
|
+
<div class="council-voice-detail">
|
|
1302
|
+
<div class="council-voice-snippet" style="display: none;"></div>
|
|
1303
|
+
</div>
|
|
1304
|
+
</div>
|
|
1305
|
+
`;
|
|
1306
|
+
});
|
|
1114
1307
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1308
|
+
// Orchestration child (always last)
|
|
1309
|
+
html += `
|
|
1310
|
+
<div class="council-voice connector-last" data-vc-voice="${voiceKey}" data-vc-level="4">
|
|
1311
|
+
<span class="council-voice-connector connector-last"></span>
|
|
1312
|
+
<span class="council-voice-icon pending">\u25CB</span>
|
|
1313
|
+
<span class="council-voice-label">Consolidation</span>
|
|
1314
|
+
<span class="council-voice-status pending">Pending</span>
|
|
1315
|
+
</div>
|
|
1316
|
+
`;
|
|
1317
|
+
}
|
|
1124
1318
|
|
|
1125
1319
|
html += `
|
|
1126
1320
|
</div>
|
|
@@ -1205,6 +1399,28 @@ class CouncilProgressModal {
|
|
|
1205
1399
|
body.innerHTML = html;
|
|
1206
1400
|
}
|
|
1207
1401
|
|
|
1402
|
+
/**
|
|
1403
|
+
* Rebuild the modal body for no-levels providers (e.g., executable providers).
|
|
1404
|
+
* Shows a single "Running analysis..." entry instead of the L1/L2/L3 breakdown.
|
|
1405
|
+
*/
|
|
1406
|
+
_rebuildBodyNoLevels() {
|
|
1407
|
+
const body = this.modal.querySelector('.council-progress-body');
|
|
1408
|
+
if (!body) return;
|
|
1409
|
+
|
|
1410
|
+
body.innerHTML = `
|
|
1411
|
+
<div class="council-progress-tree">
|
|
1412
|
+
<div class="council-level" data-level="analysis">
|
|
1413
|
+
<div class="council-level-header" data-level="analysis">
|
|
1414
|
+
<span class="council-level-icon pending">\u25CB</span>
|
|
1415
|
+
<span class="council-level-title">Running analysis\u2026</span>
|
|
1416
|
+
<span class="council-level-status pending">Pending</span>
|
|
1417
|
+
</div>
|
|
1418
|
+
<div class="council-level-snippet" style="display: none;"></div>
|
|
1419
|
+
</div>
|
|
1420
|
+
</div>
|
|
1421
|
+
`;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1208
1424
|
_resetFooter() {
|
|
1209
1425
|
const bgBtn = this.modal.querySelector('.council-bg-btn');
|
|
1210
1426
|
const cancelBtn = this.modal.querySelector('.council-cancel-btn');
|
|
@@ -1404,9 +1620,11 @@ class CouncilProgressModal {
|
|
|
1404
1620
|
* Example: { provider: 'claude', model: 'sonnet-4-5', tier: 'balanced' }
|
|
1405
1621
|
* -> "Claude sonnet-4-5 (Balanced)"
|
|
1406
1622
|
*/
|
|
1407
|
-
_formatVoiceLabel(voice) {
|
|
1408
|
-
const
|
|
1623
|
+
_formatVoiceLabel(voice, { isExecutable = false } = {}) {
|
|
1624
|
+
const providersMap = window.analysisConfigModal?.providers || {};
|
|
1625
|
+
const provider = providersMap[voice.provider]?.name || this._capitalize(voice.provider || 'unknown');
|
|
1409
1626
|
const model = voice.model || 'default';
|
|
1627
|
+
if (isExecutable) return `${provider} ${model}`;
|
|
1410
1628
|
const tier = this._capitalize(voice.tier || 'balanced');
|
|
1411
1629
|
return `${provider} ${model} (${tier})`;
|
|
1412
1630
|
}
|