fix(plugins): stabilize bundled setup runtimes (#67200)

Merged via squash.

Prepared head SHA: e8d6738fd0
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Gustavo Madeira Santana
2026-04-15 12:35:18 -04:00
committed by GitHub
parent ee6b7daca3
commit 78ac118427
43 changed files with 1831 additions and 209 deletions

View File

@@ -493,6 +493,11 @@ should use `resolveInboundMentionDecision({ facts, policy })`.
or unconfigured. It avoids pulling in heavy runtime code during setup flows.
See [Setup and Config](/plugins/sdk-setup#setup-entry) for details.
Bundled workspace channels that split setup-safe exports into sidecar
modules can use `defineBundledChannelSetupEntry(...)` from
`openclaw/plugin-sdk/channel-entry-contract` when they also need an
explicit setup-time runtime setter.
</Step>
<Step title="Handle inbound messages">

View File

@@ -145,6 +145,31 @@ families:
Keep heavy SDKs, CLI registration, and long-lived runtime services in the full
entry.
Bundled workspace channels that split setup and runtime surfaces can use
`defineBundledChannelSetupEntry(...)` from
`openclaw/plugin-sdk/channel-entry-contract` instead. That contract lets the
setup entry keep setup-safe plugin/secrets exports while still exposing a
runtime setter:
```typescript
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./channel-plugin-api.js",
exportName: "myChannelPlugin",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setMyChannelRuntime",
},
});
```
Use that bundled contract only when setup flows truly need a lightweight runtime
setter before the full channel entry loads.
## Registration mode
`api.registrationMode` tells your plugin how it was loaded:

View File

@@ -385,7 +385,10 @@ the `register` callback:
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
const store = createPluginRuntimeStore<PluginRuntime>("my-plugin runtime not initialized");
const store = createPluginRuntimeStore<PluginRuntime>({
pluginId: "my-plugin",
errorMessage: "my-plugin runtime not initialized",
});
// In your entry point
export default defineChannelPluginEntry({
@@ -406,6 +409,10 @@ export function tryGetRuntime() {
}
```
Prefer `pluginId` for the runtime-store identity. The lower-level `key` form is
for uncommon cases where one plugin intentionally needs more than one runtime
slot.
## Other top-level `api` fields
Beyond `api.runtime`, the API object also provides:

View File

@@ -279,6 +279,12 @@ export default defineSetupPluginEntry(myChannelPlugin);
This avoids loading heavy runtime code (crypto libraries, CLI registrations,
background services) during setup flows.
Bundled workspace channels that keep setup-safe exports in sidecar modules can
use `defineBundledChannelSetupEntry(...)` from
`openclaw/plugin-sdk/channel-entry-contract` instead of
`defineSetupPluginEntry(...)`. That bundled contract also supports an optional
`runtime` export so setup-time runtime wiring can stay lightweight and explicit.
**When OpenClaw uses `setupEntry` instead of the full entry:**
- The channel is disabled but needs setup/onboarding surfaces

View File

@@ -155,7 +155,10 @@ For code that uses `createPluginRuntimeStore`, mock the runtime in tests:
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
const store = createPluginRuntimeStore<PluginRuntime>("test runtime not set");
const store = createPluginRuntimeStore<PluginRuntime>({
pluginId: "test-plugin",
errorMessage: "test runtime not set",
});
// In test setup
const mockRuntime = {