The native adapter's route was named deepseek, colliding with pi-ai's catalog provider of the same name, so the two DeepSeek paths could never be mounted side by side. The web settings page needs both configurable at once. Compositions, fixtures, goldens, scaffolding defaults, and docs all move together (pre-release, no shim); TUI/session-query-spill/ missing-credential goldens re-recorded through their keyless refresh modes because provider-name length shifts box padding and spill truncation points.
122 lines
5.0 KiB
TypeScript
122 lines
5.0 KiB
TypeScript
import { readFile, writeFile } from 'node:fs/promises'
|
|
import { dirname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { Context } from 'cordis'
|
|
import { normalizeSessionLog, scrubRequestHeaders, type NormalizeContext } from '@deepseek-ai/dsh-acp-snapshot'
|
|
import { LOADER_SMOKE_TEST_TIMEOUT_MS, runLoaderSmoke } from '@deepseek-ai/dsh-loader-smoke'
|
|
import { createUserMessage, CallId , createMessage } from '@deepseek-ai/dsh-llm'
|
|
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
|
|
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
const fixtureDir = join(dirname(fileURLToPath(import.meta.url)), 'semantic-checkpoint-snapshots/tool-outcome-unknown')
|
|
const replayFixture = join(fixtureDir, 'replay.jsonl')
|
|
const replayOverride = join(fixtureDir, 'replay.override.json')
|
|
const sessionExpected = join(fixtureDir, 'session.expected.jsonl')
|
|
const configPath = fileURLToPath(new URL('../semantic-checkpoint.cordis.snapshot.yml', import.meta.url))
|
|
const binScript = fileURLToPath(new URL('../../../packages/examples/cli-demo/src/bin.ts', import.meta.url))
|
|
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
|
|
const sessionId = SessionId('semantic-checkpoint-unknown-outcome')
|
|
const refreshing = process.env.DSH_SNAPSHOT === 'refresh'
|
|
const task = 'Continue safely from the interrupted operation.'
|
|
|
|
async function seedInterruptedSession(root: string, cwd: string): Promise<string> {
|
|
const ctx = new Context()
|
|
await ctx.plugin(SessionStore)
|
|
await ctx.plugin(SessionPersistenceJsonl, { root, compression: 'none' })
|
|
const meta: SessionHeader = {
|
|
version: SESSION_FORMAT_VERSION,
|
|
id: sessionId,
|
|
createdAt: 1,
|
|
cwd,
|
|
delegationDepth: 0,
|
|
}
|
|
const events: SessionEvent[] = [
|
|
{ type: 'turn/start', seq: 0, time: 10, data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } } },
|
|
{ type: 'user/message', seq: 1, time: 11, data: createUserMessage({
|
|
content: [{ type: 'text', text: 'Perform one side-effecting remote mutation.' }], source: { kind: 'user' },
|
|
}), surfaceOp: 'append' },
|
|
{ type: 'step/start', seq: 2, time: 12, data: { turn: 1, step: 1 } },
|
|
{
|
|
type: 'assistant/message',
|
|
seq: 3,
|
|
time: 13,
|
|
data: {
|
|
turn: 1,
|
|
step: 1,
|
|
message: createMessage({
|
|
role: 'assistant',
|
|
content: [{ type: 'tool-call', id: CallId('unknown-outcome-call'), name: 'write_remote', arguments: '{"value":1}' }],
|
|
source: {
|
|
kind: 'model',
|
|
...{ provider: 'deepseek-official', model: 'deepseek-v4-flash' },
|
|
},
|
|
}),
|
|
},
|
|
surfaceOp: 'append',
|
|
},
|
|
{
|
|
type: 'tool/call',
|
|
seq: 4,
|
|
time: 14,
|
|
data: {
|
|
turn: 1,
|
|
step: 1,
|
|
callId: CallId('unknown-outcome-call'),
|
|
name: 'write_remote',
|
|
arguments: '{"value":1}',
|
|
},
|
|
},
|
|
]
|
|
try {
|
|
await ctx.sessionPersistence.create(meta)
|
|
await ctx.sessionPersistence.append(sessionId, events)
|
|
const location = ctx.sessionPersistence.locate(meta)
|
|
if (location === undefined) throw new Error('JSONL backend did not locate the seeded session')
|
|
return location.path
|
|
} finally {
|
|
await ctx.fiber.dispose()
|
|
}
|
|
}
|
|
|
|
describe('semantic checkpoint recovery snapshot', () => {
|
|
it('resumes an unknown tool outcome through the headless stream-json app', async () => {
|
|
let cwd = ''
|
|
let sessionPath = ''
|
|
const result = await runLoaderSmoke({
|
|
label: 'semantic checkpoint headless stream-json snapshot',
|
|
tempDirPrefix: 'dsh-semantic-snapshot-',
|
|
binScript,
|
|
configPath,
|
|
binArgs: ['--config', configPath, '--output-format', 'stream-json', task],
|
|
tsconfigPath,
|
|
env: {
|
|
DSH_SNAPSHOT_FILE: replayFixture,
|
|
DSH_SNAPSHOT_OVERRIDE: replayOverride,
|
|
},
|
|
prepare: async (runCwd) => {
|
|
cwd = runCwd
|
|
sessionPath = await seedInterruptedSession(join(runCwd, '.sessions'), runCwd)
|
|
},
|
|
inspect: async () => {
|
|
const normalization: NormalizeContext = { sessionIds: [sessionId], cwd }
|
|
const session = scrubRequestHeaders(normalizeSessionLog(await readFile(sessionPath, 'utf8'), normalization))
|
|
if (refreshing) await writeFile(sessionExpected, session)
|
|
expect(session).toBe(await readFile(sessionExpected, 'utf8'))
|
|
expect(session).toContain('TOOL_OUTCOME_UNKNOWN')
|
|
expect(session).toContain('Do not retry blindly.')
|
|
},
|
|
})
|
|
|
|
expect(result.stderr).toBe('')
|
|
const records = result.stdout.trimEnd().split('\n').map(line => JSON.parse(line) as Record<string, unknown>)
|
|
expect(records.at(-1)).toMatchObject({
|
|
type: 'result',
|
|
success: true,
|
|
sessionId,
|
|
result: 'I will verify the external state before deciding whether to retry the side-effecting operation.',
|
|
reason: { kind: 'completed' },
|
|
})
|
|
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
|
})
|