Merge remote-tracking branch 'origin/master' into worktree/semantic-session-checkpoints

# Conflicts:
#	packages/session-persistence/session-persistence-jsonl/README.md
This commit is contained in:
Yichen Jiang
2026-07-22 10:54:08 +08:00
72 files changed
+2448 -317

No files matched your search

+30 -5
View File
@@ -1,6 +1,6 @@
import { cp, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { basename, dirname, join } from 'node:path'
import { basename, dirname, isAbsolute, join, relative, sep } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterAll, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
@@ -109,6 +109,13 @@ function snapshotModeFromEnv(value: string | undefined): SnapshotMode {
const MODE = snapshotModeFromEnv(process.env.DSH_SNAPSHOT)
const observedScenarios = new Set<string>()
function snapshotDisplayPath(displayPath: string, cwd: string, displayCwd: string): string {
const rel = relative(cwd, displayPath)
if (rel === '') return displayCwd
if (isAbsolute(rel) || rel === '..' || rel.startsWith(`..${sep}`)) return displayPath
return `${displayCwd}/${rel.split(sep).join('/')}`
}
function scenarioDir(scenario: Scenario): string {
return join(SNAPSHOTS_DIR, scenario.name)
}
@@ -139,9 +146,10 @@ function rawSessionLog(session: Session): string {
].join('\n')
}
function normalizeTerminalSnapshot(snapshot: string, cwd: string): string {
function normalizeTerminalSnapshot(snapshot: string, cwd: string, displayCwd: string): string {
return snapshot
.split(`/private${cwd}`).join('/workspace/project')
.split(displayCwd).join('/workspace/project')
.split(cwd).join('/workspace/project')
.replace(UUID_RE, '{{uuid}}')
}
@@ -160,9 +168,20 @@ async function settleTerminal(terminal: HeadlessTerminal): Promise<void> {
async function mountScenarioContext(
scenario: Scenario,
cwd: string,
displayCwd: string,
fixtureFile: string,
childFiles: string[],
): Promise<Context> {
class SnapshotLocalFileSystem extends LocalFileSystem {
override async resolve(
path: string,
opts?: { cwd?: string; signal?: AbortSignal },
): Promise<Awaited<ReturnType<LocalFileSystem['resolve']>>> {
const target = await super.resolve(path, opts)
return { ...target, displayPath: snapshotDisplayPath(target.displayPath, cwd, displayCwd) }
}
}
const ctx = new Context()
await ctx.plugin(AgentCore, {
agents: [],
@@ -173,7 +192,7 @@ async function mountScenarioContext(
})
await ctx.plugin(TokenMeterService)
await ctx.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 })
await ctx.plugin(LocalFileSystem, { cwd: '/' })
await ctx.plugin(SnapshotLocalFileSystem, { cwd: '/' })
await ctx.plugin(FsPolicy)
await ctx.plugin(ToolFs)
await ctx.plugin(UserInteractionService)
@@ -213,6 +232,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
expect(prompts.length, `${scenario.name} must carry at least one recorded user prompt`).toBeGreaterThan(0)
const cwd = await mkdtemp(join(SNAPSHOT_TMP_ROOT, `dsh-tui-snapshot-${scenario.name}-`))
const displayCwd = `/tmp/${basename(cwd)}`
let ctx: Context | undefined
let controller: ReturnType<typeof createTuiChat> | undefined
const terminal = new HeadlessTerminal(100, 36)
@@ -221,7 +241,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
const source = join(scenarioDir(scenario), 'workspace')
await cp(source, cwd, { recursive: true })
}
ctx = await mountScenarioContext(scenario, cwd, fixtureFile, childFiles)
ctx = await mountScenarioContext(scenario, cwd, displayCwd, fixtureFile, childFiles)
const disposedSessions: Session[] = []
ctx.on('session/disposed', (session) => { disposedSessions.push(session) })
const workflowEvents: string[] = []
@@ -241,7 +261,11 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
title: 'DSH TUI snapshot',
welcome: `Recorded replay: ${scenario.name}`,
maxToolOutputLines: 8,
}, { terminal, exit: () => {} })
}, {
terminal,
exit: () => {},
formatCwd: () => displayCwd,
})
await settleTerminal(terminal)
for (const prompt of prompts) {
@@ -272,6 +296,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
const snapshot = normalizeTerminalSnapshot(
await terminal.snapshot({ includeScrollback: true }),
cwd,
displayCwd,
)
await handle.dispose()
const children = disposedSessions