Matrix-js: adopt scoped plugin-sdk subpath on plugin branch

This commit is contained in:
Gustavo Madeira Santana
2026-03-04 09:59:07 -05:00
parent 268086ed31
commit ff3ee813b0
49 changed files with 847 additions and 109 deletions

View File

@@ -46,8 +46,7 @@ Looking for third-party listings? See [Community plugins](/plugins/community).
- Memory (LanceDB) — bundled long-term memory plugin (auto-recall/capture; set `plugins.slots.memory = "memory-lancedb"`)
- [Voice Call](/plugins/voice-call) — `@openclaw/voice-call`
- [Zalo Personal](/plugins/zalouser) — `@openclaw/zalouser`
- [Matrix-js](/channels/matrix-js) — `@openclaw/matrix-js`
- [Matrix (legacy)](/channels/matrix) — `@openclaw/matrix`
- [Matrix](/channels/matrix) — `@openclaw/matrix`
- [Nostr](/channels/nostr) — `@openclaw/nostr`
- [Zalo](/channels/zalo) — `@openclaw/zalo`
- [Microsoft Teams](/channels/msteams) — `@openclaw/msteams`
@@ -107,6 +106,57 @@ Notes:
- Uses core media-understanding audio configuration (`tools.media.audio`) and provider fallback order.
- Returns `{ text: undefined }` when no transcription output is produced (for example skipped/unsupported input).
## Plugin SDK import paths
Use SDK subpaths instead of the monolithic `openclaw/plugin-sdk` import when
authoring plugins:
- `openclaw/plugin-sdk/core` for generic plugin APIs, provider auth types, and shared helpers.
- `openclaw/plugin-sdk/compat` for bundled/internal plugin code that needs broader shared runtime helpers than `core`.
- `openclaw/plugin-sdk/telegram` for Telegram channel plugins.
- `openclaw/plugin-sdk/discord` for Discord channel plugins.
- `openclaw/plugin-sdk/slack` for Slack channel plugins.
- `openclaw/plugin-sdk/signal` for Signal channel plugins.
- `openclaw/plugin-sdk/imessage` for iMessage channel plugins.
- `openclaw/plugin-sdk/whatsapp` for WhatsApp channel plugins.
- `openclaw/plugin-sdk/line` for LINE channel plugins.
- `openclaw/plugin-sdk/msteams` for the bundled Microsoft Teams plugin surface.
- Bundled extension-specific subpaths are also available:
`openclaw/plugin-sdk/acpx`, `openclaw/plugin-sdk/bluebubbles`,
`openclaw/plugin-sdk/copilot-proxy`, `openclaw/plugin-sdk/device-pair`,
`openclaw/plugin-sdk/diagnostics-otel`, `openclaw/plugin-sdk/diffs`,
`openclaw/plugin-sdk/feishu`,
`openclaw/plugin-sdk/google-gemini-cli-auth`, `openclaw/plugin-sdk/googlechat`,
`openclaw/plugin-sdk/irc`, `openclaw/plugin-sdk/llm-task`,
`openclaw/plugin-sdk/lobster`, `openclaw/plugin-sdk/matrix`,
`openclaw/plugin-sdk/matrix-js`,
`openclaw/plugin-sdk/mattermost`, `openclaw/plugin-sdk/memory-core`,
`openclaw/plugin-sdk/memory-lancedb`,
`openclaw/plugin-sdk/minimax-portal-auth`,
`openclaw/plugin-sdk/nextcloud-talk`, `openclaw/plugin-sdk/nostr`,
`openclaw/plugin-sdk/open-prose`, `openclaw/plugin-sdk/phone-control`,
`openclaw/plugin-sdk/qwen-portal-auth`, `openclaw/plugin-sdk/synology-chat`,
`openclaw/plugin-sdk/talk-voice`, `openclaw/plugin-sdk/test-utils`,
`openclaw/plugin-sdk/thread-ownership`, `openclaw/plugin-sdk/tlon`,
`openclaw/plugin-sdk/twitch`, `openclaw/plugin-sdk/voice-call`,
`openclaw/plugin-sdk/zalo`, and `openclaw/plugin-sdk/zalouser`.
Compatibility note:
- `openclaw/plugin-sdk` remains supported for existing external plugins.
- New and migrated bundled plugins should use channel or extension-specific
subpaths; use `core` for generic surfaces and `compat` only when broader
shared helpers are required.
Performance note:
- Plugin discovery and manifest metadata use short in-process caches to reduce
bursty startup/reload work.
- Set `OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1` or
`OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1` to disable these caches.
- Tune cache windows with `OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS` and
`OPENCLAW_PLUGIN_MANIFEST_CACHE_MS`.
## Discovery & precedence
OpenClaw scans, in order:
@@ -125,13 +175,21 @@ OpenClaw scans, in order:
- `~/.openclaw/extensions/*.ts`
- `~/.openclaw/extensions/*/index.ts`
4. Bundled extensions (shipped with OpenClaw, **disabled by default**)
4. Bundled extensions (shipped with OpenClaw, mostly disabled by default)
- `<openclaw>/extensions/*`
Bundled plugins must be enabled explicitly via `plugins.entries.<id>.enabled`
or `openclaw plugins enable <id>`. Installed plugins are enabled by default,
but can be disabled the same way.
Most bundled plugins must be enabled explicitly via
`plugins.entries.<id>.enabled` or `openclaw plugins enable <id>`.
Default-on bundled plugin exceptions:
- `device-pair`
- `phone-control`
- `talk-voice`
- active memory slot plugin (default slot: `memory-core`)
Installed plugins are enabled by default, but can be disabled the same way.
Hardening notes:

View File

