diff --git a/internal/cloud/access-control.test.ts b/internal/cloud/access-control.test.ts index e2f5d77e..e882e7ab 100644 --- a/internal/cloud/access-control.test.ts +++ b/internal/cloud/access-control.test.ts @@ -1944,6 +1944,7 @@ test("cloud api trims folded progress when only conversation messages preserve t const threadId = "thread-issue-934-message-only"; const finalText = "当前工作区没有 `.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 progressText = "Let me check the available HWPODs using the `hwpod` CLI tool.当前工作区还没有 `.hwlab/hwpod-spec.yaml` 文件,所以 `hwpod inspect` 无法获取可用 HWPOD 信息。 让我看看仓库里有没有现成的 spec 示例或相关配置:"; + const whitespaceFoldedFinalText = finalText.replace(/\s+/gu, " "); const accessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => nowValue @@ -1976,7 +1977,7 @@ test("cloud api trims folded progress when only conversation messages preserve t { id: "msg_issue934_message_only_first_user", role: "user", title: "用户", text: "你好", status: "sent", traceId: firstTraceId, conversationId, sessionId, threadId, createdAt: nowValue }, { id: "msg_issue934_message_only_first_agent", role: "agent", title: "Code Agent 回复", text: finalText, status: "completed", traceId: firstTraceId, conversationId, sessionId, threadId, createdAt: nowValue }, { id: "msg_issue934_message_only_second_user", role: "user", title: "用户", text: "看看有几个hwpod可用?", status: "sent", traceId: secondTraceId, conversationId, sessionId, threadId, createdAt: nowValue }, - { id: "msg_issue934_message_only_second_agent", role: "agent", title: "Code Agent 回复", text: `${progressText}${finalText}`, status: "completed", traceId: secondTraceId, conversationId, sessionId, threadId, createdAt: nowValue } + { id: "msg_issue934_message_only_second_agent", role: "agent", title: "Code Agent 回复", text: `${progressText}${whitespaceFoldedFinalText}`, status: "completed", traceId: secondTraceId, conversationId, sessionId, threadId, createdAt: nowValue } ], messageCount: 4, firstUserMessagePreview: "你好", diff --git a/internal/cloud/access-control.ts b/internal/cloud/access-control.ts index 5c7572fe..56bd5f9a 100644 --- a/internal/cloud/access-control.ts +++ b/internal/cloud/access-control.ts @@ -1906,11 +1906,15 @@ function isFoldedProgressBeforeFinalResponse(messageText, finalText) { 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(); + const foldedIndex = index > 0 ? index : collapseConversationWhitespace(text).indexOf(collapseConversationWhitespace(final)); + if (foldedIndex <= 0) return false; + const prefix = text.slice(0, Math.min(foldedIndex, text.length)).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 collapseConversationWhitespace(value) { + return textOr(value, "").replace(/\s+/gu, " ").trim(); +} function normalizeFoldedProgressConversationMessages(messages) { if (!Array.isArray(messages) || messages.length < 2) return messages; const cleanAgentTexts = [];