Merge remote-tracking branch 'origin/master' into codex/fix-deepseek-keepalive

# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-21-bounded-llm-request-recovery.i18n.yaml
#	examples/headless-agent/tests/fixtures/deepseek-defaults.cordis.yml
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/util/timeout/README.i18n.yaml
This commit is contained in:
fz
2026-08-09 15:00:08 +08:00
4661 changed files with 164891 additions and 79798 deletions

View File

@@ -1,4 +1,4 @@
import { readFile, readdir, writeFile } from 'node:fs/promises'
import { copyFile, mkdir, 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'
@@ -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')
@@ -29,16 +33,29 @@ const goalScenarioDir = join(snapshotsDir, 'goal-tools')
const goalConfigPath = fileURLToPath(new URL('../goal.cordis.snapshot.yml', import.meta.url))
const retryScenarioDir = join(snapshotsDir, 'provider-retry')
const retryConfigPath = fileURLToPath(new URL('../retry.cordis.snapshot.yml', import.meta.url))
const compactionScenarioDir = join(snapshotsDir, 'compaction-recovery')
const compactionSessionFixture = join(compactionScenarioDir, 'session.jsonl')
const compactionStreamExpected = join(compactionScenarioDir, 'stream-json.expected.jsonl')
const compactionConfigPath = fileURLToPath(new URL('../compaction.cordis.snapshot.yml', import.meta.url))
const credentialsScenarioDir = join(snapshotsDir, 'missing-credential')
const credentialsConfigPath = fileURLToPath(new URL('../credentials.cordis.snapshot.yml', import.meta.url))
// Same keyless composition as the missing-credential scenario: the endpoint is
// never dialed either way, because a supplied-but-unusable key fails credential
// resolution exactly where an absent one does.
const invalidCredentialScenarioDir = join(snapshotsDir, 'invalid-credential')
const ralphScenarioDir = join(snapshotsDir, 'ralph-loop')
const ralphConfigPath = fileURLToPath(new URL('../ralph.cordis.snapshot.yml', import.meta.url))
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 binScript = fileURLToPath(new URL('./fixtures/headless-driver.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 dshRunFailureExpected = join(snapshotsDir, 'dsh-run', 'stderr.expected.txt')
const cliMockLlmPluginPath = fileURLToPath(new URL('./fixtures/cli-mock-llm.ts', import.meta.url))
const refreshing = process.env.DSH_SNAPSHOT === 'refresh'
interface JsonObject {
@@ -168,23 +185,100 @@ 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] ?? {} }
}))
}
/** Install the keyless product-CLI adapter into the temporary headless profile. */
async function prepareCliMockFixture(cwd: string): Promise<void> {
const fixtureDir = join(cwd, '.dsh', 'profiles', 'headless', 'snapshot-fixtures')
await mkdir(fixtureDir, { recursive: true })
await Promise.all([
copyFile(cliMockLlmPluginPath, join(fixtureDir, 'cli-mock-llm.ts')),
writeFile(join(fixtureDir, 'package.json'), '{"type":"module"}\n'),
])
}
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_PERMISSION_MODE: 'danger-full-access',
DSH_TELEMETRY_DISABLED: '1',
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
prepare: prepareCliMockFixture,
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).toBe('')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('prints a terminal model failure through the product dsh run command', async () => {
const result = await runLoaderSmoke({
label: 'product dsh run model failure snapshot',
tempDirPrefix: 'headless-snapshot-dsh-run-failure-',
binScript: dshBinScript,
configPath: dshRunOverlayPath,
binArgs: ['run', '--patch', dshRunOverlayPath, 'Trigger the keyless model failure.'],
tsconfigPath,
expectedExitCode: 1,
env: {
DSH_CLI_MOCK_FAILURE: '1',
DSH_TELEMETRY_DISABLED: '1',
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
prepare: prepareCliMockFixture,
})
expect(result.stdout).toBe('\n')
await expect(result.stderr).toMatchFileSnapshot(dshRunFailureExpected)
}, 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',
tempDirPrefix: 'headless-snapshot-startup-error-',
binScript,
libBinScript: binScript,
configPath: startupFailureConfigPath,
binArgs: ['--config', startupFailureConfigPath, '--output-format', 'stream-json', 'unreachable task'],
binArgs: [startupFailureConfigPath, 'unreachable task'],
tsconfigPath,
expectedExitCode: 1,
})
@@ -200,8 +294,9 @@ describe('headless stream-json snapshots', () => {
label: 'provider retry headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-provider-retry-',
binScript,
libBinScript: binScript,
configPath: retryConfigPath,
binArgs: ['--config', retryConfigPath, '--output-format', 'stream-json', prompt],
binArgs: [retryConfigPath, prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
@@ -232,15 +327,86 @@ describe('headless stream-json snapshots', () => {
expect(normalized).toBe(await readFile(streamExpected, 'utf8'))
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('surfaces actionable missing-credential guidance through the one-shot app', async () => {
it('recovers from context overflow through an assembled compaction', async () => {
const prompt = await scenarioPrompt(compactionScenarioDir, 'compaction-recovery')
let expectedSession = await readFile(compactionSessionFixture, 'utf8')
let runCwd = ''
const result = await runLoaderSmoke({
label: 'compaction recovery headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-compaction-recovery-',
binScript,
libBinScript: binScript,
configPath: compactionConfigPath,
binArgs: [compactionConfigPath, prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
DSH_SNAPSHOT_FILE: compactionSessionFixture,
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
prepare: (cwd) => { runCwd = cwd },
inspect: async (cwd) => {
const logs = await persistedLogs(cwd)
expect(logs).toHaveLength(1)
const actual = logs[0]
if (actual === undefined) throw new Error('compaction snapshot did not persist its session')
const records = parseJsonl(actual.content)
const types = records.map(record => record.type)
expect(types.filter(type => type === 'compact/start')).toHaveLength(1)
expect(types.filter(type => type === 'compact/summary')).toHaveLength(1)
expect(types.filter(type => type === 'compact/end')).toHaveLength(1)
const start = types.indexOf('compact/start')
const summary = types.indexOf('compact/summary')
const replacement = records.findIndex((record) => {
if (record.type !== 'user/message') return false
const surfaceOp = record.surfaceOp as JsonObject | undefined
return surfaceOp?.op === 'replace'
})
const end = types.indexOf('compact/end')
expect(start).toBeLessThan(summary)
expect(summary).toBeLessThan(replacement)
expect(replacement).toBeLessThan(end)
const summaryRecord = records[summary]
const summaryData = summaryRecord?.data as JsonObject | undefined
expect(summaryData?.shadowedSeqs).toEqual(expect.arrayContaining([expect.any(Number)]))
const final = [...records].reverse().find(record => record.type === 'assistant/message')
expect(JSON.stringify(final)).toContain('COMPACTION RECOVERED')
const actualContext = contextFromLogs([actual.content])
if (refreshing) {
const harvested: HarvestedLog = {
id: String(actual.header.id),
createdAt: Number(actual.header.createdAt),
content: actual.content,
}
const replacements = refreshFixtureReplacements([harvested], [expectedSession])
expectedSession = tokenizeSessionFixtureCwd(
stabilizeRefreshLog(actual.content, expectedSession, replacements, actualContext),
)
await writeFile(compactionSessionFixture, expectedSession)
}
const expectedContext = contextFromLogs([expectedSession])
expect(scrubRequestHeaders(normalizeSessionLog(actual.content, actualContext)))
.toBe(scrubRequestHeaders(normalizeSessionLog(expectedSession, expectedContext)))
},
})
expect(result.stderr).toBe('')
const normalized = normalizeHeadlessStream(result.stdout, runCwd)
if (refreshing) await writeFile(compactionStreamExpected, normalized)
expect(normalized).toBe(await readFile(compactionStreamExpected, 'utf8'))
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('logs actionable missing-credential guidance through the one-shot app', async () => {
const streamExpected = join(credentialsScenarioDir, 'stream-json.expected.jsonl')
let runCwd = ''
const result = await runLoaderSmoke({
label: 'missing-credential headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-missing-credential-',
binScript,
libBinScript: binScript,
configPath: credentialsConfigPath,
binArgs: ['--config', credentialsConfigPath, '--output-format', 'stream-json', 'say pong'],
binArgs: [credentialsConfigPath, 'say pong'],
tsconfigPath,
env: {
// First-run posture: no key in the environment, none under ./.dsh.
@@ -248,22 +414,62 @@ describe('headless stream-json snapshots', () => {
DEEPSEEK_BASE_URL: '',
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
// The designed failure surface: the one-shot app reports the failed turn.
expectedExitCode: 1,
prepare: (cwd) => { runCwd = cwd },
})
// The guidance leads with the credential store — the path that keeps the
// secret out of configuration files — and offers a literal key last.
expect(result.stderr).toBe(
'dsh-cli-demo: turn 1 failed at step 1: llm-deepseek: no API key for provider route "deepseek-official";'
+ ' store DEEPSEEK_API_KEY through the credentials service (the web Models page writes it),'
+ ' export DEEPSEEK_API_KEY in the launching environment, or — as a last resort — set a literal'
+ ' "apiKey" in the llm-deepseek settings section\n',
)
// The failure reaches the caller through the stream, not stderr; the
// recorded transcript below pins the guidance text itself, which names
// both places a credential can come from and nothing else.
expect(result.stderr).toBe('')
const normalized = normalizeHeadlessStream(result.stdout, runCwd)
if (refreshing) await writeFile(streamExpected, normalized)
expect(normalized).toBe(await readFile(streamExpected, 'utf8'))
// The durable failure leads with the credential store — the path that
// keeps the secret out of configuration files — then names the launching
// environment, and stops there: configuration carries the reference, so
// there is no literal-key escape hatch left to offer.
expect(normalized).toContain(
'store DEEPSEEK_API_KEY through the credentials service (the web Models page writes it),',
)
expect(normalized).toContain('or export DEEPSEEK_API_KEY in the launching environment')
expect(normalized).not.toContain('as a last resort')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('logs actionable invalid-credential guidance through the one-shot app', async () => {
const streamExpected = join(invalidCredentialScenarioDir, 'stream-json.expected.jsonl')
let runCwd = ''
const result = await runLoaderSmoke({
label: 'invalid-credential headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-invalid-credential-',
binScript,
libBinScript: binScript,
configPath: credentialsConfigPath,
binArgs: [credentialsConfigPath, 'say pong'],
tsconfigPath,
env: {
// A key that exists but no HTTP header can carry — the paste this
// change exists for. Before it, `fetch` refused to build the header
// and the turn ended on a retried ByteString TypeError.
DEEPSEEK_API_KEY: 'sk-\u{1F600}pasted-from-a-chat-window',
DEEPSEEK_BASE_URL: '',
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
prepare: (cwd) => { runCwd = cwd },
})
expect(result.stderr).toBe('')
const normalized = normalizeHeadlessStream(result.stdout, runCwd)
if (refreshing) await writeFile(streamExpected, normalized)
expect(normalized).toBe(await readFile(streamExpected, 'utf8'))
// The durable failure names the reference to correct and the writer that
// usually owns it, and stays true in a composition that mounts no Models
// page at all.
expect(normalized).toContain('the API key resolved from DEEPSEEK_API_KEY contains characters')
expect(normalized).toContain('the web Models page writes it')
// Neither the key nor the transport-level symptom it used to produce may
// reach the user: the code point of one character is still the key.
expect(normalized).not.toContain('pasted-from-a-chat-window')
expect(normalized).not.toContain('ByteString')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('logs the model default and a dynamic next-step reasoning effort', async () => {
@@ -271,8 +477,9 @@ describe('headless stream-json snapshots', () => {
label: 'reasoning effort headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-reasoning-effort-',
binScript,
libBinScript: binScript,
configPath: reasoningConfigPath,
binArgs: ['--config', reasoningConfigPath, '--output-format', 'stream-json', 'prove dynamic reasoning effort'],
binArgs: [reasoningConfigPath, 'prove dynamic reasoning effort'],
tsconfigPath,
})
@@ -313,16 +520,17 @@ describe('headless stream-json snapshots', () => {
label: 'DeepSeek adapter defaults headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-deepseek-defaults-',
binScript,
libBinScript: binScript,
configPath: deepseekDefaultsConfigPath,
binArgs: [
'--config',
deepseekDefaultsConfigPath,
'--output-format',
'stream-json',
'return the deterministic response',
],
tsconfigPath,
env: {
// Configuration carries only the reference; the key rides the
// launching environment, which is the whole credential plane here.
DEEPSEEK_API_KEY: 'snapshot-key',
DSH_SNAPSHOT_BASE_URL: server.url,
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
@@ -370,8 +578,9 @@ describe('headless stream-json snapshots', () => {
label: 'advanced headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-advanced-',
binScript,
libBinScript: binScript,
configPath: advancedConfigPath,
binArgs: ['--config', advancedConfigPath, '--output-format', 'stream-json', prompt],
binArgs: [advancedConfigPath, prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
@@ -441,8 +650,9 @@ describe('headless stream-json snapshots', () => {
label: 'goal tools headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-goal-tools-',
binScript,
libBinScript: binScript,
configPath: goalConfigPath,
binArgs: ['--config', goalConfigPath, '--output-format', 'stream-json', prompt],
binArgs: [goalConfigPath, prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
@@ -470,19 +680,11 @@ describe('headless stream-json snapshots', () => {
const probeContent = probeMessage?.content as JsonObject[] | undefined
expect(probeContent?.[0]?.isError).toBe(true)
expect((probeData?.error as JsonObject | undefined)?.code).toBe('GOAL_NOT_FOUND')
const goalChanges = records.filter((record) => {
if (record.type !== 'user/message') return false
const data = record.data as JsonObject | undefined
const source = data?.source as JsonObject | undefined
const change = source?.change as JsonObject | undefined
return source?.kind === 'goal' && change?.kind === 'goal/change'
})
const goalChanges = records.filter(record => record.type === 'goal/change')
expect(goalChanges).toHaveLength(1)
const data = goalChanges[0]?.data as JsonObject | undefined
const source = data?.source as JsonObject | undefined
const change = source?.change as JsonObject | undefined
const goal = change?.goal as JsonObject | undefined
expect(change?.operation).toBe('create')
const goal = data?.goal as JsonObject | undefined
expect(data?.operation).toBe('create')
expect(goal).toMatchObject({
objective: 'Finish the headless goal-tool snapshot proof',
phase: 'active',
@@ -505,8 +707,9 @@ describe('headless stream-json snapshots', () => {
label: 'Ralph loop headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-ralph-loop-',
binScript,
libBinScript: binScript,
configPath: ralphConfigPath,
binArgs: ['--config', ralphConfigPath, '--output-format', 'stream-json', prompt],
binArgs: [ralphConfigPath, prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
@@ -586,8 +789,9 @@ describe('headless stream-json snapshots', () => {
label: 'headless persistent PTY snapshot',
tempDirPrefix: 'headless-snapshot-pty-',
binScript,
libBinScript: binScript,
configPath: ptyConfigPath,
binArgs: ['--config', ptyConfigPath, '--output-format', 'stream-json', prompt],
binArgs: [ptyConfigPath, prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',