fix(memory-wiki): route wiki bridge import CLI through gateway RPC#67208
fix(memory-wiki): route wiki bridge import CLI through gateway RPC#67208moorsecopers99 wants to merge 2 commits into
Conversation
Greptile SummaryFixes a silent "0 artifacts" bug in
Confidence Score: 5/5Safe to merge — the fix is a clean routing change with appropriate tests and no protocol or persistence changes. All findings are P2 style suggestions. The core logic (RPC routing, opts forwarding, error handling) is correct and well-tested. No security, data-integrity, or reliability concerns were identified. No files require special attention beyond the minor Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/memory-wiki/src/cli.ts
Line: 510-520
Comment:
**`config` is now a required-but-unused parameter**
`config: ResolvedMemoryWikiConfig` is declared as required in the new implementation, but the function body never reads `params.config` (nor `params.appConfig`). Callers — including external plugin consumers of this exported function — are forced to construct and supply a full `ResolvedMemoryWikiConfig` that the gateway RPC path silently discards. Making `config` optional (`config?: ResolvedMemoryWikiConfig`) is non-breaking for every existing caller (they already pass it) and avoids the misleading API surface.
```suggestion
export async function runWikiBridgeImport(params: {
config?: ResolvedMemoryWikiConfig;
appConfig?: OpenClawConfig;
gatewayOpts?: GatewayRpcOpts;
json?: boolean;
stdout?: Pick<NodeJS.WriteStream, "write">;
// Injectable for tests; production code routes through the gateway RPC so the
// command sees the memory-core capability that is only registered inside the
// gateway daemon process (issue #67190).
callGateway?: typeof callGatewayFromCli;
}) {
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(memory-wiki): route wiki bridge impo..." | Re-trigger Greptile |
| export async function runWikiBridgeImport(params: { | ||
| config: ResolvedMemoryWikiConfig; | ||
| appConfig?: OpenClawConfig; | ||
| gatewayOpts?: GatewayRpcOpts; | ||
| json?: boolean; | ||
| stdout?: Pick<NodeJS.WriteStream, "write">; | ||
| // Injectable for tests; production code routes through the gateway RPC so the | ||
| // command sees the memory-core capability that is only registered inside the | ||
| // gateway daemon process (issue #67190). | ||
| callGateway?: typeof callGatewayFromCli; | ||
| }) { |
There was a problem hiding this comment.
config is now a required-but-unused parameter
config: ResolvedMemoryWikiConfig is declared as required in the new implementation, but the function body never reads params.config (nor params.appConfig). Callers — including external plugin consumers of this exported function — are forced to construct and supply a full ResolvedMemoryWikiConfig that the gateway RPC path silently discards. Making config optional (config?: ResolvedMemoryWikiConfig) is non-breaking for every existing caller (they already pass it) and avoids the misleading API surface.
| export async function runWikiBridgeImport(params: { | |
| config: ResolvedMemoryWikiConfig; | |
| appConfig?: OpenClawConfig; | |
| gatewayOpts?: GatewayRpcOpts; | |
| json?: boolean; | |
| stdout?: Pick<NodeJS.WriteStream, "write">; | |
| // Injectable for tests; production code routes through the gateway RPC so the | |
| // command sees the memory-core capability that is only registered inside the | |
| // gateway daemon process (issue #67190). | |
| callGateway?: typeof callGatewayFromCli; | |
| }) { | |
| export async function runWikiBridgeImport(params: { | |
| config?: ResolvedMemoryWikiConfig; | |
| appConfig?: OpenClawConfig; | |
| gatewayOpts?: GatewayRpcOpts; | |
| json?: boolean; | |
| stdout?: Pick<NodeJS.WriteStream, "write">; | |
| // Injectable for tests; production code routes through the gateway RPC so the | |
| // command sees the memory-core capability that is only registered inside the | |
| // gateway daemon process (issue #67190). | |
| callGateway?: typeof callGatewayFromCli; | |
| }) { |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/memory-wiki/src/cli.ts
Line: 510-520
Comment:
**`config` is now a required-but-unused parameter**
`config: ResolvedMemoryWikiConfig` is declared as required in the new implementation, but the function body never reads `params.config` (nor `params.appConfig`). Callers — including external plugin consumers of this exported function — are forced to construct and supply a full `ResolvedMemoryWikiConfig` that the gateway RPC path silently discards. Making `config` optional (`config?: ResolvedMemoryWikiConfig`) is non-breaking for every existing caller (they already pass it) and avoids the misleading API surface.
```suggestion
export async function runWikiBridgeImport(params: {
config?: ResolvedMemoryWikiConfig;
appConfig?: OpenClawConfig;
gatewayOpts?: GatewayRpcOpts;
json?: boolean;
stdout?: Pick<NodeJS.WriteStream, "write">;
// Injectable for tests; production code routes through the gateway RPC so the
// command sees the memory-core capability that is only registered inside the
// gateway daemon process (issue #67190).
callGateway?: typeof callGatewayFromCli;
}) {
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Applied — made config optional since the gateway RPC path doesn't use it.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 111f4dff50
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| delete entry.MediaPath; | ||
| delete entry.MediaPaths; | ||
| delete entry.MediaType; | ||
| delete entry.MediaTypes; |
There was a problem hiding this comment.
Preserve non-image media metadata during image promotion
When a user message contains mixed media types, this branch promotes only image/* paths into content blocks but then deletes MediaPath, MediaPaths, MediaType, and MediaTypes unconditionally. That drops audio/video/document attachment metadata from chat.history responses whenever at least one image is present, which is a regression for any client flow that still reads non-image attachments from the legacy media fields. Keep non-image entries (or only remove the promoted image entries) instead of clearing all media fields.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Want me to implement the fix and push?
|
Can confirm this bug on v2026.4.15. Running |
The `openclaw wiki bridge import` CLI command ran in a fresh subprocess where memory-core's `registerMemoryCapability` is never called, so `listActiveMemoryPublicArtifacts` always returned [] and the command reported 0 artifacts even when the gateway RPC `wiki.bridge.import` found real artifacts. Route the CLI through `callGatewayFromCli` so both paths see the same capability registered inside the gateway daemon. Adds `--url` / `--token` / `--timeout` / `--expect-final` to the `wiki bridge import` subcommand via `addGatewayClientOptions`, and surfaces a clear error when the gateway is unreachable instead of the prior silent zero-artifact false negative. Closes openclaw#67190
Server transcript stores user images as MediaPath/MediaPaths fields rather than inline image content blocks. After chat.history reload replaced optimistic local messages, extractImages found nothing to render. Promote image MediaPaths into content blocks server-side and resolve local file paths through the assistant-media endpoint client-side. Closes openclaw#67487
111f4df to
31228f2
Compare
|
Hi @zwj0117, thanks for the confirmation on v2026.4.15. Fix is in this PR: runWikiBridgeImport was calling syncMemoryWikiImportedSources in-process, but that helper depends on memoryPluginState.capability which only gets registered inside the gateway daemon. In a fresh CLI subprocess the capability is undefined, so listActiveMemoryPublicArtifacts short-circuits to [] and you get 0 artifacts — which is also exactly why the unsafe-local path worked for you (different code path that doesn't touch that capability). The PR now routes wiki bridge import through the wiki.bridge.import gateway RPC via callGatewayFromCli, so the CLI reports the same count as openclaw gateway call wiki.bridge.import. Just rebased onto latest main and the conflicts are resolved — should be mergeable now. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31228f2d49
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const mime = (mediaTypes[i] ?? "").toLowerCase(); | ||
| if (!mime.startsWith("image/")) { |
There was a problem hiding this comment.
Align media types with paths before promoting images
The promotion loop assumes MediaTypes[i] always describes MediaPaths[i], but that invariant is not guaranteed across the repo (for example buildMediaPayload can drop empty MIME entries, producing shorter MediaTypes arrays). In that case this code can mark the wrong path as an image (or skip a real image), and because sanitizeChatHistoryMessage deletes legacy media fields after any promotion, chat history reloads can permanently show the wrong attachment set for that message.
Useful? React with 👍 / 👎.
|
This PR fixes Root Cause #1 (CLI loader gap) from our investigation. Confirmed that the CLI path loads However, there's a second root cause that this PR alone won't fix: Both fixes are needed for reliable bridge import. See detailed investigation in #66082. |
|
Detailed investigation report covering both root causes and why this PR alone is a partial fix: https://altiera.jp/research/bridge-zero-artifacts.md |
|
Thanks for the detailed root-cause split. Confirmed the second issue against Pushing a follow-up that captures |
|
Thanks for pushing the Gateway-routing direction here. I opened the scoped maintainer fix at #71479 and credited this PR there. I'm closing this one because it mixes the Memory Wiki CLI bridge fix with unrelated chat-history media-path changes, which makes it riskier to land as-is. The replacement PR keeps the same root-cause path: active bridge-mode CLI commands now call the Gateway so they see the runtime memory plugin artifacts instead of the empty standalone CLI registry. |
Summary
openclaw wiki bridge importalways returns 0 artifacts, even whenopenclaw gateway call wiki.bridge.importagainst the same store returns the real count.openclaw wiki doctorthen falsely warns that memory-core is not exporting any public artifacts.runWikiBridgeImportnow calls thewiki.bridge.importgateway RPC viacallGatewayFromCliinstead of invokingsyncMemoryWikiImportedSourcesin-process.addGatewayClientOptionsis attached to thewiki bridge importsubcommand so users can point at a remote gateway or pass an explicit token. Error path surfaces a clear "gateway running?" message instead of a silent 0.wiki status,wiki doctor,wiki unsafe-local import) still run in-process and are out of scope for this PR. The gateway RPC handler, the sync implementation, and memory-core's capability registration are all untouched.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
openclaw wiki bridge importCLI returns 0 artifacts whileopenclaw gateway call wiki.bridge.importworks correctly #67190Root Cause (if applicable)
registerMemoryCapability— which populatesmemoryPluginState.capability.capability.publicArtifacts.listArtifacts— runs insidememory-core'sregister()hook, which only fires in the gateway daemon process. The CLI command path loads each plugin'scli-metadata.jsto wire subcommands but does not execute the full pluginregister()flow. Result: in a fresh CLI subprocess,memoryPluginState.capabilityisundefinedandlistActiveMemoryPublicArtifactsshort-circuits to[]. The gateway RPC handlerwiki.bridge.importworks because it runs inside the daemon where the capability is already registered.listActiveMemoryPublicArtifactsintentionally degrades gracefully to[]when the capability is absent — that is correct for plugin-aware callers inside the daemon, but catastrophic for a CLI entrypoint expecting artifacts.Regression Test Plan (if applicable)
extensions/memory-wiki/src/cli.test.ts(newdescribe("runWikiBridgeImport", ...)block).runWikiBridgeImportmust callcallGatewayFromCli("wiki.bridge.import", ...)with forwarded opts and renderBridge import synced N artifacts...; it must throw a clear "gateway running?" error when the RPC returns nothing.callGatewaymock catches any future regression that reroutes the command back to the in-process sync helper.runWikiBridgeImporthad no direct test.User-visible / Behavior Changes
openclaw wiki bridge importnow reports the sameimportedCount/artifactCountasopenclaw gateway call wiki.bridge.importfor the same store.openclaw wiki bridge import --helpnow lists--url,--token,--timeout,--expect-finalin addition to--json(fromaddGatewayClientOptions).Diagram (if applicable)
Security Impact (required)
NoNo— the new--tokenflag on the subcommand uses the sameaddGatewayClientOptionssurface already used by other CLI commands (browser, qa-lab).Yes, but same target: the CLI now opens a WebSocket to the local gateway instead of calling the in-process sync helper. The RPC target, scope, and payload are identical to the existingwiki.bridge.importmethod that has been shipped for prior releases.NoNo— the RPC handler usesWRITE_SCOPE, same as before. Anyone who could already run the CLI locally already had that authority.Repro + Verification
Environment
pnpm openclaw gateway --force,memory-wikiextension bundledvaultMode: "bridge", all bridge flags enabled)plugins.entries.memory-wiki.config.vaultMode = "bridge"Steps
pnpm openclaw gateway --forcepnpm openclaw wiki bridge import --jsonpnpm openclaw gateway call wiki.bridge.import --jsonpnpm openclaw wiki bridge importto exercise the failure pathExpected
importedCount/artifactCount0 artifactspnpm openclaw wiki bridge import --helplists--url,--token,--timeout,--expect-finalActual
callGatewayFromCliand assert both the happy path and the empty-result error path (extensions/memory-wiki/src/cli.test.ts).Evidence
Human Verification (required)
runWikiBridgeImportnow routes through the mockedcallGatewayFromCliwith"wiki.bridge.import"as the method and forwardsurl/token/jsonoptsBridge import synced N artifacts across W workspaces (...)output from the RPC resultundefinedWikiBridgeImportCommandOptionsnow extendsGatewayRpcOpts)gatewayOptsstill works (spread handlesundefined)syncMemoryWikiImportedSourcesare unchangedgateway call wiki.bridge.importthe issue author validated.Review Conversations
Compatibility / Migration
Yes—runWikiBridgeImportkeeps its exported signature (new fields are optional), and the command name / output format are unchanged.NoNoRisks and Mitigations
openclaw wiki bridge importwithout a running gateway used to get a silent0 artifactsresult; they will now get a loud WebSocket error.callGatewayFromClialready behaves in other CLI commands (browser,qa-lab). The error message explicitly hints at the gateway.callGateway?param) leaks into the public signature ofrunWikiBridgeImport.callGatewayFromClicontract fromopenclaw/plugin-sdk/browser-node-runtime, and documented inline. Production callers pass nothing and get the real runtime.