fix: preserve interaction replies across chat recovery
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Event as OpencodeEvent, Part } from "@opencode-ai/sdk/v2";
|
||||
|
||||
import { containsForbiddenPersistentContent } from "../utils/persistencePolicy.js";
|
||||
import { logger } from "../logger.js";
|
||||
import { type QuestionAnswers } from "../runtime/opencode.js";
|
||||
|
||||
@@ -61,8 +62,8 @@ const isDevelopmentDebugLoggingEnabled = process.env.NODE_ENV === "development";
|
||||
|
||||
const toolLabels: Record<string, string> = {
|
||||
clear_scada_analysis: "清除 SCADA 地图分析",
|
||||
memory_manager: "记忆写入",
|
||||
render_scada_analysis: "渲染 SCADA 地图分析",
|
||||
memory_manager: "记忆写入",
|
||||
session_search: "历史会话检索",
|
||||
skill_manager: "流程沉淀",
|
||||
};
|
||||
@@ -356,6 +357,40 @@ export const normalizeToolStatus = (status: string) => {
|
||||
return "running";
|
||||
};
|
||||
|
||||
/** Browser-visible tool inputs: retain useful values without credentials or large payloads. */
|
||||
export const sanitizeToolProgressInput = (params: Record<string, unknown>): Record<string, unknown> => {
|
||||
let remaining = 16_000;
|
||||
let nodes = 512;
|
||||
const visit = (value: unknown, depth: number): unknown => {
|
||||
if (remaining <= 0 || nodes-- <= 0) return "[truncated]";
|
||||
if (typeof value === "string") {
|
||||
if (containsForbiddenPersistentContent(value)) return "[redacted]";
|
||||
const limit = Math.min(remaining, 4_000);
|
||||
remaining -= Math.min(value.length, limit);
|
||||
return value.length > limit ? `${value.slice(0, limit)}… [truncated]` : value;
|
||||
}
|
||||
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
||||
if (depth >= 5) return "[truncated]";
|
||||
if (Array.isArray(value)) {
|
||||
const items = value.slice(0, 64).map((item) => visit(item, depth + 1));
|
||||
if (value.length > 64) items.push("[truncated]");
|
||||
return items;
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
const entries = Object.entries(value).slice(0, 64).map(([rawKey, item]) => {
|
||||
const key = rawKey.slice(0, 100);
|
||||
remaining -= key.length;
|
||||
return [key, /authorization|cookie|password|secret|token|api[_-]?key|credential|headers?/i.test(key)
|
||||
? "[redacted]" : visit(item, depth + 1)];
|
||||
});
|
||||
if (Object.keys(value).length > 64) entries.push(["…", "[truncated]"]);
|
||||
return Object.fromEntries(entries);
|
||||
}
|
||||
return "[unsupported]";
|
||||
};
|
||||
return visit(params, 0) as Record<string, unknown>;
|
||||
};
|
||||
|
||||
const formatProgressValue = (value: unknown): string => {
|
||||
if (typeof value === "string") {
|
||||
return value.length > 120 ? `${value.slice(0, 117)}...` : value;
|
||||
@@ -435,11 +470,11 @@ export const buildToolProgressDetail = (
|
||||
error?: string,
|
||||
) => {
|
||||
const toolName = toolLabels[tool] ?? tool;
|
||||
const reasonText = reason ? `;说明:${reason}` : "";
|
||||
const paramsText = `;关键参数:${summarizeToolParams(params)}`;
|
||||
const reasonText = reason ? `;说明:${containsForbiddenPersistentContent(reason) ? "[redacted]" : reason}` : "";
|
||||
const paramsText = `;关键参数:${summarizeToolParams(sanitizeToolProgressInput(params))}`;
|
||||
|
||||
if (status === "error") {
|
||||
const errorText = error ? `;错误:${error}` : "";
|
||||
const errorText = error ? `;错误:${containsForbiddenPersistentContent(error) ? "[redacted]" : error}` : "";
|
||||
return `${toolName} 调用失败${reasonText}${paramsText}${errorText}`;
|
||||
}
|
||||
if (status === "completed") {
|
||||
|
||||
Reference in New Issue
Block a user