fix(chat): restore tool execution details
Generic Container CI/CD / test-build-publish (push) Successful in 2m4s
Agent CI/CD v2 / build-test-publish-and-deploy (push) Successful in 2m4s

This commit is contained in:
2026-08-24 18:45:45 +08:00
parent c6efccb88a
commit 774f39cbbe
3 changed files with 48 additions and 6 deletions
+3
View File
@@ -772,6 +772,9 @@ export const streamPromptResponse = async ({
detail: buildToolProgressDetail( detail: buildToolProgressDetail(
part.tool, part.tool,
part.state.status, part.state.status,
toolParams,
reason,
part.state.status === "error" ? part.state.error : undefined,
), ),
}); });
if ( if (
+41 -4
View File
@@ -356,6 +356,37 @@ export const normalizeToolStatus = (status: string) => {
return "running"; return "running";
}; };
const formatProgressValue = (value: unknown): string => {
if (typeof value === "string") {
return value.length > 120 ? `${value.slice(0, 117)}...` : value;
}
if (
typeof value === "number" ||
typeof value === "boolean" ||
value === null ||
value === undefined
) {
return String(value);
}
try {
const serialized = JSON.stringify(value);
return serialized.length > 120 ? `${serialized.slice(0, 117)}...` : serialized;
} catch {
return "[unserializable]";
}
};
const summarizeToolParams = (params: Record<string, unknown>) => {
const ignoredKeys = new Set(["reason", "request_reason", "why", "purpose", "rationale"]);
const summary = Object.entries(params)
.filter(([key]) => !ignoredKeys.has(key))
.slice(0, 4)
.map(([key, value]) => `${key}=${formatProgressValue(value)}`)
.join(", ");
return summary || "无附加参数";
};
export const buildSessionStatusDetail = (status: { type: string; message?: string }) => { export const buildSessionStatusDetail = (status: { type: string; message?: string }) => {
if (status.type === "retry") { if (status.type === "retry") {
return status.message return status.message
@@ -382,19 +413,25 @@ export const buildReasoningProgressDetail = (
export const buildToolProgressDetail = ( export const buildToolProgressDetail = (
tool: string, tool: string,
status: string, status: string,
params: Record<string, unknown>,
reason: string,
error?: string,
) => { ) => {
const toolName = toolLabels[tool] ?? tool; const toolName = toolLabels[tool] ?? tool;
const reasonText = reason ? `;调用原因:${reason}` : "";
const paramsText = `;关键参数:${summarizeToolParams(params)}`;
if (status === "error") { if (status === "error") {
return `${toolName} 调用失败。`; const errorText = error ? `;错误:${error}` : "";
return `${toolName} 调用失败${reasonText}${paramsText}${errorText}`;
} }
if (status === "completed") { if (status === "completed") {
return `${toolName} 已执行完成`; return `${toolName} 已执行完成${reasonText}${paramsText}`;
} }
if (status === "pending") { if (status === "pending") {
return `${toolName} 等待执行。`; return `${toolName} 已进入待执行状态${reasonText}${paramsText}`;
} }
return `${toolName} 正在执行`; return `${toolName} 正在执行${reasonText}${paramsText}`;
}; };
export const getToolProgressTitle = (tool: string, status: string) => { export const getToolProgressTitle = (tool: string, status: string) => {
+4 -2
View File
@@ -168,7 +168,7 @@ describe("streamPromptResponse", () => {
).toBe("最终分析结果。"); ).toBe("最终分析结果。");
}); });
it("keeps reasoning, tool parameters, and raw errors out of progress details", async () => { it("keeps reasoning generic while preserving tool execution details", async () => {
const runtime = { const runtime = {
subscribeEvents: async () => subscribeEvents: async () =>
createEventStream([ createEventStream([
@@ -246,7 +246,9 @@ describe("streamPromptResponse", () => {
(item) => item.event === "progress" && item.data.id === "tool-part-1", (item) => item.event === "progress" && item.data.id === "tool-part-1",
); );
expect(reasoningProgress?.data.detail).toBe("分析步骤已整理完成。"); expect(reasoningProgress?.data.detail).toBe("分析步骤已整理完成。");
expect(toolProgress?.data.detail).toBe("tjwater_cli 调用失败。"); expect(toolProgress?.data.detail).toBe(
"tjwater_cli 调用失败;调用原因:尝试突破分页限制;关键参数:command=network get-all-pipes-properties --limit 5000;错误:HTTP_422 raw backend payload with trace_id=secret-trace",
);
}); });
it("forwards opencode permission requests as SSE payloads", async () => { it("forwards opencode permission requests as SSE payloads", async () => {