Merge teacher reply cleanup with continuous conversations
This commit is contained in:
@@ -91,6 +91,68 @@ describe('continuous conversation persistence', () => {
|
||||
await other.importProject(project);
|
||||
expect(await other.exists()).toBe(false);
|
||||
});
|
||||
it('imports reply history and its execution identity without reviving archived components', async () => {
|
||||
const dir = await root(), project = { id: randomUUID(), path: path.join(dir, 'project'), name: '旧项目' };
|
||||
const sourceId = randomUUID();
|
||||
const archived = { obsoleteComponent: 'never-send-this-to-model', broken: ['not', 'a', 'component'] };
|
||||
const oldStore = new TeacherTopicStore(path.join(project.path, '.makelore', 'teacher-conversations', 'account', sourceId));
|
||||
const request: TeacherRequest = { ...turn(1), sourceConversationId: sourceId,
|
||||
presentation: 'discussion-v1', discussionContext: archived, discussionSnapshot: archived,
|
||||
discussionError: 'historical parser error', cloudRequestId: 'old-cloud-request',
|
||||
response: '保留的正文', suggestedQuestions: ['继续聊这个想法'] };
|
||||
const old: TeacherTopic = { schemaVersion: 1, revision: 1, accountId: 'account', id: randomUUID(),
|
||||
projectId: project.id, sourceConversationId: sourceId, definition, version: 7,
|
||||
createdAt: 'now', updatedAt: 'now', requests: [request], discussion: archived };
|
||||
await oldStore.save(old);
|
||||
const file = path.join(oldStore.directory, old.id + '.json'), before = await readFile(file, 'utf8');
|
||||
const target = path.join(dir, 'new');
|
||||
const store = new TeacherConversationStore(target, 'account', 'agent-a');
|
||||
await store.importProject(project);
|
||||
const resumed = new TeacherConversationStore(target, 'account', 'agent-a');
|
||||
await resumed.importProject(project);
|
||||
const topic = await resumed.read();
|
||||
expect(topic.requests).toHaveLength(1);
|
||||
expect(topic.requests[0]).toMatchObject({
|
||||
origin: { projectId: project.id, topicId: old.id, requestId: request.id },
|
||||
projectId: project.id, projectName: project.name, sourceConversationId: sourceId,
|
||||
teacherVersion: 7, runtimeThreadId: old.id, cloudRequestId: 'old-cloud-request',
|
||||
response: '保留的正文', suggestedQuestions: ['继续聊这个想法'],
|
||||
discussionContext: archived, discussionSnapshot: archived, discussionError: 'historical parser error',
|
||||
});
|
||||
expect(topic.conversation).not.toHaveProperty('discussions');
|
||||
expect(topic).not.toHaveProperty('discussion');
|
||||
const manifest = JSON.parse(await readFile(path.join(target, 'conversation.json'), 'utf8'));
|
||||
expect(manifest).not.toHaveProperty('importedDiscussions');
|
||||
expect(JSON.stringify((await resumed.history()).messages)).not.toContain(archived.obsoleteComponent);
|
||||
const history = await resumed.history();
|
||||
expect((await history.read(`teacher:${topic.requests[0].id}:assistant`))?.text)
|
||||
.toBe('保留的正文\n\n可以接着聊的问题:\n- 继续聊这个想法');
|
||||
expect(await readFile(file, 'utf8')).toBe(before);
|
||||
});
|
||||
it('preserves existing continuous-chat component archives opaquely across recovery and writes', async () => {
|
||||
const dir = await root();
|
||||
const store = new TeacherConversationStore(dir, 'account', 'agent-a');
|
||||
const topic = await store.ensure(definition, 1);
|
||||
expect(topic.conversation).not.toHaveProperty('discussions');
|
||||
const manifestPath = path.join(dir, 'conversation.json');
|
||||
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
|
||||
const archives = { project: { malformedLegacyState: true, active: ['opaque', 'archive'] } };
|
||||
const imported = { project: { legacyMarker: true } };
|
||||
manifest.topic.conversation.discussions = archives;
|
||||
manifest.importedDiscussions = imported;
|
||||
await atomicWriteJson(manifestPath, manifest);
|
||||
const pending = { ...turn(1), status: 'running' as const, discussionSnapshot: { malformed: true } };
|
||||
await atomicWriteJson(path.join(dir, 'turns', pending.id + '.json'), pending);
|
||||
const resumed = new TeacherConversationStore(dir, 'account', 'agent-a');
|
||||
const restored = await resumed.read();
|
||||
expect(restored.requests[0]).toMatchObject({ status: 'interrupted', discussionSnapshot: { malformed: true } });
|
||||
restored.requests.push(turn(2));
|
||||
await resumed.save(restored, restored.requests[1].id);
|
||||
const saved = JSON.parse(await readFile(manifestPath, 'utf8'));
|
||||
expect(saved.topic.conversation.discussions).toEqual(archives);
|
||||
expect(saved.importedDiscussions).toEqual(imported);
|
||||
expect((await new TeacherConversationStore(dir, 'account', 'agent-a').page()).requests).toHaveLength(2);
|
||||
});
|
||||
it('recovers an import index failure before retrying in the same process', async () => {
|
||||
const dir = await root(), project = { id: randomUUID(), path: path.join(dir, 'project'), name: '旧项目' };
|
||||
const oldStore = new TeacherTopicStore(path.join(project.path, '.makelore', 'teacher-conversations', 'account', 'project'));
|
||||
|
||||
Reference in New Issue
Block a user