Merge branch 'master' into codex/responsive-queue-panel

This commit is contained in:
Wenlu Wang
2026-07-31 12:34:57 +08:00
committed by GitHub
132 changed files with 3161 additions and 473 deletions

View File

@@ -111,6 +111,18 @@ it('boots the built plugin graph and renders a fixture session end to end', asyn
expect(document.querySelector('[data-sample="bash-global"]')).not.toBeNull()
}, { timeout: 10_000 })
// The web render intent reaches the assembled boot graph: the fixture's
// web_search / web_fetch turns render their keyed WebRow cards, proving the
// registration, wire projection, and card rendering survive the real bundle
// path (not just the per-package src benches). The selector pins the KEYED
// WebRow (its own `data-variant="web"` wrapper), not the `[data-web]` attribute
// WebBlock draws — the generic fallback renders the same WebBlock, so a silent
// keyed-registration failure would still satisfy a bare `[data-web]` check.
await waitFor(() => {
expect(document.querySelector('[data-variant="web"][data-tool="web_search"]')).not.toBeNull()
expect(document.querySelector('[data-variant="web"][data-tool="web_fetch"]')).not.toBeNull()
}, { timeout: 10_000 })
// Every bundle injected its plugin-owned style tag (the loader's CSS path).
const styleOwners = [...document.head.querySelectorAll('style[data-plugin]')]
.map(style => style.getAttribute('data-plugin'))

View File

@@ -25,6 +25,8 @@ import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './suppor
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/lifecycle-chrome', import.meta.url))
const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
const HERO_EXPECTED = join(SNAPSHOT_DIR, 'hero.expected.md')
const COMMAND_MENU_EXPECTED = join(SNAPSHOT_DIR, 'command-menu.expected.md')
const PLAN_ACTIVE_EXPECTED = join(SNAPSHOT_DIR, 'plan-active.expected.md')
// Post-reload golden: the same settled conversation rebuilt purely from
// persistence + history — byte-equal rendering is exactly the recovery claim.
const RELOADED_EXPECTED = join(SNAPSHOT_DIR, 'reloaded.expected.md')
@@ -56,6 +58,88 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', ()
await scaffold?.close()
})
it.skipIf(MODE === 'record')('opens the shared slash menu from plus with only Command candidates', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-command-menu-launcher'))
const launcher = page.getByRole('button', { name: 'Commands' })
await launcher.click()
const menu = page.getByRole('listbox', { name: 'Trigger suggestions' })
await menu.waitFor({ timeout: 10_000 })
const snapshot = await captureStableAria(page, '[role="listbox"]', scaffold.workspaceCwd)
await compareOrRefreshGolden(COMMAND_MENU_EXPECTED, snapshot, MODE)
expect(snapshot).toContain('text: Commands')
expect(snapshot).not.toContain('text: Skills')
expect(snapshot).not.toContain('text: Subagents')
const launchedBox = await menu.boundingBox()
await page.locator('textarea').first().press('Escape')
await expect.poll(() => menu.count()).toBe(0)
const input = page.locator('textarea').first()
await input.fill('/')
await menu.waitFor({ timeout: 10_000 })
const typedBox = await menu.boundingBox()
expect(launchedBox).not.toBeNull()
expect(typedBox).not.toBeNull()
expect(Math.abs(launchedBox!.x - typedBox!.x)).toBeLessThan(1)
expect(Math.abs(
launchedBox!.y + launchedBox!.height - typedBox!.y - typedBox!.height,
)).toBeLessThan(1)
await input.fill('')
await expect.poll(() => menu.count()).toBe(0)
})
it.skipIf(MODE === 'record')('shows active Plan as the warn-state status action', async () => {
const activeScaffold = await launchWebScaffold()
const activePage = await newEnglishPage(browser)
const activeTripwire = watchConsole(activePage)
try {
await activePage.goto(activeScaffold.baseUrl, { waitUntil: 'load' })
await activePage.waitForSelector('[class*="frame"]', { timeout: 30_000 })
await connectFreshWorkspace(activePage)
const input = activePage.locator('textarea').first()
await activePage.getByRole('button', { name: 'Commands' }).click()
const menu = activePage.getByRole('listbox', { name: 'Trigger suggestions' })
await menu.waitFor({ timeout: 10_000 })
await menu.getByRole('option', { name: 'plan Enter or leave plan mode' }).click()
await expect.poll(() => input.inputValue()).toBe('/plan ')
await input.press('Enter')
const planButton = activePage.getByRole('button', { name: 'Plan mode on, press to turn off' })
await planButton.waitFor({ timeout: 10_000 })
const planSnapshot = await captureStableAria(activePage, '[class*="frame"]', activeScaffold.workspaceCwd)
await compareOrRefreshGolden(PLAN_ACTIVE_EXPECTED, planSnapshot, MODE)
const planStyle = await planButton.evaluate((element) => {
const probe = document.createElement('span')
probe.style.color = 'var(--dsw-alias-state-warn-label)'
probe.style.backgroundColor = 'var(--dsw-alias-state-warn-tertiary)'
document.body.append(probe)
const actual = getComputedStyle(element)
const reference = getComputedStyle(probe)
const result = {
color: actual.color,
backgroundColor: actual.backgroundColor,
borderRadius: actual.borderRadius,
fontSize: actual.fontSize,
referenceColor: reference.color,
referenceBackgroundColor: reference.backgroundColor,
}
probe.remove()
return result
})
expect(planStyle.color).toBe(planStyle.referenceColor)
expect(planStyle.backgroundColor).toBe(planStyle.referenceBackgroundColor)
expect(planStyle.borderRadius).toBe('999px')
expect(planStyle.fontSize).toBe('13px')
await planButton.click()
await expect.poll(() => planButton.count()).toBe(0)
expect(activeTripwire.pageErrors).toEqual([])
expect(activeTripwire.warnings).toEqual([])
} catch (error) {
await saveFailureShot(activePage, 'web-e2e-plan-active').catch(() => undefined)
throw error
} finally {
await activePage.close()
await activeScaffold.close()
}
})
it('sends the first prompt from the empty-state hero (all modes)', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-lifecycle-send'))
if (MODE !== 'record') {
@@ -152,6 +236,8 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', ()
it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => {
expect(tripwire.warnings).toEqual([])
await assertFixtureInventory(SNAPSHOT_DIR, ['session.jsonl', 'hero.expected.md', 'reloaded.expected.md'])
await assertFixtureInventory(SNAPSHOT_DIR, [
'session.jsonl', 'command-menu.expected.md', 'hero.expected.md', 'plan-active.expected.md', 'reloaded.expected.md',
])
})
})

