Merge remote-tracking branch 'origin/master' into xtr/react-loop-simplification
# Conflicts: # .agents/notes/implemented/feature/2026-07-22-docked-web-goal-bar.i18n.yaml # .agents/notes/implemented/feature/2026-07-22-docked-web-goal-bar.md # .agents/notes/implemented/feature/2026-07-22-docked-web-goal-bar.zh.md # docs/config-catalog.md # packages/client/ui-goal/README.i18n.yaml # packages/host/apiproxy/tests/api-proxy-fork.spec.ts # packages/ui/tui/README.i18n.yaml
This commit is contained in:
@@ -70,14 +70,7 @@ describe('web e2e: Full access confirmation', () => {
|
||||
const access = page.locator('button[aria-label^="访问模式"]').first()
|
||||
await access.waitFor({ timeout: 10_000 })
|
||||
|
||||
// Normalize the starting preset through the real command path. The
|
||||
// shipped web config may already start at Full access.
|
||||
if ((await access.getAttribute('aria-label'))?.endsWith('Full access') === true) {
|
||||
await access.click()
|
||||
await page.getByRole('menuitem', { name: 'Workspace Write' }).click()
|
||||
await expect.poll(() => access.getAttribute('aria-label'), { timeout: 10_000 })
|
||||
.toBe('访问模式,当前:Workspace Write')
|
||||
}
|
||||
expect(await access.getAttribute('aria-label')).toBe('访问模式,当前:Workspace Write')
|
||||
|
||||
await access.click()
|
||||
await page.getByRole('menuitem', { name: 'Full access' }).click()
|
||||
|
||||
@@ -108,7 +108,7 @@ it('boots the built plugin graph and renders a fixture session end to end', asyn
|
||||
// Opening a session reaches chat content through the fixture transport.
|
||||
fireEvent.click(await within(tree).findByText('Fixture 历史会话'))
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector('[data-sample="bash-global"]')).not.toBeNull()
|
||||
expect(document.querySelector('[data-sample="bash"]')).not.toBeNull()
|
||||
}, { timeout: 10_000 })
|
||||
|
||||
// The write/edit turns render a real diff card through the assembled graph
|
||||
|
||||
@@ -112,7 +112,7 @@ describe('web e2e: Code Mode round renders nested sub-calls', () => {
|
||||
// the bash sub-call landed in the bash sample registration.
|
||||
const nest = page.locator('[data-subcalls]').first()
|
||||
await nest.waitFor({ timeout: 10_000 })
|
||||
expect(await nest.locator('[data-sample="bash-global"]').count()).toBeGreaterThanOrEqual(1)
|
||||
expect(await nest.locator('[data-sample="bash"]').count()).toBeGreaterThanOrEqual(1)
|
||||
// The failing read sub-call wears the same error state a native failed
|
||||
// row wears (the recorded program tolerates a read of missing.txt).
|
||||
expect(await nest.locator('[data-state="error"]').count()).toBeGreaterThanOrEqual(1)
|
||||
@@ -123,7 +123,7 @@ describe('web e2e: Code Mode round renders nested sub-calls', () => {
|
||||
const nest = page.locator('[data-subcalls]').first()
|
||||
const frame = page.locator('[style*="grid-template-columns"]').first()
|
||||
expect(await frame.getAttribute('data-details-collapsed')).toBe('true')
|
||||
await nest.locator('[data-sample="bash-global"]').first().click()
|
||||
await nest.locator('[data-sample="bash"]').first().click()
|
||||
// Tool rows do not drive layout geometry; the Session's default panel stays closed.
|
||||
await expect.poll(() => frame.getAttribute('data-details-collapsed'), { timeout: 5_000 }).toBe('true')
|
||||
})
|
||||
|
||||
71
apps/web/tests/goal-bar.e2e.ts
Normal file
71
apps/web/tests/goal-bar.e2e.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
// Keyless assembled-browser coverage for the goal bar over the shipped Web
|
||||
// bundles and FixtureApiClient wire. The command creates a real projected
|
||||
// goal in the fixture session; the golden pins the active strip, while the
|
||||
// clear gesture proves the acknowledged tombstone leaves neither stale chrome
|
||||
// nor a duplicate-mutation error.
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { join } from 'node:path'
|
||||
import type { Browser, Page } from 'playwright'
|
||||
import { chromium } from 'playwright'
|
||||
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
||||
import {
|
||||
assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
|
||||
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { newEnglishPage, saveFailureShot } from './support.ts'
|
||||
|
||||
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/goal-bar', import.meta.url))
|
||||
const ACTIVE_EXPECTED = join(SNAPSHOT_DIR, 'active.expected.md')
|
||||
const OVERLAY = fileURLToPath(new URL('./goal-bar.overlay.yml', import.meta.url))
|
||||
const MODE = webSnapshotMode()
|
||||
|
||||
describe('web e2e: goal bar clear convergence', () => {
|
||||
let scaffold: WebScaffold
|
||||
let browser: Browser
|
||||
let page: Page
|
||||
let tripwire: ReturnType<typeof watchConsole>
|
||||
|
||||
beforeAll(async () => {
|
||||
scaffold = await launchWebScaffold({ extraOverlayPath: OVERLAY, welcomeNoticePending: true })
|
||||
browser = await chromium.launch()
|
||||
page = await newEnglishPage(browser)
|
||||
tripwire = watchConsole(page)
|
||||
await page.goto(`${scaffold.baseUrl}?fixture`, { waitUntil: 'load' })
|
||||
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
|
||||
}, 120_000)
|
||||
|
||||
afterAll(async () => {
|
||||
await browser?.close()
|
||||
await scaffold?.close()
|
||||
})
|
||||
|
||||
it('renders one active goal and clears it without exposing a stale error', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-goal-bar-clear'))
|
||||
// Startup reuses the fixture workspace's blank session, keeping this
|
||||
// command independent of alpha's running replay and pending question.
|
||||
const input = page.getByPlaceholder('Describe what you want to build')
|
||||
await input.waitFor({ timeout: 10_000 })
|
||||
await input.fill('/goal guard rapid clear clicks')
|
||||
await input.press('Enter')
|
||||
|
||||
const bar = page.locator('[data-goal-bar]')
|
||||
await bar.waitFor({ timeout: 10_000 })
|
||||
const snapshot = await captureStableAria(page, '[data-goal-bar]', scaffold.workspaceCwd)
|
||||
await compareOrRefreshGolden(ACTIVE_EXPECTED, snapshot, MODE)
|
||||
|
||||
const clear = bar.getByRole('button', { name: 'Clear goal' })
|
||||
await clear.evaluate((button) => {
|
||||
const control = button as HTMLButtonElement
|
||||
control.click()
|
||||
control.click()
|
||||
})
|
||||
await expect.poll(() => page.locator('[data-goal-bar]').count(), { timeout: 10_000 }).toBe(0)
|
||||
expect(await page.getByText(/no current goal/iu).count()).toBe(0)
|
||||
expect(tripwire.pageErrors).toEqual([])
|
||||
expect(tripwire.warnings).toEqual([])
|
||||
}, 60_000)
|
||||
|
||||
it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => {
|
||||
await assertFixtureInventory(SNAPSHOT_DIR, ['active.expected.md'])
|
||||
})
|
||||
})
|
||||
5
apps/web/tests/goal-bar.overlay.yml
Normal file
5
apps/web/tests/goal-bar.overlay.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
# The client-side FixtureApiClient intentionally rejects settings writes, so
|
||||
# this goal-only scenario omits the durable welcome step that would otherwise
|
||||
# cover the page. Onboarding owns separate assembled-browser coverage.
|
||||
- id: ui-settings-general
|
||||
disabled: true
|
||||
@@ -184,7 +184,7 @@ describe('web e2e: navigation & panes over a rich seeded session', () => {
|
||||
it.skipIf(MODE === 'record')('bash and file-path rows leave the default details column closed', async () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-details'))
|
||||
await page.getByRole('tab', { name: 'Chat' }).click()
|
||||
const bashRow = page.locator('[data-sample="bash-global"]').first()
|
||||
const bashRow = page.locator('[data-sample="bash"]').first()
|
||||
await bashRow.waitFor({ timeout: 15_000 })
|
||||
const frame = page.locator('[style*="grid-template-columns"]').first()
|
||||
expect(await frame.getAttribute('data-details-collapsed')).toBe('true')
|
||||
@@ -194,7 +194,7 @@ describe('web e2e: navigation & panes over a rich seeded session', () => {
|
||||
await expect.poll(() => frame.getAttribute('data-details-collapsed'), { timeout: 5_000 }).toBe('true')
|
||||
// The card's own controls are outside the summary row and must not open
|
||||
// details either — the expanded terminal card is read in place.
|
||||
await page.locator('[data-sample="bash-global"] ~ div [data-terminal] [class*="_copyButton_"]').first().click()
|
||||
await page.locator('[data-sample="bash"] ~ div [data-terminal] [class*="_copyButton_"]').first().click()
|
||||
await expect.poll(() => frame.getAttribute('data-details-collapsed'), { timeout: 5_000 }).toBe('true')
|
||||
// Read summaries are host-open file links; they also must not open details.
|
||||
const fileLink = page.locator('[data-variant="read"] button').first()
|
||||
@@ -210,10 +210,10 @@ describe('web e2e: navigation & panes over a rich seeded session', () => {
|
||||
// tool-row interaction): open it if a previous case left it collapsed.
|
||||
// Expanded, the recorded command's own output sits in the message flow,
|
||||
// derived from the logged call/result presentations alone.
|
||||
const bashRow = page.locator('[data-sample="bash-global"]').first()
|
||||
const bashRow = page.locator('[data-sample="bash"]').first()
|
||||
await bashRow.waitFor({ timeout: 15_000 })
|
||||
if (await bashRow.getAttribute('aria-expanded') !== 'true') await bashRow.click()
|
||||
const card = page.locator('[data-sample="bash-global"] ~ div [data-terminal]').first()
|
||||
const card = page.locator('[data-sample="bash"] ~ div [data-terminal]').first()
|
||||
await card.waitFor({ timeout: 15_000 })
|
||||
// Real layout, not jsdom's stub (which computes no geometry at all):
|
||||
// squeeze the output pane below its content width and the line must keep
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('assembled search card', () => {
|
||||
// Wait for chat content to reach the fixture's later turns (the bash sample
|
||||
// is turn 65, the grep card turn 66).
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector('[data-sample="bash-global"]')).not.toBeNull()
|
||||
expect(document.querySelector('[data-sample="bash"]')).not.toBeNull()
|
||||
}, { timeout: 10_000 })
|
||||
// The grep turn's keyed SearchRow composes ToolRow: the card is collapsed
|
||||
// by default, so wait for the summary row, then expand it to reach the card.
|
||||
|
||||
@@ -339,19 +339,19 @@ describe('web e2e: seeded history renders through cold resume', () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-seeded-command-row'))
|
||||
// The Access chip submits `/permission <preset>` — a host command with no
|
||||
// model call, so the settled row renders keylessly over this cold history.
|
||||
// The row copy is the assertion: `permission · preset workspace-write`,
|
||||
// The row copy is the assertion: `permission · preset read-only`,
|
||||
// where neither half repeats the other (the dispatched `/` and its
|
||||
// argument stay out of the title, and the settlement text never restates
|
||||
// the command's own name).
|
||||
await page.getByRole('button', { name: 'Access mode, current: Full access' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Workspace Write' }).click()
|
||||
await page.getByRole('button', { name: 'Access mode, current: Workspace Write' }).waitFor({ timeout: 10_000 })
|
||||
await page.getByRole('button', { name: 'Access mode, current: Workspace Write' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Read Only' }).click()
|
||||
await page.getByRole('button', { name: 'Access mode, current: Read Only' }).waitFor({ timeout: 10_000 })
|
||||
// Scoped to the row itself, so unrelated page text that happens to read
|
||||
// `permission` (a future resident slash menu) cannot satisfy or break it.
|
||||
const row = page.locator('[data-variant="others"]').filter({ hasText: 'preset workspace-write' })
|
||||
const row = page.locator('[data-variant="others"]').filter({ hasText: 'preset read-only' })
|
||||
await expect.poll(() => row.count(), { timeout: 10_000 }).toBe(1)
|
||||
expect(await row.getByText('permission', { exact: true }).count()).toBe(1)
|
||||
expect(await row.getByText('/permission workspace-write', { exact: true }).count()).toBe(0)
|
||||
expect(await row.getByText('/permission read-only', { exact: true }).count()).toBe(0)
|
||||
const snapshot = (await captureStableAria(page, '[class*="centerCol"]', scaffold.workspaceCwd))
|
||||
.split(SEED_ID).join('{{seededId}}')
|
||||
await compareOrRefreshGolden(COMMAND_ROW_EXPECTED, snapshot, MODE)
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
expect(await trigger.getAttribute('aria-expanded')).toBe('true')
|
||||
// General is active by default; Permission, Language and Appearance are functional.
|
||||
expect(await dialog.getByRole('button', { name: '通用设置' }).getAttribute('aria-current')).toBe('true')
|
||||
await dialog.getByRole('button', { name: 'Full access' }).waitFor({ timeout: 10_000 })
|
||||
await dialog.getByRole('button', { name: 'Workspace Write' }).waitFor({ timeout: 10_000 })
|
||||
await expect.poll(() => dialog.getByText('语言', { exact: true }).count(), { timeout: 5_000 }).toBe(1)
|
||||
await expect.poll(() => dialog.getByText('外观', { exact: true }).count(), { timeout: 5_000 }).toBe(1)
|
||||
// Golden of the freshly opened dialog (default zh, General active).
|
||||
@@ -82,12 +82,12 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
onTestFailed(() => saveFailureShot(page, 'web-e2e-settings-permission'))
|
||||
const existing = scaffold.ctx.sessions.create(SessionId('settings-permission-before'))
|
||||
expect(existing.events.find(event => event.type === 'permission/preset')?.data)
|
||||
.toEqual({ preset: 'danger-full-access' })
|
||||
.toEqual({ preset: 'workspace-write' })
|
||||
|
||||
await page.getByRole('button', { name: '设置', exact: true }).click()
|
||||
const dialog = page.getByRole('dialog', { name: '设置' })
|
||||
await dialog.waitFor({ timeout: 10_000 })
|
||||
const selector = dialog.getByRole('button', { name: 'Full access' })
|
||||
const selector = dialog.getByRole('button', { name: 'Workspace Write' })
|
||||
await selector.waitFor({ timeout: 10_000 })
|
||||
await expect.poll(() => selector.isEnabled(), { timeout: 5_000 }).toBe(true)
|
||||
await selector.click()
|
||||
@@ -98,7 +98,7 @@ describe('web e2e: settings modal and General preferences', () => {
|
||||
expect(document).toContain('permission:')
|
||||
expect(document).toContain('defaultPreset: read-only')
|
||||
expect(existing.events.find(event => event.type === 'permission/preset')?.data)
|
||||
.toEqual({ preset: 'danger-full-access' })
|
||||
.toEqual({ preset: 'workspace-write' })
|
||||
|
||||
const created = scaffold.ctx.sessions.create(SessionId('settings-permission-after'))
|
||||
expect(created.events.map(event => [event.type, event.data])).toEqual([
|
||||
|
||||
@@ -10,6 +10,7 @@ import { canonicalPath, writableRoots } from '@deepseek-ai/dsh-sandbox'
|
||||
import type {} from '@deepseek-ai/dsh-tools'
|
||||
import type {} from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import type {} from '@deepseek-ai/dsh-user-approval'
|
||||
import type {} from '@deepseek-ai/dsh-permission'
|
||||
import { launchWebScaffold, type WebScaffold } from './scaffold.ts'
|
||||
|
||||
/**
|
||||
@@ -63,7 +64,7 @@ afterEach(async () => {
|
||||
scaffold = undefined
|
||||
})
|
||||
|
||||
it('assembles the shipped Web catalog and keeps its access default', async () => {
|
||||
it('assembles the shipped Web catalog with the confined access default', async () => {
|
||||
scaffold = await launchWebScaffold()
|
||||
const names = scaffold.ctx.tools.schemas().map(schema => schema.name).sort()
|
||||
expect(names.filter(name => !RIPGREP_TOOLS.includes(name))).toEqual(EXPECTED_TOOLS)
|
||||
@@ -76,8 +77,7 @@ it('assembles the shipped Web catalog and keeps its access default', async () =>
|
||||
expect(writableRoots(scaffold.ctx.sandboxPolicy.resolve({ mode: 'workspace-write' }))).toEqual(
|
||||
expect.arrayContaining([canonicalPath('/tmp'), canonicalPath(tmpdir())]),
|
||||
)
|
||||
// The Web surface keeps its shipped access default; the base's confined one
|
||||
// reaches the TUI. Pinning both keeps a base change from moving Web silently.
|
||||
expect(scaffold.ctx.sandboxPolicy.defaultMode).toBe('danger-full-access')
|
||||
expect(scaffold.ctx.approval.config.policy).toBe('never')
|
||||
expect(scaffold.ctx.sandboxPolicy.defaultMode).toBe('workspace-write')
|
||||
expect(scaffold.ctx.approval.config.policy).toBe('ask')
|
||||
expect(scaffold.ctx.permission.defaultPreset).toBe('workspace-write')
|
||||
}, 120_000)
|
||||
|
||||
@@ -588,7 +588,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY || notReady.length > 0)('web smoke
|
||||
// Bash renders through the third-party sample registration. Match that
|
||||
// exact row: other clickable variants (for example Think disclosure)
|
||||
// may precede the tool call in document order.
|
||||
const toolRow = page.locator('[data-sample="bash-global"]')
|
||||
const toolRow = page.locator('[data-sample="bash"]')
|
||||
await toolRow.waitFor({ timeout: 120_000 })
|
||||
await screen(page, '08-bash-round')
|
||||
expect(await detailsTrack(page)).toBe(0)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
8
apps/web/tests/snapshots/goal-bar/active.expected.md
Normal file
8
apps/web/tests/snapshots/goal-bar/active.expected.md
Normal file
@@ -0,0 +1,8 @@
|
||||
- img
|
||||
- text: Ongoing Goal guard rapid clear clicks
|
||||
- button "Pause goal":
|
||||
- img
|
||||
- button "Edit goal":
|
||||
- img
|
||||
- button "Clear goal":
|
||||
- img
|
||||
@@ -28,7 +28,7 @@
|
||||
- textbox "Describe what you want to build"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
- textbox "Describe what you want to build"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Plan mode on, press to turn off": Plan
|
||||
- button "Select model, current deepseek-v4-flash":
|
||||
- text: deepseek-v4-flash
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current deepseek-v4-flash":
|
||||
- text: deepseek-v4-flash
|
||||
- img
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
- banner:
|
||||
- navigation "Session hierarchy":
|
||||
- button "Reply with a one-sentence description" [disabled]
|
||||
- heading "Reply with a one-sentence description" [level=1]
|
||||
- tablist:
|
||||
- tab "Chat" [selected]
|
||||
- tab "Trajectory"
|
||||
@@ -36,7 +35,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
- img
|
||||
- text: Context injection
|
||||
- img
|
||||
- text: permission preset workspace-write
|
||||
- text: permission preset read-only
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- 'button "Access mode, current: Read Only"': Read Only
|
||||
- button "Select model, current deepseek-v4-flash":
|
||||
- text: deepseek-v4-flash
|
||||
- img
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current deepseek-v4-flash":
|
||||
- text: deepseek-v4-flash
|
||||
- img
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
- img
|
||||
- text: 关闭
|
||||
- text: 权限 选择新会话的默认权限模式
|
||||
- button "Full access":
|
||||
- text: Full access
|
||||
- button "Workspace Write":
|
||||
- text: Workspace Write
|
||||
- img
|
||||
- text: 语言
|
||||
- button "中文":
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
- textbox "Message the agent"
|
||||
- button "Commands":
|
||||
- img
|
||||
- 'button "Access mode, current: Full access"': Full access
|
||||
- 'button "Access mode, current: Workspace Write"': Workspace Write
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function newEnglishPage(browser: Browser, height = 1000): Promise<P
|
||||
/** Fail loud on a stale checkout instead of testing yesterday's bundle. */
|
||||
export function requireDist(): void {
|
||||
if (!existsSync(DIST_INDEX)) {
|
||||
throw new Error('web app dist not built — run `pnpm --filter @deepseek-ai/dsh-frontend build` (pnpm run test:web does this first)')
|
||||
throw new Error('web app dist not built — run `pnpm run build` from the repository root (`pnpm run test:web` does this first)')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"tests/permission-policy-context.e2e.ts",
|
||||
"tests/access-confirmation.e2e.ts",
|
||||
"tests/shipped-composition.e2e.ts",
|
||||
"tests/goal-bar.e2e.ts",
|
||||
"tests/startup-auto-selection.e2e.ts"
|
||||
],
|
||||
"references": [
|
||||
|
||||
Reference in New Issue
Block a user