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:
Andy Ye
2026-06-22 21:21:57 -07:00
committed by GitHub
parent 667e5bf67e
commit 33b23214d9
2 changed files with 90 additions and 1 deletions

View File

@@ -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({

View File

@@ -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,