diff --git a/apps/web/tests/minimal-preset.snapshot.ts b/apps/web/tests/minimal-preset.snapshot.ts index 1ac84dc56a..75aec1c7e3 100644 --- a/apps/web/tests/minimal-preset.snapshot.ts +++ b/apps/web/tests/minimal-preset.snapshot.ts @@ -1,25 +1,41 @@ import { mkdir, writeFile } from 'node:fs/promises' import { join } from 'node:path' import { fileURLToPath } from 'node:url' -import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import type { Browser, Page } from 'playwright' +import { chromium } from 'playwright' +import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest' import type { AgentHandle } from '@deepseek-ai/dsh-agent' import { CallId, createUserMessage } from '@deepseek-ai/dsh-llm' import { SessionId } from '@deepseek-ai/dsh-session' import type {} from '@deepseek-ai/dsh-agent-presets' import type {} from '@deepseek-ai/dsh-system-prompt' -import { assertFixtureInventory, launchWebScaffold, type WebScaffold } from './scaffold.ts' +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/web/minimal-preset', import.meta.url)) const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl') -const PROMPT = 'Reply exactly MINIMAL_PRESET_REQUEST_OK and stop.' +const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md') +const MODE = webSnapshotMode() +const PROMPT = "Use the bash tool to run exactly: printf 'MINIMAL_BASH_CARD_OK\\n'. Then reply exactly MINIMAL_PRESET_REQUEST_OK and stop." describe('minimal agent preset', () => { let scaffold: WebScaffold let agentHandle: AgentHandle let disposeInjectedPrompt: () => void + let browser: Browser | undefined + let page: Page | undefined + let tripwire: ReturnType | undefined beforeAll(async () => { - scaffold = await launchWebScaffold({ replayFixture: FIXTURE, compareReplaySession: true }) + scaffold = await launchWebScaffold({ replayFixture: FIXTURE, compareReplaySession: true, paceMs: 10 }) disposeInjectedPrompt = scaffold.ctx.systemPrompt.section({ name: 'test:injected-prompt', order: 999, @@ -31,10 +47,17 @@ describe('minimal agent preset', () => { agentOptions: { provider: 'deepseek-official', model: 'deepseek-v4-flash' }, setup: agentCtx => scaffold.ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined), }) + agentHandle.agent.followup(createUserMessage({ + content: [{ type: 'text', text: PROMPT }], + source: { kind: 'user' }, + })) + await agentHandle.agent.whenIdle() }) afterAll(async () => { const failures: unknown[] = [] + await page?.close().catch((error: unknown) => failures.push(error)) + await browser?.close().catch((error: unknown) => failures.push(error)) await agentHandle?.dispose().catch((error: unknown) => failures.push(error)) try { disposeInjectedPrompt?.() @@ -47,12 +70,6 @@ describe('minimal agent preset', () => { }) it('sends the exact RL prompt and schemas, then executes the persistent shell and editor', async () => { - agentHandle.agent.followup(createUserMessage({ - content: [{ type: 'text', text: PROMPT }], - source: { kind: 'user' }, - })) - await agentHandle.agent.whenIdle() - const requestHeader = agentHandle.agent.session.requestHeader() if (requestHeader === undefined) throw new Error('the minimal agent issued no model request') expect(agentHandle.agent.session.events.some(event => event.type === 'user/message' @@ -119,10 +136,48 @@ describe('minimal agent preset', () => { `) expect(requestHeader.tools?.toSorted((left, right) => left.name.localeCompare(right.name))) .toEqual(scaffold.ctx.tools.schemas(agentHandle.agent).toSorted((left, right) => left.name.localeCompare(right.name))) + }) + + it.skipIf(MODE === 'record')('expands the completed persistent Bash call in the Web conversation', async () => { + onTestFailed(() => { if (page !== undefined) void saveFailureShot(page, 'web-minimal-persistent-bash-card') }) + browser = await chromium.launch() + page = await newEnglishPage(browser) + tripwire = watchConsole(page) + await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' }) + await page.waitForSelector('[class*="frame"]', { timeout: 30_000 }) + + const groupRow = page.locator('[role="treeitem"]').first() + await groupRow.waitFor({ timeout: 15_000 }) + await groupRow.click() + const sessionRow = page.locator('[role="treeitem"]').nth(1) + await sessionRow.waitFor({ timeout: 10_000 }) + await sessionRow.click() + await page.getByText('MINIMAL_PRESET_REQUEST_OK', { exact: true }).waitFor({ timeout: 15_000 }) + + const row = page.locator('[data-sample="bash"]').first() + await row.waitFor({ timeout: 15_000 }) + await expect.poll(() => row.getAttribute('aria-expanded')).toBe('false') + await row.click() + + await expect.poll(() => row.getAttribute('aria-expanded')).toBe('true') + const call = row.locator('xpath=..') + await call.getByText('IN', { exact: true }).waitFor() + await call.getByText('OUT', { exact: true }).waitFor() + await call.getByText('MINIMAL_BASH_CARD_OK', { exact: true }).waitFor() + await call.getByText(/"command": "printf 'MINIMAL_BASH_CARD_OK/).waitFor() + + const snapshot = await captureStableAria(page, '[class*="centerCol"]', scaffold.workspaceCwd) + await compareOrRefreshGolden(UI_EXPECTED, snapshot, MODE) + expect(tripwire.pageErrors).toEqual([]) + expect(tripwire.warnings).toEqual([]) + }, 60_000) + + it('keeps its snapshot inventory closed', async () => { await assertFixtureInventory(SNAPSHOT_DIR, [ 'session.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', + 'ui.expected.md', ]) }) }) diff --git a/packages/client/ui-tool/README.i18n.yaml b/packages/client/ui-tool/README.i18n.yaml index 3f895f0eb9..8d9b975bd9 100644 --- a/packages/client/ui-tool/README.i18n.yaml +++ b/packages/client/ui-tool/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-tool/README.md -README.md: 2db7d716dc80fbf40a953b217810fb8674e2e98f -README.zh.md: 79ed5befe751b329984c1320144921339fdf3d3f +README.md: 8e3b4290d2402cf0bcb907f9887ddace35b349a6 +README.zh.md: f15899aaedcd52fe7f181c163ab7ab87e9bd20b8 diff --git a/packages/client/ui-tool/README.md b/packages/client/ui-tool/README.md index 2db7d716dc..8e3b4290d2 100644 --- a/packages/client/ui-tool/README.md +++ b/packages/client/ui-tool/README.md @@ -30,7 +30,7 @@ ctx.slots.inject('tool.call.toolview', () => The owner payload is `ToolCallOwnerProps`: `callId`, `toolName`, the frozen `block`, optional `cwd` and `home`, and plain `openFile`/`inspect` callbacks. A Code Dispatch block retains its event's `parentCallId`; the field is absent on a root Session call, so row and Details card models preserve the generic flattened form for descendants without another placement flag. Path summaries relativize to the Session cwd first, then replace a leftover POSIX Host home with `~`; `filePath` and Host open keep the authored filesystem path. The registration receives the normal Session slot runtime share but no React node or Runtime service. -This package currently owns the generic fallback and the built-in shell/pwsh, read, write/edit, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and Code Dispatch presentations. Structured cards derive directly from first-party raw event fields; Host `presentCall`/`presentResult` values never enter the Client. `ui-skill` demonstrates a business-owned registration for `skill`. +This package currently owns the generic fallback and the built-in shell/pwsh, read, write/edit, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and Code Dispatch presentations. Structured cards derive directly from first-party raw event fields; Host `presentCall`/`presentResult` values never enter the Client. Foreground one-shot shell results use terminal cards. Settled persistent-shell results use the expandable generic input/output card because reset and partial-output diagnostics do not always describe one process exit status; background acknowledgements remain collapsed. `ui-skill` demonstrates a business-owned registration for `skill`. Card-specific limits and fallback rules remain in the owning [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md), [diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md), [read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.md), [search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md), and [web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md) notes. diff --git a/packages/client/ui-tool/README.zh.md b/packages/client/ui-tool/README.zh.md index 79ed5befe7..f15899aaed 100644 --- a/packages/client/ui-tool/README.zh.md +++ b/packages/client/ui-tool/README.zh.md @@ -30,7 +30,7 @@ ctx.slots.inject('tool.call.toolview', () => owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、冻结的 `block`、可选 `cwd` 与 `home`,以及普通的 `openFile`、`inspect` 回调。Code Dispatch block 保留其事件已有的 `parentCallId`;root Session call 没有该字段,因此 row 与 Details card model 无需另一项 placement 标志即可让 descendant 保持 generic 压平形态。路径摘要先相对 Session cwd 缩短,再把剩余的 POSIX Host home 写成 `~`;`filePath` 与 Host 打开仍使用作者给出的文件系统路径。注册项会收到常规 Session slot runtime share,但不会收到 React node 或 runtime service。 -本包当前拥有 generic fallback,以及 shell/pwsh、read、write/edit、running `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 和 Code Dispatch 的内置展示。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall`/`presentResult` 值不会进入 Client。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。 +本包当前拥有 generic fallback,以及 shell/pwsh、read、write/edit、running `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 和 Code Dispatch 的内置展示。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall`/`presentResult` 值不会进入 Client。前台一次性 shell 结果使用 terminal 卡片。已完成的持久 shell 结果使用可展开的 generic 输入/输出卡片,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;后台启动回执保持折叠。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。 各类卡片的上限与 fallback 规则仍由对应的 [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.zh.md)、[diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.zh.md)、[read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.zh.md)、[search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.zh.md) 和 [web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.zh.md) Agent Note 负责。 diff --git a/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts b/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts index 1956fe232e..159120f438 100644 --- a/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts +++ b/packages/client/ui-tool/src/client/tool/models/terminal-card-model.ts @@ -205,6 +205,20 @@ function shellCall(name: string, args: Record): ShellCall | nul } } +/** + * Identify a settled root call from the persistent Bash or PowerShell tool. + * Its result stays on the generic input/output path because the persistent + * shell can report resets and partial output without one process exit status. + * @param block - running or settled Tool block. + * @returns whether the block is a settled persistent-shell call. + */ +export function isSettledPersistentShellCall(block: ToolCallBlock): boolean { + if (!('kind' in block) || block.parentCallId !== undefined) return false + const parsed = parsedToolCall(block) + if (parsed === null) return false + return shellCall(parsed.name, parsed.args)?.persistent === true +} + interface TerminalSendCall { kind: 'terminal-send' text: string @@ -244,7 +258,8 @@ function parseExitStatus(text: string): { output: string; exitCode?: number; sig * Derive terminal props for supported root shell and terminal-send calls. * Standard shell results parse their final status marker; persistent shell * results, background calls, errors, malformed input, or child dispatches use - * the generic path. + * the generic path. {@link isSettledPersistentShellCall} lets that generic + * persistent result remain expandable without inventing one process status. * @param block - running or settled Tool block. * @param sessionCwd - session workspace root used to resolve workdir. * @returns locale-neutral terminal-card data, or null for the generic path. diff --git a/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx b/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx index 321b156078..9490d92ec5 100644 --- a/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx +++ b/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.tsx @@ -7,7 +7,11 @@ import { import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots' import type { ToolCallViewProps } from '../../contract/slots.ts' import { - localizeTerminalCardModel, terminalBlockLabels, terminalCardModel, terminalFailed, + isSettledPersistentShellCall, + localizeTerminalCardModel, + terminalBlockLabels, + terminalCardModel, + terminalFailed, } from '../models/terminal-card-model.ts' import { toolRowModel, type ToolRowState } from '../models/tool-call-model.ts' import { CONVERSATION_NS as NS } from '../../locale.ts' @@ -49,13 +53,13 @@ export function BashRow({ toolName, block, sessionId, useSessions, inspect, t }: : model.state const status = stateStatus(state, t) const [expanded, setExpanded] = useState(false) - // Execution failures (for example cancellation before the process reports a - // terminal result) use the generic body. Keep their recorded args and - // full error reachable instead of collapsing the row to the first line. - const genericError = terminal === null - && model.state === 'error' + // Execution failures and persistent-shell results have no terminal card. + // Keep their recorded args and complete output reachable through the generic + // body; background acknowledgements and malformed calls remain collapsed. + const genericBody = terminal === null + && (model.state === 'error' || isSettledPersistentShellCall(block)) && (model.body !== null || model.output !== null) - const expandable = terminal !== null || genericError + const expandable = terminal !== null || genericBody const open = expanded && expandable const failureLine = model.state === 'error' ? model.errorSummary : null const toggleExpand = () => { @@ -123,7 +127,7 @@ export function BashRow({ toolName, block, sessionId, useSessions, inspect, t }: {model.output !== null && (
{t('row.output')} - + {model.output}
diff --git a/packages/client/ui-tool/tests/terminal-card.client.spec.tsx b/packages/client/ui-tool/tests/terminal-card.client.spec.tsx index 8d57084ab3..54ed4542cc 100644 --- a/packages/client/ui-tool/tests/terminal-card.client.spec.tsx +++ b/packages/client/ui-tool/tests/terminal-card.client.spec.tsx @@ -442,11 +442,24 @@ describe('BashRow terminal card', () => { expect(view.queryByText('List files')).toBeNull() }) - it('keeps the command summary for a persistent shell with no description', () => { + it('expands a settled persistent shell through the generic input/output card', () => { const view = render() + const row = view.container.querySelector('[data-sample="bash"]')! expect(view.getByText('ls -la')).toBeTruthy() + expect(row.getAttribute('role')).toBe('button') + expect(row.getAttribute('aria-expanded')).toBe('false') + + fireEvent.click(row) + + expect(row.getAttribute('aria-expanded')).toBe('true') + expect(view.getByText('输入')).toBeTruthy() + expect(view.getByText('输出')).toBeTruthy() + expect(view.getByText(/"command": "ls -la"/)).toBeTruthy() + expect(view.container.querySelector('[class*="_ioText_"][data-error]')).toBeNull() + expect(view.container.querySelectorAll('[class*="_ioText_"]')[1]?.textContent) + .toBe('a.ts b.ts\nc.ts d.ts\n') }) it('a non-terminal bash call (background start) renders the summary row alone', () => { diff --git a/snapshots/web/minimal-preset/session.jsonl b/snapshots/web/minimal-preset/session.jsonl index 6553fa3faa..84d24d9378 100644 --- a/snapshots/web/minimal-preset/session.jsonl +++ b/snapshots/web/minimal-preset/session.jsonl @@ -1,20 +1,30 @@ -{"type":"session","version":0,"id":"{{session:1}}","createdAt":1787520042622,"cwd":"{{cwd}}","agentPreset":"minimal"} +{"type":"session","version":0,"id":"{{session:1}}","createdAt":1787660564247,"cwd":"{{cwd}}","agentPreset":"minimal"} {"type":"permission/preset","data":{"preset":"workspace-write"}} {"type":"sandbox/mode","data":{"mode":"workspace-write"}} {"type":"approval/policy","data":{"policy":"ask"}} -{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply exactly MINIMAL_PRESET_REQUEST_OK and stop."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"}]}} +{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Use the bash tool to run exactly: printf 'MINIMAL_BASH_CARD_OK\\n'. Then reply exactly MINIMAL_PRESET_REQUEST_OK and stop."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"}]}} {"type":"turn/start","data":{"turn":1}} {"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}} {"type":"step/start","data":{"turn":1,"step":1}} -{"type":"user/message","data":{"content":[{"type":"text","text":"Reply exactly MINIMAL_PRESET_REQUEST_OK and stop."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"} -{"type":"session/title","data":{"title":"Reply exactly MINIMAL_PRESET_REQUEST_OK","messageSeqs":[7],"source":{"kind":"fallback"}}} +{"type":"user/message","data":{"content":[{"type":"text","text":"Use the bash tool to run exactly: printf 'MINIMAL_BASH_CARD_OK\\n'. Then reply exactly MINIMAL_PRESET_REQUEST_OK and stop."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"} +{"type":"session/title","data":{"title":"Use the bash tool to","messageSeqs":[7],"source":{"kind":"fallback"}}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}} -{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}} -{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"MINIMAL_PRESET_REQUEST_OK"}}} -{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"MINIMAL_PRESET_REQUEST_OK"}}}} +{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}} +{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"minimal-bash-card","name":"bash","argumentsDelta":"{\"command\":\"printf 'MINIMAL_BASH_CARD_OK\\\\n'\"}"}}} +{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"minimal-bash-card","name":"bash","arguments":"{\"command\":\"printf 'MINIMAL_BASH_CARD_OK\\\\n'\"}"}}}} {"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}}} -{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"MINIMAL_PRESET_REQUEST_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:2}}"},"usage":{"inputTokens":10,"outputTokens":4}},"sourceEventSeqs":[11,12,13,14,15],"surfaceOp":"append"} +{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"minimal-bash-card","name":"bash","arguments":"{\"command\":\"printf 'MINIMAL_BASH_CARD_OK\\\\n'\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:2}}"},"usage":{"inputTokens":10,"outputTokens":4}},"sourceEventSeqs":[11,12,13,14,15],"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"minimal-bash-card","name":"bash","arguments":"{\"command\":\"printf 'MINIMAL_BASH_CARD_OK\\\\n'\"}"}} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"minimal-bash-card"},"content":[{"type":"tool-result","toolCallId":"minimal-bash-card","content":[{"type":"text","text":"MINIMAL_BASH_CARD_OK"}],"isError":false}],"role":"user","id":"{{message:3}}"}},"sourceEventSeqs":[17],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} +{"type":"step/start","data":{"turn":1,"step":2}} +{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}} +{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"MINIMAL_PRESET_REQUEST_OK"}}} +{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"MINIMAL_PRESET_REQUEST_OK"}}}} +{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}}} +{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"MINIMAL_PRESET_REQUEST_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":4}},"sourceEventSeqs":[21,22,23,24,25],"surfaceOp":"append"} +{"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/web/minimal-preset/ui.expected.md b/snapshots/web/minimal-preset/ui.expected.md new file mode 100644 index 0000000000..2de9eef677 --- /dev/null +++ b/snapshots/web/minimal-preset/ui.expected.md @@ -0,0 +1,43 @@ +- banner: + - navigation "Session hierarchy": + - button "Use the bash tool to" [disabled] + - img + - text: Minimal mode + - button "Session log": + - text: Session log + - img + - tablist: + - tab "Chat" [selected] + - tab "Trajectory" +- button "System prompt": + - img + - img + - text: System prompt +- text: "Use the bash tool to run exactly: printf 'MINIMAL_BASH_CARD_OK\\n'. Then reply exactly MINIMAL_PRESET_REQUEST_OK and stop. {{clock}}" +- button "Copy": + - img +- button "Bash printf 'MINIMAL_BASH_CARD_OK\\n'" [expanded]: + - img + - text: Bash printf 'MINIMAL_BASH_CARD_OK\n' +- text: "IN { \"command\": \"printf 'MINIMAL_BASH_CARD_OK\\\\n'\" } OUT MINIMAL_BASH_CARD_OK" +- button "Inspect" +- paragraph: MINIMAL_PRESET_REQUEST_OK +- button "Copy": + - img +- button "Good response": + - img +- button "Bad response": + - img +- button "Branch into a new conversation": + - img +- text: {{clock}} Ran for {{duration}} TTFT {{duration}} {{throughput}} tok/s +- textbox "Message the agent" +- button "Commands": + - img +- 'button "Access mode, current: Workspace Write"': Workspace Write +- button "Select model, current DeepSeek-V4-Flash": + - text: DeepSeek-V4-Flash + - img +- button "0% of context used" +- button "Send message" [disabled] +- text: 1 turns · 2 steps LLM {{duration}} · Tool call {{duration}} TTFT avg {{duration}} · {{throughput}} tok/s Cache hit 0% Input 20 tok · Output 8 tok