Merge pull request #656 from deepseek-harness/web-e2e-navigation

test(web): navigation & panes e2e — tabs, details column, sidebar search
This commit is contained in:
Tianyi Cui
2026-07-26 11:54:30 +08:00
committed by GitHub
11 changed files with 666 additions and 2 deletions
@@ -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
2026-07-24-web-gui-browser-e2e-lane.md: 78a01652ef35f371c35c059033cd28f29f5bf94e
2026-07-24-web-gui-browser-e2e-lane.zh.md: b5fb29c63ab10352d50ef6ba9ce7b65e92989387
2026-07-24-web-gui-browser-e2e-lane.md: 88730cdecf527ece8033ddab1151afcbc6edd83f
2026-07-24-web-gui-browser-e2e-lane.zh.md: 9850023a49a860a8f4bbdacc8c48fc389ec77210
@@ -47,6 +47,8 @@ The typecheck plane split is structural: the three files that boot the host spin
3. **`live-interactions`** — one tool-free recorded turn serves three replay-only scenarios through override sidecars whose CONTENT is authored in the spec and minted as a per-run file in a spec-owned temp dir (the derived success entry for the retry append is re-derived from the fixture via `deriveReplayScript`, never copied into a committed sidecar). Cancel: a `{ patches }` `hang` with a `readyFile` marker — the marker's existence proves the stream is parked mid-turn before the test clicks Stop, making mid-stream cancellation deterministic by construction (`turn/end` reason `aborted`, composer re-enabled). AUTH error: a pre-chunk `throw` outside llm-retry's retryable set (`turn/end` reason `error`, zero `llm/retry` events, composer recovers). SERVER retry: `throw` at call 0 + the fixture's own success appended at 1, proving llm-retry end-to-end in the browser via the durable `llm/retry` record (`request/header` logs only on change, so attempt count is invisible there).
4. **`question-composer`** — the shipped composition's resident `ask_user_question` takeover: a recorded turn blocks mid-step on the real userInteraction seam, the composer (`[data-question-key]`) renders in the browser, the test answers through it (the ONE sanctioned place a drive step reacts to model content: the turn cannot complete without the answer, in record and replay alike), and the tool result carries the chosen label. Golden: the composer's stable waiting state.
5. **`steering`** — mid-turn steer while the question composer blocks the step (the deterministic mid-turn window; no timing dependence). The composer locks while running, so the steer POSTs `session.prompt` `mode:'steer'` from the page over the same same-origin `/api` wire the client uses (`TODO(web-steer-composer)`: drive a composer gesture once one exists); everything downstream is product — gateway → `Agent.steer` → step-boundary drain → durable `steering/message` → SSE → badged interjection bubble. Record-mode fixture honesty: the recording is rejected unless the live model's final reply obeys an instruction only the steering message carries.
6. **`navigation-panes`** — one rich two-turn seed (turn 1: bash + two parallel reads in one assistant message; turn 2: a markdown-heavy reply) rendered cold through the seeded-history pattern (zero model calls), serving four surfaces: sidebar search (client-side title filter — asserted only after the durable title lands with the attach baseline, because a cold `SessionSummary` carries no title and search matches the `displayTitle` the user sees; negative query empties the tree, positive narrows, clear restores), the Trajectory tab (turn sections + the step group's tool mix plus the view-area aria golden), the Waterfall tab (span stats + one lane per span — the P-I fold counts a turn-0 prologue span because only assistant/steering nodes carry a turn number, pinned as-is), and the details column (the bash toolview row routes click to openDetails; open/closed is asserted on the frame's `data-details-collapsed` attribute because close collapses the grid column to width 0 without unmounting the subtree).
7. **`lifecycle-chrome`** — one tiny recorded text turn drives three whole-page concerns. Workspace flow over the real wire: the empty-state hero's first send materializes a real Workspace + Session (the jsdom `workspace-flow.snapshot.ts` suite pins this state machine over the fixture client; this scenario pins it through HTTP RPC + SSE + the gateway), proven durably by the session header's cwd being the create-by-name target `<workspaceRoot>/workspace`, plus the hero waiting-state aria golden. Reload recovery: collapse the sidebar (persisted `dsh.layout.panels`), `page.reload`, and the surface comes back whole from persistence alone — layout collapsed, selection restored (`dsh.sessions.current`), the recorded turn re-rendered from `session.history` with zero model calls (the drained replay cursor makes any stray request fail loud at close). Dark mode: no product control flips the theme yet, so the scenario drives the ThemeService's entire DOM contract — the `body[data-ds-dark-theme]` attribute — and pins the shipped cascade: the alias token flips, a painted surface repaints, and removing the attribute restores the light sample exactly (`TODO(web-theme-gesture)` upgrades to a real settings control); per the scope ruling there is no theme/layout golden (aria is color-blind).
### CI stance
@@ -47,6 +47,8 @@ Web GUI 以一条真实组装链交付——chromium 页面 → client 插件 bu
3. **`live-interactions`**——一段不含工具调用的已录轮次经覆写 sidecar 承载三个仅回放的场景:sidecar 的内容本身写在 spec 里,每次运行时在 spec 自有的临时目录中生成文件(重试追加所用的派生成功条目经 `deriveReplayScript` 从 fixture 重新派生,绝不复制进已提交的 sidecar)。取消:一个带 `readyFile` 标记的 `{ patches }` `hang`——标记文件的存在证明流在测试点击 Stop 之前已停驻在轮次中途,使流中取消按构造即确定(`turn/end` 原因为 `aborted`,输入框重新启用)。AUTH 错误:一次落在 llm-retry 可重试集合之外的分片前 `throw``turn/end` 原因为 `error`,零条 `llm/retry` 事件,输入框恢复可用)。SERVER 重试:第 0 次调用 `throw` + 在第 1 次调用处追加 fixture 自身的成功条目,凭持久的 `llm/retry` 记录在浏览器中端到端证明 llm-retry(`request/header` 仅在变化时记录,因此尝试次数在那里不可见)。
4. **`question-composer`**——已交付组合中常驻的 `ask_user_question` 接管:一段已录轮次在真实的 userInteraction seam 上阻塞于步骤中途,提问输入框(`[data-question-key]`)在浏览器中渲染,测试经它作答(这是驱动步骤对模型内容作出反应的唯一获准之处:没有这个回答,轮次无法完成,record 与 replay 皆然),工具结果携带所选的 label。预期输出:提问输入框稳定的等待态。
5. **`steering`**——在提问输入框阻塞该步骤时做轮次中途 steering(中途引导),此即确定性的轮次中途窗口,不依赖任何时序。输入框在运行期间锁定,因此这一 steer 由页面经客户端所用的同一条同源 `/api` wire POST `session.prompt` `mode:'steer'``TODO(web-steer-composer)`:待有输入框手势后改为驱动它);下游的一切都是产品路径——gateway → `Agent.steer` → 步骤边界排空 → 持久的 `steering/message` → SSE → 带徽标的插话气泡。record 模式的 fixture 诚实性:除非真实模型的最终回复遵循了一条只有 steering 消息才携带的指令,否则该次录制被拒绝。
6. **`navigation-panes`**——一份内容丰富的双轮次种子(轮次 1:同一条 assistant 消息内的 bash + 两次并行 read;轮次 2:一段 markdown 密集的回复)经 seeded-history 模式冷渲染(零模型调用),承载四个表面:侧栏搜索(客户端标题过滤;仅在持久的标题随 attach 基线一同到达后才断言,因为冷的 `SessionSummary` 不携带标题,而搜索匹配的是用户所见的 `displayTitle`;反例查询清空整棵树,正例查询收窄,清除后复原)、Trajectory 标签页(轮次分节 + 步骤组的工具构成,外加视图区 aria 预期输出)、Waterfall 标签页(span 统计 + 每个 span 一条泳道;只有 assistant/steering 节点携带轮次编号,因此 P-I 折叠会将一个轮次 0 的序幕 span 计入,按原样钉住)与详情列(bash 工具视图行把点击路由到 openDetails;打开/关闭状态断言在 frame 的 `data-details-collapsed` 属性上,因为关闭把网格列收缩到宽度 0 而不卸载子树)。
7. **`lifecycle-chrome`**——一段极小的已录纯文本轮次驱动三个整页关注点。真实 wire 上的 Workspace 动线:空态 hero 的首次发送物化出真实的 Workspace + Sessionjsdom 的 `workspace-flow.snapshot.ts` 套件基于 fixture 客户端钉住这一状态机;本场景则经 HTTP RPC + SSE + gateway 钉住它),其持久证据是会话头部的 cwd 恰为按名创建的目标 `<workspaceRoot>/workspace`,外加 hero 等待态的 aria 预期输出。重新加载恢复:折叠侧栏(持久化于 `dsh.layout.panels`),`page.reload`,整个表面纯凭持久化完整归来——布局保持折叠,选中项恢复(`dsh.sessions.current`),已录轮次从 `session.history` 重新渲染且零模型调用(已耗尽的回放游标使任何离群请求都在 close 时大声失败)。暗色模式:产品尚无任何控件能切换主题,因此本场景驱动 ThemeService 的整个 DOM 契约(即 `body[data-ds-dark-theme]` 属性),并钉住已交付的级联:alias token 翻转,某个实际绘制的表面重绘,移除该属性则精确还原亮色采样值(`TODO(web-theme-gesture)`:待有真实设置控件后升级为驱动它);按范围裁定,主题/布局不设预期输出(aria 感知不到颜色)。
### CI 立场
+152
View File
@@ -0,0 +1,152 @@
// Web e2e scenarios: lifecycle & chrome — the workspace-aware first-send
// flow over the real wire, reload recovery, and the dark-mode token cascade.
// One tiny recorded turn (text-only) drives the whole spec: the empty-state
// hero materializes a real Workspace + Session on first send (the jsdom
// workspace-flow suite pins the object-layer state machine over the fixture
// client; THIS spec pins the same flow through HTTP RPC + SSE + the host
// gateway), reload replays everything from the log (zero further model
// calls), and the theme scenario proves the shipped dark palette actually
// cascades: attribute -> alias token flip -> painted surface change. Per the
// lane's scope ruling there is no theme/layout golden (aria is color-blind);
// the hero's waiting state gets the one golden here.
import { readFile } from 'node:fs/promises'
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 type { SessionEvent } from '@deepseek-ai/dsh-session'
import {
assertFixtureInventory, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold,
} from './scaffold.ts'
import { saveFailureShot } from './support.ts'
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 MODE = webSnapshotMode()
const PROMPT = 'Reply with the single word LIGHTHOUSE and stop.'
describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
let tripwire: ReturnType<typeof watchConsole>
const sessionEvents: SessionEvent[] = []
beforeAll(async () => {
scaffold = await launchWebScaffold(MODE === 'record' ? {} : { replayFixture: FIXTURE, paceMs: 15 })
scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { sessionEvents.push(event) })
browser = await chromium.launch()
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
tripwire = watchConsole(page)
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
}, 120_000)
afterAll(async () => {
await browser?.close()
await scaffold?.close()
})
it('sends the first prompt from the empty-state hero (all modes)', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-lifecycle-send'))
if (MODE !== 'record') {
expect(fixtureUserPrompts(await readFile(FIXTURE, 'utf8'))).toEqual([PROMPT])
}
// The blank frame renders the hero, not the resident composer: the
// headline plus the guidance placeholder are the empty state's anchors.
await expect.poll(() => page.getByText("Let's start building", { exact: false }).count(), { timeout: 15_000 }).toBe(1)
const input = page.locator('textarea').first()
await input.waitFor({ timeout: 10_000 })
if (MODE !== 'record') {
// Golden of the hero's stable waiting state (captured before any send;
// the conversation-region goldens belong to the other scenarios).
const snapshot = await captureStableAria(page, '[class*="frame"]', scaffold.workspaceCwd)
await compareOrRefreshGolden(HERO_EXPECTED, snapshot, MODE)
}
const settled = scaffold.whenTurnSettled()
await input.fill(PROMPT)
await input.press('Enter')
const sessionId = await settled
if (MODE === 'record') {
await recordFixture(scaffold, sessionId, FIXTURE)
}
}, 200_000)
it.skipIf(MODE === 'record')('materialized a real Workspace and Session over the wire', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-lifecycle-materialize'))
// Browser: the sidebar tree now carries the auto-created workspace group
// with its one session, and the opened session is the selected row.
await expect.poll(() => page.getByText('1 session', { exact: true }).count(), { timeout: 15_000 }).toBeGreaterThanOrEqual(1)
await expect.poll(() => page.locator('[role="treeitem"][aria-selected="true"]').count(), { timeout: 10_000 }).toBe(1)
await expect.poll(() => page.getByText('LIGHTHOUSE', { exact: true }).count(), { timeout: 15_000 }).toBeGreaterThanOrEqual(1)
// Host: the session's durable header cwd is the workspace flow's
// create-by-name target (<workspaceRoot>/workspace, the composer's
// default draft name) — the proof the send went through workspace
// materialization rather than a bare default-cwd session.
const cwds = scaffold.ctx.sessions.list().map(session => session.header.cwd)
expect(cwds).toEqual([join(scaffold.workspaceCwd, 'workspace')])
const turnEnds = sessionEvents.filter(e => e.type === 'turn/end')
expect(turnEnds).toHaveLength(1)
expect((turnEnds[0] as SessionEvent & { data: { reason: { kind: string } } }).data.reason.kind).toBe('completed')
}, 60_000)
it.skipIf(MODE === 'record')('recovers the whole surface across a reload from the log alone', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-lifecycle-reload'))
// Fold a layout preference into the same reload: collapse the sidebar
// (persisted under dsh.layout.panels) before reloading.
await page.getByRole('button', { name: 'Collapse sidebar' }).click()
await expect.poll(() => page.getByRole('button', { name: 'Open sidebar' }).count(), { timeout: 10_000 }).toBe(1)
await page.reload({ waitUntil: 'load' })
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
// Layout persisted: the sidebar comes back collapsed.
await expect.poll(() => page.getByRole('button', { name: 'Open sidebar' }).count(), { timeout: 10_000 }).toBe(1)
// Selection persisted (dsh.sessions.current) and history replayed: the
// recorded turn re-renders from session.history with zero model calls —
// the replay cursor was fully consumed before the reload, so any stray
// request would fail the scenario loudly at close().
await expect.poll(() => page.getByText('LIGHTHOUSE', { exact: true }).count(), { timeout: 15_000 }).toBeGreaterThanOrEqual(1)
// Expand back and confirm the tree still lists the materialized session.
await page.getByRole('button', { name: 'Open sidebar' }).click()
await expect.poll(() => page.locator('[role="treeitem"][aria-selected="true"]').count(), { timeout: 10_000 }).toBe(1)
expect(tripwire.pageErrors).toEqual([])
}, 90_000)
it.skipIf(MODE === 'record')('cascades the dark theme from the body attribute to painted surfaces', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-lifecycle-dark'))
// No product control flips the theme yet — the ThemeService's whole DOM
// contract is the body[data-ds-dark-theme] attribute, so the scenario
// drives exactly that seam and pins the shipped stylesheet's cascade.
// TODO(web-theme-gesture): drive a real settings control once one exists.
const sample = async (): Promise<{ token: string; sidebarBg: string; bodyBg: string }> =>
await page.evaluate(() => {
const sidebar = document.querySelector('[class*="sidebar"], [class*="rail"]') ?? document.body
return {
token: getComputedStyle(document.body).getPropertyValue('--dsw-alias-bg-base').trim(),
sidebarBg: getComputedStyle(sidebar).backgroundColor,
bodyBg: getComputedStyle(document.body).backgroundColor,
}
})
const light = await sample()
await page.evaluate(() => { document.body.setAttribute('data-ds-dark-theme', '') })
const dark = await sample()
// The alias token itself must flip — the cascade's root fact.
expect(dark.token).not.toBe(light.token)
// And a real painted surface must consume it (not just variables in a
// void): at least one of the sampled backgrounds repaints.
expect(dark.sidebarBg !== light.sidebarBg || dark.bodyBg !== light.bodyBg).toBe(true)
// Removing the attribute restores the light values exactly (the palettes
// live in one stylesheet; activation is attribute-only by design).
await page.evaluate(() => { document.body.removeAttribute('data-ds-dark-theme') })
const restored = await sample()
expect(restored).toEqual(light)
expect(tripwire.pageErrors).toEqual([])
}, 60_000)
it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => {
await assertFixtureInventory(SNAPSHOT_DIR, ['session.jsonl', 'hero.expected.md'])
})
})
+179
View File
@@ -0,0 +1,179 @@
// Web e2e scenarios: navigation & panes — the view tabs (Trajectory /
// Waterfall), the details column, and sidebar search, all over ONE rich
// two-turn seeded fixture rendered purely from the log (the seeded-history
// pattern: zero model calls in replay, so every surface here is the client
// fold + host history RPC, not replay binding). The seed is recorded live
// under the standard discipline: turn 1 produces a bash call plus two
// parallel reads in one assistant message (tool-call density for the
// trajectory/waterfall lanes and a details-capable bash row), turn 2 a
// markdown-rich reply (a second turn so the waterfall has two lanes).
import { mkdir, readFile, writeFile } from 'node:fs/promises'
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 { parseSessionLog } from '@deepseek-ai/dsh-llm-replay'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import {
assertFixtureInventory, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
launchWebScaffold, recordFixture, seedSession, watchConsole, webSnapshotMode, type WebScaffold,
} from './scaffold.ts'
import { saveFailureShot } from './support.ts'
const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/navigation-panes', import.meta.url))
const SEED = join(SNAPSHOT_DIR, 'seed.jsonl')
const TRAJECTORY_EXPECTED = join(SNAPSHOT_DIR, 'trajectory.expected.md')
const MODE = webSnapshotMode()
const SEED_ID = 'navigation-panes-web-e2e'
// Turn 1 leads with a distinctive word: the session-title fallback takes the
// first words of the first message, so the sidebar-search scenario has a
// known-matching query ('navscenario') without depending on a live title call.
const PROMPT_TURN1 = 'NavScenario: first run bash to print exactly NAVIGATION_OK, then read nav-a.md and nav-b.md using two read calls in ONE assistant message, then reply with the single word FIRST_DONE and stop.'
const PROMPT_TURN2 = 'Reply in markdown with: a level-2 heading "Navigation Summary", a bulleted list of exactly two items, and a fenced code block containing echo WATERFALL. Then stop.'
describe('web e2e: navigation & panes over a rich seeded session', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
let tripwire: ReturnType<typeof watchConsole>
beforeAll(async () => {
scaffold = await launchWebScaffold({})
// The workspace-aware flow runs sessions in <workspaceRoot>/workspace;
// the read targets must live in that session cwd (pre-creation is safe:
// create-by-name adopts an existing directory).
const sessionCwd = join(scaffold.workspaceCwd, 'workspace')
await mkdir(sessionCwd, { recursive: true })
await writeFile(join(sessionCwd, 'nav-a.md'), '# alpha nav\n')
await writeFile(join(sessionCwd, 'nav-b.md'), '# beta nav\n')
if (MODE !== 'record') {
const raw = await readFile(SEED, 'utf8')
expect(fixtureUserPrompts(raw), 'seed fixture must carry exactly the two drive prompts')
.toEqual([PROMPT_TURN1, PROMPT_TURN2])
await seedSession(scaffold, raw, SEED_ID)
}
browser = await chromium.launch()
page = await browser.newPage({ viewport: { width: 1680, height: 1000 } })
tripwire = watchConsole(page)
await page.goto(scaffold.baseUrl, { waitUntil: 'load' })
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
}, 120_000)
afterAll(async () => {
await browser?.close()
await scaffold?.close()
})
it.skipIf(MODE !== 'record')('records the two-turn seed live through the composer', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-record'))
const input = page.locator('textarea').first()
await input.waitFor({ timeout: 10_000 })
let sessionId: Awaited<ReturnType<WebScaffold['whenTurnSettled']>> | undefined
for (const prompt of [PROMPT_TURN1, PROMPT_TURN2]) {
const settled = scaffold.whenTurnSettled()
// Turn 2 types into the same composer once turn 1 unlocks it.
await expect.poll(() => input.isEnabled(), { timeout: 15_000 }).toBe(true)
await input.fill(prompt)
await input.press('Enter')
sessionId = await settled
}
await recordFixture(scaffold, sessionId!, SEED)
// Fixture honesty: the recording must carry the shape the replay
// scenarios assert on — three calls in turn 1 and two closed turns.
const recorded = parseSessionLog(await readFile(SEED, 'utf8'))
expect(recorded.filter(e => e.type === 'turn/end')).toHaveLength(2)
const calls = recorded.filter((e): e is SessionEvent & { data: { name: string } } => e.type === 'tool/call')
expect(calls.map(e => e.data.name).sort()).toEqual(['bash', 'read', 'read'])
}, 400_000)
it.skipIf(MODE === 'record')('opens the seeded session and renders both turns from the log', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-open'))
// Expand the collapsed group row, then open the revealed session row.
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 expect.poll(() => page.getByText('FIRST_DONE', { exact: true }).count(), { timeout: 15_000 }).toBeGreaterThanOrEqual(1)
await expect.poll(() => page.getByRole('heading', { name: 'Navigation Summary' }).count(), { timeout: 15_000 }).toBe(1)
}, 90_000)
it.skipIf(MODE === 'record')('filters the sidebar tree by title through the search box', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-search'))
// Runs after the session is open: a cold summary carries no title (the
// sidebar shows the cwd basename), and the durable title lands with the
// attach subscription's baseline — which is itself worth pinning: search
// matches the title the user sees, not a hidden cold field.
const search = page.getByPlaceholder('Search name, keywords', { exact: false })
await expect.poll(() => page.getByText('NavScenario', { exact: false }).count(), { timeout: 15_000 }).toBeGreaterThanOrEqual(1)
// Negative: a garbage query empties the tree (group rows hide too).
await search.fill('zzzqx-no-such-session')
await expect.poll(() => page.locator('[role="treeitem"]').count(), { timeout: 10_000 }).toBe(0)
// Positive: a title word narrows to the matched session + its group,
// force-expanded by search mode (case-insensitive client-side filter).
await search.fill('navscenario')
await expect.poll(() => page.locator('[role="treeitem"]').count(), { timeout: 10_000 }).toBeGreaterThanOrEqual(2)
// Clear restores the unfiltered tree.
await page.getByRole('button', { name: 'Clear search' }).click()
await expect.poll(() => search.inputValue(), { timeout: 5_000 }).toBe('')
await expect.poll(() => page.locator('[role="treeitem"]').count(), { timeout: 10_000 }).toBeGreaterThanOrEqual(1)
}, 60_000)
it.skipIf(MODE === 'record')('renders the trajectory tab with turn sections and step cells', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-trajectory'))
await page.getByRole('tab', { name: 'Trajectory' }).click()
// Two sticky turn sections; turn 1's step group summarizes its tool mix
// (bash + the two parallel reads collapse to 'bash read×2').
await expect.poll(() => page.getByText('Turn 1', { exact: true }).count(), { timeout: 15_000 }).toBe(1)
await expect.poll(() => page.getByText('Turn 2', { exact: true }).count(), { timeout: 10_000 }).toBe(1)
await expect.poll(() => page.getByText('bash read×2', { exact: false }).count(), { timeout: 10_000 }).toBe(1)
const snapshot = (await captureStableAria(page, '[class*="viewArea"]', scaffold.workspaceCwd))
.split(SEED_ID).join('{{seededId}}')
await compareOrRefreshGolden(TRAJECTORY_EXPECTED, snapshot, MODE)
}, 60_000)
it.skipIf(MODE === 'record')('renders the waterfall tab with span stats and one lane per span', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-waterfall'))
await page.getByRole('tab', { name: 'Waterfall' }).click()
// The stats header rides the waterfall body. The span fold counts THREE
// spans for this two-turn log: only assistant/steering nodes carry a turn
// number, so the first user message lands in a turn-0 prologue span (a
// P-I placeholder shape — pinned as-is; real spans are deferred to
// P-III per the view's deviation ledger). Calls: bash + two reads.
await expect.poll(() => page.getByText(/3 turns · \d+ steps · 3 tool calls/).count(), { timeout: 15_000 }).toBe(1)
// One lane per span, tagged by turn number, prologue included.
for (const tag of ['turn 0', 'turn 1', 'turn 2']) {
await expect.poll(() => page.getByText(tag, { exact: true }).count(), { timeout: 10_000 }).toBe(1)
}
}, 60_000)
it.skipIf(MODE === 'record')('opens the details column from the bash row and closes it', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-navigation-details'))
await page.getByRole('tab', { name: 'Chat' }).click()
// The bash toolview row routes its click to openDetails (read rows are
// expand-in-place instead — the seeded-history scenario owns that fold).
const bashRow = page.locator('[data-sample="bash-global"]').first()
await bashRow.waitFor({ timeout: 15_000 })
// Open/closed is the frame's collapsed attribute: the column collapses to
// width 0 but its subtree deliberately never unmounts (hidden, not
// absent), so element presence/visibility cannot express the state.
const frame = page.locator('[data-details-collapsed], [class*="frame"]').first()
expect(await frame.getAttribute('data-details-collapsed')).not.toBeNull()
await bashRow.click()
await expect.poll(() => frame.getAttribute('data-details-collapsed'), { timeout: 10_000 }).toBeNull()
// The open panel shows the selected call's name, arguments, and durable
// result (NAVIGATION_OK appears in the chat row too, hence >= 2 total).
await expect.poll(() => page.getByText('NAVIGATION_OK', { exact: false }).count(), { timeout: 10_000 }).toBeGreaterThanOrEqual(2)
await page.getByRole('button', { name: '关闭详情' }).click()
await expect.poll(() => frame.getAttribute('data-details-collapsed'), { timeout: 10_000 }).not.toBeNull()
}, 60_000)
it.skipIf(MODE === 'record')('issued zero model calls and stayed clean', async () => {
expect(tripwire.pageErrors).toEqual([])
expect(tripwire.warnings).toEqual([])
await assertFixtureInventory(SNAPSHOT_DIR, ['seed.jsonl', 'trajectory.expected.md'])
})
})
@@ -0,0 +1,35 @@
- 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": No sessions yet
- 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 "Add attachment":
- img
- combobox "Plan mode":
- option "Plan" [selected]
- option "Agent"
- combobox "Access mode":
- option "Read-only" [selected]
- option "Read-write"
- combobox "Model":
- option "DeepSeek-V4-Pro High" [selected]
- option "DeepSeek-V4-Pro"
- button "Send message" [disabled]
@@ -0,0 +1,35 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":1785015039278,"cwd":"{{cwd}}/workspace"}
{"type":"turn/start","seq":0,"time":1785015039291,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user","rpcId":"{{rpcId}}"}}}}
{"type":"user/message","seq":1,"time":1785015039292,"data":{"content":[{"type":"text","text":"Reply with the single word LIGHTHOUSE and stop."}],"source":{"kind":"user","rpcId":"{{rpcId}}"}},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1785015039294,"data":{"title":"Reply with the single word","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785015039362,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1785015039363,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}","messagePrefix":["{{messagePrefix}}"]},"reason":"initial"}}
{"type":"assistant/chunk","seq":5,"time":1785015039930,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"assistant/chunk","seq":6,"time":1785015039930,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
{"type":"assistant/chunk","seq":7,"time":1785015040092,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
{"type":"assistant/chunk","seq":8,"time":1785015040120,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
{"type":"assistant/chunk","seq":9,"time":1785015040121,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":10,"time":1785015040121,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
{"type":"assistant/chunk","seq":11,"time":1785015040121,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
{"type":"assistant/chunk","seq":12,"time":1785015040167,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
{"type":"assistant/chunk","seq":13,"time":1785015040168,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
{"type":"assistant/chunk","seq":14,"time":1785015040168,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
{"type":"assistant/chunk","seq":15,"time":1785015040168,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
{"type":"assistant/chunk","seq":16,"time":1785015040168,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":17,"time":1785015040179,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Let"}}}
{"type":"assistant/chunk","seq":18,"time":1785015040179,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":19,"time":1785015040179,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" comply"}}}
{"type":"assistant/chunk","seq":20,"time":1785015040209,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":21,"time":1785015040209,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":22,"time":1785015040209,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"L"}}}
{"type":"assistant/chunk","seq":23,"time":1785015040210,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"IGH"}}}
{"type":"assistant/chunk","seq":24,"time":1785015040210,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"TH"}}}
{"type":"assistant/chunk","seq":25,"time":1785015040240,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"O"}}}
{"type":"assistant/chunk","seq":26,"time":1785015040241,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"USE"}}}
{"type":"assistant/chunk","seq":27,"time":1785015040241,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with a single word. Let me comply."}}}}
{"type":"assistant/chunk","seq":28,"time":1785015040242,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"LIGHTHOUSE"}}}}
{"type":"assistant/chunk","seq":29,"time":1785015040242,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":109,"outputTokens":21,"cacheReadTokens":7680,"reasoningTokens":15}}}}
{"type":"assistant/chunk","seq":30,"time":1785015040242,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":31,"time":1785015040244,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with a single word. Let me comply."},{"type":"text","text":"LIGHTHOUSE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":109,"outputTokens":21,"cacheReadTokens":7680,"reasoningTokens":15}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"surfaceOp":"append"}
{"type":"step/end","seq":32,"time":1785015040246,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":33,"time":1785015040247,"data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,254 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":1785011380476,"cwd":"{{cwd}}/workspace"}
{"type":"turn/start","seq":0,"time":1785011380489,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user","rpcId":"{{rpcId}}"}}}}
{"type":"user/message","seq":1,"time":1785011380490,"data":{"content":[{"type":"text","text":"NavScenario: first run bash to print exactly NAVIGATION_OK, then read nav-a.md and nav-b.md using two read calls in ONE assistant message, then reply with the single word FIRST_DONE and stop."}],"source":{"kind":"user","rpcId":"{{rpcId}}"}},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1785011380492,"data":{"title":"NavScenario: first run bash to","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785011380549,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1785011380550,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}","messagePrefix":["{{messagePrefix}}"]},"reason":"initial"}}
{"type":"assistant/chunk","seq":5,"time":1785011380917,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"assistant/chunk","seq":6,"time":1785011380917,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
{"type":"assistant/chunk","seq":7,"time":1785011381027,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
{"type":"assistant/chunk","seq":8,"time":1785011381052,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
{"type":"assistant/chunk","seq":9,"time":1785011381053,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":10,"time":1785011381053,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
{"type":"assistant/chunk","seq":11,"time":1785011381053,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" follow"}}}
{"type":"assistant/chunk","seq":12,"time":1785011381078,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
{"type":"assistant/chunk","seq":13,"time":1785011381079,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" specific"}}}
{"type":"assistant/chunk","seq":14,"time":1785011381079,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" navigation"}}}
{"type":"assistant/chunk","seq":15,"time":1785011381105,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" scenario"}}}
{"type":"assistant/chunk","seq":16,"time":1785011381105,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":17,"time":1785011381106,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Let"}}}
{"type":"assistant/chunk","seq":18,"time":1785011381106,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":19,"time":1785011381133,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":":\n\n"}}}
{"type":"assistant/chunk","seq":20,"time":1785011381134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"1"}}}
{"type":"assistant/chunk","seq":21,"time":1785011381134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":22,"time":1785011381134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Run"}}}
{"type":"assistant/chunk","seq":23,"time":1785011381134,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
{"type":"assistant/chunk","seq":24,"time":1785011381160,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
{"type":"assistant/chunk","seq":25,"time":1785011381161,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" print"}}}
{"type":"assistant/chunk","seq":26,"time":1785011381161,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
{"type":"assistant/chunk","seq":27,"time":1785011381161,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
{"type":"assistant/chunk","seq":28,"time":1785011381187,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"V"}}}
{"type":"assistant/chunk","seq":29,"time":1785011381187,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"IG"}}}
{"type":"assistant/chunk","seq":30,"time":1785011381187,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ATION"}}}
{"type":"assistant/chunk","seq":31,"time":1785011381187,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
{"type":"assistant/chunk","seq":32,"time":1785011381187,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n"}}}
{"type":"assistant/chunk","seq":33,"time":1785011381188,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"2"}}}
{"type":"assistant/chunk","seq":34,"time":1785011381213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":35,"time":1785011381213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Read"}}}
{"type":"assistant/chunk","seq":36,"time":1785011381213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" nav"}}}
{"type":"assistant/chunk","seq":37,"time":1785011381238,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"-a"}}}
{"type":"assistant/chunk","seq":38,"time":1785011381238,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".md"}}}
{"type":"assistant/chunk","seq":39,"time":1785011381239,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
{"type":"assistant/chunk","seq":40,"time":1785011381239,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" nav"}}}
{"type":"assistant/chunk","seq":41,"time":1785011381239,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"-b"}}}
{"type":"assistant/chunk","seq":42,"time":1785011381239,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":".md"}}}
{"type":"assistant/chunk","seq":43,"time":1785011381265,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" in"}}}
{"type":"assistant/chunk","seq":44,"time":1785011381266,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" two"}}}
{"type":"assistant/chunk","seq":45,"time":1785011381266,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" read"}}}
{"type":"assistant/chunk","seq":46,"time":1785011381291,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" calls"}}}
{"type":"assistant/chunk","seq":47,"time":1785011381291,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" in"}}}
{"type":"assistant/chunk","seq":48,"time":1785011381291,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" ONE"}}}
{"type":"assistant/chunk","seq":49,"time":1785011381318,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" message"}}}
{"type":"assistant/chunk","seq":50,"time":1785011381319,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\n"}}}
{"type":"assistant/chunk","seq":51,"time":1785011381319,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"3"}}}
{"type":"assistant/chunk","seq":52,"time":1785011381319,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":53,"time":1785011381319,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Reply"}}}
{"type":"assistant/chunk","seq":54,"time":1785011381319,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
{"type":"assistant/chunk","seq":55,"time":1785011381344,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
{"type":"assistant/chunk","seq":56,"time":1785011381372,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
{"type":"assistant/chunk","seq":57,"time":1785011381372,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
{"type":"assistant/chunk","seq":58,"time":1785011381373,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_D"}}}
{"type":"assistant/chunk","seq":59,"time":1785011381373,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
{"type":"assistant/chunk","seq":60,"time":1785011381373,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n\n"}}}
{"type":"assistant/chunk","seq":61,"time":1785011381373,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"Let"}}}
{"type":"assistant/chunk","seq":62,"time":1785011381400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":63,"time":1785011381400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" start"}}}
{"type":"assistant/chunk","seq":64,"time":1785011381400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
{"type":"assistant/chunk","seq":65,"time":1785011381400,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
{"type":"assistant/chunk","seq":66,"time":1785011381425,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
{"type":"assistant/chunk","seq":67,"time":1785011381426,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
{"type":"assistant/chunk","seq":68,"time":1785011381450,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
{"type":"assistant/chunk","seq":69,"time":1785011381451,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
{"type":"assistant/chunk","seq":70,"time":1785011381451,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reads"}}}
{"type":"assistant/chunk","seq":71,"time":1785011381476,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":72,"time":1785011381556,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":73,"time":1785011381557,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":""}}}
{"type":"assistant/chunk","seq":74,"time":1785011381557,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"{"}}}
{"type":"assistant/chunk","seq":75,"time":1785011381557,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":76,"time":1785011381583,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"command"}}}
{"type":"assistant/chunk","seq":77,"time":1785011381583,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":78,"time":1785011381583,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":": "}}}
{"type":"assistant/chunk","seq":79,"time":1785011381583,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":80,"time":1785011381608,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"echo"}}}
{"type":"assistant/chunk","seq":81,"time":1785011381609,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":" NAV"}}}
{"type":"assistant/chunk","seq":82,"time":1785011381635,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"IG"}}}
{"type":"assistant/chunk","seq":83,"time":1785011381635,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"ATION"}}}
{"type":"assistant/chunk","seq":84,"time":1785011381635,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"_OK"}}}
{"type":"assistant/chunk","seq":85,"time":1785011381636,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":86,"time":1785011381669,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":", "}}}
{"type":"assistant/chunk","seq":87,"time":1785011381670,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":88,"time":1785011381687,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"description"}}}
{"type":"assistant/chunk","seq":89,"time":1785011381687,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":90,"time":1785011381687,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":": "}}}
{"type":"assistant/chunk","seq":91,"time":1785011381687,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":92,"time":1785011381715,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"Print"}}}
{"type":"assistant/chunk","seq":93,"time":1785011381716,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":" NAV"}}}
{"type":"assistant/chunk","seq":94,"time":1785011381716,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"IG"}}}
{"type":"assistant/chunk","seq":95,"time":1785011381716,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"ATION"}}}
{"type":"assistant/chunk","seq":96,"time":1785011381716,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"_OK"}}}
{"type":"assistant/chunk","seq":97,"time":1785011381740,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":98,"time":1785011381741,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","argumentsDelta":"}"}}}
{"type":"assistant/chunk","seq":99,"time":1785011381793,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":2,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":100,"time":1785011381793,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":""}}}
{"type":"assistant/chunk","seq":101,"time":1785011381819,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"{"}}}
{"type":"assistant/chunk","seq":102,"time":1785011381819,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":103,"time":1785011381819,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"file"}}}
{"type":"assistant/chunk","seq":104,"time":1785011381819,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"_path"}}}
{"type":"assistant/chunk","seq":105,"time":1785011381820,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":106,"time":1785011381847,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":": "}}}
{"type":"assistant/chunk","seq":107,"time":1785011381847,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":108,"time":1785011381847,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"nav"}}}
{"type":"assistant/chunk","seq":109,"time":1785011381847,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"-a"}}}
{"type":"assistant/chunk","seq":110,"time":1785011381873,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":".md"}}}
{"type":"assistant/chunk","seq":111,"time":1785011381874,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":112,"time":1785011381897,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":2,"id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","argumentsDelta":"}"}}}
{"type":"assistant/chunk","seq":113,"time":1785011381924,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":3,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":114,"time":1785011381924,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":""}}}
{"type":"assistant/chunk","seq":115,"time":1785011381950,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"{"}}}
{"type":"assistant/chunk","seq":116,"time":1785011381951,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":117,"time":1785011381951,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"file"}}}
{"type":"assistant/chunk","seq":118,"time":1785011381977,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"_path"}}}
{"type":"assistant/chunk","seq":119,"time":1785011381977,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":120,"time":1785011381977,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":": "}}}
{"type":"assistant/chunk","seq":121,"time":1785011381977,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":122,"time":1785011382003,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"nav"}}}
{"type":"assistant/chunk","seq":123,"time":1785011382003,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"-b"}}}
{"type":"assistant/chunk","seq":124,"time":1785011382003,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":".md"}}}
{"type":"assistant/chunk","seq":125,"time":1785011382003,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"\""}}}
{"type":"assistant/chunk","seq":126,"time":1785011382029,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":3,"id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","argumentsDelta":"}"}}}
{"type":"assistant/chunk","seq":127,"time":1785011382086,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to follow a specific navigation scenario. Let me:\n\n1. Run bash to print \"NAVIGATION_OK\"\n2. Read nav-a.md and nav-b.md in two read calls in ONE message\n3. Reply with \"FIRST_DONE\"\n\nLet me start with the bash command and the reads."}}}}
{"type":"assistant/chunk","seq":128,"time":1785011382087,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","arguments":"{\"command\": \"echo NAVIGATION_OK\", \"description\": \"Print NAVIGATION_OK\"}"}}}}
{"type":"assistant/chunk","seq":129,"time":1785011382087,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":2,"block":{"type":"tool-call","id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","arguments":"{\"file_path\": \"nav-a.md\"}"}}}}
{"type":"assistant/chunk","seq":130,"time":1785011382087,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":3,"block":{"type":"tool-call","id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","arguments":"{\"file_path\": \"nav-b.md\"}"}}}}
{"type":"assistant/chunk","seq":131,"time":1785011382087,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":140,"outputTokens":197,"cacheReadTokens":7680,"reasoningTokens":66}}}}
{"type":"assistant/chunk","seq":132,"time":1785011382087,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":133,"time":1785011382091,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to follow a specific navigation scenario. Let me:\n\n1. Run bash to print \"NAVIGATION_OK\"\n2. Read nav-a.md and nav-b.md in two read calls in ONE message\n3. Reply with \"FIRST_DONE\"\n\nLet me start with the bash command and the reads."},{"type":"tool-call","id":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","arguments":"{\"command\": \"echo NAVIGATION_OK\", \"description\": \"Print NAVIGATION_OK\"}"},{"type":"tool-call","id":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","arguments":"{\"file_path\": \"nav-a.md\"}"},{"type":"tool-call","id":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","arguments":"{\"file_path\": \"nav-b.md\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":140,"outputTokens":197,"cacheReadTokens":7680,"reasoningTokens":66}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132],"surfaceOp":"append"}
{"type":"tool/call","seq":134,"time":1785011382092,"data":{"turn":1,"step":1,"callId":"call_00_kFKHaEXcTYEex0iDZw0C2432","name":"bash","arguments":"{\"command\": \"echo NAVIGATION_OK\", \"description\": \"Print NAVIGATION_OK\"}"}}
{"type":"tool/result","seq":135,"time":1785011382105,"data":{"turn":1,"step":1,"callId":"call_00_kFKHaEXcTYEex0iDZw0C2432","content":[{"type":"text","text":"NAVIGATION_OK\n"}],"isError":false},"sourceEventSeqs":[134],"surfaceOp":"append"}
{"type":"tool/call","seq":136,"time":1785011382105,"data":{"turn":1,"step":1,"callId":"call_01_tK4hIIRVTMgAvdzs7m9j6212","name":"read","arguments":"{\"file_path\": \"nav-a.md\"}"}}
{"type":"tool/call","seq":137,"time":1785011382106,"data":{"turn":1,"step":1,"callId":"call_02_k8Z6wGirxfnW96Iv8mkz9224","name":"read","arguments":"{\"file_path\": \"nav-b.md\"}"}}
{"type":"tool/result","seq":138,"time":1785011382113,"data":{"turn":1,"step":1,"callId":"call_01_tK4hIIRVTMgAvdzs7m9j6212","content":[{"type":"text","text":"<path>{{cwd}}/workspace/nav-a.md</path>\n<type>file</type>\n<content>\n1: # alpha nav\n\n(End of file - total 1 lines)\n</content>"}],"isError":false},"sourceEventSeqs":[136],"surfaceOp":"append"}
{"type":"tool/result","seq":139,"time":1785011382114,"data":{"turn":1,"step":1,"callId":"call_02_k8Z6wGirxfnW96Iv8mkz9224","content":[{"type":"text","text":"<path>{{cwd}}/workspace/nav-b.md</path>\n<type>file</type>\n<content>\n1: # beta nav\n\n(End of file - total 1 lines)\n</content>"}],"isError":false},"sourceEventSeqs":[137],"surfaceOp":"append"}
{"type":"step/end","seq":140,"time":1785011382117,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":141,"time":1785011382118,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":142,"time":1785011382673,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"assistant/chunk","seq":143,"time":1785011382673,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"All"}}}
{"type":"assistant/chunk","seq":144,"time":1785011382763,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" three"}}}
{"type":"assistant/chunk","seq":145,"time":1785011382790,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" calls"}}}
{"type":"assistant/chunk","seq":146,"time":1785011382817,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" succeeded"}}}
{"type":"assistant/chunk","seq":147,"time":1785011382818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":":\n"}}}
{"type":"assistant/chunk","seq":148,"time":1785011382818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"1"}}}
{"type":"assistant/chunk","seq":149,"time":1785011382818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":150,"time":1785011382818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
{"type":"assistant/chunk","seq":151,"time":1785011382844,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" printed"}}}
{"type":"assistant/chunk","seq":152,"time":1785011382845,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
{"type":"assistant/chunk","seq":153,"time":1785011382845,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"NA"}}}
{"type":"assistant/chunk","seq":154,"time":1785011382845,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"V"}}}
{"type":"assistant/chunk","seq":155,"time":1785011382845,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"IG"}}}
{"type":"assistant/chunk","seq":156,"time":1785011382845,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ATION"}}}
{"type":"assistant/chunk","seq":157,"time":1785011382872,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
{"type":"assistant/chunk","seq":158,"time":1785011382872,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n"}}}
{"type":"assistant/chunk","seq":159,"time":1785011382872,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"2"}}}
{"type":"assistant/chunk","seq":160,"time":1785011382872,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":161,"time":1785011382872,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" nav"}}}
{"type":"assistant/chunk","seq":162,"time":1785011382899,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"-a"}}}
{"type":"assistant/chunk","seq":163,"time":1785011382899,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":".md"}}}
{"type":"assistant/chunk","seq":164,"time":1785011382899,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" contains"}}}
{"type":"assistant/chunk","seq":165,"time":1785011382926,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \"#"}}}
{"type":"assistant/chunk","seq":166,"time":1785011382926,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" alpha"}}}
{"type":"assistant/chunk","seq":167,"time":1785011382926,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" nav"}}}
{"type":"assistant/chunk","seq":168,"time":1785011382926,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n"}}}
{"type":"assistant/chunk","seq":169,"time":1785011382926,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"3"}}}
{"type":"assistant/chunk","seq":170,"time":1785011382927,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":171,"time":1785011382952,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" nav"}}}
{"type":"assistant/chunk","seq":172,"time":1785011382953,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"-b"}}}
{"type":"assistant/chunk","seq":173,"time":1785011382953,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":".md"}}}
{"type":"assistant/chunk","seq":174,"time":1785011382953,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" contains"}}}
{"type":"assistant/chunk","seq":175,"time":1785011382953,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \"#"}}}
{"type":"assistant/chunk","seq":176,"time":1785011382953,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" beta"}}}
{"type":"assistant/chunk","seq":177,"time":1785011382979,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" nav"}}}
{"type":"assistant/chunk","seq":178,"time":1785011382980,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n\n"}}}
{"type":"assistant/chunk","seq":179,"time":1785011382980,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"Now"}}}
{"type":"assistant/chunk","seq":180,"time":1785011382980,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
{"type":"assistant/chunk","seq":181,"time":1785011382980,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" need"}}}
{"type":"assistant/chunk","seq":182,"time":1785011382980,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
{"type":"assistant/chunk","seq":183,"time":1785011383005,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
{"type":"assistant/chunk","seq":184,"time":1785011383006,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
{"type":"assistant/chunk","seq":185,"time":1785011383006,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
{"type":"assistant/chunk","seq":186,"time":1785011383032,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
{"type":"assistant/chunk","seq":187,"time":1785011383033,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
{"type":"assistant/chunk","seq":188,"time":1785011383033,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
{"type":"assistant/chunk","seq":189,"time":1785011383033,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
{"type":"assistant/chunk","seq":190,"time":1785011383033,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
{"type":"assistant/chunk","seq":191,"time":1785011383033,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_D"}}}
{"type":"assistant/chunk","seq":192,"time":1785011383059,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
{"type":"assistant/chunk","seq":193,"time":1785011383060,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
{"type":"assistant/chunk","seq":194,"time":1785011383060,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":195,"time":1785011383060,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"FIR"}}}
{"type":"assistant/chunk","seq":196,"time":1785011383060,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ST"}}}
{"type":"assistant/chunk","seq":197,"time":1785011383060,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"_D"}}}
{"type":"assistant/chunk","seq":198,"time":1785011383089,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
{"type":"assistant/chunk","seq":199,"time":1785011383090,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"All three calls succeeded:\n1. bash printed \"NAVIGATION_OK\"\n2. nav-a.md contains \"# alpha nav\"\n3. nav-b.md contains \"# beta nav\"\n\nNow I need to reply with the single word \"FIRST_DONE\"."}}}}
{"type":"assistant/chunk","seq":200,"time":1785011383090,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"FIRST_DONE"}}}}
{"type":"assistant/chunk","seq":201,"time":1785011383090,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":349,"outputTokens":56,"cacheReadTokens":7808,"reasoningTokens":51}}}}
{"type":"assistant/chunk","seq":202,"time":1785011383090,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":203,"time":1785011383091,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"All three calls succeeded:\n1. bash printed \"NAVIGATION_OK\"\n2. nav-a.md contains \"# alpha nav\"\n3. nav-b.md contains \"# beta nav\"\n\nNow I need to reply with the single word \"FIRST_DONE\"."},{"type":"text","text":"FIRST_DONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":349,"outputTokens":56,"cacheReadTokens":7808,"reasoningTokens":51}},"sourceEventSeqs":[142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202],"surfaceOp":"append"}
{"type":"step/end","seq":204,"time":1785011383091,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":205,"time":1785011383092,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"turn/start","seq":206,"time":1785011383106,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user","rpcId":"{{rpcId}}"}}}}
{"type":"user/message","seq":207,"time":1785011383107,"data":{"content":[{"type":"text","text":"Reply in markdown with: a level-2 heading \"Navigation Summary\", a bulleted list of exactly two items, and a fenced code block containing echo WATERFALL. Then stop."}],"source":{"kind":"user","rpcId":"{{rpcId}}"}},"surfaceOp":"append"}
{"type":"step/start","seq":208,"time":1785011383107,"data":{"turn":2,"step":1}}
{"type":"assistant/chunk","seq":209,"time":1785011383497,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"assistant/chunk","seq":210,"time":1785011383497,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
{"type":"assistant/chunk","seq":211,"time":1785011383622,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
{"type":"assistant/chunk","seq":212,"time":1785011383645,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
{"type":"assistant/chunk","seq":213,"time":1785011383646,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":214,"time":1785011383646,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
{"type":"assistant/chunk","seq":215,"time":1785011383646,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
{"type":"assistant/chunk","seq":216,"time":1785011383734,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
{"type":"assistant/chunk","seq":217,"time":1785011383734,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
{"type":"assistant/chunk","seq":218,"time":1785011383734,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" specific"}}}
{"type":"assistant/chunk","seq":219,"time":1785011383739,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" format"}}}
{"type":"assistant/chunk","seq":220,"time":1785011383739,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":221,"time":1785011383740,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Let"}}}
{"type":"assistant/chunk","seq":222,"time":1785011383740,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
{"type":"assistant/chunk","seq":223,"time":1785011383740,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" do"}}}
{"type":"assistant/chunk","seq":224,"time":1785011383747,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" that"}}}
{"type":"assistant/chunk","seq":225,"time":1785011383747,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
{"type":"assistant/chunk","seq":226,"time":1785011383748,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":227,"time":1785011383748,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"##"}}}
{"type":"assistant/chunk","seq":228,"time":1785011383772,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" Navigation"}}}
{"type":"assistant/chunk","seq":229,"time":1785011383773,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" Summary"}}}
{"type":"assistant/chunk","seq":230,"time":1785011383798,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"\n\n"}}}
{"type":"assistant/chunk","seq":231,"time":1785011383798,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"-"}}}
{"type":"assistant/chunk","seq":232,"time":1785011383798,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" alpha"}}}
{"type":"assistant/chunk","seq":233,"time":1785011383823,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" nav"}}}
{"type":"assistant/chunk","seq":234,"time":1785011383849,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"\n"}}}
{"type":"assistant/chunk","seq":235,"time":1785011383850,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"-"}}}
{"type":"assistant/chunk","seq":236,"time":1785011383850,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" beta"}}}
{"type":"assistant/chunk","seq":237,"time":1785011383850,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" nav"}}}
{"type":"assistant/chunk","seq":238,"time":1785011383850,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"\n\n"}}}
{"type":"assistant/chunk","seq":239,"time":1785011383875,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"```\n"}}}
{"type":"assistant/chunk","seq":240,"time":1785011383875,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"echo"}}}
{"type":"assistant/chunk","seq":241,"time":1785011383875,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":" WATER"}}}
{"type":"assistant/chunk","seq":242,"time":1785011383875,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"F"}}}
{"type":"assistant/chunk","seq":243,"time":1785011383876,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ALL"}}}
{"type":"assistant/chunk","seq":244,"time":1785011383902,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"\n"}}}
{"type":"assistant/chunk","seq":245,"time":1785011383903,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"```"}}}
{"type":"assistant/chunk","seq":246,"time":1785011383903,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with a specific format. Let me do that."}}}}
{"type":"assistant/chunk","seq":247,"time":1785011383903,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"## Navigation Summary\n\n- alpha nav\n- beta nav\n\n```\necho WATERFALL\n```"}}}}
{"type":"assistant/chunk","seq":248,"time":1785011383903,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":141,"outputTokens":36,"cacheReadTokens":8064,"reasoningTokens":16}}}}
{"type":"assistant/chunk","seq":249,"time":1785011383903,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":250,"time":1785011383904,"data":{"turn":2,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with a specific format. Let me do that."},{"type":"text","text":"## Navigation Summary\n\n- alpha nav\n- beta nav\n\n```\necho WATERFALL\n```"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":141,"outputTokens":36,"cacheReadTokens":8064,"reasoningTokens":16}},"sourceEventSeqs":[209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249],"surfaceOp":"append"}
{"type":"step/end","seq":251,"time":1785011383904,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":252,"time":1785011383904,"data":{"turn":2,"reason":{"kind":"completed"}}}
@@ -0,0 +1 @@
- text: "Turn 1 Message {{duration}} #1 User NavScenario: first run bash to print exactly NAVIGATION_OK, then read nav-a.md and nav-b.md using two read calls in ONE assistant message, then reply with the single word FIRST_DONE and stop. +{{duration}} Step 1 {{duration}} bash read×2 #2 Tool bash · {\"command\": \"echo NAVIGATION_OK\", \"description\": \"Print NAVIGATION_OK\"} +{{duration}} #3 Tool read · {\"file_path\": \"nav-a.md\"} +{{duration}} #4 Tool read · {\"file_path\": \"nav-b.md\"} +{{duration}} Step 2 {{duration}} #5 Message FIRST_DONE 349 56 51 +{{duration}} Turn 2 Message {{duration}} #6 User Reply in markdown with: a level-2 heading \"Navigation Summary\", a bulleted list of exactly two items, and a fenced code block containing echo WATERFALL. Then stop. +{{duration}} Step 1 {{duration}} #7 Message ## Navigation Summary - alpha nav - beta nav ``` echo WATERFALL ``` 141 36 16 +{{duration}}"
+2
View File
@@ -26,6 +26,8 @@
"tests/live-interactions.e2e.ts",
"tests/question-composer.e2e.ts",
"tests/steering.e2e.ts",
"tests/navigation-panes.e2e.ts",
"tests/lifecycle-chrome.e2e.ts",
"tests/replay-round-trip.e2e.ts",
"tests/seeded-history.e2e.ts"
],
+2
View File
@@ -13,6 +13,8 @@
"apps/web/tests/live-interactions.e2e.ts",
"apps/web/tests/question-composer.e2e.ts",
"apps/web/tests/steering.e2e.ts",
"apps/web/tests/navigation-panes.e2e.ts",
"apps/web/tests/lifecycle-chrome.e2e.ts",
"apps/web/tests/replay-round-trip.e2e.ts",
"apps/web/tests/seeded-history.e2e.ts",
"apps/cli/tests/**/*.ts",