Repository navigation
修复 Agent 扫描记忆标题及详情页用户内容展示 #725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Orsted290
wants to merge
6
commits into
MemTensor:v1.2.6
Choose a base branch
from
Orsted290:fix-agent-source-memory-titles-v126
base: v1.2.6
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+891
−83
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
15f1100
fix: filter source context from pending memory previews
Orsted290 0a5fe6f
fix: preserve source HTML text in memory details
Orsted290 9f56fed
fix: cover source envelopes and nonpending memory titles
Orsted290 1eac16c
fix: unify trace summaries and source envelope previews
Orsted290 1434a1b
fix: preserve source boundaries and legacy trace bodies
Orsted290 3520195
test: extend Windows runtime fixture timeouts
Orsted290 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
App/frontend/desktop/src/pages/memory/tests/memory-markdown.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| import { readFileSync } from "node:fs"; | ||
| import { renderToStaticMarkup } from "react-dom/server"; | ||
| import { describe, expect, it } from "vitest"; | ||
| import { MemoryMarkdown } from "../memory-markdown.js"; | ||
|
|
||
| const render = (text: string) => renderToStaticMarkup(<MemoryMarkdown text={text} literalHtml />); | ||
| const escape = (text: string) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); | ||
|
|
||
| describe("MemoryMarkdown literal HTML", () => { | ||
| it("keeps HTML hidden unless literal rendering is explicitly enabled", () => { | ||
| const text = '<div>旧页面隐藏内容</div>\n\n**正常正文**'; | ||
| const html = renderToStaticMarkup(<MemoryMarkdown text={text} />); | ||
| expect(html).not.toContain("旧页面隐藏内容"); | ||
| expect(html).not.toContain("literal-html"); | ||
| expect(html).toContain("<strong>正常正文</strong>"); | ||
| for (const file of ["world-model-sub-page.tsx", "computer-history-sub-page.tsx"]) { | ||
| const source = readFileSync(new URL(`../${file}`, import.meta.url), "utf8"); | ||
| expect(source).not.toMatch(/<MemoryMarkdown[^>]*literalHtml/); | ||
| } | ||
| }); | ||
|
|
||
| it.each([ | ||
| '<in-app-browser-context source="ambient-ui-state">\n示例项目\n</in-app-browser-context>\n给搜索框增加清空按钮。', | ||
| '<in-app-browser-context>\n示例项目\n</in-app-browser-context>\n\n给搜索框增加清空按钮。', | ||
| '<in-app-browser-context>\n未闭合的上下文\n给搜索框增加清空按钮。', | ||
| '<timestamp>Tuesday, Jun 16, 2026</timestamp>', | ||
| '<!-- 原始注释 -->\n给搜索框增加清空按钮。', | ||
| '<div>\n多行原文\n</div>\n给搜索框增加清空按钮。' | ||
| ])("preserves envelope and request text: %s", (text) => { | ||
| const html = render(text); | ||
| for (const line of text.split("\n").filter(Boolean)) expect(html.replace(/<[^>]*>/g, "")).toContain(escape(line)); | ||
| expect(html).toContain('class="memory-markdown__literal-html"'); | ||
| }); | ||
|
|
||
| it("preserves raw block line breaks with a scoped style", () => { | ||
| const html = render('<context>\n第一行\n第二行\n</context>'); | ||
| expect(html).toContain('<context>\n第一行\n第二行\n</context>'); | ||
| const css = readFileSync(new URL("../../../styles.css", import.meta.url), "utf8"); | ||
| expect(css).toMatch(/\.memory-markdown__literal-html\s*\{[^}]*white-space:\s*pre-wrap/s); | ||
| }); | ||
|
|
||
| it.each([ | ||
| '<script>alert("test")</script>', | ||
| '<iframe src="https://example.invalid"></iframe>', | ||
| '<img src="https://example.invalid/a" onerror="alert(1)">', | ||
| '<div onclick="alert(1)">原文</div>', | ||
| '<a href="javascript:alert(1)">原文</a>', | ||
| '<style>body{display:none}</style>' | ||
| ])("renders active HTML only as text: %s", (text) => { | ||
| const html = render(text); | ||
| expect(html.replace(/<[^>]*>/g, "")).toContain(escape(text)); | ||
| expect(html).not.toMatch(/<(?:script|iframe|img|div\s+onclick|a\s+href|style)\b/i); | ||
| }); | ||
|
|
||
| it("keeps inline tags literal and normal Markdown formatted", () => { | ||
| const html = render('正文 <b>示例</b>,**加粗**。\n\n- 项目\n\n| 列 |\n| --- |\n| 值 |\n\n`<code>`\n\n```html\n<div>代码</div>\n```'); | ||
| expect(html).toContain('<b>'); | ||
| expect(html).toContain('</b>'); | ||
| expect(html).toContain('<strong>加粗</strong>'); | ||
| expect(html).toContain('<li>项目</li>'); | ||
| expect(html).toContain('<table'); | ||
| expect(html).toContain('<pre'); | ||
| expect(html).toContain('<div>代码</div>'); | ||
| expect(html).not.toContain('<b>'); | ||
| }); | ||
|
|
||
| it("keeps existing inert links and images and accepts empty input", () => { | ||
| const html = render('[链接](https://example.invalid) '); | ||
| expect(html).toContain('链接'); | ||
| expect(html).toContain('图片说明'); | ||
| expect(html).not.toMatch(/<(?:a|img)\b/); | ||
| expect(render('')).toBe('<div class="memory-markdown"></div>'); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
旧记录没有摘要时,原文标签仍会盖过已清洗的用户请求
问题
打开一条尚无有效摘要、也没有生成标题的旧版 L1 记录,如果它以未分段的原文保存,例如 浏览器上下文\n请修复扫描标题,本次读模型已经能提取出 sourceText = 请修复扫描标题,但当记录处于 ready 或 failed 时,列表标题仍显示浏览器标签和上下文。没有有效用户请求的同类记录,也不能可靠回退到记录 ID。
原因
数据库没有摘要,并不意味着前端收到的 item.summary 为空。列表读取先经过 Repositories.toListItem();其中 listSummaryForMemory() 在没有存储摘要时,会把 memoryValue 的第一条可读行填入 summary。详情的 detailSummaryForMemory() 也有正文回退。与此同时,本次 displayFieldsForMemory() 把来源封装过滤掉,另行给出正确的 sourceText。
前端 readySummary() 无法区分真正存储的摘要和这条从正文回填的内容,会接受浏览器标签。这里的 summary ?? waitingPrimaryText(item) 因而优先返回原文,使已经清洗的 sourceText 没有机会使用。新增前端测试直接把 summary 设置为 "" 或占位文案,绕过了服务端实际回填过程,所以未覆盖这条链路。
建议做法
对原始对话记忆统一列表和详情的摘要读取约定:没有有效的存储摘要时,服务返回空的 summary,不要拿原文首行填充。sourceText 是适合标题预览的用户请求文本,这个字段原本就存在,本 PR 改进了它的来源封装过滤规则。没有生成标题或有效摘要时,让前端已有逻辑回退到 sourceText;连有效请求也没有时再显示记录 ID。无需把 sourceText 再伪装成摘要,也不要在前端另加一套标签过滤。
调整列表 listSummaryForMemory()、详情 detailSummaryForMemory() 与前端 displayMemoryTitle() 的衔接,并核对读取详情摘要的 panel/task 调用方。改动限定在此次原始对话摘要回退问题,保留聚合记忆、工作记忆等其它类型的原有字段含义。