From 00550dc802f347c3e662e09b56302cb61998e0ad Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 3 Aug 2026 16:53:04 +0100 Subject: [PATCH] fix: activate the canonical corpus indexer (#980) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent faults left the canonical corpus permanently empty when canonicalCorpus.enabled was set: 1. canonicalCorpusIndexer.sync() was never invoked. Its only call sites are inside the memory capability (search()/sync()), reached via getActiveMemorySearchManager — but agent searches resolve to the plugin's own memory_search/memory_get tools and bypass the capability entirely. Add a fire-and-forget sync at service start; sync() already checks enabled, rate-limits via syncIntervalMs, and de-duplicates concurrent runs. 2. resolveCanonicalCorpusWorkspaces read agents.list, but OpenClaw stores agents as an object keyed by id under agents.entries. The loop matched nothing and the function fell back to the single default workspace, silently skipping every per-agent workspace. Support both shapes. Verified on a 6-agent install: workspaces resolved went 1 -> 6, and the startup sync indexed 2505/2505 chunks from 796 documents. --- index.ts | 22 ++++++++++++++++++++++ package-lock.json | 3 --- src/corpus-indexer.ts | 12 ++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/index.ts b/index.ts index 12b5fa75b..b78ae93d0 100644 --- a/index.ts +++ b/index.ts @@ -6128,6 +6128,28 @@ const memoryLanceDBProPlugin = { // Fire-and-forget: allow gateway to start serving immediately. setTimeout(() => void runStartupChecks(), 0); + // Canonical corpus: nothing else invokes canonicalCorpusIndexer.sync(). + // Its only call sites are inside the memory capability (search()/sync()), + // which the host reaches via getActiveMemorySearchManager — but agent + // searches resolve to this plugin's own memory_search/memory_get tools + // and bypass the capability, so with canonicalCorpus.enabled the index + // silently stayed empty. Kick one sync at service start; sync() checks + // canonicalCorpus.enabled itself, rate-limits via syncIntervalMs, and + // de-duplicates concurrent runs, so this is safe and cheap when current. + setTimeout(() => void (async () => { + try { + const corpusStats = await canonicalCorpusIndexer.sync({ reason: "startup" }); + api.logger.info( + `memory-lancedb-pro: canonical corpus startup sync — indexed ${corpusStats.indexed}/${corpusStats.chunks} chunk(s) ` + + `from ${corpusStats.documents} document(s); ${corpusStats.staleDeleted} stale removed`, + ); + } catch (err) { + api.logger.warn( + `memory-lancedb-pro: canonical corpus startup sync failed: ${err instanceof Error ? err.message : String(err)}`, + ); + } + })(), 0); + // Check for legacy memories that could be upgraded setTimeout(async () => { try { diff --git a/package-lock.json b/package-lock.json index 7526c0c73..1dc108e40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,9 +86,6 @@ "node": ">= 18" } }, - "node_modules/@lancedb/lancedb-darwin-x64": { - "optional": true - }, "node_modules/@lancedb/lancedb-linux-arm64-gnu": { "version": "0.26.2", "resolved": "https://registry.npmjs.org/@lancedb/lancedb-linux-arm64-gnu/-/lancedb-linux-arm64-gnu-0.26.2.tgz", diff --git a/src/corpus-indexer.ts b/src/corpus-indexer.ts index 73b809c7f..a21692ee6 100644 --- a/src/corpus-indexer.ts +++ b/src/corpus-indexer.ts @@ -217,6 +217,18 @@ export function resolveCanonicalCorpusWorkspaces(cfg: unknown, homeDir = homedir add(entry.workspace ?? entry.workspaceDir ?? entry.cwd, entry.id); } + // OpenClaw stores agents as an object keyed by agent id under `agents.entries` + // (not an array under `agents.list`). Without this, the loop above matches + // nothing on current OpenClaw configs and the function silently falls back to + // the single default workspace, so per-agent workspaces are never indexed. + const entries = isRecord(agents?.entries) ? agents.entries : undefined; + if (entries) { + for (const [entryAgentId, entry] of Object.entries(entries)) { + if (!isRecord(entry)) continue; + add(entry.workspace ?? entry.workspaceDir ?? entry.cwd, entryAgentId); + } + } + const defaults = isRecord(agents?.defaults) ? agents.defaults : undefined; add(defaults?.workspace ?? defaults?.workspaceDir ?? defaults?.cwd, "main");