Files
openclaw/docs/plugins/sdk-runtime.md
2026-03-22 08:50:48 -07:00

5.1 KiB
Raw Blame History

title, sidebarTitle, summary, read_when
title sidebarTitle summary read_when
Plugin Runtime Runtime How `api.runtime` works, when to use it, and how to manage plugin runtime state safely
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 OpenClaws 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

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

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.

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.