合并 AI 设计多会话客户端
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

整合 Enter 发送与多会话交互,保留服务端持久 Conversation Session,并补齐迁移、回归、Electron E2E 与 canonical 文档。
This commit is contained in:
2026-08-11 16:18:52 +08:00
32 changed files with 2417 additions and 804 deletions

View File

@@ -130,6 +130,8 @@ describe('AI design Main route boundary', () => {
const workspace = {
bootstrap: vi.fn().mockResolvedValue(bootstrap),
createWorkspace: vi.fn().mockResolvedValue({ workspaceId: 'workspace-one' }),
createConversation: vi.fn().mockResolvedValue({ conversationId: 'conversation-one' }),
getConversation: vi.fn().mockResolvedValue({ conversationId: 'conversation/one' }),
renameWorkspace: vi.fn(),
getWorkspace: vi.fn(),
submitMessage: vi.fn().mockResolvedValue({ workspaceId: 'workspace/one' }),
@@ -166,6 +168,43 @@ describe('AI design Main route boundary', () => {
title: '角色设计',
});
const createConversationResponse = createResponse();
await handleImageWorkspaceRoutes(
createRequest('POST', {
clientConversationId: 'client-conversation-one',
title: '第二会话',
}),
createConversationResponse.res,
new URL(
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/conversations',
),
ctx,
);
expect(workspace.createConversation).toHaveBeenCalledWith({
workspaceId: 'workspace/one',
clientConversationId: 'client-conversation-one',
title: '第二会话',
});
expect(createConversationResponse.json()).toMatchObject({
success: true,
data: { conversationId: 'conversation-one' },
});
const getConversationResponse = createResponse();
await handleImageWorkspaceRoutes(
createRequest('GET'),
getConversationResponse.res,
new URL(
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/conversations/conversation%2Fone',
),
ctx,
);
expect(workspace.getConversation).toHaveBeenCalledWith('workspace/one', 'conversation/one');
expect(getConversationResponse.json()).toMatchObject({
success: true,
data: { conversationId: 'conversation/one' },
});
const messageResponse = createResponse();
await handleImageWorkspaceRoutes(
createRequest('POST', {
@@ -177,12 +216,13 @@ describe('AI design Main route boundary', () => {
}),
messageResponse.res,
new URL(
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/messages',
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/conversations/conversation%2Fone/messages',
),
ctx,
);
expect(workspace.submitMessage).toHaveBeenCalledWith({
workspaceId: 'workspace/one',
conversationId: 'conversation/one',
clientTurnId: 'turn-one',
expectedTurnRevision: 3,
message: '继续编辑',
@@ -221,12 +261,13 @@ describe('AI design Main route boundary', () => {
}),
confirmResponse.res,
new URL(
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/quotes/quote%2Fone/confirm',
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/conversations/conversation%2Fone/quotes/quote%2Fone/confirm',
),
ctx,
);
expect(workspace.confirmGeneration).toHaveBeenCalledWith({
workspaceId: 'workspace/one',
conversationId: 'conversation/one',
quoteId: 'quote/one',
clientTurnId: 'turn-two',
expectedTurnRevision: 4,
@@ -391,6 +432,7 @@ describe('AI design Main route boundary', () => {
generationTask: {
taskId: 'task-live',
workspaceId: 'workspace/one',
conversationId: 'conversation/one',
medium: 'image' as const,
status: 'succeeded' as const,
briefVersion: 1,
@@ -414,7 +456,7 @@ describe('AI design Main route boundary', () => {
request,
response as unknown as ServerResponse,
new URL(
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/events',
'http://127.0.0.1/api/works/image-workspace/workspaces/workspace%2Fone/conversations/conversation%2Fone/events',
),
{ imageWorkspace: { openWorkspaceEvents } } as unknown as HostApiContext,
);
@@ -422,6 +464,7 @@ describe('AI design Main route boundary', () => {
expect(handled).toBe(true);
expect(openWorkspaceEvents).toHaveBeenCalledWith({
workspaceId: 'workspace/one',
conversationId: 'conversation/one',
afterEventId: 'session-one:6',
});
expect(response.statusCode).toBe(200);