fix(workbench): seal final response lifecycle
This commit is contained in:
@@ -1542,6 +1542,7 @@ function agentRunProjectionActivitySignature(result, runnerTrace) {
|
||||
|
||||
function agentRunProjectionObservedTerminal(result, runnerTrace) {
|
||||
const agentRun = result?.agentRun && typeof result.agentRun === "object" ? result.agentRun : {};
|
||||
if (codeAgentPayloadHasSealedFinalResponse(result)) return true;
|
||||
if (
|
||||
isTraceCommandTerminalStatus(result?.status) ||
|
||||
isTraceCommandTerminalStatus(agentRun.status) ||
|
||||
@@ -2021,7 +2022,10 @@ function codeAgentTurnStatusPayload({ traceId, result, snapshot, resultPollError
|
||||
const lifecycle = codeAgentTurnLifecycleFields(traceId, resultObject ?? snapshotObject ?? {});
|
||||
const events = Array.isArray(snapshotObject?.events) ? snapshotObject.events : Array.isArray(resultObject?.runnerTrace?.events) ? resultObject.runnerTrace.events : [];
|
||||
const lastEvent = events.at(-1) ?? null;
|
||||
const finalResponse = resultObject?.finalResponse ?? snapshotObject?.finalResponse ?? snapshotObject?.terminalEvidence?.finalResponse ?? codeAgentFinalResponseEvidence(resultObject ?? snapshotObject ?? {}, traceId);
|
||||
const sealedFinalStatus = codeAgentPayloadHasSealedFinalResponse(resultObject ?? snapshotObject ?? {}) ? "completed" : null;
|
||||
const status = normalizeTurnStatus(
|
||||
sealedFinalStatus,
|
||||
resultObject?.status,
|
||||
resultObject?.agentRun?.terminalStatus,
|
||||
resultObject?.agentRun?.commandState,
|
||||
@@ -2056,7 +2060,7 @@ function codeAgentTurnStatusPayload({ traceId, result, snapshot, resultPollError
|
||||
runnerTrace: runnerTrace ? compactRunnerTraceForResult(runnerTrace, resultTraceEventLimit(options)) : null,
|
||||
agentRun: resultObject?.agentRun ?? snapshotObject?.agentRun ?? null,
|
||||
terminalEvidence: snapshotObject?.terminalEvidence ?? null,
|
||||
finalResponse: resultObject?.finalResponse ?? snapshotObject?.finalResponse ?? snapshotObject?.terminalEvidence?.finalResponse ?? null,
|
||||
finalResponse,
|
||||
traceSummary: resultObject?.traceSummary ?? snapshotObject?.traceSummary ?? snapshotObject?.terminalEvidence?.traceSummary ?? null,
|
||||
retention: snapshotObject?.retention ?? null,
|
||||
eventCount: numberOrNull(snapshotObject?.eventCount ?? runnerTrace?.eventCount ?? events.length),
|
||||
@@ -2726,6 +2730,7 @@ async function recordCodeAgentSessionOwner({ payload = {}, params = {}, options
|
||||
function codeAgentOwnerStatusForResult(result = {}) {
|
||||
const sessionStatus = textValue(result?.session?.status ?? result?.sessionSummary?.status ?? result?.sessionLifecycleStatus);
|
||||
if (sessionStatus === "thread-resume-failed") return "thread-resume-failed";
|
||||
if (codeAgentPayloadHasSealedFinalResponse(result)) return "completed";
|
||||
if (result?.status === "completed") return "completed";
|
||||
if (result?.status === "canceled" || result?.status === "cancelled") return "canceled";
|
||||
return result?.status ?? "active";
|
||||
@@ -2782,7 +2787,7 @@ function codeAgentTraceResultEvidence(payload = {}, params = {}, traceId = null,
|
||||
if (!agentRun && !finalResponse && !traceSummary) return null;
|
||||
return {
|
||||
traceId: resolvedTraceId,
|
||||
status: payload.status ?? agentRun?.terminalStatus ?? agentRun?.commandState ?? null,
|
||||
status: codeAgentPayloadHasSealedFinalResponse(payload) ? "completed" : payload.status ?? agentRun?.terminalStatus ?? agentRun?.commandState ?? null,
|
||||
conversationId,
|
||||
sessionId,
|
||||
threadId,
|
||||
@@ -3036,12 +3041,27 @@ function safeMessageId(value) {
|
||||
|
||||
function codeAgentConversationAgentMessageStatus(payload = {}) {
|
||||
const status = textValue(payload.status ?? payload.agentRun?.terminalStatus ?? payload.commandState ?? payload.runStatus).toLowerCase();
|
||||
if (codeAgentPayloadHasSealedFinalResponse(payload)) return "completed";
|
||||
if (["completed", "done", "success", "active"].includes(status)) return "completed";
|
||||
if (["failed", "blocked", "error", "timeout", "canceled", "cancelled"].includes(status)) return "failed";
|
||||
if (payload.error || payload.blocker) return "failed";
|
||||
return "running";
|
||||
}
|
||||
|
||||
function codeAgentPayloadHasSealedFinalResponse(payload = {}) {
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
if (payload.error || payload.blocker) return false;
|
||||
if (!codeAgentFinalResponseText(payload)) return false;
|
||||
const projectionStatus = textValue(payload.projectionStatus ?? payload.projection?.projectionStatus).toLowerCase().replace(/_/gu, "-");
|
||||
if (projectionStatus === "caught-up") return true;
|
||||
const agentRun = payload.agentRun && typeof payload.agentRun === "object" ? payload.agentRun : {};
|
||||
return isTraceCommandTerminalStatus(payload.status) ||
|
||||
isTraceCommandTerminalStatus(agentRun.status) ||
|
||||
isTraceCommandTerminalStatus(agentRun.runStatus) ||
|
||||
isTraceCommandTerminalStatus(agentRun.commandState) ||
|
||||
isTraceCommandTerminalStatus(agentRun.terminalStatus);
|
||||
}
|
||||
|
||||
function codeAgentConversationRunnerTraceEvidence(runnerTrace = {}, traceId = null) {
|
||||
const events = Array.isArray(runnerTrace.events) ? runnerTrace.events : [];
|
||||
const lastEvent = runnerTrace.lastEvent && typeof runnerTrace.lastEvent === "object" ? runnerTrace.lastEvent : events.at(-1) ?? null;
|
||||
@@ -3069,22 +3089,26 @@ function codeAgentConversationRunnerTraceEvidence(runnerTrace = {}, traceId = nu
|
||||
}
|
||||
|
||||
function codeAgentFinalResponseEvidence(payload = {}, traceId = null) {
|
||||
const text = conversationText(payload.reply?.content ?? payload.message?.content ?? payload.assistantText);
|
||||
const text = codeAgentFinalResponseText(payload);
|
||||
if (!text) return null;
|
||||
return {
|
||||
text,
|
||||
textChars: text.length,
|
||||
messageId: payload.reply?.messageId ?? payload.messageId ?? null,
|
||||
messageId: payload.finalResponse?.messageId ?? payload.reply?.messageId ?? payload.messageId ?? null,
|
||||
role: payload.reply?.role ?? payload.message?.role ?? "assistant",
|
||||
status: payload.status ?? null,
|
||||
status: codeAgentPayloadHasSealedFinalResponse(payload) ? "completed" : payload.finalResponse?.status ?? payload.status ?? null,
|
||||
traceId,
|
||||
createdAt: payload.reply?.createdAt ?? payload.createdAt ?? null,
|
||||
updatedAt: payload.updatedAt ?? null,
|
||||
source: "code-agent-result",
|
||||
createdAt: payload.finalResponse?.createdAt ?? payload.reply?.createdAt ?? payload.createdAt ?? null,
|
||||
updatedAt: payload.finalResponse?.updatedAt ?? payload.updatedAt ?? null,
|
||||
source: payload.finalResponse?.source ?? "code-agent-result",
|
||||
valuesPrinted: false
|
||||
};
|
||||
}
|
||||
|
||||
function codeAgentFinalResponseText(payload = {}) {
|
||||
return conversationText(payload?.finalResponse?.text ?? payload?.finalResponse?.content ?? payload?.reply?.content ?? payload?.message?.content ?? payload?.assistantText);
|
||||
}
|
||||
|
||||
function codeAgentTraceSummaryEvidence(payload = {}, traceId = null, finalResponse = null) {
|
||||
const runnerTrace = payload.runnerTrace && typeof payload.runnerTrace === "object" ? payload.runnerTrace : null;
|
||||
const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : [];
|
||||
|
||||
@@ -244,12 +244,15 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own
|
||||
const projection = createWorkbenchTurnProjection({ traceId: safeId, result: payload, session: { id: resolvedSessionId, status: normalizedStatus, session }, trace: payload?.runnerTrace ?? null });
|
||||
const timing = projectionTimingForStatus(projection.timing, terminal);
|
||||
const diagnostic = projectionDiagnostics({ traceId: safeId, result: payload, trace: payload?.runnerTrace ?? null, projection });
|
||||
const finalText = finalResponseTextValue(projection.finalResponse, payload?.finalResponse, session?.finalResponse, payload?.assistantText);
|
||||
const messages = normalizeMessages(session?.messages, {
|
||||
traceId: safeId,
|
||||
sessionId: resolvedSessionId,
|
||||
conversationId: resolvedConversationId,
|
||||
threadId: resolvedThreadId,
|
||||
terminal,
|
||||
terminalStatus: projection.status ?? normalizedStatus,
|
||||
finalText,
|
||||
timestamp,
|
||||
timing
|
||||
});
|
||||
@@ -340,22 +343,28 @@ function buildWorkbenchProjectionFacts({ traceId = null, ownerUserId = null, own
|
||||
}
|
||||
|
||||
function normalizeMessages(messages, context) {
|
||||
if (!Array.isArray(messages)) return [];
|
||||
return messages.map((message, index) => normalizeMessageFact(message, index, context)).filter(Boolean);
|
||||
const normalized = Array.isArray(messages) ? messages.map((message, index) => normalizeMessageFact(message, index, context)).filter(Boolean) : [];
|
||||
if (context?.terminal && context?.finalText && context?.traceId && !normalized.some((message) => message.role !== "user" && message.traceId === context.traceId)) {
|
||||
const projected = normalizeMessageFact({ role: "agent", status: context.terminalStatus ?? "completed", text: context.finalText, traceId: context.traceId, source: "workbench-terminal-projection" }, normalized.length, context);
|
||||
if (projected) normalized.push(projected);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizeMessageFact(message = {}, index, { traceId, sessionId, conversationId, threadId, terminal, timestamp, timing: contextTiming }) {
|
||||
function normalizeMessageFact(message = {}, index, { traceId, sessionId, conversationId, threadId, terminal, terminalStatus = "completed", finalText = null, timestamp, timing: contextTiming }) {
|
||||
const messageId = textValue(message.messageId ?? message.id) || stableFactId("msg", { traceId, index, role: message.role });
|
||||
if (!sessionId || !messageId) return null;
|
||||
const role = textValue(message.role) || "agent";
|
||||
const status = normalizeWorkbenchStatus(message.status ?? (terminal && role !== "user" ? "completed" : role === "user" ? "sent" : "running"));
|
||||
const messageTerminal = role !== "user" && TERMINAL_STATUSES.has(status);
|
||||
const messageTraceId = safeTraceId(message.traceId ?? message.turnId);
|
||||
const appliesToContextTrace = role !== "user" && Boolean(traceId && messageTraceId && messageTraceId === traceId);
|
||||
const appliesToContextTrace = role !== "user" && Boolean(traceId && (!messageTraceId || messageTraceId === traceId));
|
||||
const contextTerminalStatus = normalizeWorkbenchStatus(terminalStatus) || "completed";
|
||||
const status = appliesToContextTrace && terminal ? contextTerminalStatus : normalizeWorkbenchStatus(message.status ?? (terminal && role !== "user" ? contextTerminalStatus : role === "user" ? "sent" : "running"));
|
||||
const messageTerminal = role !== "user" && TERMINAL_STATUSES.has(status);
|
||||
const messageTiming = normalizeTimingProjection(message.timing) ?? normalizeTimingProjection(message);
|
||||
const contextTimingProjection = appliesToContextTrace ? normalizeTimingProjection(contextTiming) : null;
|
||||
const timing = role === "user" ? messageTiming ?? emptyTimingProjection() : contextTimingProjection ?? messageTiming ?? emptyTimingProjection();
|
||||
const terminalCompleted = messageTerminal && status === "completed";
|
||||
const text = appliesToContextTrace && terminal && finalText ? finalText : textValue(message.text ?? message.content ?? message.message);
|
||||
return {
|
||||
...message,
|
||||
messageId,
|
||||
@@ -371,7 +380,7 @@ function normalizeMessageFact(message = {}, index, { traceId, sessionId, convers
|
||||
sourceEventId: textValue(message.sourceEventId) || `${messageId}:0`,
|
||||
terminal: messageTerminal,
|
||||
sealed: messageTerminal,
|
||||
text: textValue(message.text ?? message.content ?? message.message),
|
||||
text,
|
||||
timing,
|
||||
startedAt: timing.startedAt,
|
||||
lastEventAt: timing.lastEventAt,
|
||||
@@ -384,6 +393,19 @@ function normalizeMessageFact(message = {}, index, { traceId, sessionId, convers
|
||||
};
|
||||
}
|
||||
|
||||
function finalResponseTextValue(...values) {
|
||||
for (const value of values) {
|
||||
if (value && typeof value === "object") {
|
||||
const nested = finalResponseTextValue(value.text, value.content, value.message, value.summary, value.preview);
|
||||
if (nested) return nested;
|
||||
continue;
|
||||
}
|
||||
const text = textValue(value);
|
||||
if (text) return text;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function latestWorkbenchCheckpoint(runtimeStore, traceId) {
|
||||
if (typeof runtimeStore?.queryWorkbenchFacts !== "function") return null;
|
||||
const result = await runtimeStore.queryWorkbenchFacts({ traceId, families: ["checkpoints"], limit: 1 });
|
||||
|
||||
Reference in New Issue
Block a user