fix(web): synchronize subagent navigation state

This commit is contained in:
imccyu
2026-08-02 01:38:28 +08:00
committed by Tianyi Cui
parent b94d2f9c1d
commit 23680e838b
25 changed files with 462 additions and 106 deletions

View File

@@ -2,5 +2,5 @@
- treeitem "workspace 2 sessions" [expanded]:
- img
- text: workspace 2 sessions
- treeitem "Explain event sourcing in one now" [selected]
- treeitem "Explain event sourcing in one (1) now" [selected]
- treeitem "Ask a research subagent to now"

View File

@@ -1,8 +1,8 @@
- tree "子代理会话":
- treeitem "event-sourcing researcher 可继续 · 当前未运行" [expanded] [level=1]:
- treeitem "event-sourcing researcher Explain event sourcing in one · 可继续 · 当前未运行 刚刚" [expanded] [level=1]:
- button "收起 event-sourcing researcher 的下级子代理":
- img
- text: event-sourcing researcher 可继续 · 当前未运行
- text: event-sourcing researcher Explain event sourcing in one · 可继续 · 当前未运行 刚刚
- group:
- treeitem "example editor 可继续 · 当前未运行" [level=2]
- treeitem "event-sourcing reviewer 一次性 · 当前未运行" [level=1]
- treeitem "example editor 可继续 · 当前未运行 刚刚" [level=2]
- treeitem "event-sourcing reviewer 一次性 · 当前未运行 刚刚" [level=1]

View File

