Fix Multi Agent Mode
Release rolling / release (push) Canceled after 9m26s

This commit is contained in:
Raven Scott
2026-07-30 18:12:08 -04:00
parent 1dd8197e05
commit e4ed0aadd2
3 changed files with 376 additions and 50 deletions
+41 -7
View File
@@ -281,12 +281,31 @@ export function createQvacView(opts) {
})
}
const swarm = createSwarmPanel(opts.els.agentBar, {
// Swarm mounts into each assistant message (transcript), not a sticky chrome bar
const swarm = createSwarmPanel(null, {
onStop: () => stopInference(),
})
function clearAgentBar() {
swarm.hide()
/** Release live swarm controller only; past message nodes stay in the transcript. */
function releaseLiveSwarm() {
if (typeof swarm.release === 'function') swarm.release()
else swarm.hide()
}
/**
* Mount a swarm host inside the assistant message (above answer body).
* @param {{ div: HTMLElement, body: HTMLElement }|null|undefined} assistantUi
* @returns {HTMLElement|null}
*/
function ensureSwarmSlot(assistantUi) {
if (!assistantUi?.div) return null
let slot = assistantUi.div.querySelector('.qvac-swarm-slot')
if (slot) return /** @type {HTMLElement} */ (slot)
slot = document.createElement('div')
slot.className = 'qvac-swarm-slot'
// Insert before message body so the answer streams below the swarm card
assistantUi.div.insertBefore(slot, assistantUi.body || assistantUi.div.firstChild)
return slot
}
/** Finish onboarding in tools-only mode (no model download). */
@@ -814,7 +833,10 @@ export function createQvacView(opts) {
let acc = ''
let thinkingAcc = ''
setStatus('Thinking…', 'busy')
clearAgentBar()
releaseLiveSwarm()
// Hide legacy sticky bar if still in DOM
opts.els.agentBar?.classList.add('hidden')
opts.els.agentBar && (opts.els.agentBar.innerHTML = '')
try {
// History for the model: clean answers only (no think tags)
const hist = messages
@@ -833,18 +855,19 @@ export function createQvacView(opts) {
subAgents: settings().qvacSubAgents !== false,
maxSubAgents: Number(settings().qvacMaxSubAgents) || 3,
}
if (
shouldUseSubAgents(text, prefs) &&
opts.isConnected?.() &&
!engine.isAborted?.()
) {
setStatus('Multi-agent swarm…', 'busy')
const swarmSlot = ensureSwarmSlot(assistantUi)
if (streamBody) {
streamBody.innerHTML = formatMdLite(
'_Swarm is running — watch specialists above while tools gather live data…_'
'_Specialists are gathering live data in this turn…_'
)
}
followMessagesScroll({ force: true })
const pack = await runSubAgents({
tools,
query: text,
@@ -857,8 +880,10 @@ export function createQvacView(opts) {
label: s.label,
description: s.description,
})),
text
text,
{ mount: swarmSlot }
)
followMessagesScroll({ force: true })
},
onAgent: (ev) => {
if (engine.isAborted?.()) return
@@ -866,6 +891,7 @@ export function createQvacView(opts) {
if (ev.status === 'start' || ev.status === 'running') {
setStatus(`${ev.label}`, 'busy')
}
followMessagesScroll()
},
})
if (engine.isAborted?.()) {
@@ -876,6 +902,10 @@ export function createQvacView(opts) {
return
}
swarm.complete('All agents done — synthesizing answer…')
// Collapse into transcript so the answer can stream below without covering chat
swarm.collapse(
`${pack.agents?.length || 0} specialist${(pack.agents?.length || 0) === 1 ? '' : 's'} · complete`
)
completeOpts.agentBriefing = pack.contextText
completeOpts.agentFallbackText = synthesizeFromAgents(text, pack)
completeOpts.agentToolCalls = pack.agents.map((a) => ({
@@ -896,6 +926,7 @@ export function createQvacView(opts) {
assistantUi.div.appendChild(renderToolChips(toolLog))
}
setStatus('Synthesizing…', 'busy')
followMessagesScroll({ force: true })
}
if (engine.isAborted?.()) {
@@ -1005,8 +1036,11 @@ export function createQvacView(opts) {
}
function newChat() {
releaseLiveSwarm()
messages = []
if (opts.els.messages) opts.els.messages.innerHTML = ''
opts.els.agentBar?.classList.add('hidden')
if (opts.els.agentBar) opts.els.agentBar.innerHTML = ''
appendMsg(
'assistant',
'New chat. Ask about host health, metrics, anomalies, or processes.'