mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-26 04:49:36 +00:00
* fix(memory-wiki): preserve human notes block on source re-ingest Re-ingesting an existing source regenerated the page with an empty wrote inside the human-managed markers. This broke the documented contract that human note blocks are preserved, and diverged from the synthesis and chatgpt-import writers that already preserve the block. When a source page already exists, read it and re-inject its human Notes block before writing. The block is located by scanning past the fenced the content, then taking the first human start marker and the last end marker, so the whole Notes block is preserved verbatim even when the source content or the note text contains the markers or Markdown headings. The same preservation is applied to writeImportedSourcePage so the bridge and unsafe-local source-update writers keep notes too. New page creation is unchanged. Adds regressions for plain re-ingest, marker text in source content, marker text inside the note, a heading inside the note, and an imported source page update. * fix(memory-wiki): preserve notes on CRLF source pages
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
// Memory Wiki plugin module implements source page shared behavior.
|
|
import fs from "node:fs/promises";
|
|
import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
import { FsSafeError, root as fsRoot } from "openclaw/plugin-sdk/security-runtime";
|
|
import { preserveHumanNotesBlock } from "./markdown.js";
|
|
import {
|
|
setImportedSourceEntry,
|
|
shouldSkipImportedSourceWrite,
|
|
type MemoryWikiImportedSourceGroup,
|
|
} from "./source-sync-state.js";
|
|
import { writeGuardedVaultPage } from "./vault-page-write.js";
|
|
|
|
type ImportedSourceState = Parameters<typeof shouldSkipImportedSourceWrite>[0]["state"];
|
|
|
|
export async function writeImportedSourcePage(params: {
|
|
vaultRoot: string;
|
|
syncKey: string;
|
|
sourcePath: string;
|
|
sourceUpdatedAtMs: number;
|
|
sourceSize: number;
|
|
renderFingerprint: string;
|
|
pagePath: string;
|
|
group: MemoryWikiImportedSourceGroup;
|
|
state: ImportedSourceState;
|
|
buildRendered: (raw: string, updatedAt: string) => string;
|
|
}): Promise<{ pagePath: string; changed: boolean; created: boolean }> {
|
|
const vault = await fsRoot(params.vaultRoot);
|
|
const pageStat = await vault.stat(params.pagePath).catch((error: unknown) => {
|
|
if (
|
|
error instanceof FsSafeError &&
|
|
(error.code === "not-found" || error.code === "path-alias")
|
|
) {
|
|
return null;
|
|
}
|
|
throw error;
|
|
});
|
|
const created = !pageStat;
|
|
const updatedAt = timestampMsToIsoString(params.sourceUpdatedAtMs) ?? new Date().toISOString();
|
|
const shouldSkip = await shouldSkipImportedSourceWrite({
|
|
vaultRoot: params.vaultRoot,
|
|
syncKey: params.syncKey,
|
|
expectedPagePath: params.pagePath,
|
|
expectedSourcePath: params.sourcePath,
|
|
sourceUpdatedAtMs: params.sourceUpdatedAtMs,
|
|
sourceSize: params.sourceSize,
|
|
renderFingerprint: params.renderFingerprint,
|
|
state: params.state,
|
|
});
|
|
if (shouldSkip) {
|
|
return { pagePath: params.pagePath, changed: false, created };
|
|
}
|
|
|
|
const raw = await fs.readFile(params.sourcePath, "utf8");
|
|
const rendered = params.buildRendered(raw, updatedAt);
|
|
const existing = pageStat ? await vault.readText(params.pagePath).catch(() => "") : "";
|
|
const nextRendered = existing ? preserveHumanNotesBlock(rendered, existing) : rendered;
|
|
if (existing !== nextRendered) {
|
|
await writeGuardedVaultPage({
|
|
vault,
|
|
pagePath: params.pagePath,
|
|
content: nextRendered,
|
|
pageStat,
|
|
pageLabel: "imported source page",
|
|
});
|
|
}
|
|
|
|
setImportedSourceEntry({
|
|
syncKey: params.syncKey,
|
|
state: params.state,
|
|
entry: {
|
|
group: params.group,
|
|
pagePath: params.pagePath,
|
|
sourcePath: params.sourcePath,
|
|
sourceUpdatedAtMs: params.sourceUpdatedAtMs,
|
|
sourceSize: params.sourceSize,
|
|
renderFingerprint: params.renderFingerprint,
|
|
},
|
|
});
|
|
return { pagePath: params.pagePath, changed: existing !== nextRendered, created };
|
|
}
|