























1+import fs from "node:fs/promises";
2+import path from "node:path";
3+import { describe, expect, it } from "vitest";
4+import { ingestMemoryWikiSource } from "./ingest.js";
5+import { createMemoryWikiTestHarness } from "./test-helpers.js";
6+7+const { createTempDir, createVault } = createMemoryWikiTestHarness();
8+9+describe("ingestMemoryWikiSource human notes", () => {
10+it("preserves user notes when the same source is re-ingested", async () => {
11+const rootDir = await createTempDir("memory-wiki-reingest-");
12+const inputPath = path.join(rootDir, "roadmap.txt");
13+const { config } = await createVault({ rootDir: path.join(rootDir, "vault") });
14+15+await fs.writeFile(inputPath, "v1 content\n", "utf8");
16+await ingestMemoryWikiSource({
17+ config,
18+ inputPath,
19+nowMs: Date.UTC(2026, 3, 5, 12, 0, 0),
20+});
21+22+const pagePath = path.join(config.vault.path, "sources", "roadmap.md");
23+const userNote = "KEY INSIGHT: covers $1 of the Q2 roadmap";
24+const edited = (await fs.readFile(pagePath, "utf8")).replace(
25+"<!-- openclaw:human:start -->\n<!-- openclaw:human:end -->",
26+`<!-- openclaw:human:start -->\n${userNote}\n<!-- openclaw:human:end -->`,
27+);
28+await fs.writeFile(pagePath, edited, "utf8");
29+30+await fs.writeFile(inputPath, "v2 content updated\n", "utf8");
31+await ingestMemoryWikiSource({
32+ config,
33+ inputPath,
34+nowMs: Date.UTC(2026, 3, 6, 12, 0, 0),
35+});
36+37+const after = await fs.readFile(pagePath, "utf8");
38+expect(after).toContain("v2 content updated");
39+expect(after).toContain(userNote);
40+});
41+42+it("preserves notes without corrupting source content that contains human markers", async () => {
43+const rootDir = await createTempDir("memory-wiki-markers-");
44+const inputPath = path.join(rootDir, "notes.txt");
45+const { config } = await createVault({ rootDir: path.join(rootDir, "vault") });
46+47+await fs.writeFile(inputPath, "first body\n", "utf8");
48+await ingestMemoryWikiSource({
49+ config,
50+ inputPath,
51+nowMs: Date.UTC(2026, 3, 5, 12, 0, 0),
52+});
53+54+const pagePath = path.join(config.vault.path, "sources", "notes.md");
55+const userNote = "MY PRIVATE NOTE";
56+const edited = (await fs.readFile(pagePath, "utf8")).replace(
57+"<!-- openclaw:human:start -->\n<!-- openclaw:human:end -->",
58+`<!-- openclaw:human:start -->\n${userNote}\n<!-- openclaw:human:end -->`,
59+);
60+await fs.writeFile(pagePath, edited, "utf8");
61+62+const sourceWithMarkers = [
63+"second body",
64+"<!-- openclaw:human:start -->",
65+"INJECTED FROM SOURCE",
66+"<!-- openclaw:human:end -->",
67+"",
68+].join("\n");
69+await fs.writeFile(inputPath, sourceWithMarkers, "utf8");
70+await ingestMemoryWikiSource({
71+ config,
72+ inputPath,
73+nowMs: Date.UTC(2026, 3, 6, 12, 0, 0),
74+});
75+76+const after = await fs.readFile(pagePath, "utf8");
77+const notesBlock = after.slice(after.indexOf("## Notes"));
78+expect(after).toContain("INJECTED FROM SOURCE");
79+expect(notesBlock).toContain(userNote);
80+expect(notesBlock).not.toContain("INJECTED FROM SOURCE");
81+});
82+83+it("preserves CRLF notes without copying marker comments from existing source content", async () => {
84+const rootDir = await createTempDir("memory-wiki-crlf-markers-");
85+const inputPath = path.join(rootDir, "windows-notes.txt");
86+const { config } = await createVault({ rootDir: path.join(rootDir, "vault") });
87+88+const sourceWithMarkers = [
89+"first body",
90+"<!-- openclaw:human:start -->",
91+"OLD SOURCE MARKER PAYLOAD",
92+"<!-- openclaw:human:end -->",
93+"",
94+].join("\n");
95+await fs.writeFile(inputPath, sourceWithMarkers, "utf8");
96+await ingestMemoryWikiSource({
97+ config,
98+ inputPath,
99+nowMs: Date.UTC(2026, 3, 5, 12, 0, 0),
100+});
101+102+const pagePath = path.join(config.vault.path, "sources", "windows-notes.md");
103+const userNote = "CRLF USER NOTE";
104+const edited = (await fs.readFile(pagePath, "utf8")).replace(
105+"<!-- openclaw:human:start -->\n<!-- openclaw:human:end -->",
106+`<!-- openclaw:human:start -->\n${userNote}\n<!-- openclaw:human:end -->`,
107+);
108+await fs.writeFile(pagePath, edited.replace(/\n/g, "\r\n"), "utf8");
109+110+await fs.writeFile(inputPath, "second body without marker comments\n", "utf8");
111+await ingestMemoryWikiSource({
112+ config,
113+ inputPath,
114+nowMs: Date.UTC(2026, 3, 6, 12, 0, 0),
115+});
116+117+const after = await fs.readFile(pagePath, "utf8");
118+const notesBlock = after.slice(after.indexOf("## Notes"));
119+expect(after).toContain("second body without marker comments");
120+expect(notesBlock).toContain(userNote);
121+expect(notesBlock).not.toContain("OLD SOURCE MARKER PAYLOAD");
122+});
123+124+it("preserves the whole note when the note text itself contains a marker comment", async () => {
125+const rootDir = await createTempDir("memory-wiki-innermarker-");
126+const inputPath = path.join(rootDir, "diary.txt");
127+const { config } = await createVault({ rootDir: path.join(rootDir, "vault") });
128+129+await fs.writeFile(inputPath, "first body\n", "utf8");
130+await ingestMemoryWikiSource({
131+ config,
132+ inputPath,
133+nowMs: Date.UTC(2026, 3, 5, 12, 0, 0),
134+});
135+136+const pagePath = path.join(config.vault.path, "sources", "diary.md");
137+const noteWithMarker = [
138+"EARLY NOTE before any quoted marker",
139+"<!-- openclaw:human:start -->",
140+"LATE NOTE after a pasted marker",
141+].join("\n");
142+const edited = (await fs.readFile(pagePath, "utf8")).replace(
143+"<!-- openclaw:human:start -->\n<!-- openclaw:human:end -->",
144+`<!-- openclaw:human:start -->\n${noteWithMarker}\n<!-- openclaw:human:end -->`,
145+);
146+await fs.writeFile(pagePath, edited, "utf8");
147+148+await fs.writeFile(inputPath, "second body\n", "utf8");
149+await ingestMemoryWikiSource({
150+ config,
151+ inputPath,
152+nowMs: Date.UTC(2026, 3, 6, 12, 0, 0),
153+});
154+155+const after = await fs.readFile(pagePath, "utf8");
156+expect(after).toContain("second body");
157+expect(after).toContain("EARLY NOTE before any quoted marker");
158+expect(after).toContain("LATE NOTE after a pasted marker");
159+});
160+161+it("preserves the note when the note text contains a Markdown heading", async () => {
162+const rootDir = await createTempDir("memory-wiki-heading-");
163+const inputPath = path.join(rootDir, "log.txt");
164+const { config } = await createVault({ rootDir: path.join(rootDir, "vault") });
165+166+await fs.writeFile(inputPath, "first body\n", "utf8");
167+await ingestMemoryWikiSource({
168+ config,
169+ inputPath,
170+nowMs: Date.UTC(2026, 3, 5, 12, 0, 0),
171+});
172+173+const pagePath = path.join(config.vault.path, "sources", "log.md");
174+const noteWithHeading = ["NOTE TOP", "## Notes", "NOTE BOTTOM under a pasted heading"].join(
175+"\n",
176+);
177+const edited = (await fs.readFile(pagePath, "utf8")).replace(
178+"<!-- openclaw:human:start -->\n<!-- openclaw:human:end -->",
179+`<!-- openclaw:human:start -->\n${noteWithHeading}\n<!-- openclaw:human:end -->`,
180+);
181+await fs.writeFile(pagePath, edited, "utf8");
182+183+await fs.writeFile(inputPath, "second body\n", "utf8");
184+await ingestMemoryWikiSource({
185+ config,
186+ inputPath,
187+nowMs: Date.UTC(2026, 3, 6, 12, 0, 0),
188+});
189+190+const after = await fs.readFile(pagePath, "utf8");
191+expect(after).toContain("second body");
192+expect(after).toContain("NOTE TOP");
193+expect(after).toContain("NOTE BOTTOM under a pasted heading");
194+});
195+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。