@@ -1,5 +1,8 @@
import type { GatewayRequestHandlerOptions, OpenClawPluginApi } from "openclaw/plugin-sdk";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
import type {
GatewayRequestHandlerOptions,
OpenClawPluginApi,
} from "openclaw/plugin-sdk/matrix-js";
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/matrix-js";
import { matrixPlugin } from "./src/channel.js";
import { registerMatrixJsCli } from "./src/cli.js";
import {

View File

@@ -6,7 +6,7 @@ import {
type ChannelMessageActionContext,
type ChannelMessageActionName,
type ChannelToolSend,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import { resolveMatrixAccount } from "./matrix/accounts.js";
import { handleMatrixAction } from "./tool-actions.js";
import type { CoreConfig } from "./types.js";

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk";
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix-js";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { matrixPlugin } from "./channel.js";
import { migrateMatrixLegacyCredentialsToDefaultAccount } from "./config-migration.js";

View File

@@ -12,7 +12,7 @@ import {
setAccountEnabledInConfigSection,
type ChannelSetupInput,
type ChannelPlugin,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import { matrixMessageActions } from "./actions.js";
import { migrateMatrixLegacyCredentialsToDefaultAccount } from "./config-migration.js";
import { MatrixConfigSchema } from "./config-schema.js";

View File

@@ -1,5 +1,5 @@
import { Command } from "commander";
import { formatZonedTimestamp } from "openclaw/plugin-sdk";
import { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix-js";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const bootstrapMatrixVerificationMock = vi.fn();

View File

@@ -3,7 +3,7 @@ import {
formatZonedTimestamp,
normalizeAccountId,
type ChannelSetupInput,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import { matrixPlugin } from "./channel.js";
import { updateMatrixOwnProfile } from "./matrix/actions/profile.js";
import {

View File

@@ -1,4 +1,4 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/matrix-js";
import type { CoreConfig, MatrixAccountConfig, MatrixConfig } from "./types.js";
type LegacyAccountField =

View File

@@ -1,4 +1,4 @@
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk";
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk/matrix-js";
import { z } from "zod";
const allowFromEntry = z.union([z.string(), z.number()]);

View File

@@ -1,4 +1,4 @@
import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk";
import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk/matrix-js";
import { resolveMatrixAuth } from "./matrix/client.js";
type MatrixUserResult = {

View File

@@ -1,4 +1,4 @@
import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk";
import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk/matrix-js";
import { resolveMatrixAccountConfig } from "./matrix/accounts.js";
import { resolveMatrixRoomConfig } from "./matrix/monitor/rooms.js";
import type { CoreConfig } from "./types.js";

View File

@@ -55,22 +55,22 @@ describe("resolveSharedMatrixClient", () => {
it("keeps account clients isolated when resolves are interleaved", async () => {
const mainAuth = authFor("main");
const poeAuth = authFor("poe");
const poeAuth = authFor("ops");
const mainClient = createMockClient("main");
const poeClient = createMockClient("poe");
const poeClient = createMockClient("ops");
resolveMatrixAuthMock.mockImplementation(async ({ accountId }: { accountId?: string }) =>
accountId === "poe" ? poeAuth : mainAuth,
accountId === "ops" ? poeAuth : mainAuth,
);
createMatrixClientMock.mockImplementation(async ({ accountId }: { accountId?: string }) => {
if (accountId === "poe") {
if (accountId === "ops") {
return poeClient;
}
return mainClient;
});
const firstMain = await resolveSharedMatrixClient({ accountId: "main", startClient: false });
const firstPoe = await resolveSharedMatrixClient({ accountId: "poe", startClient: false });
const firstPoe = await resolveSharedMatrixClient({ accountId: "ops", startClient: false });
const secondMain = await resolveSharedMatrixClient({ accountId: "main" });
expect(firstMain).toBe(mainClient);
@@ -83,22 +83,22 @@ describe("resolveSharedMatrixClient", () => {
it("stops only the targeted account client", async () => {
const mainAuth = authFor("main");
const poeAuth = authFor("poe");
const poeAuth = authFor("ops");
const mainClient = createMockClient("main");
const poeClient = createMockClient("poe");
const poeClient = createMockClient("ops");
resolveMatrixAuthMock.mockImplementation(async ({ accountId }: { accountId?: string }) =>
accountId === "poe" ? poeAuth : mainAuth,
accountId === "ops" ? poeAuth : mainAuth,
);
createMatrixClientMock.mockImplementation(async ({ accountId }: { accountId?: string }) => {
if (accountId === "poe") {
if (accountId === "ops") {
return poeClient;
}
return mainClient;
});
await resolveSharedMatrixClient({ accountId: "main", startClient: false });
await resolveSharedMatrixClient({ accountId: "poe", startClient: false });
await resolveSharedMatrixClient({ accountId: "ops", startClient: false });
stopSharedClientForAccount(mainAuth, "main");

View File

@@ -1,4 +1,4 @@
import { normalizeAccountId } from "openclaw/plugin-sdk";
import { normalizeAccountId } from "openclaw/plugin-sdk/matrix-js";
import type { CoreConfig, MatrixConfig } from "../types.js";
export type MatrixAccountPatch = {

View File

@@ -1,8 +1,8 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { writeJsonFileAtomically } from "openclaw/plugin-sdk";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { writeJsonFileAtomically } from "openclaw/plugin-sdk/matrix-js";
import { getMatrixRuntime } from "../runtime.js";
export type MatrixStoredCredentials = {

View File

@@ -3,7 +3,7 @@ import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import type { RuntimeEnv } from "openclaw/plugin-sdk/matrix-js";
const REQUIRED_MATRIX_PACKAGES = ["matrix-js-sdk", "@matrix-org/matrix-sdk-crypto-nodejs"];

View File

@@ -1,4 +1,4 @@
import type { AllowlistMatch } from "openclaw/plugin-sdk";
import type { AllowlistMatch } from "openclaw/plugin-sdk/matrix-js";
function normalizeAllowList(list?: Array<string | number>) {
return (list ?? []).map((entry) => String(entry).trim()).filter(Boolean);

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime } from "openclaw/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk/matrix-js";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { setMatrixRuntime } from "../../runtime.js";
import type { CoreConfig } from "../../types.js";
@@ -53,7 +53,7 @@ describe("registerMatrixAutoJoin", () => {
runtime: {
log: vi.fn(),
error: vi.fn(),
} as unknown as import("openclaw/plugin-sdk").RuntimeEnv,
} as unknown as import("openclaw/plugin-sdk/matrix-js").RuntimeEnv,
});
const inviteHandler = getInviteHandler();
@@ -84,7 +84,7 @@ describe("registerMatrixAutoJoin", () => {
runtime: {
log: vi.fn(),
error: vi.fn(),
} as unknown as import("openclaw/plugin-sdk").RuntimeEnv,
} as unknown as import("openclaw/plugin-sdk/matrix-js").RuntimeEnv,
});
const inviteHandler = getInviteHandler();
@@ -115,7 +115,7 @@ describe("registerMatrixAutoJoin", () => {
runtime: {
log: vi.fn(),
error: vi.fn(),
} as unknown as import("openclaw/plugin-sdk").RuntimeEnv,
} as unknown as import("openclaw/plugin-sdk/matrix-js").RuntimeEnv,
});
const inviteHandler = getInviteHandler();

View File

@@ -1,4 +1,4 @@
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import type { RuntimeEnv } from "openclaw/plugin-sdk/matrix-js";
import { getMatrixRuntime } from "../../runtime.js";
import type { CoreConfig } from "../../types.js";
import type { MatrixClient } from "../sdk.js";

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk";
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/matrix-js";
import type { MatrixAuth } from "../client.js";
import type { MatrixClient } from "../sdk.js";
import type { MatrixRawEvent } from "./types.js";

View File

@@ -33,7 +33,7 @@ describe("matrix monitor handler pairing account scope", () => {
},
} as never,
cfg: {} as never,
accountId: "poe",
accountId: "ops",
runtime: {} as never,
logger: {
info: () => {},
@@ -107,7 +107,7 @@ describe("matrix monitor handler pairing account scope", () => {
},
} as never,
cfg: {} as never,
accountId: "poe",
accountId: "ops",
runtime: {} as never,
logger: {
info: () => {},
@@ -177,7 +177,7 @@ describe("matrix monitor handler pairing account scope", () => {
},
} as never,
cfg: {} as never,
accountId: "poe",
accountId: "ops",
runtime: {} as never,
logger: {
info: () => {},
@@ -217,23 +217,23 @@ describe("matrix monitor handler pairing account scope", () => {
expect(readAllowFromStore).toHaveBeenCalledWith({
channel: "matrix-js",
env: process.env,
accountId: "poe",
accountId: "ops",
});
expect(upsertPairingRequest).toHaveBeenCalledWith({
channel: "matrix-js",
id: "@user:example.org",
accountId: "poe",
accountId: "ops",
meta: { name: "sender" },
});
});
it("passes accountId into route resolution for inbound dm messages", async () => {
const resolveAgentRoute = vi.fn(() => ({
agentId: "poe",
agentId: "ops",
channel: "matrix-js",
accountId: "poe",
sessionKey: "agent:poe:main",
mainSessionKey: "agent:poe:main",
accountId: "ops",
sessionKey: "agent:ops:main",
mainSessionKey: "agent:ops:main",
matchedBy: "binding.account",
}));
@@ -259,7 +259,7 @@ describe("matrix monitor handler pairing account scope", () => {
},
} as never,
cfg: {} as never,
accountId: "poe",
accountId: "ops",
runtime: {
error: () => {},
} as never,
@@ -301,7 +301,7 @@ describe("matrix monitor handler pairing account scope", () => {
expect(resolveAgentRoute).toHaveBeenCalledWith(
expect.objectContaining({
channel: "matrix-js",
accountId: "poe",
accountId: "ops",
}),
);
});

View File

@@ -9,7 +9,7 @@ import {
type ReplyPayload,
type RuntimeEnv,
type RuntimeLogger,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import type { CoreConfig, MatrixRoomConfig, ReplyToMode } from "../../types.js";
import {
formatPollAsText,

View File

@@ -7,7 +7,7 @@ import {
summarizeMapping,
warnMissingProviderGroupPolicyFallbackOnce,
type RuntimeEnv,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import { resolveMatrixTargets } from "../../resolve-targets.js";
import { getMatrixRuntime } from "../../runtime.js";
import type { CoreConfig, ReplyToMode } from "../../types.js";

View File

@@ -2,7 +2,7 @@ import {
formatLocationText,
toLocationContext,
type NormalizedLocation,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import type { LocationMessageEventContent } from "../sdk.js";
import { EventType } from "./types.js";

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime } from "openclaw/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk/matrix-js";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { setMatrixRuntime } from "../../runtime.js";
import { downloadMatrixMedia } from "./media.js";

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk";
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix-js";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { MatrixClient } from "../sdk.js";

View File

@@ -1,4 +1,4 @@
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "openclaw/plugin-sdk";
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "openclaw/plugin-sdk/matrix-js";
import { getMatrixRuntime } from "../../runtime.js";
import type { MatrixClient } from "../sdk.js";
import { sendMessageMatrix } from "../send.js";

View File

@@ -1,4 +1,4 @@
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "openclaw/plugin-sdk";
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "openclaw/plugin-sdk/matrix-js";
import type { MatrixRoomConfig } from "../../types.js";
export type MatrixRoomConfigResolved = {

View File

@@ -7,7 +7,7 @@
* - m.poll.end - Closes a poll
*/
import type { PollInput } from "openclaw/plugin-sdk";
import type { PollInput } from "openclaw/plugin-sdk/matrix-js";
export const M_POLL_START = "m.poll.start" as const;
export const M_POLL_RESPONSE = "m.poll.response" as const;

View File

@@ -1,4 +1,4 @@
import type { BaseProbeResult } from "openclaw/plugin-sdk";
import type { BaseProbeResult } from "openclaw/plugin-sdk/matrix-js";
import { createMatrixClient, isBunRuntime } from "./client.js";
export type MatrixProbe = BaseProbeResult & {

View File

@@ -1,5 +1,5 @@
import { format } from "node:util";
import type { RuntimeLogger } from "openclaw/plugin-sdk";
import type { RuntimeLogger } from "openclaw/plugin-sdk/matrix-js";
import { getMatrixRuntime } from "../../runtime.js";
export type Logger = {

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime } from "openclaw/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk/matrix-js";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { setMatrixRuntime } from "../runtime.js";

View File

@@ -1,4 +1,4 @@
import type { PollInput } from "openclaw/plugin-sdk";
import type { PollInput } from "openclaw/plugin-sdk/matrix-js";
import { getMatrixRuntime } from "../runtime.js";
import { buildPollStartContent, M_POLL_START } from "./poll-types.js";
import type { MatrixClient } from "./sdk.js";

View File

@@ -1,4 +1,4 @@
import type { RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk";
import type { RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/matrix-js";
import { afterEach, describe, expect, it, vi } from "vitest";
import { matrixOnboardingAdapter } from "./onboarding.js";
import { setMatrixRuntime } from "./runtime.js";

View File

@@ -1,4 +1,5 @@
import type { DmPolicy } from "openclaw/plugin-sdk";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import type { DmPolicy } from "openclaw/plugin-sdk/matrix-js";
import {
addWildcardAllowFrom,
formatDocsLink,
@@ -10,8 +11,7 @@ import {
type ChannelOnboardingAdapter,
type ChannelOnboardingDmPolicy,
type WizardPrompter,
} from "openclaw/plugin-sdk";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
} from "openclaw/plugin-sdk/matrix-js";
import { migrateMatrixLegacyCredentialsToDefaultAccount } from "./config-migration.js";
import { listMatrixDirectoryGroupsLive } from "./directory-live.js";
import {

View File

@@ -1,4 +1,4 @@
import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk";
import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk/matrix-js";
import { sendMessageMatrix, sendPollMatrix } from "./matrix/send.js";
import { getMatrixRuntime } from "./runtime.js";

View File

@@ -1,4 +1,4 @@
import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk";
import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk/matrix-js";
import { describe, expect, it, vi, beforeEach } from "vitest";
import { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js";
import { resolveMatrixTargets } from "./resolve-targets.js";

View File

@@ -3,7 +3,7 @@ import type {
ChannelResolveKind,
ChannelResolveResult,
RuntimeEnv,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js";
function findExactDirectoryMatches(

View File

@@ -1,4 +1,4 @@
import type { PluginRuntime } from "openclaw/plugin-sdk";
import type { PluginRuntime } from "openclaw/plugin-sdk/matrix-js";
let runtime: PluginRuntime | null = null;

View File

@@ -5,7 +5,7 @@ import {
readNumberParam,
readReactionParams,
readStringParam,
} from "openclaw/plugin-sdk";
} from "openclaw/plugin-sdk/matrix-js";
import {
bootstrapMatrixVerification,
acceptMatrixVerification,

View File

@@ -1,4 +1,4 @@
import type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk";
import type { DmPolicy, GroupPolicy } from "openclaw/plugin-sdk/matrix-js";
export type { DmPolicy, GroupPolicy };
export type ReplyToMode = "off" | "first" | "all";

View File

@@ -1,6 +1,6 @@
{
"name": "openclaw",
"version": "2026.3.2",
"version": "2026.3.3",
"description": "Multi-channel AI gateway with extensible messaging integrations",
"keywords": [],
"homepage": "https://github.com/openclaw/openclaw#readme",
@@ -40,6 +40,174 @@
"types": "./dist/plugin-sdk/index.d.ts",
"default": "./dist/plugin-sdk/index.js"
},
"./plugin-sdk/core": {
"types": "./dist/plugin-sdk/core.d.ts",
"default": "./dist/plugin-sdk/core.js"
},
"./plugin-sdk/compat": {
"types": "./dist/plugin-sdk/compat.d.ts",
"default": "./dist/plugin-sdk/compat.js"
},
"./plugin-sdk/telegram": {
"types": "./dist/plugin-sdk/telegram.d.ts",
"default": "./dist/plugin-sdk/telegram.js"
},
"./plugin-sdk/discord": {
"types": "./dist/plugin-sdk/discord.d.ts",
"default": "./dist/plugin-sdk/discord.js"
},
"./plugin-sdk/slack": {
"types": "./dist/plugin-sdk/slack.d.ts",
"default": "./dist/plugin-sdk/slack.js"
},
"./plugin-sdk/signal": {
"types": "./dist/plugin-sdk/signal.d.ts",
"default": "./dist/plugin-sdk/signal.js"
},
"./plugin-sdk/imessage": {
"types": "./dist/plugin-sdk/imessage.d.ts",
"default": "./dist/plugin-sdk/imessage.js"
},
"./plugin-sdk/whatsapp": {
"types": "./dist/plugin-sdk/whatsapp.d.ts",
"default": "./dist/plugin-sdk/whatsapp.js"
},
"./plugin-sdk/line": {
"types": "./dist/plugin-sdk/line.d.ts",
"default": "./dist/plugin-sdk/line.js"
},
"./plugin-sdk/msteams": {
"types": "./dist/plugin-sdk/msteams.d.ts",
"default": "./dist/plugin-sdk/msteams.js"
},
"./plugin-sdk/acpx": {
"types": "./dist/plugin-sdk/acpx.d.ts",
"default": "./dist/plugin-sdk/acpx.js"
},
"./plugin-sdk/bluebubbles": {
"types": "./dist/plugin-sdk/bluebubbles.d.ts",
"default": "./dist/plugin-sdk/bluebubbles.js"
},
"./plugin-sdk/copilot-proxy": {
"types": "./dist/plugin-sdk/copilot-proxy.d.ts",
"default": "./dist/plugin-sdk/copilot-proxy.js"
},
"./plugin-sdk/device-pair": {
"types": "./dist/plugin-sdk/device-pair.d.ts",
"default": "./dist/plugin-sdk/device-pair.js"
},
"./plugin-sdk/diagnostics-otel": {
"types": "./dist/plugin-sdk/diagnostics-otel.d.ts",
"default": "./dist/plugin-sdk/diagnostics-otel.js"
},
"./plugin-sdk/diffs": {
"types": "./dist/plugin-sdk/diffs.d.ts",
"default": "./dist/plugin-sdk/diffs.js"
},
"./plugin-sdk/feishu": {
"types": "./dist/plugin-sdk/feishu.d.ts",
"default": "./dist/plugin-sdk/feishu.js"
},
"./plugin-sdk/google-gemini-cli-auth": {
"types": "./dist/plugin-sdk/google-gemini-cli-auth.d.ts",
"default": "./dist/plugin-sdk/google-gemini-cli-auth.js"
},
"./plugin-sdk/googlechat": {
"types": "./dist/plugin-sdk/googlechat.d.ts",
"default": "./dist/plugin-sdk/googlechat.js"
},
"./plugin-sdk/irc": {
"types": "./dist/plugin-sdk/irc.d.ts",
"default": "./dist/plugin-sdk/irc.js"
},
"./plugin-sdk/llm-task": {
"types": "./dist/plugin-sdk/llm-task.d.ts",
"default": "./dist/plugin-sdk/llm-task.js"
},
"./plugin-sdk/lobster": {
"types": "./dist/plugin-sdk/lobster.d.ts",
"default": "./dist/plugin-sdk/lobster.js"
},
"./plugin-sdk/matrix": {
"types": "./dist/plugin-sdk/matrix.d.ts",
"default": "./dist/plugin-sdk/matrix.js"
},
"./plugin-sdk/matrix-js": {
"types": "./dist/plugin-sdk/matrix-js.d.ts",
"default": "./dist/plugin-sdk/matrix-js.js"
},
"./plugin-sdk/mattermost": {
"types": "./dist/plugin-sdk/mattermost.d.ts",
"default": "./dist/plugin-sdk/mattermost.js"
},
"./plugin-sdk/memory-core": {
"types": "./dist/plugin-sdk/memory-core.d.ts",
"default": "./dist/plugin-sdk/memory-core.js"
},
"./plugin-sdk/memory-lancedb": {
"types": "./dist/plugin-sdk/memory-lancedb.d.ts",
"default": "./dist/plugin-sdk/memory-lancedb.js"
},
"./plugin-sdk/minimax-portal-auth": {
"types": "./dist/plugin-sdk/minimax-portal-auth.d.ts",
"default": "./dist/plugin-sdk/minimax-portal-auth.js"
},
"./plugin-sdk/nextcloud-talk": {
"types": "./dist/plugin-sdk/nextcloud-talk.d.ts",
"default": "./dist/plugin-sdk/nextcloud-talk.js"
},
"./plugin-sdk/nostr": {
"types": "./dist/plugin-sdk/nostr.d.ts",
"default": "./dist/plugin-sdk/nostr.js"
},
"./plugin-sdk/open-prose": {
"types": "./dist/plugin-sdk/open-prose.d.ts",
"default": "./dist/plugin-sdk/open-prose.js"
},
"./plugin-sdk/phone-control": {
"types": "./dist/plugin-sdk/phone-control.d.ts",
"default": "./dist/plugin-sdk/phone-control.js"
},
"./plugin-sdk/qwen-portal-auth": {
"types": "./dist/plugin-sdk/qwen-portal-auth.d.ts",
"default": "./dist/plugin-sdk/qwen-portal-auth.js"
},
"./plugin-sdk/synology-chat": {
"types": "./dist/plugin-sdk/synology-chat.d.ts",
"default": "./dist/plugin-sdk/synology-chat.js"
},
"./plugin-sdk/talk-voice": {
"types": "./dist/plugin-sdk/talk-voice.d.ts",
"default": "./dist/plugin-sdk/talk-voice.js"
},
"./plugin-sdk/test-utils": {
"types": "./dist/plugin-sdk/test-utils.d.ts",
"default": "./dist/plugin-sdk/test-utils.js"
},
"./plugin-sdk/thread-ownership": {
"types": "./dist/plugin-sdk/thread-ownership.d.ts",
"default": "./dist/plugin-sdk/thread-ownership.js"
},
"./plugin-sdk/tlon": {
"types": "./dist/plugin-sdk/tlon.d.ts",
"default": "./dist/plugin-sdk/tlon.js"
},
"./plugin-sdk/twitch": {
"types": "./dist/plugin-sdk/twitch.d.ts",
"default": "./dist/plugin-sdk/twitch.js"
},
"./plugin-sdk/voice-call": {
"types": "./dist/plugin-sdk/voice-call.d.ts",
"default": "./dist/plugin-sdk/voice-call.js"
},
"./plugin-sdk/zalo": {
"types": "./dist/plugin-sdk/zalo.d.ts",
"default": "./dist/plugin-sdk/zalo.js"
},
"./plugin-sdk/zalouser": {
"types": "./dist/plugin-sdk/zalouser.d.ts",
"default": "./dist/plugin-sdk/zalouser.js"
},
"./plugin-sdk/account-id": {
"types": "./dist/plugin-sdk/account-id.d.ts",
"default": "./dist/plugin-sdk/account-id.js"
@@ -59,11 +227,11 @@
"android:run": "cd apps/android && ./gradlew :app:installDebug && adb shell am start -n ai.openclaw.android/.MainActivity",
"android:test": "cd apps/android && ./gradlew :app:testDebugUnitTest",
"android:test:integration": "OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_ANDROID_NODE=1 vitest run --config vitest.live.config.ts src/gateway/android-node.capabilities.live.test.ts",
"build": "pnpm canvas:a2ui:bundle && tsdown && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
"build": "pnpm canvas:a2ui:bundle && tsdown && node scripts/copy-plugin-sdk-root-alias.mjs && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --import tsx scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
"build:plugin-sdk:dts": "tsc -p tsconfig.plugin-sdk.dts.json",
"build:strict-smoke": "pnpm canvas:a2ui:bundle && tsdown && pnpm build:plugin-sdk:dts",
"build:strict-smoke": "pnpm canvas:a2ui:bundle && tsdown && node scripts/copy-plugin-sdk-root-alias.mjs && pnpm build:plugin-sdk:dts",
"canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh",
"check": "pnpm format:check && pnpm tsgo && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope && pnpm check:host-env-policy:swift",
"check": "pnpm format:check && pnpm tsgo && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope && pnpm check:host-env-policy:swift",
"check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-links",
"check:host-env-policy:swift": "node scripts/generate-host-env-security-policy-swift.mjs --check",
"check:loc": "node --import tsx scripts/check-ts-max-loc.ts --max 500",
@@ -107,6 +275,7 @@
"lint:docs": "pnpm dlx markdownlint-cli2",
"lint:docs:fix": "pnpm dlx markdownlint-cli2 --fix",
"lint:fix": "oxlint --type-aware --fix && pnpm format",
"lint:plugins:no-monolithic-plugin-sdk-entry-imports": "node --import tsx scripts/check-no-monolithic-plugin-sdk-entry-imports.ts",
"lint:plugins:no-register-http-handler": "node scripts/check-no-register-http-handler.mjs",
"lint:swift": "swiftlint lint --config .swiftlint.yml && (cd apps/ios && swiftlint lint --config .swiftlint.yml)",
"lint:tmp:channel-agnostic-boundaries": "node scripts/check-channel-agnostic-boundaries.mjs",
@@ -173,7 +342,6 @@
"@grammyjs/runner": "^2.0.3",
"@grammyjs/transformer-throttler": "^1.2.1",
"@homebridge/ciao": "^1.3.5",
"@larksuiteoapi/node-sdk": "^1.59.0",
"@line/bot-sdk": "^10.6.0",
"@lydell/node-pty": "1.2.0-beta.3",
"@mariozechner/pi-agent-core": "0.55.3",
@@ -197,7 +365,6 @@
"express": "^5.2.1",
"file-type": "^21.3.0",
"gaxios": "7.1.3",
"google-auth-library": "10.6.1",
"grammy": "^1.41.0",
"https-proxy-agent": "^7.0.6",
"ipaddr.js": "^2.3.0",
@@ -249,9 +416,6 @@
"@napi-rs/canvas": "^0.1.89",
"node-llama-cpp": "3.16.2"
},
"optionalDependencies": {
"@discordjs/opus": "^0.10.0"
},
"engines": {
"node": ">=22.12.0"
},
@@ -260,7 +424,7 @@
"minimumReleaseAge": 2880,
"overrides": {
"hono": "4.11.10",
"fast-xml-parser": "5.3.6",
"fast-xml-parser": "5.3.8",
"request": "npm:@cypress/request@3.0.10",
"request-promise": "npm:@cypress/request-promise@5.0.0",
"form-data": "2.5.4",

View File

@@ -41,6 +41,55 @@ const exportedNames = exportMatch[1]
const exportSet = new Set(exportedNames);
const requiredSubpathEntries = [
"core",
"compat",
"telegram",
"discord",
"slack",
"signal",
"imessage",
"whatsapp",
"line",
"msteams",
"acpx",
"bluebubbles",
"copilot-proxy",
"device-pair",
"diagnostics-otel",
"diffs",
"feishu",
"google-gemini-cli-auth",
"googlechat",
"irc",
"llm-task",
"lobster",
"matrix",
"matrix-js",
"mattermost",
"memory-core",
"memory-lancedb",
"minimax-portal-auth",
"nextcloud-talk",
"nostr",
"open-prose",
"phone-control",
"qwen-portal-auth",
"synology-chat",
"talk-voice",
"test-utils",
"thread-ownership",
"tlon",
"twitch",
"voice-call",
"zalo",
"zalouser",
"account-id",
"keyed-async-queue",
];
const requiredRuntimeShimEntries = ["root-alias.cjs"];
// Critical functions that channel extension plugins import from openclaw/plugin-sdk.
// If any of these are missing, plugins will fail at runtime with:
// TypeError: (0 , _pluginSdk.<name>) is not a function
@@ -76,10 +125,33 @@ for (const name of requiredExports) {
}
}
for (const entry of requiredSubpathEntries) {
const jsPath = resolve(__dirname, "..", "dist", "plugin-sdk", `${entry}.js`);
const dtsPath = resolve(__dirname, "..", "dist", "plugin-sdk", `${entry}.d.ts`);
if (!existsSync(jsPath)) {
console.error(`MISSING SUBPATH JS: dist/plugin-sdk/${entry}.js`);
missing += 1;
}
if (!existsSync(dtsPath)) {
console.error(`MISSING SUBPATH DTS: dist/plugin-sdk/${entry}.d.ts`);
missing += 1;
}
}
for (const entry of requiredRuntimeShimEntries) {
const shimPath = resolve(__dirname, "..", "dist", "plugin-sdk", entry);
if (!existsSync(shimPath)) {
console.error(`MISSING RUNTIME SHIM: dist/plugin-sdk/${entry}`);
missing += 1;
}
}
if (missing > 0) {
console.error(`\nERROR: ${missing} required export(s) missing from dist/plugin-sdk/index.js.`);
console.error(
`\nERROR: ${missing} required plugin-sdk artifact(s) missing (named exports or subpath files).`,
);
console.error("This will break channel extension plugins at runtime.");
console.error("Check src/plugin-sdk/index.ts and rebuild.");
console.error("Check src/plugin-sdk/index.ts, subpath entries, and rebuild.");
process.exit(1);
}

View File

@@ -14,6 +14,95 @@ const requiredPathGroups = [
["dist/entry.js", "dist/entry.mjs"],
"dist/plugin-sdk/index.js",
"dist/plugin-sdk/index.d.ts",
"dist/plugin-sdk/core.js",
"dist/plugin-sdk/core.d.ts",
"dist/plugin-sdk/root-alias.cjs",
"dist/plugin-sdk/compat.js",
"dist/plugin-sdk/compat.d.ts",
"dist/plugin-sdk/telegram.js",
"dist/plugin-sdk/telegram.d.ts",
"dist/plugin-sdk/discord.js",
"dist/plugin-sdk/discord.d.ts",
"dist/plugin-sdk/slack.js",
"dist/plugin-sdk/slack.d.ts",
"dist/plugin-sdk/signal.js",
"dist/plugin-sdk/signal.d.ts",
"dist/plugin-sdk/imessage.js",
"dist/plugin-sdk/imessage.d.ts",
"dist/plugin-sdk/whatsapp.js",
"dist/plugin-sdk/whatsapp.d.ts",
"dist/plugin-sdk/line.js",
"dist/plugin-sdk/line.d.ts",
"dist/plugin-sdk/msteams.js",
"dist/plugin-sdk/msteams.d.ts",
"dist/plugin-sdk/acpx.js",
"dist/plugin-sdk/acpx.d.ts",
"dist/plugin-sdk/bluebubbles.js",
"dist/plugin-sdk/bluebubbles.d.ts",
"dist/plugin-sdk/copilot-proxy.js",
"dist/plugin-sdk/copilot-proxy.d.ts",
"dist/plugin-sdk/device-pair.js",
"dist/plugin-sdk/device-pair.d.ts",
"dist/plugin-sdk/diagnostics-otel.js",
"dist/plugin-sdk/diagnostics-otel.d.ts",
"dist/plugin-sdk/diffs.js",
"dist/plugin-sdk/diffs.d.ts",
"dist/plugin-sdk/feishu.js",
"dist/plugin-sdk/feishu.d.ts",
"dist/plugin-sdk/google-gemini-cli-auth.js",
"dist/plugin-sdk/google-gemini-cli-auth.d.ts",
"dist/plugin-sdk/googlechat.js",
"dist/plugin-sdk/googlechat.d.ts",
"dist/plugin-sdk/irc.js",
"dist/plugin-sdk/irc.d.ts",
"dist/plugin-sdk/llm-task.js",
"dist/plugin-sdk/llm-task.d.ts",
"dist/plugin-sdk/lobster.js",
"dist/plugin-sdk/lobster.d.ts",
"dist/plugin-sdk/matrix.js",
"dist/plugin-sdk/matrix.d.ts",
"dist/plugin-sdk/matrix-js.js",
"dist/plugin-sdk/matrix-js.d.ts",
"dist/plugin-sdk/mattermost.js",
"dist/plugin-sdk/mattermost.d.ts",
"dist/plugin-sdk/memory-core.js",
"dist/plugin-sdk/memory-core.d.ts",
"dist/plugin-sdk/memory-lancedb.js",
"dist/plugin-sdk/memory-lancedb.d.ts",
"dist/plugin-sdk/minimax-portal-auth.js",
"dist/plugin-sdk/minimax-portal-auth.d.ts",
"dist/plugin-sdk/nextcloud-talk.js",
"dist/plugin-sdk/nextcloud-talk.d.ts",
"dist/plugin-sdk/nostr.js",
"dist/plugin-sdk/nostr.d.ts",
"dist/plugin-sdk/open-prose.js",
"dist/plugin-sdk/open-prose.d.ts",
"dist/plugin-sdk/phone-control.js",
"dist/plugin-sdk/phone-control.d.ts",
"dist/plugin-sdk/qwen-portal-auth.js",
"dist/plugin-sdk/qwen-portal-auth.d.ts",
"dist/plugin-sdk/synology-chat.js",
"dist/plugin-sdk/synology-chat.d.ts",
"dist/plugin-sdk/talk-voice.js",
"dist/plugin-sdk/talk-voice.d.ts",
"dist/plugin-sdk/test-utils.js",
"dist/plugin-sdk/test-utils.d.ts",
"dist/plugin-sdk/thread-ownership.js",
"dist/plugin-sdk/thread-ownership.d.ts",
"dist/plugin-sdk/tlon.js",
"dist/plugin-sdk/tlon.d.ts",
"dist/plugin-sdk/twitch.js",
"dist/plugin-sdk/twitch.d.ts",
"dist/plugin-sdk/voice-call.js",
"dist/plugin-sdk/voice-call.d.ts",
"dist/plugin-sdk/zalo.js",
"dist/plugin-sdk/zalo.d.ts",
"dist/plugin-sdk/zalouser.js",
"dist/plugin-sdk/zalouser.d.ts",
"dist/plugin-sdk/account-id.js",
"dist/plugin-sdk/account-id.d.ts",
"dist/plugin-sdk/keyed-async-queue.js",
"dist/plugin-sdk/keyed-async-queue.d.ts",
"dist/build-info.json",
];
const forbiddenPrefixes = ["dist/OpenClaw.app/"];

View File

@@ -6,7 +6,53 @@ import path from "node:path";
//
// Our package export map points subpath `types` at `dist/plugin-sdk/<entry>.d.ts`, so we
// generate stable entry d.ts files that re-export the real declarations.
const entrypoints = ["index", "account-id"] as const;
const entrypoints = [
"index",
"core",
"compat",
"telegram",
"discord",
"slack",
"signal",
"imessage",
"whatsapp",
"line",
"msteams",
"acpx",
"bluebubbles",
"copilot-proxy",
"device-pair",
"diagnostics-otel",
"diffs",
"feishu",
"google-gemini-cli-auth",
"googlechat",
"irc",
"llm-task",
"lobster",
"matrix",
"matrix-js",
"mattermost",
"memory-core",
"memory-lancedb",
"minimax-portal-auth",
"nextcloud-talk",
"nostr",
"open-prose",
"phone-control",
"qwen-portal-auth",
"synology-chat",
"talk-voice",
"test-utils",
"thread-ownership",
"tlon",
"twitch",
"voice-call",
"zalo",
"zalouser",
"account-id",
"keyed-async-queue",
] as const;
for (const entry of entrypoints) {
const out = path.join(process.cwd(), `dist/plugin-sdk/${entry}.d.ts`);
fs.mkdirSync(path.dirname(out), { recursive: true });

View File

@@ -0,0 +1,11 @@
// Matrix-js plugin-sdk surface.
// Reuse root plugin-sdk exports on branches that don't yet expose matrix-specific subpaths.
export * from "./index.js";
export type { ChannelSetupInput } from "../channels/plugins/types.js";
export type { GatewayRequestHandlerOptions } from "../gateway/server-methods/types.js";
export { migrateBaseNameToDefaultAccount } from "../channels/plugins/setup-helpers.js";
export { promptAccountId } from "../channels/plugins/onboarding/helpers.js";
export { writeJsonFileAtomically } from "./json-store.js";
export { formatZonedTimestamp } from "../infra/format-time/format-datetime.js";

View File

@@ -22,6 +22,7 @@ import { isPathInside, safeStatSync } from "./path-safety.js";
import { createPluginRegistry, type PluginRecord, type PluginRegistry } from "./registry.js";
import { setActivePluginRegistry } from "./runtime.js";
import { createPluginRuntime } from "./runtime/index.js";
import type { PluginRuntime } from "./runtime/types.js";
import { validateJsonSchemaValue } from "./schema-validator.js";
import type {
OpenClawPluginDefinition,
@@ -85,10 +86,111 @@ const resolvePluginSdkAliasFile = (params: {
};
const resolvePluginSdkAlias = (): string | null =>
resolvePluginSdkAliasFile({ srcFile: "index.ts", distFile: "index.js" });
resolvePluginSdkAliasFile({ srcFile: "root-alias.cjs", distFile: "root-alias.cjs" });
const resolvePluginSdkAccountIdAlias = (): string | null => {
return resolvePluginSdkAliasFile({ srcFile: "account-id.ts", distFile: "account-id.js" });
const pluginSdkScopedAliasEntries = [
{ subpath: "core", srcFile: "core.ts", distFile: "core.js" },
{ subpath: "compat", srcFile: "compat.ts", distFile: "compat.js" },
{ subpath: "telegram", srcFile: "telegram.ts", distFile: "telegram.js" },
{ subpath: "discord", srcFile: "discord.ts", distFile: "discord.js" },
{ subpath: "slack", srcFile: "slack.ts", distFile: "slack.js" },
{ subpath: "signal", srcFile: "signal.ts", distFile: "signal.js" },
{ subpath: "imessage", srcFile: "imessage.ts", distFile: "imessage.js" },
{ subpath: "whatsapp", srcFile: "whatsapp.ts", distFile: "whatsapp.js" },
{ subpath: "line", srcFile: "line.ts", distFile: "line.js" },
{ subpath: "msteams", srcFile: "msteams.ts", distFile: "msteams.js" },
{ subpath: "acpx", srcFile: "acpx.ts", distFile: "acpx.js" },
{ subpath: "bluebubbles", srcFile: "bluebubbles.ts", distFile: "bluebubbles.js" },
{
subpath: "copilot-proxy",
srcFile: "copilot-proxy.ts",
distFile: "copilot-proxy.js",
},
{ subpath: "device-pair", srcFile: "device-pair.ts", distFile: "device-pair.js" },
{
subpath: "diagnostics-otel",
srcFile: "diagnostics-otel.ts",
distFile: "diagnostics-otel.js",
},
{ subpath: "diffs", srcFile: "diffs.ts", distFile: "diffs.js" },
{ subpath: "feishu", srcFile: "feishu.ts", distFile: "feishu.js" },
{
subpath: "google-gemini-cli-auth",
srcFile: "google-gemini-cli-auth.ts",
distFile: "google-gemini-cli-auth.js",
},
{ subpath: "googlechat", srcFile: "googlechat.ts", distFile: "googlechat.js" },
{ subpath: "irc", srcFile: "irc.ts", distFile: "irc.js" },
{ subpath: "llm-task", srcFile: "llm-task.ts", distFile: "llm-task.js" },
{ subpath: "lobster", srcFile: "lobster.ts", distFile: "lobster.js" },
{ subpath: "matrix", srcFile: "matrix.ts", distFile: "matrix.js" },
{ subpath: "matrix-js", srcFile: "matrix-js.ts", distFile: "matrix-js.js" },
{ subpath: "mattermost", srcFile: "mattermost.ts", distFile: "mattermost.js" },
{ subpath: "memory-core", srcFile: "memory-core.ts", distFile: "memory-core.js" },
{
subpath: "memory-lancedb",
srcFile: "memory-lancedb.ts",
distFile: "memory-lancedb.js",
},
{
subpath: "minimax-portal-auth",
srcFile: "minimax-portal-auth.ts",
distFile: "minimax-portal-auth.js",
},
{
subpath: "nextcloud-talk",
srcFile: "nextcloud-talk.ts",
distFile: "nextcloud-talk.js",
},
{ subpath: "nostr", srcFile: "nostr.ts", distFile: "nostr.js" },
{ subpath: "open-prose", srcFile: "open-prose.ts", distFile: "open-prose.js" },
{
subpath: "phone-control",
srcFile: "phone-control.ts",
distFile: "phone-control.js",
},
{
subpath: "qwen-portal-auth",
srcFile: "qwen-portal-auth.ts",
distFile: "qwen-portal-auth.js",
},
{
subpath: "synology-chat",
srcFile: "synology-chat.ts",
distFile: "synology-chat.js",
},
{ subpath: "talk-voice", srcFile: "talk-voice.ts", distFile: "talk-voice.js" },
{ subpath: "test-utils", srcFile: "test-utils.ts", distFile: "test-utils.js" },
{
subpath: "thread-ownership",
srcFile: "thread-ownership.ts",
distFile: "thread-ownership.js",
},
{ subpath: "tlon", srcFile: "tlon.ts", distFile: "tlon.js" },
{ subpath: "twitch", srcFile: "twitch.ts", distFile: "twitch.js" },
{ subpath: "voice-call", srcFile: "voice-call.ts", distFile: "voice-call.js" },
{ subpath: "zalo", srcFile: "zalo.ts", distFile: "zalo.js" },
{ subpath: "zalouser", srcFile: "zalouser.ts", distFile: "zalouser.js" },
{ subpath: "account-id", srcFile: "account-id.ts", distFile: "account-id.js" },
{
subpath: "keyed-async-queue",
srcFile: "keyed-async-queue.ts",
distFile: "keyed-async-queue.js",
},
] as const;
const resolvePluginSdkScopedAliasMap = (): Record<string, string> => {
const aliasMap: Record<string, string> = {};
for (const entry of pluginSdkScopedAliasEntries) {
const resolved = resolvePluginSdkAliasFile({
srcFile: entry.srcFile,
distFile: entry.distFile,
});
if (resolved) {
aliasMap[`openclaw/plugin-sdk/${entry.subpath}`] = resolved;
}
}
return aliasMap;
};
export const __testing = {
@@ -393,7 +495,39 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
// Clear previously registered plugin commands before reloading
clearPluginCommands();
const runtime = createPluginRuntime();
// Lazily initialize the runtime so startup paths that discover/skip plugins do
// not eagerly load every channel runtime dependency.
let resolvedRuntime: PluginRuntime | null = null;
const resolveRuntime = (): PluginRuntime => {
resolvedRuntime ??= createPluginRuntime();
return resolvedRuntime;
};
const runtime = new Proxy({} as PluginRuntime, {
get(_target, prop, receiver) {
return Reflect.get(resolveRuntime(), prop, receiver);
},
set(_target, prop, value, receiver) {
return Reflect.set(resolveRuntime(), prop, value, receiver);
},
has(_target, prop) {
return Reflect.has(resolveRuntime(), prop);
},
ownKeys() {
return Reflect.ownKeys(resolveRuntime() as object);
},
getOwnPropertyDescriptor(_target, prop) {
return Reflect.getOwnPropertyDescriptor(resolveRuntime() as object, prop);
},
defineProperty(_target, prop, attributes) {
return Reflect.defineProperty(resolveRuntime() as object, prop, attributes);
},
deleteProperty(_target, prop) {
return Reflect.deleteProperty(resolveRuntime() as object, prop);
},
getPrototypeOf() {
return Reflect.getPrototypeOf(resolveRuntime() as object);
},
});
const { registry, createApi } = createPluginRegistry({
logger,
runtime,
@@ -403,6 +537,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
const discovery = discoverOpenClawPlugins({
workspaceDir: options.workspaceDir,
extraPaths: normalized.loadPaths,
cache: options.cache,
});
const manifestRegistry = loadPluginManifestRegistry({
config: cfg,
@@ -434,18 +569,16 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
return jitiLoader;
}
const pluginSdkAlias = resolvePluginSdkAlias();
const pluginSdkAccountIdAlias = resolvePluginSdkAccountIdAlias();
const aliasMap = {
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
...resolvePluginSdkScopedAliasMap(),
};
jitiLoader = createJiti(import.meta.url, {
interopDefault: true,
extensions: [".ts", ".tsx", ".mts", ".cts", ".mtsx", ".ctsx", ".js", ".mjs", ".cjs", ".json"],
...(pluginSdkAlias || pluginSdkAccountIdAlias
...(Object.keys(aliasMap).length > 0
? {
alias: {
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
...(pluginSdkAccountIdAlias
? { "openclaw/plugin-sdk/account-id": pluginSdkAccountIdAlias }
: {}),
},
alias: aliasMap,
}
: {}),
});
@@ -528,6 +661,25 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
continue;
}
// Fast-path bundled memory plugins that are guaranteed disabled by slot policy.
// This avoids opening/importing heavy memory plugin modules that will never register.
if (candidate.origin === "bundled" && manifestRecord.kind === "memory") {
const earlyMemoryDecision = resolveMemorySlotDecision({
id: record.id,
kind: "memory",
slot: memorySlot,
selectedId: selectedMemoryPluginId,
});
if (!earlyMemoryDecision.enabled) {
record.enabled = false;
record.status = "disabled";
record.error = earlyMemoryDecision.reason;
registry.plugins.push(record);
seenIds.set(pluginId, candidate.origin);
continue;
}
}
if (!manifestRecord.configSchema) {
pushPluginLoadError("missing config schema");
continue;

View File

@@ -12,8 +12,50 @@
},
"include": [
"src/plugin-sdk/index.ts",
"src/plugin-sdk/core.ts",
"src/plugin-sdk/compat.ts",
"src/plugin-sdk/telegram.ts",
"src/plugin-sdk/discord.ts",
"src/plugin-sdk/slack.ts",
"src/plugin-sdk/signal.ts",
"src/plugin-sdk/imessage.ts",
"src/plugin-sdk/whatsapp.ts",
"src/plugin-sdk/line.ts",
"src/plugin-sdk/msteams.ts",
"src/plugin-sdk/account-id.ts",
"src/plugin-sdk/keyed-async-queue.ts",
"src/plugin-sdk/acpx.ts",
"src/plugin-sdk/bluebubbles.ts",
"src/plugin-sdk/copilot-proxy.ts",
"src/plugin-sdk/device-pair.ts",
"src/plugin-sdk/diagnostics-otel.ts",
"src/plugin-sdk/diffs.ts",
"src/plugin-sdk/feishu.ts",
"src/plugin-sdk/google-gemini-cli-auth.ts",
"src/plugin-sdk/googlechat.ts",
"src/plugin-sdk/irc.ts",
"src/plugin-sdk/llm-task.ts",
"src/plugin-sdk/lobster.ts",
"src/plugin-sdk/matrix.ts",
"src/plugin-sdk/matrix-js.ts",
"src/plugin-sdk/mattermost.ts",
"src/plugin-sdk/memory-core.ts",
"src/plugin-sdk/memory-lancedb.ts",
"src/plugin-sdk/minimax-portal-auth.ts",
"src/plugin-sdk/nextcloud-talk.ts",
"src/plugin-sdk/nostr.ts",
"src/plugin-sdk/open-prose.ts",
"src/plugin-sdk/phone-control.ts",
"src/plugin-sdk/qwen-portal-auth.ts",
"src/plugin-sdk/synology-chat.ts",
"src/plugin-sdk/talk-voice.ts",
"src/plugin-sdk/test-utils.ts",
"src/plugin-sdk/thread-ownership.ts",
"src/plugin-sdk/tlon.ts",
"src/plugin-sdk/twitch.ts",
"src/plugin-sdk/voice-call.ts",
"src/plugin-sdk/zalo.ts",
"src/plugin-sdk/zalouser.ts",
"src/types/**/*.d.ts"
],
"exclude": ["node_modules", "dist", "src/**/*.test.ts"]

View File

@@ -4,6 +4,54 @@ const env = {
NODE_ENV: "production",
};
const pluginSdkEntrypoints = [
"index",
"core",
"compat",
"telegram",
"discord",
"slack",
"signal",
"imessage",
"whatsapp",
"line",
"msteams",
"acpx",
"bluebubbles",
"copilot-proxy",
"device-pair",
"diagnostics-otel",
"diffs",
"feishu",
"google-gemini-cli-auth",
"googlechat",
"irc",
"llm-task",
"lobster",
"matrix",
"matrix-js",
"mattermost",
"memory-core",
"memory-lancedb",
"minimax-portal-auth",
"nextcloud-talk",
"nostr",
"open-prose",
"phone-control",
"qwen-portal-auth",
"synology-chat",
"talk-voice",
"test-utils",
"thread-ownership",
"tlon",
"twitch",
"voice-call",
"zalo",
"zalouser",
"account-id",
"keyed-async-queue",
] as const;
export default defineConfig([
{
entry: "src/index.ts",
@@ -31,19 +79,30 @@ export default defineConfig([
platform: "node",
},
{
entry: "src/plugin-sdk/index.ts",
outDir: "dist/plugin-sdk",
// Keep sync lazy-runtime channel modules as concrete dist files.
entry: {
"channels/plugins/agent-tools/whatsapp-login":
"src/channels/plugins/agent-tools/whatsapp-login.ts",
"channels/plugins/actions/discord": "src/channels/plugins/actions/discord.ts",
"channels/plugins/actions/signal": "src/channels/plugins/actions/signal.ts",
"channels/plugins/actions/telegram": "src/channels/plugins/actions/telegram.ts",
"telegram/audit": "src/telegram/audit.ts",
"telegram/token": "src/telegram/token.ts",
"line/accounts": "src/line/accounts.ts",
"line/send": "src/line/send.ts",
"line/template-messages": "src/line/template-messages.ts",
},
env,
fixedExtension: false,
platform: "node",
},
{
entry: "src/plugin-sdk/account-id.ts",
...pluginSdkEntrypoints.map((entry) => ({
entry: `src/plugin-sdk/${entry}.ts`,
outDir: "dist/plugin-sdk",
env,
fixedExtension: false,
platform: "node",
},
platform: "node" as const,
})),
{
entry: "src/extensionAPI.ts",
env,

View File

@@ -8,19 +8,61 @@ const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
const isWindows = process.platform === "win32";
const localWorkers = Math.max(4, Math.min(16, os.cpus().length));
const ciWorkers = isWindows ? 2 : 3;
const pluginSdkSubpaths = [
"account-id",
"core",
"compat",
"telegram",
"discord",
"slack",
"signal",
"imessage",
"whatsapp",
"line",
"msteams",
"acpx",
"bluebubbles",
"copilot-proxy",
"device-pair",
"diagnostics-otel",
"diffs",
"feishu",
"google-gemini-cli-auth",
"googlechat",
"irc",
"llm-task",
"lobster",
"matrix",
"matrix-js",
"mattermost",
"memory-core",
"memory-lancedb",
"minimax-portal-auth",
"nextcloud-talk",
"nostr",
"open-prose",
"phone-control",
"qwen-portal-auth",
"synology-chat",
"talk-voice",
"test-utils",
"thread-ownership",
"tlon",
"twitch",
"voice-call",
"zalo",
"zalouser",
"keyed-async-queue",
] as const;
export default defineConfig({
resolve: {
// Keep this ordered: the base `openclaw/plugin-sdk` alias is a prefix match.
alias: [
{
find: "openclaw/plugin-sdk/account-id",
replacement: path.join(repoRoot, "src", "plugin-sdk", "account-id.ts"),
},
{
find: "openclaw/plugin-sdk/keyed-async-queue",
replacement: path.join(repoRoot, "src", "plugin-sdk", "keyed-async-queue.ts"),
},
...pluginSdkSubpaths.map((subpath) => ({
find: `openclaw/plugin-sdk/${subpath}`,
replacement: path.join(repoRoot, "src", "plugin-sdk", `${subpath}.ts`),
})),
{
find: "openclaw/plugin-sdk",
replacement: path.join(repoRoot, "src", "plugin-sdk", "index.ts"),