Skip to content

Commit 4644e0c

Browse files
authored
fix(memory-wiki): tolerate artifacts without agent ids
Fixes #92207. Normalize public memory artifacts at the memory host boundary so providers that omit agentIds produce an empty list instead of throwing during artifact cloning, sorting, or memory-wiki bridge import. The bridge now renders those artifacts with unknown agents while downstream consumers still receive stable array-shaped metadata. Verification: - node scripts/run-vitest.mjs src/plugins/memory-state.test.ts extensions/memory-wiki/src/bridge.test.ts --maxWorkers=1 - .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main - Crabbox run_2a30de5d0a00 / cbx_3684cb0b7ea5: OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed - GitHub PR checks clean on 19678ed
1 parent fff1934 commit 4644e0c

3 files changed

Lines changed: 72 additions & 1 deletion

File tree

extensions/memory-wiki/src/bridge.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,46 @@ describe("syncMemoryWikiBridgeSources", () => {
150150
expect(logLines).toHaveLength(2);
151151
});
152152

153+
it("imports bridge artifacts from legacy providers without agent ids", async () => {
154+
const workspaceDir = await createBridgeWorkspace("legacy-agentids-workspace");
155+
const { rootDir: vaultDir, config } = await createVault({
156+
rootDir: nextCaseRoot("legacy-agentids-vault"),
157+
config: {
158+
vaultMode: "bridge",
159+
bridge: {
160+
enabled: true,
161+
readMemoryArtifacts: true,
162+
indexMemoryRoot: true,
163+
},
164+
},
165+
});
166+
const memoryPath = path.join(workspaceDir, "MEMORY.md");
167+
await fs.writeFile(memoryPath, "# Durable Memory\n", "utf8");
168+
registerBridgeArtifacts([
169+
{
170+
kind: "memory-root",
171+
workspaceDir,
172+
relativePath: "MEMORY.md",
173+
absolutePath: memoryPath,
174+
contentType: "markdown",
175+
} as Omit<MemoryPluginPublicArtifact, "agentIds"> as MemoryPluginPublicArtifact,
176+
]);
177+
178+
const appConfig: OpenClawConfig = {
179+
agents: {
180+
list: [{ id: "main", default: true, workspace: workspaceDir }],
181+
},
182+
};
183+
184+
const result = await syncMemoryWikiBridgeSources({ config, appConfig });
185+
186+
expect(result.importedCount).toBe(1);
187+
expect(result.artifactCount).toBe(1);
188+
const page = await fs.readFile(path.join(vaultDir, result.pagePaths[0] ?? ""), "utf8");
189+
expect(page).toContain("# Memory Bridge: MEMORY");
190+
expect(page).toContain("- Agents: unknown");
191+
});
192+
153193
it("returns a no-op result outside bridge mode", async () => {
154194
const { config } = await createVault({ rootDir: nextCaseRoot("isolated") });
155195

src/plugins/memory-state.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
registerMemoryRuntime,
1919
resolveMemoryFlushPlan,
2020
restoreMemoryPluginState,
21+
type MemoryPluginPublicArtifact,
2122
} from "./memory-state.js";
2223

2324
function createMemoryRuntime() {
@@ -205,6 +206,35 @@ describe("memory plugin state", () => {
205206
]);
206207
});
207208

209+
it("normalizes public memory artifacts without agent ids", async () => {
210+
const legacyArtifact = {
211+
kind: "memory-root",
212+
workspaceDir: "/tmp/workspace",
213+
relativePath: "MEMORY.md",
214+
absolutePath: "/tmp/workspace/MEMORY.md",
215+
contentType: "markdown" as const,
216+
} as Omit<MemoryPluginPublicArtifact, "agentIds"> as MemoryPluginPublicArtifact;
217+
218+
registerMemoryCapability("memory-core", {
219+
publicArtifacts: {
220+
async listArtifacts() {
221+
return [legacyArtifact];
222+
},
223+
},
224+
});
225+
226+
await expect(listActiveMemoryPublicArtifacts({ cfg: {} as never })).resolves.toEqual([
227+
{
228+
kind: "memory-root",
229+
workspaceDir: "/tmp/workspace",
230+
relativePath: "MEMORY.md",
231+
absolutePath: "/tmp/workspace/MEMORY.md",
232+
agentIds: [],
233+
contentType: "markdown",
234+
},
235+
]);
236+
});
237+
208238
it("preserves sidecar runtime fields when a memory plugin adds public artifacts only", async () => {
209239
const runtime = createMemoryRuntime();
210240
const flushPlanResolver = () => createMemoryFlushPlan("memory/sidecar.md");

src/plugins/memory-state.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,10 @@ export function hasMemoryRuntime(): boolean {
303303
function cloneMemoryPublicArtifact(
304304
artifact: MemoryPluginPublicArtifact,
305305
): MemoryPluginPublicArtifact {
306+
const agentIds = Array.isArray(artifact.agentIds) ? artifact.agentIds : [];
306307
return {
307308
...artifact,
308-
agentIds: [...artifact.agentIds],
309+
agentIds: [...agentIds],
309310
};
310311
}
311312

0 commit comments

Comments
 (0)