fix(web): orient agents to the running GUI

This commit is contained in:
NI0317
2026-07-28 17:20:20 +08:00
parent 15ed8e5d55
commit 568866d5a8
11 changed files with 131 additions and 9 deletions

View File

@@ -9,20 +9,22 @@
// Record: DSH_SNAPSHOT=record rewrites session.jsonl, then a keyless
// DSH_SNAPSHOT=refresh regenerates ui.expected.md.
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
import {
assertFixtureInventory, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspace, saveFailureShot } from './support.ts'
import { connectFreshWorkspace, REPO_ROOT, saveFailureShot } from './support.ts'
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/fresh-round-trip', import.meta.url))
const FIXTURE = fileURLToPath(new URL('./snapshots/fresh-round-trip/session.jsonl', import.meta.url))
const UI_EXPECTED = fileURLToPath(new URL('./snapshots/fresh-round-trip/ui.expected.md', import.meta.url))
const SYSTEM_PROMPT_EXPECTED = fileURLToPath(new URL('./snapshots/fresh-round-trip/system-prompt.expected.md', import.meta.url))
const MODE = webSnapshotMode()
// The scenario's one drive prompt. Record sends it; replay asserts the
@@ -35,6 +37,7 @@ describe('web e2e: fresh round trip through the real assembly', () => {
let browser: Browser
let page: Page
let tripwire: ReturnType<typeof watchConsole>
let settledSessionId: SessionId | undefined
const sessionEvents: SessionEvent[] = []
beforeAll(async () => {
@@ -69,11 +72,24 @@ describe('web e2e: fresh round trip through the real assembly', () => {
await input.fill(PROMPT)
await input.press('Enter')
const sessionId = await settled
settledSessionId = sessionId
if (MODE === 'record') {
await recordFixture(scaffold, sessionId, FIXTURE)
}
}, 200_000)
it('records the Web surface, source checkout, and session cwd in the request header', async () => {
if (settledSessionId === undefined) throw new Error('the drive turn did not publish a session id')
const agent = scaffold.ctx.agents.get(settledSessionId)
if (agent === undefined) throw new Error(`the settled Web agent ${settledSessionId} is no longer live`)
const system = agent.session.requestHeader()?.system
if (system === undefined) throw new Error('the settled Web request has no system prompt')
const prefix = system.split('\n\n').slice(0, 4).join('\n\n')
.split(REPO_ROOT).join('{{sourceRoot}}')
.split(join(scaffold.workspaceCwd, 'workspace')).join('{{cwd}}')
await compareOrRefreshGolden(SYSTEM_PROMPT_EXPECTED, prefix, MODE)
})
it.skipIf(MODE === 'record')('rendered the settled turn: markdown, tool row, composer restore', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-round-trip-settled'))
// Browser settled-poll after host completion (host strictly precedes render).
@@ -129,6 +145,6 @@ describe('web e2e: fresh round trip through the real assembly', () => {
it.skipIf(MODE === 'record')('stayed clean: no pageerrors, no reconnect self-healing, no server errors', async () => {
expect(tripwire.pageErrors).toEqual([])
expect(tripwire.warnings).toEqual([])
await assertFixtureInventory(SNAPSHOT_DIR, ['session.jsonl', 'ui.expected.md'])
await assertFixtureInventory(SNAPSHOT_DIR, ['session.jsonl', 'system-prompt.expected.md', 'ui.expected.md'])
})
})

View File

@@ -44,6 +44,7 @@ import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
// Empty type imports carry the httpServer/agents/sessionPersistence Context merges.
import type {} from '@deepseek-ai/dsh-host-webserver'
import type {} from '@deepseek-ai/dsh-agent'
import { installWebPromptContext } from '../../cli/src/web.ts'
import { DIST_INDEX, REPO_ROOT, requireDist } from './support.ts'
/** Snapshot mode for the lane, from $DSH_SNAPSHOT (same vocabulary as the ACP/TUI suites). */
@@ -202,6 +203,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
})
await ctx.loader.await()
assertEntriesLoaded(ctx, 'web e2e scaffold')
installWebPromptContext(ctx, REPO_ROOT)
const boundPort = ctx.get('httpServer')?.port
if (boundPort === undefined) {
throw new Error('web e2e scaffold: httpServer service missing after settled boot')

View File

@@ -0,0 +1,7 @@
You are an AI agent powered by the DeepSeek Harness SDK.
Your own source code is the checkout at {{sourceRoot}}; you can read it there to learn how dsh works and how to extend it.
You are interacting with the user through the DeepSeek Harness Web GUI. When the user refers to "this page", "this GUI", or "this app" without naming another target, they mean this GUI. The browser provides no implicit DOM, route, or screenshot context.
You are a coding agent powered by the deepseek-v4-flash model. Your working directory is {{cwd}}.