@@ -11,7 +11,8 @@ import {
import type {} from '@deepseek-ai/dsh-agent'
import { snapshotSubagentDescriptor } from '@deepseek-ai/dsh-subagent'
import {
captureStableAria, compareOrRefreshGolden, launchWebScaffold, watchConsole,
acknowledgeReloadConnectionLoss, captureStableAria, compareOrRefreshGolden,
launchWebScaffold, watchConsole,
webSnapshotMode, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspace, saveFailureShot } from './support.ts'
@@ -73,6 +74,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
scaffold = await launchWebScaffold({
replayFixture: BASE_FIXTURE,
replayChildFixtures: [childFixturePath],
paceMs: 25,
})
browser = await chromium.launch()
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
@@ -209,7 +211,20 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
label: NESTED_LABEL, activity: 'inactive', hasChildren: false,
},
])
await page.getByRole('button', { name: '2 个子代理' }).waitFor({ timeout: 15_000 })
// These two cold fixtures were authored after the page's initial
// session.list and intentionally emitted no session-added frame. Reload
// to exercise the restart baseline that discovers their full lineage.
const warningStart = tripwire.warnings.length
await page.reload({ waitUntil: 'load' })
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
const catalogButton = page.getByRole('button', { name: /个子代理/ })
await catalogButton.waitFor({ timeout: 15_000 })
await catalogButton.click()
const catalogTree = page.getByRole('tree', { name: '子代理会话' })
await catalogTree.getByRole('treeitem').nth(1).waitFor({ timeout: 15_000 })
await catalogTree.press('Escape')
await page.getByRole('button', { name: '3 个子代理' }).waitFor({ timeout: 15_000 })
acknowledgeReloadConnectionLoss(tripwire, warningStart)
}, 120_000)
afterAll(async () => {
@@ -226,7 +241,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
it('expands a persisted grandchild progressively without activating either level', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-subagent-tree'))
await page.getByRole('button', { name: '2 个子代理' }).click()
await page.getByRole('button', { name: '3 个子代理' }).click()
expect(await page.getByRole('button', {
name: `展开 ${ONE_SHOT_LABEL} 的下级子代理`,
}).count()).toBe(0)
@@ -245,7 +260,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
it('opens the completed child from persistence without activating it', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-subagent-open'))
await page.getByRole('button', { name: '2 个子代理' }).click()
await page.getByRole('button', { name: '3 个子代理' }).click()
await page.getByRole('treeitem', { name: new RegExp(LABEL) }).click()
await expect.poll(
() => page.getByText(INITIAL_PROMPT, { exact: true }).count(),
@@ -281,7 +296,26 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
const input = page.getByRole('textbox', { name: 'Message the agent' })
await input.fill(FOLLOWUP)
await input.press('Enter')
await expect.poll(
() => scaffold.ctx.agents.get(childId)?.status,
{ timeout: 10_000 },
).toBe('running')
const hierarchy = page.getByRole('navigation', { name: 'Session hierarchy' })
await hierarchy.getByRole('button').first().click()
const runningTrigger = page.getByRole('button', { name: '3 个子代理,正在运行' })
await runningTrigger.waitFor({ timeout: 10_000 })
expect(await runningTrigger.locator('[data-state="ongoing"]').count()).toBe(1)
await runningTrigger.click()
await page.getByRole('treeitem', {
name: new RegExp(`${LABEL}.*正在运行`),
}).waitFor({ timeout: 10_000 })
await ended
await page.getByRole('treeitem', {
name: new RegExp(`${LABEL}.*当前未运行`),
}).waitFor({ timeout: 10_000 })
expect(await page.getByRole('button', { name: '3 个子代理' })
.locator('[data-state="ongoing"]').count()).toBe(0)
await page.getByRole('treeitem', { name: new RegExp(LABEL) }).click()
await expect.poll(() => page.getByText(FOLLOWUP, { exact: true }).count(), { timeout: 10_000 }).toBe(1)
await expect.poll(() => scaffold.ctx.agents.get(childId), { timeout: 10_000 }).toBeUndefined()
expect(await page.getByRole('button', { name: 'Stop generating' }).count()).toBe(0)
@@ -318,7 +352,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
.getByRole('treeitem')
.last()
await parentSession.click()
await page.getByRole('button', { name: '2 个子代理' }).click()
await page.getByRole('button', { name: '3 个子代理' }).click()
await page.getByRole('treeitem', { name: new RegExp(ONE_SHOT_LABEL) }).click()
await page.getByText('一次性任务不支持后续消息,可在这里查看完整执行记录。').waitFor()
expect(scaffold.ctx.agents.get(oneShotId)).toBeUndefined()
@@ -329,7 +363,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
await page.getByRole('tree', { name: 'Sessions' })
.getByRole('treeitem', { name: /Ask a research subagent to/ })
.click()
await page.getByRole('button', { name: '2 个子代理' }).click()
await page.getByRole('button', { name: '3 个子代理' }).click()
await page.getByRole('treeitem', { name: new RegExp(LABEL) }).click()
await page.getByRole('textbox', { name: 'Message the agent' }).waitFor()
const forkResponse = page.waitForResponse(response =>
@@ -355,7 +389,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
onTestFailed(() => saveFailureShot(page, 'web-e2e-subagent-post-fork-followup'))
const sessions = page.getByRole('tree', { name: 'Sessions' })
await sessions.getByRole('treeitem', { name: /Ask a research subagent to/ }).click()
await page.getByRole('button', { name: '2 个子代理' }).click()
await page.getByRole('button', { name: '3 个子代理' }).click()
await page.getByRole('treeitem', { name: new RegExp(LABEL) }).click()
await page.locator('textarea:enabled').first().waitFor()
expect(scaffold.ctx.agents.get(childId)).toBeUndefined()
@@ -372,7 +406,7 @@ describe('web e2e: persisted subagent conversation and human continuation', () =
await expect.poll(() => scaffold.ctx.agents.get(forkId)).not.toBeUndefined()
await sessions.getByRole('treeitem', { name: /Ask a research subagent to/ }).click()
await page.getByRole('button', { name: '2 个子代理' }).click()
await page.getByRole('button', { name: '3 个子代理' }).click()
await page.getByRole('treeitem', { name: new RegExp(LABEL) }).click()
const input = page.locator('textarea:enabled').first()
await input.waitFor()