fix(workbench): expose turn lifecycle ids

This commit is contained in:
lyon
2026-06-17 12:34:22 +08:00
parent 9e460346b9
commit 8a4a3f1248
4 changed files with 94 additions and 4 deletions
+19 -1
View File
@@ -1,3 +1,6 @@
// SPEC: PJ2026-010403 API契约 draft-2026-06-17-r0; PJ2026-010401 Web工作台 draft-2026-06-17-r0.
// Responsibility: Workbench client state orchestration for session selection, turn admission, and trace lifecycle rendering.
import { computed, ref } from "vue";
import { defineStore } from "pinia";
import { api } from "@/api";
@@ -377,6 +380,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
return;
}
void refreshSessionStatusById(sessionId);
alignOptimisticTurnMessages(traceId, response.data);
applyTurnStatusSnapshot(traceId, response.data);
void refreshConversations(conversationId);
subscribe(traceId, response.data);
@@ -656,6 +660,19 @@ export const useWorkbenchStore = defineStore("workbench", () => {
messages.value = messages.value.map((message) => message.traceId === traceId && message.role === "agent" ? { ...message, ...patch, updatedAt: new Date().toISOString() } : message);
}
function alignOptimisticTurnMessages(traceId: string, lifecycle: AgentChatResponse): void {
const turnId = firstNonEmptyString(lifecycle.turnId, traceId);
const userMessageId = firstNonEmptyString(lifecycle.userMessageId);
const assistantMessageId = firstNonEmptyString(lifecycle.assistantMessageId);
if (!turnId && !userMessageId && !assistantMessageId) return;
messages.value = messages.value.map((message) => {
if (message.traceId !== traceId) return message;
if (message.role === "user" && userMessageId) return { ...message, id: userMessageId, messageId: userMessageId, turnId, updatedAt: new Date().toISOString() };
if (message.role === "agent" && assistantMessageId) return { ...message, id: assistantMessageId, messageId: assistantMessageId, turnId, updatedAt: new Date().toISOString() };
return turnId ? { ...message, turnId, updatedAt: new Date().toISOString() } : message;
});
}
async function ensureWorkspace(): Promise<WorkspaceRecord | null> {
if (workspace.value) return workspace.value;
await hydrate();
@@ -879,7 +896,8 @@ function normalizeChatMessage(message: ChatMessage): ChatMessage {
const text = message.role === "agent" && isTerminalMessageStatus(status)
? firstNonEmptyString(finalText, errorText, baseText) ?? ""
: firstNonEmptyString(baseText, finalText, errorText) ?? "";
return { ...message, text, id: message.id ?? nextProtocolId("msg"), title: normalizeWorkbenchMessageTitle(message.role, message.title), createdAt: message.createdAt ?? new Date().toISOString(), status, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined };
const messageId = firstNonEmptyString((message as Record<string, unknown>).messageId, message.id) ?? nextProtocolId("msg");
return { ...message, text, id: messageId, messageId, title: normalizeWorkbenchMessageTitle(message.role, message.title), createdAt: message.createdAt ?? new Date().toISOString(), status, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined };
}
function activeTraceIdFromMessages(messages: ChatMessage[], turnStatusAuthority: Record<string, TurnStatusAuthority>): string | null {
+8
View File
@@ -1,3 +1,6 @@
// SPEC: PJ2026-010403 API契约 draft-2026-06-17-r0; PJ2026-010401 Web工作台 draft-2026-06-17-r0.
// Responsibility: Cloud Web API and Workbench state type contracts shared by stores and components.
export type Tone = "ok" | "pending" | "blocked" | "error" | "warn" | "dev-live" | "dry-run";
export type AuthState = "checking" | "login" | "authenticated";
export type ProviderProfile = string;
@@ -167,6 +170,8 @@ export interface ChatMessage {
createdAt: string;
updatedAt?: string;
traceId?: string | null;
turnId?: string | null;
messageId?: string | null;
conversationId?: string | null;
sessionId?: string | null;
threadId?: string | null;
@@ -200,6 +205,9 @@ export interface AgentChatResponse {
text?: string;
summary?: string;
traceId?: string;
turnId?: string | null;
userMessageId?: string | null;
assistantMessageId?: string | null;
conversationId?: string;
sessionId?: string;
threadId?: string;