merge: integrate QA-003 checkpoint follow-up

This commit is contained in:
程序员阿江(Relakkes)
2026-09-10 18:23:54 +08:00
2 changed files with 54 additions and 0 deletions
+48
View File
@@ -3426,6 +3426,54 @@ describe('Sessions API', () => {
expect(body.index?.lastErrorCode === null || typeof body.index?.lastErrorCode === 'string').toBe(true)
})
it('GET turn-checkpoints accepts a known memory session across workspace changes and persistence restore', async () => {
await fs.writeFile(path.join(tmpDir, 'settings.json'), JSON.stringify({ cleanupPeriodDays: 0 }))
resetSettingsCache()
const created = await fetch(`${baseUrl}/api/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ workDir: tmpDir }),
})
expect(created.status).toBe(201)
const { sessionId } = await created.json() as { sessionId: string }
const { sessionService } = await import('../services/sessionService.js')
const selectedWorkDir = await fs.mkdtemp(path.join(tmpDir, 'selected-project-'))
// The runtime launch boundary records the user's selected working folder
// even though retention zero deliberately never materializes a JSONL.
await sessionService.appendSessionMetadata(sessionId, { workDir: selectedWorkDir })
expect(await sessionService.getSessionWorkDir(sessionId)).toBe(selectedWorkDir)
for (const days of [0, 365]) {
await fs.writeFile(path.join(tmpDir, 'settings.json'), JSON.stringify({ cleanupPeriodDays: days }))
resetSettingsCache()
const response = await fetch(`${baseUrl}/api/sessions/${sessionId}/turn-checkpoints`)
expect(response.status).toBe(200)
expect(await response.json()).toEqual({ checkpoints: [] })
expect(await sessionService.getSessionMessagesWithEvidence(sessionId)).toEqual({
messages: [], transcriptEvidenceComplete: false,
})
expect(await sessionService.findSessionFile(sessionId)).toBeNull()
}
// After new enabled content reaches disk, it takes precedence over the
// retained launch metadata and produces normal completed-turn checkpoints.
await writeSessionFile(sanitizePath(selectedWorkDir), sessionId, [
{ type: 'session-meta', workDir: selectedWorkDir },
{ type: 'user', uuid: 'public-user', parentUuid: null, timestamp: '2026-09-10T00:00:00.000Z', message: { role: 'user', content: 'public prompt' } },
{ type: 'assistant', uuid: 'public-assistant', parentUuid: 'public-user', timestamp: '2026-09-10T00:00:01.000Z', message: { role: 'assistant', content: 'public reply' } },
])
const restored = await fetch(`${baseUrl}/api/sessions/${sessionId}/turn-checkpoints`)
expect(restored.status).toBe(200)
const body = await restored.json() as { checkpoints: Array<{ target: { targetUserMessageId: string } }> }
expect(body.checkpoints).toHaveLength(1)
expect(body.checkpoints[0]?.target.targetUserMessageId).toBe('public-user')
expect((await sessionService.getSessionMessagesWithEvidence(sessionId)).transcriptEvidenceComplete).toBe(true)
await sessionService.deleteSession(sessionId)
expect((await fetch(`${baseUrl}/api/sessions/${sessionId}/turn-checkpoints`)).status).toBe(404)
expect((await fetch(`${baseUrl}/api/sessions/${crypto.randomUUID()}/turn-checkpoints`)).status).toBe(404)
})
it('POST /api/sessions should create a session', async () => {
const workDir = await fs.mkdtemp(path.join(tmpDir, 'api-session-'))
const res = await fetch(`${baseUrl}/api/sessions`, {
+6
View File
@@ -3761,6 +3761,12 @@ export class SessionService {
): Promise<SessionMessagesWithEvidence> {
const found = await this.findSessionFile(sessionId)
if (!found) {
// Retention-zero sessions intentionally have no transcript. The desktop
// still asks for turn checkpoints after each live reply; lack of saved
// evidence is not a missing session, nor proof of an empty history.
if (this.memoryLaunchInfo.has(this.memorySessionKey(sessionId))) {
return { messages: [], transcriptEvidenceComplete: false }
}
throw ApiError.notFound(`Session not found: ${sessionId}`)
}