Merge pull request #1354 from pikasTech/fix/issue1348-final-error-display
fix: surface agentrun terminal errors in final response
This commit is contained in:
@@ -1447,6 +1447,14 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId, a
|
|||||||
const code = canceled ? "agentrun_canceled" : result?.failureKind ?? (terminalStatus === "blocked" ? "agentrun_blocked" : "agentrun_failed");
|
const code = canceled ? "agentrun_canceled" : result?.failureKind ?? (terminalStatus === "blocked" ? "agentrun_blocked" : "agentrun_failed");
|
||||||
const message = result?.failureMessage ?? result?.blocker?.message ?? (canceled ? "AgentRun command was canceled" : "AgentRun command failed");
|
const message = result?.failureMessage ?? result?.blocker?.message ?? (canceled ? "AgentRun command was canceled" : "AgentRun command failed");
|
||||||
const attribution = agentRunFailureAttribution({ code, message, canceled });
|
const attribution = agentRunFailureAttribution({ code, message, canceled });
|
||||||
|
const finalResponse = agentRunTerminalFailureFinalResponse({
|
||||||
|
base,
|
||||||
|
traceId,
|
||||||
|
now,
|
||||||
|
status: canceled ? "canceled" : "failed",
|
||||||
|
text: firstNonEmpty(message, attribution.summary, attribution.userMessage)
|
||||||
|
});
|
||||||
|
const traceSummary = agentRunTerminalFailureTraceSummary({ base, runnerTrace, finalResponse, traceId, terminalStatus: canceled ? "canceled" : terminalStatus || "failed" });
|
||||||
if (appendResultEvent) {
|
if (appendResultEvent) {
|
||||||
traceStore.append(traceId, {
|
traceStore.append(traceId, {
|
||||||
type: "result",
|
type: "result",
|
||||||
@@ -1477,6 +1485,8 @@ function agentRunResultToCodeAgentPayload({ base, result, traceStore, traceId, a
|
|||||||
toolCalls: agentRunToolCalls(result, canceled ? "canceled" : "failed"),
|
toolCalls: agentRunToolCalls(result, canceled ? "canceled" : "failed"),
|
||||||
skills: { status: "delegated", provider: ADAPTER_ID, count: 0, items: [], valuesPrinted: false },
|
skills: { status: "delegated", provider: ADAPTER_ID, count: 0, items: [], valuesPrinted: false },
|
||||||
providerTrace,
|
providerTrace,
|
||||||
|
finalResponse,
|
||||||
|
traceSummary,
|
||||||
error: {
|
error: {
|
||||||
code,
|
code,
|
||||||
layer: "agentrun",
|
layer: "agentrun",
|
||||||
@@ -1520,6 +1530,47 @@ function agentRunCompletedFinalResponse({ base, result, traceId, now }) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function agentRunTerminalFailureFinalResponse({ base, traceId, now, status, text }) {
|
||||||
|
const textValue = String(text ?? "").trim();
|
||||||
|
return {
|
||||||
|
text: textValue || "AgentRun command failed.",
|
||||||
|
textChars: (textValue || "AgentRun command failed.").length,
|
||||||
|
role: "assistant",
|
||||||
|
status,
|
||||||
|
traceId,
|
||||||
|
messageId: base.messageId ?? base.reply?.messageId ?? `msg_${traceId.slice(4)}`,
|
||||||
|
createdAt: base.reply?.createdAt ?? base.createdAt ?? now,
|
||||||
|
updatedAt: now,
|
||||||
|
valuesPrinted: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function agentRunTerminalFailureTraceSummary({ base, runnerTrace, finalResponse, traceId, terminalStatus }) {
|
||||||
|
const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : [];
|
||||||
|
const lastSeq = Math.max(0, ...events.map((event) => Number(event?.seq ?? 0)).filter(Number.isFinite));
|
||||||
|
return {
|
||||||
|
traceId,
|
||||||
|
source: "agentrun-command-result",
|
||||||
|
sourceEventCount: Number(runnerTrace?.eventCount ?? events.length ?? 0),
|
||||||
|
terminalStatus,
|
||||||
|
finalAssistantRow: {
|
||||||
|
role: finalResponse.role,
|
||||||
|
status: finalResponse.status,
|
||||||
|
textChars: finalResponse.textChars,
|
||||||
|
textPreview: finalResponse.text.slice(0, 240),
|
||||||
|
messageId: finalResponse.messageId,
|
||||||
|
valuesPrinted: false
|
||||||
|
},
|
||||||
|
agentRun: {
|
||||||
|
runId: base.agentRun?.runId ?? null,
|
||||||
|
commandId: base.agentRun?.commandId ?? null,
|
||||||
|
lastSeq,
|
||||||
|
valuesPrinted: false
|
||||||
|
},
|
||||||
|
valuesPrinted: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function agentRunCompletedTraceSummary({ base, runnerTrace, finalResponse, traceId }) {
|
function agentRunCompletedTraceSummary({ base, runnerTrace, finalResponse, traceId }) {
|
||||||
const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : [];
|
const events = Array.isArray(runnerTrace?.events) ? runnerTrace.events : [];
|
||||||
const lastSeq = Math.max(0, ...events.map((event) => Number(event?.seq ?? 0)).filter(Number.isFinite));
|
const lastSeq = Math.max(0, ...events.map((event) => Number(event?.seq ?? 0)).filter(Number.isFinite));
|
||||||
|
|||||||
@@ -1071,6 +1071,10 @@ test("cloud api AgentRun adapter exposes invalid tool-call attribution in result
|
|||||||
assert.match(payload.error.userMessage, /无效 tool-call arguments JSON/u);
|
assert.match(payload.error.userMessage, /无效 tool-call arguments JSON/u);
|
||||||
assert.match(payload.error.userMessage, /AgentRun\/provider/u);
|
assert.match(payload.error.userMessage, /AgentRun\/provider/u);
|
||||||
assert.match(payload.error.userMessage, /不是 HWPOD/u);
|
assert.match(payload.error.userMessage, /不是 HWPOD/u);
|
||||||
|
assert.equal(payload.finalResponse.status, "failed");
|
||||||
|
assert.match(payload.finalResponse.text, /invalid function arguments json string/u);
|
||||||
|
assert.equal(payload.traceSummary.terminalStatus, "failed");
|
||||||
|
assert.match(payload.traceSummary.finalAssistantRow.textPreview, /invalid function arguments json string/u);
|
||||||
assert.equal(payload.blocker.category, "provider_invalid_tool_call");
|
assert.equal(payload.blocker.category, "provider_invalid_tool_call");
|
||||||
assert.match(payload.blocker.summary, /invalid function arguments json string/u);
|
assert.match(payload.blocker.summary, /invalid function arguments json string/u);
|
||||||
assert.equal(payload.session.status, "failed");
|
assert.equal(payload.session.status, "failed");
|
||||||
|
|||||||
@@ -295,10 +295,11 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
if (message.traceId !== traceId || message.role !== "agent") return message;
|
if (message.traceId !== traceId || message.role !== "agent") return message;
|
||||||
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result as AgentChatResultResponse);
|
const runnerTrace = mergeTerminalResultTrace(message.runnerTrace, result as AgentChatResultResponse);
|
||||||
const error = normalizeAgentError((result as AgentChatResultResponse).error ?? runnerTrace?.error ?? message.error);
|
const error = normalizeAgentError((result as AgentChatResultResponse).error ?? runnerTrace?.error ?? message.error);
|
||||||
|
const errorText = agentErrorDisplayText(error);
|
||||||
const agentRun = agentRunFromResult(result as AgentChatResultResponse, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result as AgentChatResultResponse, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
const replyText = agentReplyText((result as AgentChatResultResponse).reply);
|
const replyText = agentReplyText((result as AgentChatResultResponse).reply);
|
||||||
const text = terminal
|
const text = terminal
|
||||||
? firstNonEmptyString((result as AgentChatResultResponse).assistantText, finalResponseText((result as AgentChatResultResponse).finalResponse), replyText, (result as AgentChatResultResponse).text, (result as AgentChatResultResponse).summary, message.text, "Code Agent 已完成,但没有返回可展示的 final response。") ?? message.text
|
? firstNonEmptyString((result as AgentChatResultResponse).assistantText, finalResponseText((result as AgentChatResultResponse).finalResponse), replyText, errorText, (result as AgentChatResultResponse).text, (result as AgentChatResultResponse).summary, message.text, "Code Agent 已完成,但没有返回可展示的 final response。") ?? message.text
|
||||||
: message.text;
|
: message.text;
|
||||||
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
});
|
});
|
||||||
@@ -448,8 +449,9 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
const explicitStatus = normalizedStatusText(result.status);
|
const explicitStatus = normalizedStatusText(result.status);
|
||||||
const status = explicitStatus ? statusFromResult(result.status) : message.status;
|
const status = explicitStatus ? statusFromResult(result.status) : message.status;
|
||||||
const terminal = result.terminal === true || isTerminalMessageStatus(status);
|
const terminal = result.terminal === true || isTerminalMessageStatus(status);
|
||||||
const text = terminal ? firstNonEmptyString(result.assistantText, finalResponseText(result.finalResponse), agentReplyText(result.reply), result.text, result.summary, message.text) ?? message.text : message.text;
|
|
||||||
const error = normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
const error = normalizeAgentError(result.error ?? runnerTrace?.error ?? message.error);
|
||||||
|
const errorText = agentErrorDisplayText(error);
|
||||||
|
const text = terminal ? firstNonEmptyString(result.assistantText, finalResponseText(result.finalResponse), agentReplyText(result.reply), errorText, result.text, result.summary, message.text) ?? message.text : message.text;
|
||||||
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
const agentRun = agentRunFromResult(result, runnerTrace) ?? agentRunFromMessage(message);
|
||||||
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
return { ...message, status, text, runnerTrace, error: error ?? message.error ?? null, agentRun: agentRun ?? undefined, updatedAt: new Date().toISOString() };
|
||||||
});
|
});
|
||||||
@@ -525,7 +527,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function completeTrace(traceId: string, result: AgentChatResultResponse): void {
|
function completeTrace(traceId: string, result: AgentChatResultResponse): void {
|
||||||
const text = firstNonEmptyString(result.assistantText, finalResponseText(result.finalResponse), typeof result.reply === "string" ? result.reply : result.reply?.content, result.text, result.summary) ?? "Code Agent 已完成,但没有返回可展示的 final response。";
|
const text = firstNonEmptyString(result.assistantText, finalResponseText(result.finalResponse), typeof result.reply === "string" ? result.reply : result.reply?.content, agentErrorDisplayText(result.error), result.text, result.summary) ?? "Code Agent 已完成,但没有返回可展示的 final response。";
|
||||||
const terminalStatus = result.status === "completed" ? "completed" : statusFromResult(result.status);
|
const terminalStatus = result.status === "completed" ? "completed" : statusFromResult(result.status);
|
||||||
messages.value = messages.value.map((message) => {
|
messages.value = messages.value.map((message) => {
|
||||||
if (message.traceId !== traceId || message.role !== "agent") return message;
|
if (message.traceId !== traceId || message.role !== "agent") return message;
|
||||||
@@ -732,11 +734,16 @@ function selectedConversationFromWorkspace(workspace: WorkspaceRecord | null): C
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeChatMessage(message: ChatMessage): ChatMessage {
|
function normalizeChatMessage(message: ChatMessage): ChatMessage {
|
||||||
const text = firstNonEmptyString(message.text, messageText((message as Record<string, unknown>).content), messageText((message as Record<string, unknown>).message), finalResponseText((message as Record<string, unknown>).finalResponse)) ?? "";
|
|
||||||
const runnerTrace = normalizeMessageRunnerTrace(message);
|
const runnerTrace = normalizeMessageRunnerTrace(message);
|
||||||
const error = normalizeAgentError(message.error ?? runnerTrace?.error);
|
const error = normalizeAgentError(message.error ?? runnerTrace?.error);
|
||||||
const agentRun = agentRunFromMessage(message) ?? asAgentRun(runnerTrace?.agentRun);
|
const agentRun = agentRunFromMessage(message) ?? asAgentRun(runnerTrace?.agentRun);
|
||||||
const status = normalizeChatMessageStatus(message.status);
|
const status = normalizeChatMessageStatus(message.status);
|
||||||
|
const baseText = firstNonEmptyString(message.text, messageText((message as Record<string, unknown>).content), messageText((message as Record<string, unknown>).message));
|
||||||
|
const finalText = firstNonEmptyString(finalResponseText((message as Record<string, unknown>).finalResponse), finalResponseText(runnerTrace?.finalResponse));
|
||||||
|
const errorText = agentErrorDisplayText(error);
|
||||||
|
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 };
|
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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,6 +854,12 @@ function normalizeAgentError(value: unknown): ChatMessage["error"] | null {
|
|||||||
return { message: String(value) };
|
return { message: String(value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function agentErrorDisplayText(value: unknown): string | null {
|
||||||
|
const error = normalizeAgentError(value);
|
||||||
|
if (!error) return null;
|
||||||
|
return firstNonEmptyString(error.message, typeof error.userMessage === "string" ? error.userMessage : null, error.code ? `Code Agent 请求失败:${error.code}` : null);
|
||||||
|
}
|
||||||
|
|
||||||
function isTerminalMessageStatus(value: unknown): boolean {
|
function isTerminalMessageStatus(value: unknown): boolean {
|
||||||
return ["completed", "failed", "blocked", "timeout", "canceled", "cancelled", "stale", "thread-resume-failed"].includes(String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"));
|
return ["completed", "failed", "blocked", "timeout", "canceled", "cancelled", "stale", "thread-resume-failed"].includes(String(value ?? "").trim().toLowerCase().replace(/_/gu, "-"));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user