fix(desktop): seed agent terminal tabs from process snapshots

Read-only agent terminal tabs now consume both live agent.terminal.output chunks
and the process-list/status snapshot. The snapshot seeds tabs opened after output
already exists and acts as a fallback if the live stream races startup, so agent
background tabs don't sit blank while the status stack already knows the tail.
This commit is contained in:
Brooklyn Nicholson 2026-06-28 19:35:54 -05:00
parent 520212cc59
commit 6ac9ba9fc4
2 changed files with 31 additions and 2 deletions

View file

@ -40,3 +40,29 @@ export function writeAgentTerminalChunk(procId: string, chunk: string): void {
backlog.set(procId, next.length > MAX_BACKLOG ? next.slice(-MAX_BACKLOG) : next)
writers.get(procId)?.(chunk)
}
/** Ingest a full output snapshot from process.list/status-stack. This is the
* fallback for older/not-yet-restarted gateways and a seed for tabs opened
* after output already exists. If it extends our current backlog, append only
* the delta; if the registry's rolling tail slid, reset to that tail. */
export function syncAgentTerminalSnapshot(procId: string, output: string): void {
if (!procId || !output) {
return
}
const current = backlog.get(procId) ?? ''
if (output === current) {
return
}
if (output.startsWith(current)) {
writeAgentTerminalChunk(procId, output.slice(current.length))
return
}
const next = output.length > MAX_BACKLOG ? output.slice(-MAX_BACKLOG) : output
backlog.set(procId, next)
writers.get(procId)?.(`\x1bc${next}`)
}

View file

@ -3,6 +3,7 @@ import { useEffect } from 'react'
import { $backgroundStatusBySession } from '@/store/composer-status'
import { syncAgentTerminalSnapshot } from './agent-terminal-stream'
import { setActiveTerminalId } from './buffer'
import { AgentTerminalInstance, TerminalInstance } from './instance'
import { $activeTerminalId, $terminals, ensureAgentTerminal } from './terminals'
@ -30,12 +31,14 @@ export function TerminalWorkspace({ onAddSelectionToChat }: TerminalWorkspacePro
}
}, [])
// Surface the agent's background processes as read-only tabs (once each); their
// output streams in live via agent.terminal.output, no polling needed.
// Surface the agent's background processes as read-only tabs (once each).
// Live chunks stream via agent.terminal.output; the process-list snapshot also
// seeds/falls back so the tab never stays blank if the stream races startup.
useEffect(() => {
for (const list of Object.values(background)) {
for (const item of list) {
ensureAgentTerminal(item.id, item.title)
syncAgentTerminalSnapshot(item.id, item.output ?? '')
}
}
}, [background])