diff --git a/docs/plugins/architecture-internals.md b/docs/plugins/architecture-internals.md
index 4b3ca1ff1a7..31f93f99ace 100644
--- a/docs/plugins/architecture-internals.md
+++ b/docs/plugins/architecture-internals.md
@@ -23,7 +23,7 @@ At startup, OpenClaw does roughly this:
`slots`, `load.paths`)
5. decide enablement for each candidate
6. load enabled native modules: built bundled modules use a native loader;
- unbuilt native plugins use jiti
+ third-party local source TypeScript uses the emergency Jiti fallback
7. call native `register(api)` hooks and collect registrations into the plugin registry
8. expose the registry to commands/runtime surfaces
diff --git a/docs/plugins/architecture.md b/docs/plugins/architecture.md
index ff5a4f70126..5c4933186ed 100644
--- a/docs/plugins/architecture.md
+++ b/docs/plugins/architecture.md
@@ -123,7 +123,7 @@ OpenClaw's plugin system has four layers:
Core decides whether a discovered plugin is enabled, disabled, blocked, or selected for an exclusive slot such as memory.
- Native OpenClaw plugins are loaded in-process and register capabilities into a central registry. Packaged JavaScript loads through native `require`; source TypeScript falls back to Jiti. Compatible bundles are normalized into registry records without importing runtime code.
+ Native OpenClaw plugins are loaded in-process and register capabilities into a central registry. Packaged JavaScript loads through native `require`; third-party local source TypeScript is the emergency Jiti fallback. Compatible bundles are normalized into registry records without importing runtime code.
The rest of OpenClaw reads the registry to expose tools, channels, provider setup, hooks, HTTP routes, CLI commands, and services.
diff --git a/docs/plugins/dependency-resolution.md b/docs/plugins/dependency-resolution.md
index fae919e4679..3bbb127a40c 100644
--- a/docs/plugins/dependency-resolution.md
+++ b/docs/plugins/dependency-resolution.md
@@ -61,8 +61,9 @@ Local plugins are treated as developer-controlled directories. OpenClaw does not
run `npm install`, `pnpm install`, or dependency repair for them. If a local
plugin has dependencies, install them in that plugin before loading it.
-TypeScript local plugins can use the emergency Jiti path. Packaged JavaScript
-plugins load through native import/require instead of Jiti.
+Third-party TypeScript local plugins can use the emergency Jiti path. Packaged
+JavaScript plugins and bundled internal plugins load through native
+import/require instead of Jiti.
## Startup and reload
diff --git a/extensions/matrix/src/manifest.test.ts b/extensions/matrix/src/manifest.test.ts
deleted file mode 100644
index 8db734a18ad..00000000000
--- a/extensions/matrix/src/manifest.test.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import fs from "node:fs";
-import { describe, expect, it } from "vitest";
-
-type MatrixPackageManifest = {
- dependencies?: Record;
-};
-
-describe("matrix package manifest", () => {
- it("keeps runtime dependencies in the package manifest", () => {
- const packageJson = JSON.parse(
- fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"),
- ) as MatrixPackageManifest;
-
- expect(packageJson.dependencies?.["fake-indexeddb"]).toBeDefined();
- });
-});
diff --git a/extensions/matrix/src/plugin-entry.runtime.js b/extensions/matrix/src/plugin-entry.runtime.js
index 016051e9ec3..0f267750f49 100644
--- a/extensions/matrix/src/plugin-entry.runtime.js
+++ b/extensions/matrix/src/plugin-entry.runtime.js
@@ -2,33 +2,12 @@
// while packaged dist builds resolve a distinct runtime entry that cannot loop
// back into this wrapper through the stable root runtime alias.
import fs from "node:fs";
-import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
-const require = createRequire(import.meta.url);
-
const PLUGIN_ID = "matrix";
-const OPENCLAW_PLUGIN_SDK_PACKAGE_NAMES = [
- ["openclaw", "plugin-sdk"].join("/"),
- ["@openclaw", "plugin-sdk"].join("/"),
-];
-const PLUGIN_SDK_EXPORT_PREFIX = "./plugin-sdk/";
-const PLUGIN_SDK_SOURCE_EXTENSIONS = [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"];
const PLUGIN_ENTRY_RUNTIME_BASENAME = "plugin-entry.handlers.runtime";
const NATIVE_RUNTIME_EXTENSIONS = [".js", ".mjs", ".cjs"];
-const JITI_EXTENSIONS = [
- ".ts",
- ".tsx",
- ".mts",
- ".cts",
- ".mtsx",
- ".ctsx",
- ".js",
- ".mjs",
- ".cjs",
- ".json",
-];
function readPackageJson(packageRoot) {
try {
@@ -84,55 +63,6 @@ function resolveExistingFile(basePath, extensions) {
return null;
}
-function buildPluginSdkAliasMap(moduleUrl) {
- const location = findOpenClawPackageRoot(path.dirname(fileURLToPath(moduleUrl)));
- if (!location) {
- return {};
- }
-
- const { packageRoot, packageJson } = location;
- const sourcePluginSdkDir = path.join(packageRoot, "src", "plugin-sdk");
- const distPluginSdkDir = path.join(packageRoot, "dist", "plugin-sdk");
- const aliasMap = {};
- const rootAlias =
- resolveExistingFile(path.join(sourcePluginSdkDir, "root-alias"), [".cjs"]) ??
- resolveExistingFile(path.join(distPluginSdkDir, "root-alias"), [".cjs"]);
- if (rootAlias) {
- for (const packageName of OPENCLAW_PLUGIN_SDK_PACKAGE_NAMES) {
- aliasMap[packageName] = rootAlias;
- }
- }
-
- for (const exportKey of Object.keys(packageJson.exports ?? {}).toSorted()) {
- if (!exportKey.startsWith(PLUGIN_SDK_EXPORT_PREFIX)) {
- continue;
- }
- const subpath = exportKey.slice(PLUGIN_SDK_EXPORT_PREFIX.length);
- if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(subpath)) {
- continue;
- }
- const resolvedPath =
- resolveExistingFile(path.join(sourcePluginSdkDir, subpath), PLUGIN_SDK_SOURCE_EXTENSIONS) ??
- resolveExistingFile(path.join(distPluginSdkDir, subpath), [".js"]);
- if (resolvedPath) {
- for (const packageName of OPENCLAW_PLUGIN_SDK_PACKAGE_NAMES) {
- aliasMap[`${packageName}/${subpath}`] = resolvedPath;
- }
- }
- }
-
- const extensionApi =
- resolveExistingFile(
- path.join(packageRoot, "src", "extensionAPI"),
- PLUGIN_SDK_SOURCE_EXTENSIONS,
- ) ?? resolveExistingFile(path.join(packageRoot, "dist", "extensionAPI"), [".js"]);
- if (extensionApi) {
- aliasMap["openclaw/extension-api"] = extensionApi;
- }
-
- return aliasMap;
-}
-
function resolveBundledPluginRuntimeModulePath(moduleUrl, params) {
const modulePath = fileURLToPath(moduleUrl);
const moduleDir = path.dirname(modulePath);
@@ -142,7 +72,7 @@ function resolveBundledPluginRuntimeModulePath(moduleUrl, params) {
];
for (const candidate of localCandidates) {
- const resolved = resolveExistingFile(candidate, PLUGIN_SDK_SOURCE_EXTENSIONS);
+ const resolved = resolveExistingFile(candidate, NATIVE_RUNTIME_EXTENSIONS);
if (resolved) {
return resolved;
}
@@ -157,7 +87,7 @@ function resolveBundledPluginRuntimeModulePath(moduleUrl, params) {
];
for (const candidate of packageCandidates) {
- const resolved = resolveExistingFile(candidate, PLUGIN_SDK_SOURCE_EXTENSIONS);
+ const resolved = resolveExistingFile(candidate, NATIVE_RUNTIME_EXTENSIONS);
if (resolved) {
return resolved;
}
@@ -170,17 +100,7 @@ function resolveBundledPluginRuntimeModulePath(moduleUrl, params) {
}
async function loadRuntimeModule(modulePath) {
- if (NATIVE_RUNTIME_EXTENSIONS.includes(path.extname(modulePath))) {
- return import(pathToFileURL(modulePath).href);
- }
- const { createJiti } = require("jiti");
- const jiti = createJiti(import.meta.url, {
- alias: buildPluginSdkAliasMap(import.meta.url),
- interopDefault: true,
- tryNative: false,
- extensions: JITI_EXTENSIONS,
- });
- return jiti(modulePath);
+ return import(pathToFileURL(modulePath).href);
}
const mod = await loadRuntimeModule(
diff --git a/extensions/matrix/src/plugin-entry.runtime.test.ts b/extensions/matrix/src/plugin-entry.runtime.test.ts
index 95894de4c47..0f1fcfd622d 100644
--- a/extensions/matrix/src/plugin-entry.runtime.test.ts
+++ b/extensions/matrix/src/plugin-entry.runtime.test.ts
@@ -6,13 +6,6 @@ import { afterEach, expect, it } from "vitest";
const tempDirs: string[] = [];
const REPO_ROOT = process.cwd();
-const matrixWrapperGlobal = globalThis as typeof globalThis & {
- __openclawMatrixWrapperJitiOptions?: unknown;
-};
-const PLUGIN_SDK_ROOT = ["openclaw", "plugin-sdk"].join("/");
-const SCOPED_PLUGIN_SDK_ROOT = ["@openclaw", "plugin-sdk"].join("/");
-const GROUP_ACCESS_SUBPATH = `${PLUGIN_SDK_ROOT}/group-access`;
-const SCOPED_GROUP_ACCESS_SUBPATH = `${SCOPED_PLUGIN_SDK_ROOT}/group-access`;
const MATRIX_RUNTIME_WRAPPER_SOURCE = fs.readFileSync(
path.join(REPO_ROOT, "extensions", "matrix", "src", "plugin-entry.runtime.js"),
"utf8",
@@ -37,27 +30,6 @@ function writeFixtureFile(fixtureRoot: string, relativePath: string, value: stri
fs.writeFileSync(fullPath, value, "utf8");
}
-function writeCapturingJitiFixture(fixtureRoot: string) {
- writeFixtureFile(
- fixtureRoot,
- "node_modules/jiti/index.js",
- [
- "exports.createJiti = function createJiti(_filename, options) {",
- " globalThis.__openclawMatrixWrapperJitiOptions = options;",
- " return function jiti() {",
- " return {",
- " ensureMatrixCryptoRuntime: async function ensureMatrixCryptoRuntime() {},",
- " handleVerifyRecoveryKey: async function handleVerifyRecoveryKey() {},",
- " handleVerificationBootstrap: async function handleVerificationBootstrap() {},",
- " handleVerificationStatus: async function handleVerificationStatus() {},",
- " };",
- " };",
- "};",
- "",
- ].join("\n"),
- );
-}
-
function writeOpenClawPackageFixture(fixtureRoot: string) {
writeFixtureFile(
fixtureRoot,
@@ -78,60 +50,6 @@ function writeOpenClawPackageFixture(fixtureRoot: string) {
writeFixtureFile(fixtureRoot, "dist/plugin-sdk/index.js", "export {};\n");
}
-function writeOpenClawAliasFixture(fixtureRoot: string, extraExports?: Record) {
- writeFixtureFile(
- fixtureRoot,
- "package.json",
- JSON.stringify(
- {
- name: "openclaw",
- type: "module",
- exports: {
- "./plugin-sdk": "./dist/plugin-sdk/index.js",
- "./plugin-sdk/group-access": "./dist/plugin-sdk/group-access.js",
- ...extraExports,
- },
- },
- null,
- 2,
- ) + "\n",
- );
- writeFixtureFile(fixtureRoot, "src/plugin-sdk/root-alias.cjs", "module.exports = {};\n");
- writeFixtureFile(fixtureRoot, "src/plugin-sdk/group-access.ts", "export {};\n");
- writeFixtureFile(fixtureRoot, "openclaw.mjs", "export {};\n");
- writeFixtureFile(fixtureRoot, "dist/plugin-sdk/index.js", "export {};\n");
- writeFixtureFile(fixtureRoot, "dist/plugin-sdk/root-alias.cjs", "module.exports = {};\n");
- writeFixtureFile(fixtureRoot, "dist/plugin-sdk/group-access.js", "export {};\n");
-}
-
-function writeTrustedOpenClawBinFixture(
- fixtureRoot: string,
- packageBin: string | Record,
-) {
- writeFixtureFile(
- fixtureRoot,
- "package.json",
- JSON.stringify(
- {
- name: "openclaw",
- type: "module",
- bin: packageBin,
- exports: {
- "./plugin-sdk": "./dist/plugin-sdk/index.js",
- "./plugin-sdk/group-access": "./dist/plugin-sdk/group-access.js",
- },
- },
- null,
- 2,
- ) + "\n",
- );
- writeFixtureFile(fixtureRoot, "src/plugin-sdk/root-alias.cjs", "module.exports = {};\n");
- writeFixtureFile(fixtureRoot, "src/plugin-sdk/group-access.ts", "export {};\n");
- writeFixtureFile(fixtureRoot, "dist/plugin-sdk/index.js", "export {};\n");
- writeFixtureFile(fixtureRoot, "dist/plugin-sdk/root-alias.cjs", "module.exports = {};\n");
- writeFixtureFile(fixtureRoot, "dist/plugin-sdk/group-access.js", "export {};\n");
-}
-
function writeSourceRuntimeWrapperFixture(
fixtureRoot: string,
options: { runtimeExtension?: ".js" | ".ts" } = {},
@@ -163,24 +81,6 @@ function expectRuntimeWrapperExports(mod: unknown) {
});
}
-function writeCapturingSourceRuntimeWrapperFixture(fixtureRoot: string) {
- delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
- writeOpenClawAliasFixture(fixtureRoot);
- writeCapturingJitiFixture(fixtureRoot);
- writeSourceRuntimeWrapperFixture(fixtureRoot, { runtimeExtension: ".ts" });
-}
-
-function expectSourcePluginSdkAliases(fixtureRoot: string) {
- expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
- alias: {
- [PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
- [SCOPED_PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
- [GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
- [SCOPED_GROUP_ACCESS_SUBPATH]: path.join(fixtureRoot, "src", "plugin-sdk", "group-access.ts"),
- },
- });
-}
-
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
fs.rmSync(dir, { recursive: true, force: true });
@@ -223,110 +123,18 @@ it("loads the packaged runtime wrapper without recursing through the stable root
);
}, 240_000);
-it("builds scoped and unscoped plugin-sdk aliases for the wrapper jiti loader", async () => {
- const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-aliases-");
+it("does not use Jiti when only a TypeScript Matrix runtime shim exists", async () => {
+ const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-ts-only-");
- writeCapturingSourceRuntimeWrapperFixture(fixtureRoot);
- await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
-
- expectSourcePluginSdkAliases(fixtureRoot);
-}, 240_000);
-
-it("resolves extension-api aliases through the same source extension family", async () => {
- const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-extension-api-");
-
- writeFixtureFile(fixtureRoot, "src/extensionAPI.mts", "export {};\n");
- writeCapturingSourceRuntimeWrapperFixture(fixtureRoot);
- await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
-
- expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
- alias: {
- "openclaw/extension-api": path.join(fixtureRoot, "src", "extensionAPI.mts"),
- },
- });
-}, 240_000);
-
-it("keeps wrapper plugin-sdk aliases deterministic and ignores unsafe subpaths", async () => {
- const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-alias-order-");
-
- delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
- writeOpenClawAliasFixture(fixtureRoot, {
- "./plugin-sdk/zeta": "./dist/plugin-sdk/zeta.js",
- "./plugin-sdk/../escape": "./dist/plugin-sdk/escape.js",
- "./plugin-sdk/alpha": "./dist/plugin-sdk/alpha.js",
- });
- writeFixtureFile(fixtureRoot, "src/plugin-sdk/alpha.ts", "export {};\n");
- writeFixtureFile(fixtureRoot, "src/plugin-sdk/zeta.ts", "export {};\n");
- writeCapturingJitiFixture(fixtureRoot);
+ writeOpenClawPackageFixture(fixtureRoot);
writeSourceRuntimeWrapperFixture(fixtureRoot, { runtimeExtension: ".ts" });
- await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
-
- const aliasKeys = Object.keys(
- (
- (matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions ?? {}) as {
- alias?: Record;
- }
- ).alias ?? {},
- );
- expect(aliasKeys).toEqual([
- PLUGIN_SDK_ROOT,
- SCOPED_PLUGIN_SDK_ROOT,
- `${PLUGIN_SDK_ROOT}/alpha`,
- `${SCOPED_PLUGIN_SDK_ROOT}/alpha`,
- GROUP_ACCESS_SUBPATH,
- SCOPED_GROUP_ACCESS_SUBPATH,
- `${PLUGIN_SDK_ROOT}/zeta`,
- `${SCOPED_PLUGIN_SDK_ROOT}/zeta`,
- ]);
-}, 240_000);
-
-it("ignores nearby untrusted openclaw package stubs when resolving the wrapper root", async () => {
- const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-trusted-root-");
-
- delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
- writeOpenClawAliasFixture(fixtureRoot);
writeFixtureFile(
fixtureRoot,
- "extensions/package.json",
- JSON.stringify(
- {
- name: "openclaw",
- type: "module",
- exports: {
- "./plugin-sdk": "./dist/plugin-sdk/index.js",
- "./plugin-sdk/group-access": "./dist/plugin-sdk/group-access.js",
- },
- },
- null,
- 2,
- ) + "\n",
+ "node_modules/jiti/index.js",
+ "throw new Error('matrix wrapper must not require jiti');\n",
);
- writeFixtureFile(
- fixtureRoot,
- "extensions/src/plugin-sdk/root-alias.cjs",
- "module.exports = {};\n",
- );
- writeFixtureFile(fixtureRoot, "extensions/src/plugin-sdk/group-access.ts", "export {};\n");
- writeCapturingJitiFixture(fixtureRoot);
- writeSourceRuntimeWrapperFixture(fixtureRoot, { runtimeExtension: ".ts" });
- await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
- expectSourcePluginSdkAliases(fixtureRoot);
-}, 240_000);
-
-it("treats string bin hints case-insensitively when trusting wrapper package roots", async () => {
- const fixtureRoot = makeFixtureRoot(".tmp-matrix-runtime-bin-root-");
-
- delete matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions;
- writeTrustedOpenClawBinFixture(fixtureRoot, "OpenClaw.MJS");
- writeCapturingJitiFixture(fixtureRoot);
- writeSourceRuntimeWrapperFixture(fixtureRoot, { runtimeExtension: ".ts" });
- await importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js");
-
- expect(matrixWrapperGlobal.__openclawMatrixWrapperJitiOptions).toMatchObject({
- alias: {
- [PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
- [SCOPED_PLUGIN_SDK_ROOT]: path.join(fixtureRoot, "src", "plugin-sdk", "root-alias.cjs"),
- },
- });
+ await expect(
+ importFixtureModule(fixtureRoot, "extensions/matrix/src/plugin-entry.runtime.js"),
+ ).rejects.toThrow("Cannot resolve matrix plugin runtime module plugin-entry.handlers.runtime");
}, 240_000);
diff --git a/extensions/matrix/src/runtime-api.ts b/extensions/matrix/src/runtime-api.ts
index 19445ef1ee4..261b017ca91 100644
--- a/extensions/matrix/src/runtime-api.ts
+++ b/extensions/matrix/src/runtime-api.ts
@@ -107,4 +107,4 @@ export { formatZonedTimestamp } from "openclaw/plugin-sdk/time-runtime";
export type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/plugin-runtime";
export type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
// resolveMatrixAccountStringValues already comes from the Matrix API barrel.
-// Re-exporting auth-precedence here makes Jiti try to define the same export twice.
+// Re-exporting auth-precedence here makes TS source loaders define the export twice.