mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-23 16:01:17 +00:00
157 lines
5.1 KiB
Markdown
157 lines
5.1 KiB
Markdown
---
|
||
title: "Plugin Runtime"
|
||
sidebarTitle: "Runtime"
|
||
summary: "How `api.runtime` works, when to use it, and how to manage plugin runtime state safely"
|
||
read_when:
|
||
- You need to call runtime helpers from a plugin
|
||
- You are deciding between hooks and injected runtime
|
||
- You need a safe module-level runtime store
|
||
---
|
||
|
||
# Plugin Runtime
|
||
|
||
Native OpenClaw plugins receive a trusted runtime through `api.runtime`.
|
||
|
||
Use it for **host-owned operations** that should stay inside OpenClaw’s runtime:
|
||
|
||
- reading and writing config
|
||
- agent/session helpers
|
||
- system commands with OpenClaw timeouts
|
||
- media, speech, image-generation, and web-search runtime calls
|
||
- channel-owned helpers for bundled channel plugins
|
||
|
||
## When to use runtime vs focused SDK helpers
|
||
|
||
- Use focused SDK helpers when a public subpath already models the job.
|
||
- Use `api.runtime.*` when the host owns the operation or state.
|
||
- Prefer hooks for loose integrations that do not need tight in-process access.
|
||
|
||
## Runtime namespaces
|
||
|
||
| Namespace | What it covers |
|
||
| -------------------------------- | -------------------------------------------------- |
|
||
| `api.runtime.config` | Load and persist OpenClaw config |
|
||
| `api.runtime.agent` | Agent workspace, identity, timeouts, session store |
|
||
| `api.runtime.system` | System events, heartbeats, command execution |
|
||
| `api.runtime.media` | File/media loading and transforms |
|
||
| `api.runtime.tts` | Speech synthesis and voice listing |
|
||
| `api.runtime.mediaUnderstanding` | Image/audio/video understanding |
|
||
| `api.runtime.imageGeneration` | Image generation providers |
|
||
| `api.runtime.webSearch` | Runtime web-search execution |
|
||
| `api.runtime.modelAuth` | Resolve model/provider credentials |
|
||
| `api.runtime.subagent` | Spawn, wait, inspect, and delete subagent sessions |
|
||
| `api.runtime.channel` | Channel-heavy helpers for native channel plugins |
|
||
|
||
## Example: read and persist config
|
||
|
||
```ts
|
||
import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
|
||
|
||
export default definePluginEntry({
|
||
id: "talk-settings",
|
||
name: "Talk Settings",
|
||
description: "Example runtime config write",
|
||
register(api: OpenClawPluginApi) {
|
||
api.registerCommand({
|
||
name: "talk-mode",
|
||
description: "Enable talk mode",
|
||
handler: async () => {
|
||
const cfg = api.runtime.config.loadConfig();
|
||
const nextConfig = {
|
||
...cfg,
|
||
talk: {
|
||
...cfg.talk,
|
||
enabled: true,
|
||
},
|
||
};
|
||
await api.runtime.config.writeConfigFile(nextConfig);
|
||
return { text: "talk mode enabled" };
|
||
},
|
||
});
|
||
},
|
||
});
|
||
```
|
||
|
||
## Example: use a runtime service owned by OpenClaw
|
||
|
||
```ts
|
||
const cfg = api.runtime.config.loadConfig();
|
||
const voices = await api.runtime.tts.listVoices({
|
||
provider: "openai",
|
||
cfg,
|
||
});
|
||
|
||
return {
|
||
text: voices.map((voice) => `${voice.name ?? voice.id}: ${voice.id}`).join("\n"),
|
||
};
|
||
```
|
||
|
||
## `createPluginRuntimeStore(...)`
|
||
|
||
Plugin modules often need a small mutable slot for runtime-backed helpers. Use
|
||
`plugin-sdk/runtime-store` instead of an unguarded `let runtime`.
|
||
|
||
```ts
|
||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
||
import { channelPlugin } from "./src/channel.js";
|
||
|
||
const runtimeStore = createPluginRuntimeStore<{
|
||
logger: { info(message: string): void };
|
||
}>("Example Channel runtime not initialized");
|
||
|
||
export function setExampleRuntime(runtime: { logger: { info(message: string): void } }) {
|
||
runtimeStore.setRuntime(runtime);
|
||
}
|
||
|
||
export function getExampleRuntime() {
|
||
return runtimeStore.getRuntime();
|
||
}
|
||
|
||
export default defineChannelPluginEntry({
|
||
id: "example-channel",
|
||
name: "Example Channel",
|
||
description: "Example runtime store usage",
|
||
plugin: channelPlugin,
|
||
setRuntime: setExampleRuntime,
|
||
});
|
||
```
|
||
|
||
`createPluginRuntimeStore(...)` gives you:
|
||
|
||
- `setRuntime(next)`
|
||
- `clearRuntime()`
|
||
- `tryGetRuntime()`
|
||
- `getRuntime()`
|
||
|
||
`getRuntime()` throws with your custom message if the runtime was never set.
|
||
|
||
## Channel runtime note
|
||
|
||
`api.runtime.channel.*` is the heaviest namespace. It exists for native channel
|
||
plugins that need tight coupling with the OpenClaw messaging stack.
|
||
|
||
Prefer narrower subpaths such as:
|
||
|
||
- `plugin-sdk/channel-pairing`
|
||
- `plugin-sdk/channel-actions`
|
||
- `plugin-sdk/channel-feedback`
|
||
- `plugin-sdk/channel-lifecycle`
|
||
|
||
Use `api.runtime.channel.*` when the operation is clearly host-owned and there
|
||
is no smaller public seam.
|
||
|
||
## Runtime safety guidelines
|
||
|
||
- Do not cache config snapshots longer than needed.
|
||
- Prefer `createPluginRuntimeStore(...)` for shared module state.
|
||
- Keep runtime-backed code behind small local helpers.
|
||
- Avoid reaching into runtime namespaces you do not need.
|
||
|
||
## Related
|
||
|
||
- [Plugin SDK Overview](/plugins/sdk-overview)
|
||
- [Plugin Entry Points](/plugins/sdk-entrypoints)
|
||
- [Plugin Setup](/plugins/sdk-setup)
|
||
- [Channel Plugin SDK](/plugins/sdk-channel-plugins)
|