feat(sidebar): agent 终端等待 banner(needle + 跳过等待按钮)

This commit is contained in:
HuanLinOTO
2026-09-09 13:37:08 +08:00
parent 1fa777293f
commit c4c856a450
25 changed files with 189 additions and 0 deletions
+28
View File
@@ -48,6 +48,7 @@ import {
shouldActivateTerminalLink,
openTerminalUrl,
} from './terminal-links.ts'
import { TerminalWaitBanner } from './TerminalWaitBanner.tsx'
import css from './sidebar.module.css'
/** How many consecutive unreasoned failures before showing the error banner. */
@@ -123,6 +124,27 @@ export function TerminalView(props: { scope: SessionScope; tabId: string; store:
const [fatal, setFatal] = useState<string | null>(null)
const [depsFatal, setDepsFatal] = useState<TerminalDepsInfo | null>(null)
const [lastUrl, setLastUrl] = useState<string | null>(null)
// Agent terminals only: the model's active terminal_wait_for (mirrored
// from the host's agent-terminals push into the store) drives the wait
// banner. Read + subscribe like the font prefs above; the banner vanishes
// when the host's push drops the waiting field (skip / exit / abort all
// converge through the same push). getSnapshot() is {sessionId, state?,
// prefs} — the state may be briefly undefined around session switches.
const agentUuid = isAgentTabId(tabId) ? agentUuidOf(tabId) : null
const [waiting, setWaiting] = useState<{ needle: string; since: number } | undefined>(undefined)
useEffect(() => {
if (agentUuid === null) return
const read = (): void => {
const next = store.getSnapshot().state?.agentWaits?.[agentUuid]
setWaiting(prev => {
const nextValue = next === undefined ? undefined : { needle: next.needle, since: next.since }
if (prev?.needle === nextValue?.needle && prev?.since === nextValue?.since) return prev
return nextValue
})
}
read()
return store.subscribe(read)
}, [agentUuid, store])
const connectRef = useRef<(() => void) | null>(null)
useEffect(() => {
@@ -386,6 +408,12 @@ export function TerminalView(props: { scope: SessionScope; tabId: string; store:
return (
<div className={css.terminalWrap}>
{agentUuid !== null && waiting !== undefined && (
<TerminalWaitBanner
needle={waiting.needle}
onSkip={() => { void api.agentSkipWait(agentUuid).catch(() => { /* 跳过失败时 banner 留存,可重试 */ }) }}
/>
)}
{depsFatal !== null && (
<TerminalDepsBanner deps={depsFatal} onRetry={() => { setDepsFatal(null); connectRef.current?.() }} />
)}