fix: stabilize ai design history switching

This commit is contained in:
2026-08-19 10:31:36 +08:00
parent abece81fdb
commit bf0b805d9f
14 changed files with 573 additions and 48 deletions

View File

@@ -86,6 +86,7 @@ type ServerQuote = {
};
type ServerMessage = {
message_id?: string;
role: DesignMessage['role'];
kind: DesignMessage['kind'];
text: string;
@@ -119,6 +120,10 @@ type ServerConversationSummary = {
type ServerConversation = ServerConversationSummary & {
messages: ServerMessage[];
message_page?: {
has_older: boolean;
next_before: string | null;
};
};
type ServerWorkspace = ServerWorkspaceSummary & {
@@ -264,6 +269,7 @@ type AgentCommandChannel = {
};
const AGENT_WEBSOCKET_OPEN = 1;
const AGENT_WEBSOCKET_OPEN_TIMEOUT_MS = 10_000;
const AGENT_WEBSOCKET_PING_INTERVAL_MS = 20_000;
const AGENT_COMMAND_ACK_TIMEOUT_MS = 5_000;
const AGENT_RUN_INITIAL_POLL_INTERVAL_MS = 1_000;
@@ -393,7 +399,8 @@ function mapConversationSummary(
function mapConversation(conversation: ServerConversation): DesignConversation {
const messages = conversation.messages.map((message, index) => ({
id: `${conversation.conversation_id}:${message.turn_revision}:${message.role}:${index}`,
id: message.message_id
?? `${conversation.conversation_id}:${message.turn_revision}:${message.role}:${index}`,
role: message.role,
kind: message.kind,
text: message.text,
@@ -408,6 +415,12 @@ function mapConversation(conversation: ServerConversation): DesignConversation {
?? normalizeMessagePreview(conversation.latest_message_preview)
?? normalizeMessagePreview(conversation.brief.summary),
messages,
messagePage: conversation.message_page
? {
hasOlder: conversation.message_page.has_older,
nextBefore: conversation.message_page.next_before,
}
: undefined,
};
}
@@ -935,9 +948,11 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
async getConversation(
workspaceId: string,
conversationId: string,
before?: string,
): Promise<DesignConversation> {
const query = before ? `?before=${encodeURIComponent(before)}` : '';
const conversation = await this.requestJson<ServerConversation>(
`/api/design/workspaces/${encodeURIComponent(workspaceId)}/conversations/${encodeURIComponent(conversationId)}`,
`/api/design/workspaces/${encodeURIComponent(workspaceId)}/conversations/${encodeURIComponent(conversationId)}${query}`,
);
this.rememberConversation(conversation);
return mapConversation(conversation);
@@ -1328,6 +1343,7 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
let ending = false;
let settled = false;
let heartbeat: ReturnType<typeof setInterval> | null = null;
let openedTimeout: ReturnType<typeof setTimeout> | null = null;
let unregister = () => undefined;
let resolveOpened: () => void = () => undefined;
let rejectOpened: (error: unknown) => void = () => undefined;
@@ -1349,6 +1365,8 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
socket.onclose = null;
if (heartbeat !== null) clearInterval(heartbeat);
heartbeat = null;
if (openedTimeout !== null) clearTimeout(openedTimeout);
openedTimeout = null;
unregister();
if (didOpen) {
this.unregisterAgentCommandChannel(sessionId, commandChannel);
@@ -1450,6 +1468,19 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
input.conversationId,
close,
);
openedTimeout = setTimeout(() => {
if (ending || didOpen) return;
beginEnd(new DesignWorkspaceModuleError(
504,
'DESIGN_EVENT_STREAM_TIMEOUT',
'AI 设计任务状态连接超时,请稍后重试',
));
try {
socket.close(1000, 'Timed out opening design event stream');
} catch {
// settle below remains authoritative when the transport cannot close.
}
}, AGENT_WEBSOCKET_OPEN_TIMEOUT_MS);
await opened;
return {
events: queue.events,