Merge pull request #1784 from pikasTech/fix/workbench-terminal-live-1770
fix(workbench): active turn 持续收敛到终态
This commit is contained in:
@@ -19,8 +19,8 @@ const TRACE_HYDRATION_PAGE_LIMIT = 100;
|
||||
const TRACE_HYDRATION_MAX_PAGES = 60;
|
||||
const TRACE_HYDRATION_MAX_ATTEMPTS = 3;
|
||||
const TRACE_HYDRATION_RETRY_DELAY_MS = 700;
|
||||
const ACTIVE_TURN_GAP_DELAY_MS = 1_000;
|
||||
const ACTIVE_TURN_GAP_MAX_ATTEMPTS = 12;
|
||||
const ACTIVE_TURN_GAP_INITIAL_DELAY_MS = 1_000;
|
||||
const ACTIVE_TURN_GAP_MAX_DELAY_MS = 5_000;
|
||||
const SESSION_LIST_PAGE_LIMIT = 20;
|
||||
const WORKBENCH_READ_HYDRATION_CONCURRENCY = 3;
|
||||
|
||||
@@ -726,14 +726,29 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
lastEventLabel: result.lastEventLabel ?? undefined,
|
||||
updatedAt: new Date().toISOString()
|
||||
} as NonNullable<ChatMessage["runnerTrace"]>;
|
||||
const runnerTrace = mergeRunnerTrace(message.runnerTrace, nextTrace);
|
||||
const error = isCompletedResultWithFinalResponse(result) ? null : normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
||||
const projection = projectionFromResult(result) ?? runnerTrace.projection ?? message.projection ?? null;
|
||||
const resultStatus = normalizedStatusText(result.status) ?? null;
|
||||
const terminal = result.terminal === true || isTerminalMessageStatus(resultStatus);
|
||||
const resultError = normalizeAgentError(result.error ?? null);
|
||||
const resultProjection = projectionFromResult(result);
|
||||
const mergedRunnerTrace = mergeRunnerTrace(message.runnerTrace, nextTrace);
|
||||
const runnerTrace = terminal && !resultError && !resultProjection ? clearRunnerTraceTransientDiagnostics(mergedRunnerTrace) : mergedRunnerTrace;
|
||||
const error = resultError ?? (terminal ? null : normalizeAgentError(runnerTrace?.error ?? message.error));
|
||||
const projection = resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null);
|
||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||
rememberTraceAuthority(runnerTrace);
|
||||
return { ...message, ...messageTimingPatch(result), runnerTrace, error: error ?? message.error ?? null, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||
return { ...message, ...messageTimingPatch(result), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||
}));
|
||||
markWorkbenchTraceProjected(traceId);
|
||||
if (result.terminal === true || isTerminalMessageStatus(result.status)) {
|
||||
rememberTurnStatus(traceId, result);
|
||||
clearActiveTurnGapRefresh(traceId);
|
||||
if (ownerSessionId === activeSessionId.value) {
|
||||
chatPending.value = false;
|
||||
currentRequest.value = null;
|
||||
void clearActiveTrace(traceId, "trace-hydration-terminal");
|
||||
restartRealtime("trace-hydration-terminal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setProviderProfile(value: ProviderProfile): void {
|
||||
@@ -983,13 +998,14 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
}
|
||||
if (activeTurnGapTimer) return;
|
||||
const attempts = activeTurnGapAttempts.get(traceId) ?? 0;
|
||||
if (attempts >= ACTIVE_TURN_GAP_MAX_ATTEMPTS) return;
|
||||
activeTurnGapAttempts.set(traceId, attempts + 1);
|
||||
const nextAttempt = attempts + 1;
|
||||
activeTurnGapAttempts.set(traceId, nextAttempt);
|
||||
if (typeof window === "undefined") return;
|
||||
const delayMs = Math.min(ACTIVE_TURN_GAP_MAX_DELAY_MS, ACTIVE_TURN_GAP_INITIAL_DELAY_MS * Math.max(1, nextAttempt));
|
||||
activeTurnGapTimer = window.setTimeout(() => {
|
||||
activeTurnGapTimer = null;
|
||||
void hydrateRealtimeGap(`active-turn-gap:${reason}`);
|
||||
}, ACTIVE_TURN_GAP_DELAY_MS);
|
||||
}, delayMs);
|
||||
}
|
||||
|
||||
function activeTurnGapTraceId(): string | null {
|
||||
@@ -1048,12 +1064,17 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
if (ownerSessionId === activeSessionId.value) recordActivity(`trace:terminal:${firstNonEmptyString(result.lastEventLabel, result.status, "completed") ?? "completed"}`);
|
||||
updateSessionMessages(ownerSessionId, (source) => source.map((message) => {
|
||||
if (!messageMatchesTraceAuthority(message, traceId, authoritySessionId, ownerSessionId)) return message;
|
||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||
const resultStatus = normalizedStatusText(result.status) ?? null;
|
||||
const terminal = result.terminal === true || isTerminalMessageStatus(resultStatus);
|
||||
const resultError = normalizeAgentError(result.error ?? null);
|
||||
const resultProjection = projectionFromResult(result);
|
||||
const mergedRunnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||
const runnerTrace = terminal && !resultError && !resultProjection ? clearRunnerTraceTransientDiagnostics(mergedRunnerTrace) : mergedRunnerTrace;
|
||||
rememberTraceAuthority(runnerTrace);
|
||||
const error = isCompletedResultWithFinalResponse(result) ? null : normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
||||
const projection = projectionFromResult(result) ?? runnerTrace.projection ?? message.projection ?? null;
|
||||
const error = resultError ?? (terminal ? null : normalizeAgentError(runnerTrace?.error ?? message.error));
|
||||
const projection = resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null);
|
||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||
return { ...message, ...messageTimingPatch(result), runnerTrace, error: error ?? message.error ?? null, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||
return { ...message, ...messageTimingPatch(result), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||
}));
|
||||
rememberTurnStatus(traceId, result);
|
||||
markWorkbenchTraceProjected(traceId);
|
||||
@@ -1087,12 +1108,17 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
if (!ownerSessionId) return;
|
||||
updateSessionMessages(ownerSessionId, (source) => source.map((message) => {
|
||||
if (!messageMatchesTraceAuthority(message, traceId, authoritySessionId, ownerSessionId)) return message;
|
||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||
const resultStatus = normalizedStatusText(result.status) ?? null;
|
||||
const terminal = result.terminal === true || isTerminalMessageStatus(resultStatus);
|
||||
const resultError = normalizeAgentError(result.error ?? null);
|
||||
const resultProjection = projectionFromResult(result);
|
||||
const mergedRunnerTrace = mergeTerminalResultTrace(message.runnerTrace, result);
|
||||
const runnerTrace = terminal && !resultError && !resultProjection ? clearRunnerTraceTransientDiagnostics(mergedRunnerTrace) : mergedRunnerTrace;
|
||||
rememberTraceAuthority(runnerTrace);
|
||||
const error = isCompletedResultWithFinalResponse(result) ? null : normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
||||
const error = resultError ?? (terminal ? null : normalizeAgentError(runnerTrace?.error ?? message.error));
|
||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||
const projection = projectionFromResult(result) ?? runnerTrace.projection ?? message.projection ?? null;
|
||||
return { ...message, ...messageTimingPatch(result), title: normalizeWorkbenchMessageTitle(message.role, message.title), runnerTrace, error: error ?? message.error ?? null, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||
const projection = resultProjection ?? (terminal ? null : runnerTrace.projection ?? message.projection ?? null);
|
||||
return { ...message, ...messageTimingPatch(result), title: normalizeWorkbenchMessageTitle(message.role, message.title), runnerTrace, error, projection, projectionStatus: projection?.projectionStatus ?? null, projectionHealth: projection?.projectionHealth ?? null, blocker: projection?.blocker ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||
}));
|
||||
void hydrateTraceEvents(serverState.value.messagesBySessionId[ownerSessionId] ?? []);
|
||||
void refreshSessions(ownerSessionId);
|
||||
@@ -1765,14 +1791,6 @@ function projectedAgentMessageText(input: { status: unknown; finalText?: string
|
||||
return firstNonEmptyString(input.errorText, text) ?? "";
|
||||
}
|
||||
|
||||
function isCompletedResultWithFinalResponse(value: unknown): boolean {
|
||||
const record = recordValue(value);
|
||||
if (!record) return false;
|
||||
const status = normalizedStatusText(record.status);
|
||||
if (status !== "completed") return false;
|
||||
return Boolean(finalResponseText(record.finalResponse) ?? messageText(record.assistantText) ?? messageText(record.text) ?? messageText(record.summary));
|
||||
}
|
||||
|
||||
function isFailureTerminalStatus(value: unknown): boolean {
|
||||
return ["failed", "blocked", "timeout", "canceled", "cancelled", "stale", "thread-resume-failed"].includes(String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user