View File

@@ -221,8 +221,8 @@ describe('web e2e: live-turn interactions (cancel / error / retry)', () => {
// only on change, so attempt count is invisible there).
expect(sessionEvents.filter(e => e.type === 'llm/retry').length).toBeGreaterThanOrEqual(1)
await expect.poll(() => page.getByText('event sourcing', { exact: false }).count(), { timeout: 10_000 }).toBeGreaterThan(0)
// Golden of the recovered end-state: indistinguishable from a clean
// completion — retries are deliberately invisible in the transcript.
// Golden of the recovered end-state: the discarded partial stays absent,
// while the settled retry row remains as durable recovery context.
const snapshot = await captureStableAria(page, '[class*="centerCol"]', scaffold!.workspaceCwd)
await compareOrRefreshGolden(RETRY_EXPECTED, snapshot, MODE)
expect(tripwire.pageErrors).toEqual([])

View File

@@ -267,6 +267,98 @@ describe('dsh web keyless CLI smoke', () => {
}
})
it('retries a partial transport failure through the shipped Web composition', async () => {
requireDist()
const workspace = mkdtempSync(join(tmpdir(), 'dsh-web-retry-'))
const promptMarker = 'WEB_RETRY_REQUEST'
const recoveredMarker = 'WEB_RETRY_RECOVERED'
let mainAttempts = 0
const provider = createServer((request, response) => {
let body = ''
request.setEncoding('utf8')
request.on('data', (chunk: string) => { body += chunk })
request.on('end', () => {
const parsed = JSON.parse(body) as { max_tokens?: number; messages?: unknown[] }
const titleRequest = parsed.max_tokens === 64
const mainRequest = !titleRequest && body.includes(promptMarker)
response.writeHead(200, { 'content-type': 'text/event-stream' })
if (!mainRequest) {
response.end([
'data: {"choices":[{"delta":{"content":"Web retry title"}}]}',
'data: {"choices":[{"delta":{"content":""},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":1}}',
'data: [DONE]',
'',
].join('\n\n'))
return
}
mainAttempts++
if (mainAttempts === 1) {
response.write('data: {"choices":[{"delta":{"content":"WEB_RETRY_DISCARDED"}}]}\n\n')
setTimeout(() => { response.destroy() }, 20)
return
}
response.end([
`data: {"choices":[{"delta":{"content":"${recoveredMarker}"}}]}`,
'data: {"choices":[{"delta":{"content":""},"finish_reason":"stop"}],"usage":{"prompt_tokens":3,"completion_tokens":1}}',
'data: [DONE]',
'',
].join('\n\n'))
})
})
await new Promise<void>(resolve => provider.listen(0, '127.0.0.1', resolve))
const address = provider.address()
if (address === null || typeof address === 'string') throw new Error('mock provider did not bind a TCP port')
const tsxLoader = pathToFileURL(createRequire(join(REPO_ROOT, 'package.json')).resolve('tsx')).href
const child = spawn(
process.execPath,
['--import', tsxLoader, join(REPO_ROOT, 'apps/cli/src/bin.ts'), 'web', '--port', '0'],
{
cwd: workspace,
env: {
...process.env,
DEEPSEEK_API_KEY: 'keyless-web-retry',
DEEPSEEK_BASE_URL: `http://127.0.0.1:${address.port}`,
DSH_HOME: join(workspace, '.dsh'),
TSX_TSCONFIG_PATH: join(REPO_ROOT, 'tsconfig.json'),
},
stdio: ['ignore', 'pipe', 'pipe'],
},
)
try {
const baseUrl = await waitForReadyLine(child)
const created = await rpc<{ sessionId: string }>(baseUrl, 'session.create', {})
await rpc<{ accepted: true }>(baseUrl, 'session.prompt', {
sessionId: created.sessionId,
mode: 'queue',
content: [{ type: 'text', text: promptMarker }],
})
let page: HistoryPage | undefined
await expect.poll(async () => {
page = await history(baseUrl, created.sessionId)
return hasAssistantMarker(page, recoveredMarker)
}, { timeout: 20_000 }).toBe(true)
if (page === undefined) throw new Error('retry history was not observed')
const retry = page.events.find(({ event }) => event.type === 'llm/retry')?.event
expect(mainAttempts).toBe(2)
expect(retry?.data).toMatchObject({
turn: 1,
step: 1,
retry: 1,
maxRetries: 2,
failure: { code: 'TRANSPORT' },
})
expect(JSON.stringify(page.events)).toContain('WEB_RETRY_DISCARDED')
} finally {
const closed = child.exitCode === null
? new Promise<void>((resolveClose) => { child.once('close', () => { resolveClose() }) })
: Promise.resolve()
if (child.exitCode === null) child.kill('SIGTERM')
await closed
await new Promise<void>(resolveClose => provider.close(() => { resolveClose() }))
rmSync(workspace, { recursive: true, force: true })
}
}, 30_000)
it('DSH_TOOLS_MODE=code collapses the provider wire tools to run_code with the SDK prompt section', async () => {
requireDist()
const workspace = mkdtempSync(join(tmpdir(), 'dsh-web-code-mode-'))

View File

@@ -35,10 +35,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -50,10 +50,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -30,10 +30,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -0,0 +1,6 @@
- listbox "Trigger suggestions":
- text: Commands
- option "goal set or view the goal for a long-running task" [selected]
- option "permission Switch the permission preset (sandbox mode + approval policy)"
- option "plan Enter or leave plan mode"
- option "model Select the model for this conversation"

View File

@@ -26,10 +26,9 @@
- text: workspace
- img
- textbox "Describe what you want to build"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -0,0 +1,39 @@
- button "New session"
- button "Collapse sidebar":
- img
- button "New session":
- img
- text: New Session
- text: Workspaces
- button "Group by":
- img
- button "Create workspace":
- img
- button "Search sessions":
- img
- textbox "Search name, keywords..."
- tree "Sessions":
- treeitem "workspace 1 session" [expanded]:
- img
- text: workspace 1 session
- treeitem "New Session now" [selected]
- button "Settings":
- img
- text: Settings
- text: Let's start building
- button "Choose workspace":
- img
- text: workspace
- img
- textbox "Describe what you want to build"
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode on, press to turn off": Plan
- button "Select model, current deepseek-v4-flash":
- text: deepseek-v4-flash
- img
- button "Send message" [disabled]
- text: Details
- button "Close details"
- text: Click a tool row in the message flow to view its details

View File

@@ -22,10 +22,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -19,10 +19,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -12,10 +12,9 @@
- button "Edit":
- img
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -11,6 +11,8 @@
- img
- button "Edit":
- img
- group:
- status: Retried model request (1/2) · {{duration}}
- button "Think The user is asking for a one-sentence description of event sourcing. This is a straightforward knowledge question that doesn't require any skill loading or tool calls.":
- img
- img
@@ -22,10 +24,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -37,10 +37,9 @@
- img
- text: 7/25 {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current deepseek-v4-flash":
- text: deepseek-v4-flash
- img

View File

@@ -35,10 +35,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -30,10 +30,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -14,10 +14,9 @@
- paragraph: partial
- button "2 queued messages"
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -27,10 +27,9 @@
- button "Cancel editing":
- img
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -20,10 +20,9 @@
- button "Remove queued message":
- img
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img

View File

@@ -42,10 +42,9 @@
- img
- text: permission preset workspace-write
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Workspace Write"': Workspace Write
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current deepseek-v4-flash":
- text: deepseek-v4-flash
- img

View File

@@ -40,10 +40,9 @@
- img
- text: Context injection
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current deepseek-v4-flash":
- text: deepseek-v4-flash
- img

View File

@@ -31,10 +31,9 @@
- img
- text: {{clock}}
- textbox "Message the agent"
- button "Add attachment":
- button "Commands":
- img
- 'button "Access mode, current: Danger Full Access"': Danger Full Access
- button "Plan mode off, press to turn on": Plan off
- button "Select model, current DeepSeek-V4-Flash":
- text: DeepSeek-V4-Flash
- img