Add multi-model panel with Fusion mode
Panel agents answer the user prompt in parallel (read-only, non-streaming); their responses are merged into the worker's last user message as a <panel_responses> block, and the session agent synthesizes one final streamed answer. The RunPanel engine is built layer-ready for future MoA. - New session fields: multi_model_mode (off/fusion), panel_agent_ids (CSV) with idempotent column migration - New partial Services/AnthropicService.Panel.cs holding the RunPanel engine - Provider-agnostic members: Anthropic RunNonStreamingToolLoop / OpenAI CompleteText / CLI plan-mode; all forced read-only, persisted with Tag=panel - SSE events: panel_start, panel_member_start, panel_member_done, panel_done - Frontend: multi-agent picker, live panel bubbles, history rendering - Graceful degradation: panel errors fall back to worker-only Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b23eba32b4
commit
268f11fa6d
20 files changed
+647
-35
No files matched your search
@@ -192,6 +192,61 @@ function _openPickerModal(title, items, currentId, onSelect, options) {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Multi-Select Picker Modal ──
|
||||
// Wie _openPickerModal, aber Mehrfachauswahl: Karten togglen ihren selected-Zustand,
|
||||
// ein "Übernehmen"-Button bestätigt. onConfirm bekommt das Array der gewählten _pickerId.
|
||||
|
||||
function _openMultiPickerModal(title, items, selectedIds, onConfirm, options) {
|
||||
options = options || {};
|
||||
var selected = new Set(selectedIds || []);
|
||||
|
||||
var overlay = document.createElement('div');
|
||||
overlay.className = 'agent-picker-overlay';
|
||||
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'agent-picker-modal';
|
||||
|
||||
var header = document.createElement('div');
|
||||
header.className = 'agent-picker-header';
|
||||
header.innerHTML = '<span>' + esc(title) + '</span><button class="icon-btn agent-picker-close"><i class="fa fa-times"></i></button>';
|
||||
modal.appendChild(header);
|
||||
|
||||
var body = document.createElement('div');
|
||||
body.className = 'agent-picker-body';
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
(function(item) {
|
||||
var cardEl = options.buildCard(item);
|
||||
cardEl.classList.add('agent-picker-item');
|
||||
if (selected.has(item._pickerId)) cardEl.classList.add('selected');
|
||||
cardEl.addEventListener('click', function() {
|
||||
if (selected.has(item._pickerId)) { selected.delete(item._pickerId); cardEl.classList.remove('selected'); }
|
||||
else { selected.add(item._pickerId); cardEl.classList.add('selected'); }
|
||||
});
|
||||
body.appendChild(cardEl);
|
||||
})(items[i]);
|
||||
}
|
||||
modal.appendChild(body);
|
||||
|
||||
var footer = document.createElement('div');
|
||||
footer.style.cssText = 'display:flex;justify-content:flex-end;gap:8px;padding:12px;border-top:1px solid var(--f2-border)';
|
||||
var confirmBtn = document.createElement('button');
|
||||
confirmBtn.className = 'add-cmd-btn';
|
||||
confirmBtn.textContent = 'Übernehmen';
|
||||
confirmBtn.addEventListener('click', function() {
|
||||
onConfirm(Array.from(selected));
|
||||
overlay.remove();
|
||||
});
|
||||
footer.appendChild(confirmBtn);
|
||||
modal.appendChild(footer);
|
||||
|
||||
overlay.appendChild(modal);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
header.querySelector('.agent-picker-close').addEventListener('click', function() { overlay.remove(); });
|
||||
overlay.addEventListener('click', function(e) { if (e.target === overlay) overlay.remove(); });
|
||||
}
|
||||
|
||||
// ── Telegram Bot Picker Modal ──
|
||||
|
||||
function openTelegramBotPickerModal(bots, currentBotId, onSelect) {
|
||||
|
||||
Reference in new issue
Block a user