fix(web): keep known subagent chooser visible

This commit is contained in:
Tianyi Cui
2026-08-02 21:22:39 +08:00
parent da80b0e5e6
commit 711a33ea8d
7 changed files with 107 additions and 11 deletions

View File

@@ -0,0 +1,3 @@
- tree "Subagent sessions":
- treeitem "Loading subagents" [disabled] [level=1]: Loading subagents…
- treeitem "Loading subagents" [disabled] [level=1]: Loading subagents…

View File

@@ -20,6 +20,7 @@ import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './suppor
const BASE_FIXTURE = fileURLToPath(new URL('./snapshots/live-interactions/session.jsonl', import.meta.url))
const AVAILABLE_CHILD_EXPECTED = fileURLToPath(new URL('./snapshots/subagent-conversation/ui.expected.md', import.meta.url))
const TREE_EXPECTED = fileURLToPath(new URL('./snapshots/subagent-conversation/tree.expected.md', import.meta.url))
const STALE_CATALOG_EXPECTED = fileURLToPath(new URL('./snapshots/subagent-conversation/stale-catalog.expected.md', import.meta.url))
const SIDEBAR_EXPECTED = fileURLToPath(new URL('./snapshots/subagent-conversation/sidebar.expected.md', import.meta.url))
const UNAVAILABLE_GRANDCHILD_EXPECTED = fileURLToPath(new URL('./snapshots/subagent-conversation/nested.expected.md', import.meta.url))
const FORK_EXPECTED = fileURLToPath(new URL('./snapshots/subagent-conversation/fork.expected.md', import.meta.url))
@@ -239,6 +240,59 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
if (failures.length > 1) throw new AggregateError(failures, 'subagent Web teardown failed')
})
it('keeps known descendants reachable across a stale empty catalog response', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-subagent-stale-catalog'))
const pattern = '**/api/subagent.list'
let firstClaimed = false
let emptyDelivered = false
let trailingRequested = false
let releaseCatalog = (): void => {}
const catalogHeld = new Promise<void>((resolve) => { releaseCatalog = resolve })
await page.route(pattern, async (route) => {
if (firstClaimed) {
const response = await route.fetch()
trailingRequested = true
await catalogHeld
await route.fulfill({ response })
return
}
firstClaimed = true
const response = await route.fetch()
const body = await response.json() as {
result: { ok: true; value: { entries: unknown[] } } | { ok: false }
}
if (body.result.ok) body.result.value.entries = []
await route.fulfill({ response, json: body })
emptyDelivered = true
})
const warningStart = tripwire.warnings.length
try {
await page.reload({ waitUntil: 'load' })
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
await expect.poll(() => emptyDelivered, { timeout: 15_000 }).toBe(true)
await page.getByRole('button', { name: '3 subagents' }).waitFor({ timeout: 15_000 })
acknowledgeReloadConnectionLoss(tripwire, warningStart)
await page.getByRole('button', { name: '3 subagents' }).click()
await expect.poll(() => trailingRequested, { timeout: 15_000 }).toBe(true)
const tree = page.getByRole('tree', { name: 'Subagent sessions' })
await tree.getByRole('treeitem', { name: 'Loading subagents' }).first().waitFor()
expect(await tree.getByRole('treeitem', { name: 'Loading subagents' }).count()).toBe(2)
await compareOrRefreshGolden(
STALE_CATALOG_EXPECTED,
await captureStableAria(page, '[role="tree"][aria-label="Subagent sessions"]', scaffold.workspaceCwd),
MODE,
)
releaseCatalog()
await tree.getByRole('treeitem', { name: new RegExp(LABEL) }).waitFor({ timeout: 15_000 })
await tree.press('Escape')
} finally {
releaseCatalog()
await page.unroute(pattern)
}
})
it('expands a persisted grandchild progressively without activating either level', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-subagent-tree'))
await page.getByRole('button', { name: '3 subagents' }).click()