feat(coding): add conversation archive and first-message titles

This commit is contained in:
2026-09-20 12:43:51 +08:00
parent d61226532a
commit fd0293fe3d
22 changed files with 698 additions and 69 deletions

View File

@@ -55,6 +55,38 @@ afterEach(async () => {
});
describe('coding project schema v2', () => {
it('assigns the first title once without moving the conversation, and manual names win', async () => {
const root = await makeProjectPath();
let timestamp = NOW;
const store = createCodingConversationStore(root, { now: () => timestamp });
const created = await store.create({ agentId: 'builder', title: '新对话', titleMode: 'automatic', model: MODEL, modelResolution: 'resolved' });
timestamp = NEXT;
await store.setFirstMessageTitle(created.id, '制作登录页面');
await store.setFirstMessageTitle(created.id, '第二句话不能覆盖');
expect(await store.get(created.id)).toMatchObject({ title: '制作登录页面', updatedAt: NOW, autoTitleSet: true });
await store.patchMetadata(created.id, { title: '新对话' });
await store.setFirstMessageTitle(created.id, '也不能覆盖手动占位标题');
expect(await store.get(created.id)).toMatchObject({ title: '新对话', titleMode: 'manual', updatedAt: NOW });
const archived = await store.patchMetadata(created.id, { archivedAt: NEXT });
expect(archived).toMatchObject({ archivedAt: NEXT, updatedAt: NEXT });
const restored = await store.patchMetadata(created.id, { archivedAt: null });
expect(restored.id).toBe(created.id);
expect(restored.title).toBe('新对话');
});
it('leaves historical titles alone and never resurrects a deleted conversation', async () => {
const root = await makeProjectPath();
const store = createCodingConversationStore(root);
const created = await store.create({ agentId: 'builder', title: '旧标题', model: MODEL, modelResolution: 'resolved' });
const { titleMode: _mode, autoTitleSet: _set, ...legacy } = created;
await atomicWriteJson(path.join(root, '.makelore/conversations.json'), { schemaVersion: 2, conversations: [legacy] });
expect(await store.setFirstMessageTitle(created.id, '替换标题')).toBeNull();
expect(await store.get(created.id)).toMatchObject({ title: '旧标题', titleMode: 'manual', autoTitleSet: true });
await store.delete(created.id);
expect(await store.setFirstMessageTitle(created.id, '不能复活')).toBeNull();
expect((await store.read()).conversations).toEqual([]);
});
it('accepts the native max thinking level in a project model reference', () => {
expect(normalizeProductModelRef({
accountId: 'account-local',