feat: 支持 AI 设计项目多会话交互

需求:用户可在同一设计项目中新建和切换会话,任务列表保持项目级共享。

实现:
- 增加会话选择、新建入口及本地数据迁移
- Conversation 独立消息、Brief、Quote 与流式状态
- 保留项目任务并修复 Task revision 与 ABA 异步竞态
- 保持 Enter 发送、Shift+Enter 换行及 IME 保护
This commit is contained in:
2026-08-06 18:22:15 +08:00
parent 4980894017
commit 03dae62cf3
18 changed files with 1810 additions and 702 deletions

View File

@@ -371,8 +371,16 @@ describe('Sidebar project initialization flow', () => {
const summary = {
workspaceId: 'cloud-project',
title: '云端概念设计',
turnRevision: 0,
viewRevision: 0,
conversationCount: 1,
phase: 'shaping',
updatedAt: '2026-07-31T10:00:00Z',
};
const conversation = {
conversationId: 'cloud-conversation',
workspaceId: 'cloud-project',
title: '主会话',
turnRevision: 0,
phase: 'shaping',
brief: {
version: 0,
@@ -382,8 +390,11 @@ describe('Sidebar project initialization flow', () => {
ready: false,
missingDecision: '作品形式',
},
createdAt: '2026-07-31T10:00:00Z',
updatedAt: '2026-07-31T10:00:00Z',
messages: [],
};
const { messages: _messages, ...conversationSummary } = conversation;
const bootstrap = {
capabilities: {
conversation: true,
@@ -398,7 +409,20 @@ describe('Sidebar project initialization flow', () => {
return { success: true, data: bootstrap };
}
if (path === '/api/works/image-workspace/workspaces/cloud-project' && !init?.method) {
return { success: true, data: { ...summary, messages: [] } };
return { success: true, data: { ...summary, conversations: [conversationSummary] } };
}
if (path === '/api/works/image-workspace/workspaces/cloud-project/conversations/cloud-conversation') {
return { success: true, data: conversation };
}
if (path === '/api/works/image-workspace/workspaces/cloud-project-two/conversations/cloud-conversation-two') {
return {
success: true,
data: {
...conversation,
workspaceId: 'cloud-project-two',
conversationId: 'cloud-conversation-two',
},
};
}
if (path === '/api/works/image-workspace/workspaces/cloud-project/tasks') {
return { success: true, data: [] };
@@ -406,7 +430,16 @@ describe('Sidebar project initialization flow', () => {
if (path === '/api/works/image-workspace/workspaces' && init?.method === 'POST') {
return {
success: true,
data: { ...summary, workspaceId: 'cloud-project-two', title: '角色设定', messages: [] },
data: {
...summary,
workspaceId: 'cloud-project-two',
title: '角色设定',
conversations: [{
...conversationSummary,
workspaceId: 'cloud-project-two',
conversationId: 'cloud-conversation-two',
}],
},
};
}
throw new Error(`Unexpected path ${path}`);
@@ -451,8 +484,16 @@ describe('Sidebar project initialization flow', () => {
const summary = {
workspaceId: 'local-project',
title: '概念设计',
turnRevision: 0,
viewRevision: 0,
conversationCount: 1,
phase: 'shaping',
updatedAt: '2026-07-31T10:00:00Z',
};
const conversation = {
conversationId: 'local-conversation',
workspaceId: 'local-project',
title: '主会话',
turnRevision: 0,
phase: 'shaping',
brief: {
version: 0,
@@ -462,8 +503,11 @@ describe('Sidebar project initialization flow', () => {
ready: false,
missingDecision: '作品形式',
},
createdAt: '2026-07-31T10:00:00Z',
updatedAt: '2026-07-31T10:00:00Z',
messages: [],
};
const { messages: _messages, ...conversationSummary } = conversation;
let bootstrap = {
capabilities: {
conversation: true,
@@ -478,7 +522,10 @@ describe('Sidebar project initialization flow', () => {
return { success: true, data: bootstrap };
}
if (path === '/api/works/image-workspace/workspaces/local-project' && !init?.method) {
return { success: true, data: { ...summary, messages: [] } };
return { success: true, data: { ...summary, conversations: [conversationSummary] } };
}
if (path === '/api/works/image-workspace/workspaces/local-project/conversations/local-conversation') {
return { success: true, data: conversation };
}
if (path === '/api/works/image-workspace/workspaces/local-project/tasks') {
return { success: true, data: [] };
@@ -488,11 +535,25 @@ describe('Sidebar project initialization flow', () => {
...summary,
workspaceId: 'local-project-two',
title: '角色设计',
messages: [],
conversations: [{
...conversationSummary,
workspaceId: 'local-project-two',
conversationId: 'local-conversation-two',
}],
};
bootstrap = { ...bootstrap, workspaces: [created, ...bootstrap.workspaces] };
return { success: true, data: created };
}
if (path === '/api/works/image-workspace/workspaces/local-project-two/conversations/local-conversation-two') {
return {
success: true,
data: {
...conversation,
workspaceId: 'local-project-two',
conversationId: 'local-conversation-two',
},
};
}
throw new Error(`Unexpected path ${path}`);
});