























@@ -8,6 +8,24 @@ import { createMemoryWikiTestHarness } from "./test-helpers.js";
8899const { createVault } = createMemoryWikiTestHarness();
101011+function hasLoneSurrogate(value: string): boolean {
12+for (let index = 0; index < value.length; index += 1) {
13+const code = value.charCodeAt(index);
14+if (code >= 0xd800 && code <= 0xdbff) {
15+const next = value.charCodeAt(index + 1);
16+if (!(next >= 0xdc00 && next <= 0xdfff)) {
17+return true;
18+}
19+index += 1;
20+continue;
21+}
22+if (code >= 0xdc00 && code <= 0xdfff) {
23+return true;
24+}
25+}
26+return false;
27+}
28+1129describe("listMemoryWikiImportInsights", () => {
1230it("clusters ChatGPT import pages by topic and extracts digest fields", async () => {
1331const { rootDir, config } = await createVault({
@@ -139,4 +157,57 @@ describe("listMemoryWikiImportInsights", () => {
139157expect(healthItem?.lastUserLine).toBeUndefined();
140158expect(healthItem?.assistantOpener).toBeUndefined();
141159});
160+161+it("truncates import insight summaries without leaving lone surrogates", async () => {
162+const { rootDir, config } = await createVault({
163+prefix: "memory-wiki-import-insights-surrogate-",
164+initialize: true,
165+});
166+await fs.mkdir(path.join(rootDir, "sources"), { recursive: true });
167+const assistantOpener = `${"a".repeat(178)}😀${"b".repeat(20)}`;
168+await fs.writeFile(
169+path.join(rootDir, "sources", "chatgpt-emoji.md"),
170+renderWikiMarkdown({
171+frontmatter: {
172+pageType: "source",
173+id: "source.chatgpt.emoji",
174+title: "ChatGPT Export: Emoji truncation",
175+sourceType: "chatgpt-export",
176+riskLevel: "low",
177+riskReasons: [],
178+labels: ["domain/work", "area/memory", "topic/memory"],
179+updatedAt: "2026-02-01T12:00:00.000Z",
180+},
181+body: [
182+"# ChatGPT Export: Emoji truncation",
183+"",
184+"## Auto Digest",
185+"- User messages: 1",
186+"- Assistant messages: 1",
187+"- First user line: summarize this",
188+"- Last user line: summarize this",
189+"- Preference signals:",
190+" - prefers emoji-safe summaries",
191+"",
192+"## Active Branch Transcript",
193+"### User",
194+"",
195+"summarize this",
196+"",
197+"### Assistant",
198+"",
199+assistantOpener,
200+"",
201+].join("\n"),
202+}),
203+"utf8",
204+);
205+206+const result = await listMemoryWikiImportInsights(config);
207+208+const item = result.clusters[0]?.items[0];
209+expect(item?.summary).toBe(`${"a".repeat(178)}…`);
210+expect(hasLoneSurrogate(item?.summary ?? "")).toBe(false);
211+expect(item?.summary).not.toContain("�");
212+});
142213});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。