Merge pull request #946 from pikasTech/fix/issue934-progress-prefix-history
fix: trim folded progress from agent conversations
This commit is contained in:
@@ -1860,6 +1860,80 @@ test("cloud api exposes final response for legacy progress-polluted snapshots (#
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api trims folded progress from historical multi-turn final response snapshots (#934)", async () => {
|
||||
const nowValue = "2026-06-05T13:39:48.000Z";
|
||||
const projectId = "prj_hwpod_workbench";
|
||||
const conversationId = "cnv_issue934_multiturn_history";
|
||||
const sessionId = "ses_issue934_multiturn_history";
|
||||
const firstTraceId = "trc_issue934_first_turn";
|
||||
const secondTraceId = "trc_issue934_second_turn";
|
||||
const threadId = "thread-issue-934-multiturn";
|
||||
const firstFinalText = "当前工作区没有 `.hwlab/hwpod-spec.yaml`,也没有 `.hwlab` 目录或现成的 hwpod-spec 文件。这意味着这个 AgentRun 会话还没有绑定具体的 HWPOD。\n\n目前可用的 HWPOD:**0 个**(需要先创建 spec 并绑定)。\n\n如果你有已知的 HWPOD 信息(例如节点 ID、target 或 spec 模板),可以给我,我可以帮你生成 `.hwlab/hwpod-spec.yaml`。";
|
||||
const secondUserText = "看看有几个hwpod可用?";
|
||||
const progressText = "Let me check the available HWPODs using the `hwpod` CLI tool.当前工作区还没有 `.hwlab/hwpod-spec.yaml` 文件,所以 `hwpod inspect` 无法获取可用 HWPOD 信息。 让我看看仓库里有没有现成的 spec 示例或相关配置:";
|
||||
const accessController = createAccessController({
|
||||
env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" },
|
||||
now: () => nowValue
|
||||
});
|
||||
const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, accessController, now: () => nowValue });
|
||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
try {
|
||||
const { port } = server.address();
|
||||
const login = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" });
|
||||
assert.equal(login.status, 200);
|
||||
const ownerUserId = login.body.actor.id;
|
||||
|
||||
accessController.store.agentSessions.set(sessionId, {
|
||||
id: sessionId,
|
||||
projectId,
|
||||
agentId: "hwlab-code-agent",
|
||||
status: "completed",
|
||||
startedAt: nowValue,
|
||||
endedAt: null,
|
||||
ownerUserId,
|
||||
conversationId,
|
||||
threadId,
|
||||
lastTraceId: firstTraceId,
|
||||
updatedAt: nowValue,
|
||||
session: {
|
||||
source: "legacy-multiturn-progress-polluted-snapshot",
|
||||
sessionStatus: "completed",
|
||||
lastTraceId: firstTraceId,
|
||||
finalResponse: {
|
||||
text: firstFinalText,
|
||||
textChars: firstFinalText.length,
|
||||
role: "assistant",
|
||||
status: "completed",
|
||||
traceId: firstTraceId,
|
||||
valuesPrinted: false
|
||||
},
|
||||
messages: [
|
||||
{ id: "msg_issue934_first_user", role: "user", title: "用户", text: "你好", status: "sent", traceId: firstTraceId, conversationId, sessionId, threadId, createdAt: nowValue },
|
||||
{ id: "msg_issue934_first_agent", role: "agent", title: "Code Agent 回复", text: firstFinalText, status: "completed", traceId: firstTraceId, conversationId, sessionId, threadId, createdAt: nowValue },
|
||||
{ id: "msg_issue934_second_user", role: "user", title: "用户", text: secondUserText, status: "sent", traceId: secondTraceId, conversationId, sessionId, threadId, createdAt: nowValue },
|
||||
{ id: "msg_issue934_second_agent", role: "agent", title: "Code Agent 回复", text: `${progressText}${firstFinalText}`, status: "completed", traceId: secondTraceId, conversationId, sessionId, threadId, createdAt: nowValue }
|
||||
],
|
||||
messageCount: 4,
|
||||
firstUserMessagePreview: "你好",
|
||||
valuesRedacted: true,
|
||||
secretMaterialStored: false
|
||||
}
|
||||
});
|
||||
|
||||
const direct = await getJson(port, `/v1/agent/conversations/${conversationId}?projectId=${projectId}`, login.cookie);
|
||||
assert.equal(direct.status, 200);
|
||||
assert.equal(direct.body.conversation.messages.length, 4);
|
||||
assert.equal(direct.body.conversation.messages[1].text, firstFinalText);
|
||||
assert.equal(direct.body.conversation.messages[3].text, firstFinalText);
|
||||
assert.equal(direct.body.conversation.messages[3].traceId, secondTraceId);
|
||||
assert.doesNotMatch(direct.body.conversation.messages[3].text, /Let me check|当前工作区还没有|让我看看/u);
|
||||
} finally {
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close((error) => (error ? reject(error) : resolve()));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("access controller restores AgentRun mapping by Code Agent traceId", async () => {
|
||||
const accessController = createAccessController({ now: () => "2026-06-01T00:00:00.000Z" });
|
||||
await accessController.recordAgentSessionOwner({
|
||||
|
||||
@@ -1888,9 +1888,11 @@ function mergeAgentSessionOwnerEvidence(nextValue, existingValue) {
|
||||
function normalizeFinalResponseConversationMessages(messages, finalResponse) {
|
||||
const finalText = textOr(finalResponse?.text, "");
|
||||
const traceId = safeTraceIdLocal(finalResponse?.traceId);
|
||||
if (!Array.isArray(messages) || !finalText || !traceId) return messages;
|
||||
if (!Array.isArray(messages) || !finalText) return messages;
|
||||
return messages.map((message) => {
|
||||
if (message?.role !== "agent" || safeTraceIdLocal(message.traceId) !== traceId) return message;
|
||||
if (message?.role !== "agent") return message;
|
||||
const sameTrace = traceId && safeTraceIdLocal(message.traceId) === traceId;
|
||||
if (!sameTrace && !isFoldedProgressBeforeFinalResponse(message.text, finalText)) return message;
|
||||
return redactConversationMessage({
|
||||
...message,
|
||||
text: finalText,
|
||||
@@ -1899,6 +1901,16 @@ function normalizeFinalResponseConversationMessages(messages, finalResponse) {
|
||||
});
|
||||
}).filter(Boolean);
|
||||
}
|
||||
function isFoldedProgressBeforeFinalResponse(messageText, finalText) {
|
||||
const text = textOr(messageText, "");
|
||||
const final = textOr(finalText, "");
|
||||
if (final.length < 80 || text.length <= final.length) return false;
|
||||
const index = text.indexOf(final);
|
||||
if (index <= 0) return false;
|
||||
const prefix = text.slice(0, index).trim();
|
||||
if (prefix.length < 20) return false;
|
||||
return /\bLet me (?:check|inspect|look)\b|\bI(?:'|’)?ll (?:check|inspect|look)\b|我(?:先|来|再)?(?:确认|检查|查看|看看)|让我(?:确认|检查|查看|看看)|正在(?:确认|检查|查看)|当前工作区还没有|hwpod inspect/iu.test(prefix);
|
||||
}
|
||||
function mergeAgentSessionOwnerMessages(nextMessages = [], existingMessages = [], context = {}) {
|
||||
const next = dedupeConversationMessages(nextMessages);
|
||||
if (next.some((message) => message.role === "user")) return ensureTerminalUserMessage(next, context).slice(-50);
|
||||
|
||||
Reference in New Issue
Block a user