mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-09 03:22:40 +00:00
Fix memory-wiki bridge self-import loop (#95666)
Merged via squash.
Prepared head SHA: 0f74629547
Co-authored-by: TurboTheTurtle <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
This commit is contained in:
@@ -150,6 +150,78 @@ describe("syncMemoryWikiBridgeSources", () => {
|
||||
expect(logLines).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("skips generated artifacts from its own vault", async () => {
|
||||
const workspaceDir = await createBridgeWorkspace("self-import-workspace");
|
||||
const vaultDir = path.join(workspaceDir, "memory", "wiki");
|
||||
const { config } = await createVault({
|
||||
rootDir: vaultDir,
|
||||
config: {
|
||||
vaultMode: "bridge",
|
||||
bridge: {
|
||||
enabled: true,
|
||||
readMemoryArtifacts: true,
|
||||
indexDailyNotes: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const dailyNotePath = path.join(workspaceDir, "memory", "2026-06-22.md");
|
||||
const generatedSourcePath = path.join(
|
||||
vaultDir,
|
||||
"sources",
|
||||
"bridge-workspace-remote-memory-daily-old.md",
|
||||
);
|
||||
const generatedIndexPath = path.join(vaultDir, "index.md");
|
||||
await fs.mkdir(path.dirname(dailyNotePath), { recursive: true });
|
||||
await fs.mkdir(path.dirname(generatedSourcePath), { recursive: true });
|
||||
await fs.writeFile(dailyNotePath, "# Daily Note\n", "utf8");
|
||||
await fs.writeFile(generatedSourcePath, "# Previously Imported Source\n", "utf8");
|
||||
await fs.writeFile(generatedIndexPath, "# Generated Index\n", "utf8");
|
||||
|
||||
registerBridgeArtifacts([
|
||||
{
|
||||
kind: "daily-note",
|
||||
workspaceDir,
|
||||
relativePath: "memory/2026-06-22.md",
|
||||
absolutePath: dailyNotePath,
|
||||
agentIds: ["main"],
|
||||
contentType: "markdown",
|
||||
},
|
||||
{
|
||||
kind: "daily-note",
|
||||
workspaceDir,
|
||||
relativePath: "memory/wiki/sources/bridge-workspace-remote-memory-daily-old.md",
|
||||
absolutePath: generatedSourcePath,
|
||||
agentIds: ["main"],
|
||||
contentType: "markdown",
|
||||
},
|
||||
{
|
||||
kind: "daily-note",
|
||||
workspaceDir,
|
||||
relativePath: "memory/wiki/index.md",
|
||||
absolutePath: generatedIndexPath,
|
||||
agentIds: ["main"],
|
||||
contentType: "markdown",
|
||||
},
|
||||
]);
|
||||
|
||||
const appConfig: OpenClawConfig = {
|
||||
agents: {
|
||||
list: [{ id: "main", default: true, workspace: workspaceDir }],
|
||||
},
|
||||
};
|
||||
|
||||
const result = await syncMemoryWikiBridgeSources({ config, appConfig });
|
||||
|
||||
expect(result.artifactCount).toBe(1);
|
||||
expect(result.importedCount).toBe(1);
|
||||
expect(result.pagePaths).toHaveLength(1);
|
||||
expect(result.pagePaths[0]).not.toContain("memory-wiki-sources");
|
||||
const sourcePages = await fs.readdir(path.join(vaultDir, "sources"));
|
||||
expect(sourcePages.filter((name) => name.startsWith("bridge-"))).toHaveLength(2);
|
||||
expect(sourcePages.filter((name) => name.includes("memory-wiki-sources-"))).toEqual([]);
|
||||
});
|
||||
|
||||
it("imports bridge artifacts from legacy providers without agent ids", async () => {
|
||||
const workspaceDir = await createBridgeWorkspace("legacy-agentids-workspace");
|
||||
const { rootDir: vaultDir, config } = await createVault({
|
||||
|
||||
@@ -64,14 +64,19 @@ function shouldImportArtifact(
|
||||
|
||||
async function collectBridgeArtifacts(
|
||||
bridgeConfig: ResolvedMemoryWikiConfig["bridge"],
|
||||
vaultRoot: string,
|
||||
artifacts: MemoryPluginPublicArtifact[],
|
||||
): Promise<BridgeArtifact[]> {
|
||||
const collected: BridgeArtifact[] = [];
|
||||
const vaultRootKey = await resolveArtifactKey(vaultRoot);
|
||||
for (const artifact of artifacts) {
|
||||
if (!shouldImportArtifact(artifact, bridgeConfig)) {
|
||||
continue;
|
||||
}
|
||||
const syncKey = await resolveArtifactKey(artifact.absolutePath);
|
||||
if (isPathInsideOrEqual(vaultRootKey, syncKey)) {
|
||||
continue;
|
||||
}
|
||||
collected.push({
|
||||
syncKey,
|
||||
artifactType: artifact.kind === "event-log" ? "memory-events" : "markdown",
|
||||
@@ -87,6 +92,14 @@ async function collectBridgeArtifacts(
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
function isPathInsideOrEqual(parentPath: string, candidatePath: string): boolean {
|
||||
const relative = path.relative(parentPath, candidatePath);
|
||||
return (
|
||||
relative === "" ||
|
||||
(relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative))
|
||||
);
|
||||
}
|
||||
|
||||
function resolveBridgeTitle(artifact: BridgeArtifact, agentIds: string[]): string {
|
||||
if (artifact.artifactType === "memory-events") {
|
||||
if (agentIds.length === 0) {
|
||||
@@ -227,7 +240,11 @@ export async function syncMemoryWikiBridgeSources(params: {
|
||||
const publicArtifacts = await listActiveMemoryPublicArtifacts({ cfg: params.appConfig });
|
||||
const results: Array<{ pagePath: string; changed: boolean; created: boolean }> = [];
|
||||
const activeKeys = new Set<string>();
|
||||
const artifacts = await collectBridgeArtifacts(params.config.bridge, publicArtifacts);
|
||||
const artifacts = await collectBridgeArtifacts(
|
||||
params.config.bridge,
|
||||
params.config.vault.path,
|
||||
publicArtifacts,
|
||||
);
|
||||
const state = await readMemoryWikiSourceSyncState(params.config.vault.path);
|
||||
assertMemoryWikiSourceSyncStateCapacity({
|
||||
state,
|
||||
|
||||
Reference in New Issue
Block a user