feat(cli)!: make dsh run the headless entrypoint

This commit is contained in:
Tianyi Cui
2026-08-08 02:07:11 +08:00
parent f50521ac4c
commit f32aa54aeb
41 changed files with 429 additions and 101 deletions

View File

@@ -0,0 +1,8 @@
- id: api-gateway
config:
provider: cli-mock
model: cli-mock
- insert:
- id: cli-mock-llm
name: !!js process.env.DSH_RUN_MOCK_PLUGIN_URL

View File

@@ -2,7 +2,7 @@ import { readFile, readdir, writeFile } from 'node:fs/promises'
import { createServer } from 'node:http'
import type { IncomingMessage, ServerResponse } from 'node:http'
import { delimiter, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { fileURLToPath, pathToFileURL } from 'node:url'
import {
normalizeSessionLog,
normalizeStdout,
@@ -14,6 +14,10 @@ import {
type NormalizeContext,
} from '@deepseek-ai/dsh-acp-snapshot'
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
import {
decompressZstdFrame,
scanZstdFrames,
} from '@deepseek-ai/dsh-session-persistence-jsonl/src/zstd.ts'
import { describe, expect, it } from 'vitest'
const snapshotsDir = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
@@ -44,9 +48,15 @@ const ralphConfigPath = fileURLToPath(new URL('../ralph.cordis.snapshot.yml', im
const startupFailureConfigPath = fileURLToPath(new URL('./fixtures/startup-activation-error/cordis.yml', import.meta.url))
const startupFailureExpected = join(snapshotsDir, 'startup-activation-error', 'stderr.expected.txt')
const binScript = fileURLToPath(new URL('../../../packages/examples/cli-demo/src/bin.ts', import.meta.url))
const dshBinScript = fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
const reasoningConfigPath = fileURLToPath(new URL('./fixtures/cli.cordis.yml', import.meta.url))
const deepseekDefaultsConfigPath = fileURLToPath(new URL('./fixtures/deepseek-defaults.cordis.yml', import.meta.url))
const dshRunOverlayPath = fileURLToPath(new URL('./fixtures/dsh-run.cordis.yml', import.meta.url))
const dshRunSessionExpected = join(snapshotsDir, 'dsh-run', 'session.expected.jsonl')
const cliMockLlmPluginUrl = pathToFileURL(
fileURLToPath(new URL('./fixtures/cli-mock-llm.ts', import.meta.url)),
).href
const refreshing = process.env.DSH_SNAPSHOT === 'refresh'
interface JsonObject {
@@ -167,16 +177,61 @@ async function scenarioPrompt(dir: string, label: string): Promise<string> {
return prompt
}
async function persistedLogs(cwd: string): Promise<PersistedLog[]> {
const root = join(cwd, '.sessions')
const files = (await readdir(root, { recursive: true })).filter(file => file.endsWith('.jsonl'))
async function readPersistedLog(file: string): Promise<string> {
const content = await readFile(file)
if (!file.endsWith('.zstd')) return content.toString('utf8')
const scan = scanZstdFrames(content)
if (scan.tornStart !== undefined) throw new Error(`persisted snapshot log has a torn Zstandard frame: ${file}`)
const decoded: Buffer[] = []
for (const frame of scan.frames) {
decoded.push(await decompressZstdFrame(content.subarray(frame.start, frame.end)))
}
return Buffer.concat(decoded).toString('utf8')
}
async function persistedLogs(cwd: string, root: string = join(cwd, '.sessions')): Promise<PersistedLog[]> {
const files = (await readdir(root, { recursive: true }))
.filter(file => file.endsWith('.jsonl') || file.endsWith('.jsonl.zstd'))
return Promise.all(files.map(async (file) => {
const content = await readFile(join(root, file), 'utf8')
const content = await readPersistedLog(join(root, file))
return { content, header: parseJsonl(content)[0] ?? {} }
}))
}
describe('headless stream-json snapshots', () => {
it('runs one task through the product dsh run command', async () => {
const task = 'Prove the product dsh run path with one real tool round trip.'
const result = await runLoaderSmoke({
label: 'product dsh run snapshot',
tempDirPrefix: 'headless-snapshot-dsh-run-',
binScript: dshBinScript,
configPath: dshRunOverlayPath,
binArgs: ['run', '--patch', dshRunOverlayPath, task],
tsconfigPath,
env: {
DSH_RUN_MOCK_PLUGIN_URL: cliMockLlmPluginUrl,
DSH_PERMISSION_MODE: 'danger-full-access',
DSH_TELEMETRY_DISABLED: '1',
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
inspect: async (cwd) => {
const logs = await persistedLogs(cwd, join(cwd, '.dsh', 'sessions'))
expect(logs).toHaveLength(1)
const actual = logs[0]
if (actual === undefined) throw new Error('dsh run did not persist its session')
const context = contextFromLogs([actual.content])
const session = scrubRequestHeaders(normalizeSessionLog(actual.content, context))
if (refreshing) await writeFile(dshRunSessionExpected, session)
expect(session).toBe(await readFile(dshRunSessionExpected, 'utf8'))
expect(session).toContain(task)
expect(session).toContain('CLI tool round trip complete: CLI_TOOL_ROUND_TRIP')
},
})
expect(result.stdout).toBe('CLI tool round trip complete: CLI_TOOL_ROUND_TRIP\n')
expect(result.stderr).toMatch(/^dsh: observing at http:\/\/127\.0\.0\.1:\d+\n$/u)
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('prints the original Loader activation error through the assembled one-shot app', async () => {
const result = await runLoaderSmoke({
label: 'headless startup activation error snapshot',

View File

@@ -0,0 +1,33 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"permission/preset","seq":0,"time":0,"data":{"preset":"danger-full-access"}}
{"type":"sandbox/mode","seq":1,"time":0,"data":{"mode":"danger-full-access"}}
{"type":"approval/policy","seq":2,"time":0,"data":{"policy":"never"}}
{"type":"agent/inbox/spliced","seq":3,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Prove the product dsh run path with one real tool round trip."}],"source":{"kind":"user","rpcId":"{{sessionId}}"},"role":"user","id":"{{sessionId}}"}]}}
{"type":"turn/start","seq":4,"time":0,"data":{"turn":1}}
{"type":"agent/inbox/spliced","seq":5,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":6,"time":0,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":7,"time":0,"data":{"content":[{"type":"text","text":"Prove the product dsh run path with one real tool round trip."}],"source":{"kind":"user","rpcId":"{{sessionId}}"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":8,"time":0,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":9,"time":0,"data":{"title":"Prove the product dsh run","messageSeqs":[7],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":10,"time":0,"data":{"header":{"config":{"provider":"cli-mock","model":"cli-mock","reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":11,"time":0,"data":{"provider":"cli-mock","model":"cli-mock"}}
{"type":"session/title-llm-request","seq":12,"time":0,"data":{"titleProvider":"session-title-first-message-llm","messageSeqs":[7],"route":{"provider":"cli-mock","model":"cli-mock"},"system":"Create a concise title for an AI coding-assistant session from the supplied human messages.\nReturn only the title on one line, **in plain text of natural language**, with no quotes, prefix, explanation, Markdown, XML, or terminal control codes. No code is allowed.\nUse the language of the messages.\nAim for about 5 words in non-CJK languages or 10 CJK characters.","messages":[{"content":[{"type":"text","text":"Generate the session title from this JSON array of human messages:\n[{\"seq\":7,\"text\":\"Prove the product dsh run path with one real tool round trip.\"}]"}],"source":{"kind":"plugin","plugin":"dsh-session-title-llm"},"role":"user","id":"{{sessionId}}"}],"maxTokens":64}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"cli-smoke-call","name":"bash","argumentsDelta":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}}
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"cli-smoke-call","name":"bash","arguments":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}}}
{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":11,"outputTokens":3,"cacheReadTokens":2}}}}
{"type":"assistant/chunk","seq":17,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":18,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"cli-smoke-call","name":"bash","arguments":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}],"source":{"kind":"model","provider":"cli-mock","model":"cli-mock"},"id":"{{sessionId}}"},"usage":{"inputTokens":11,"outputTokens":3,"cacheReadTokens":2}},"sourceEventSeqs":[13,14,15,16,17],"surfaceOp":"append"}
{"type":"tool/call","seq":19,"time":0,"data":{"turn":1,"step":1,"callId":"cli-smoke-call","name":"bash","arguments":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}
{"type":"tool/result","seq":20,"time":0,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"cli-smoke-call"},"content":[{"type":"tool-result","toolCallId":"cli-smoke-call","content":[{"type":"text","text":"CLI_TOOL_ROUND_TRIP"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"}
{"type":"step/end","seq":21,"time":0,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":22,"time":0,"data":{"turn":1,"step":2}}
{"type":"request/header","seq":23,"time":0,"data":{"header":{"config":{"provider":"cli-mock","model":"cli-mock","reasoningEffort":"off"},"system":"{{system}}","tools":"{{tools}}"},"reason":"change"}}
{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"CLI tool round trip complete: CLI_TOOL_ROUND_TRIP"}}}
{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"CLI tool round trip complete: CLI_TOOL_ROUND_TRIP"}}}}
{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":7,"outputTokens":5,"reasoningTokens":1}}}}
{"type":"assistant/chunk","seq":28,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":29,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"CLI tool round trip complete: CLI_TOOL_ROUND_TRIP"}],"source":{"kind":"model","provider":"cli-mock","model":"cli-mock"},"id":"{{sessionId}}"},"usage":{"inputTokens":7,"outputTokens":5,"reasoningTokens":1}},"sourceEventSeqs":[24,25,26,27,28],"surfaceOp":"append"}
{"type":"step/end","seq":30,"time":0,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":31,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}