This commit is contained in:
+41
-7
@@ -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.'
|
||||
|
||||
Reference in New Issue
Block a user