fix(matrix): resolve crypto bootstrap failure and multi-extension idHint warning (#53298)

Merged via squash.

Prepared head SHA: 6f5813ffff
Co-authored-by: keithce <2086282+keithce@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Keith Elliott
2026-03-29 12:38:10 -04:00
committed by GitHub
parent ba7911bd16
commit 2d2e386b94
7 changed files with 53 additions and 1 deletions

View File

@@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai
- Tools/web_search: localize the shared search cache to module scope so same-process global symbol lookups can no longer inspect or mutate cached web-search responses. Thanks @vincentkoc.
- Agents/silent turns: fail closed on silent memory-flush runs so narrated `NO_REPLY` self-talk cannot stream or finalize into external replies even when block streaming is enabled. (#52593)
- Browser/plugins: auto-enable the bundled browser plugin when browser config or browser tool policy already references it, and show a clearer CLI error when `plugins.allow` excludes `browser`.
- Matrix/plugin loading: ship and source-load the crypto bootstrap runtime sidecar correctly so current `main` stops warning about failed Matrix bootstrap loads and `matrix/index` plugin-id mismatches on every invocation. (#53298) thanks @keithce.
## 2026.3.28

View File

@@ -23,7 +23,8 @@
},
"openclaw": {
"extensions": [
"./index.ts"
"./index.ts",
"./src/plugin-entry.runtime.ts"
],
"setupEntry": "./setup-entry.ts",
"channel": {

View File

@@ -55,6 +55,17 @@ beforeEach(async () => {
sdkModule = await import("./sdk.js");
});
vi.mock("matrix-js-sdk", async (importOriginal) => {
const actual = await importOriginal<typeof import("matrix-js-sdk")>();
return {
...actual,
createClient: vi.fn(() => ({
// Minimal stub — auth tests spy on MatrixClient.prototype.doRequest
// rather than exercising the underlying js-sdk client.
})),
};
});
describe("resolveMatrixConfig", () => {
it("prefers config over env", () => {
const cfg = {

View File

@@ -0,0 +1,12 @@
// Thin ESM wrapper so native dynamic import() resolves in source-checkout mode
// where jiti loads index.ts but import("./src/plugin-entry.runtime.js") uses
// Node's native ESM loader which cannot resolve .ts files directly.
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const { createJiti } = require("jiti");
const jiti = createJiti(import.meta.url, { interopDefault: true });
const mod = jiti("./plugin-entry.runtime.ts");
export const ensureMatrixCryptoRuntime = mod.ensureMatrixCryptoRuntime;
export const handleVerifyRecoveryKey = mod.handleVerifyRecoveryKey;
export const handleVerificationBootstrap = mod.handleVerificationBootstrap;
export const handleVerificationStatus = mod.handleVerificationStatus;

View File

@@ -0,0 +1,22 @@
import path from "node:path";
import { pathToFileURL } from "node:url";
import { expect, it } from "vitest";
it("loads the plugin-entry runtime wrapper through native ESM import", async () => {
const wrapperPath = path.join(
process.cwd(),
"extensions",
"matrix",
"src",
"plugin-entry.runtime.js",
);
const wrapperUrl = pathToFileURL(wrapperPath);
const mod = await import(wrapperUrl.href);
expect(mod).toMatchObject({
ensureMatrixCryptoRuntime: expect.any(Function),
handleVerifyRecoveryKey: expect.any(Function),
handleVerificationBootstrap: expect.any(Function),
handleVerificationStatus: expect.any(Function),
});
}, 240_000);

View File

@@ -638,6 +638,7 @@ describe("loadPluginManifestRegistry", () => {
{ name: "provider-style", manifestId: "openai", idHint: "openai-provider" },
{ name: "plugin-style", manifestId: "brave", idHint: "brave-plugin" },
{ name: "sandbox-style", manifestId: "openshell", idHint: "openshell-sandbox" },
{ name: "multi-entry-style", manifestId: "matrix", idHint: "matrix/index" },
{
name: "media-understanding-style",
manifestId: "groq",

View File

@@ -195,6 +195,10 @@ function isCompatiblePluginIdHint(idHint: string | undefined, manifestId: string
if (normalizedHint === manifestId) {
return true;
}
// Generated idHint for multi-extension plugins takes the form "id/entryBase".
if (normalizedHint.startsWith(`${manifestId}/`)) {
return true;
}
return (
normalizedHint === `${manifestId}-provider` ||
normalizedHint === `${manifestId}-plugin` ||