From 32d3a820c849e3876e350aaed7363692a27a5d9e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 00:40:41 -0700 Subject: [PATCH] docs(sdk-runtime): rewrite with AccordionGroup for runtime namespaces, Steps for store wiring, ParamField for top-level api fields --- docs/plugins/sdk-runtime.md | 868 ++++++++++++++++++------------------ 1 file changed, 438 insertions(+), 430 deletions(-) diff --git a/docs/plugins/sdk-runtime.md b/docs/plugins/sdk-runtime.md index b58c8c6c2fe..95e99a69132 100644 --- a/docs/plugins/sdk-runtime.md +++ b/docs/plugins/sdk-runtime.md @@ -1,21 +1,23 @@ --- summary: "api.runtime -- the injected runtime helpers available to plugins" title: "Plugin runtime helpers" -sidebarTitle: "Runtime Helpers" +sidebarTitle: "Runtime helpers" read_when: - You need to call core helpers from a plugin (TTS, STT, image gen, web search, subagent, nodes) - You want to understand what api.runtime exposes - You are accessing config, agent, or media helpers from plugin code --- -Reference for the `api.runtime` object injected into every plugin during -registration. Use these helpers instead of importing host internals directly. +Reference for the `api.runtime` object injected into every plugin during registration. Use these helpers instead of importing host internals directly. - - **Looking for a walkthrough?** See [Channel Plugins](/plugins/sdk-channel-plugins) - or [Provider Plugins](/plugins/sdk-provider-plugins) for step-by-step guides - that show these helpers in context. - + + + Step-by-step guide that uses these helpers in context for channel plugins. + + + Step-by-step guide that uses these helpers in context for provider plugins. + + ```typescript register(api) { @@ -25,443 +27,449 @@ register(api) { ## Runtime namespaces -### `api.runtime.agent` - -Agent identity, directories, and session management. - -```typescript -// Resolve the agent's working directory -const agentDir = api.runtime.agent.resolveAgentDir(cfg); - -// Resolve agent workspace -const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(cfg); - -// Get agent identity -const identity = api.runtime.agent.resolveAgentIdentity(cfg); - -// Get default thinking level -const thinking = api.runtime.agent.resolveThinkingDefault(cfg, provider, model); - -// Get agent timeout -const timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg); - -// Ensure workspace exists -await api.runtime.agent.ensureAgentWorkspace(cfg); - -// Run an embedded agent turn -const agentDir = api.runtime.agent.resolveAgentDir(cfg); -const result = await api.runtime.agent.runEmbeddedAgent({ - sessionId: "my-plugin:task-1", - runId: crypto.randomUUID(), - sessionFile: path.join(agentDir, "sessions", "my-plugin-task-1.jsonl"), - workspaceDir: api.runtime.agent.resolveAgentWorkspaceDir(cfg), - prompt: "Summarize the latest changes", - timeoutMs: api.runtime.agent.resolveAgentTimeoutMs(cfg), -}); -``` - -`runEmbeddedAgent(...)` is the neutral helper for starting a normal OpenClaw -agent turn from plugin code. It uses the same provider/model resolution and -agent-harness selection as channel-triggered replies. - -`runEmbeddedPiAgent(...)` remains as a compatibility alias. - -**Session store helpers** are under `api.runtime.agent.session`: - -```typescript -const storePath = api.runtime.agent.session.resolveStorePath(cfg); -const store = api.runtime.agent.session.loadSessionStore(cfg); -await api.runtime.agent.session.saveSessionStore(cfg, store); -const filePath = api.runtime.agent.session.resolveSessionFilePath(cfg, sessionId); -``` - -### `api.runtime.agent.defaults` - -Default model and provider constants: - -```typescript -const model = api.runtime.agent.defaults.model; // e.g. "anthropic/claude-sonnet-4-6" -const provider = api.runtime.agent.defaults.provider; // e.g. "anthropic" -``` - -### `api.runtime.subagent` - -Launch and manage background subagent runs. - -```typescript -// Start a subagent run -const { runId } = await api.runtime.subagent.run({ - sessionKey: "agent:main:subagent:search-helper", - message: "Expand this query into focused follow-up searches.", - provider: "openai", // optional override - model: "gpt-4.1-mini", // optional override - deliver: false, -}); - -// Wait for completion -const result = await api.runtime.subagent.waitForRun({ runId, timeoutMs: 30000 }); - -// Read session messages -const { messages } = await api.runtime.subagent.getSessionMessages({ - sessionKey: "agent:main:subagent:search-helper", - limit: 10, -}); - -// Delete a session -await api.runtime.subagent.deleteSession({ - sessionKey: "agent:main:subagent:search-helper", -}); -``` - - - Model overrides (`provider`/`model`) require operator opt-in via - `plugins.entries..subagent.allowModelOverride: true` in config. - Untrusted plugins can still run subagents, but override requests are rejected. - - -### `api.runtime.nodes` - -List connected nodes and invoke a node-host command from Gateway-loaded plugin -code or from plugin CLI commands. Use this when a plugin owns local work on a -paired device, for example a browser or audio bridge on another Mac. - -```typescript -const { nodes } = await api.runtime.nodes.list({ connected: true }); - -const result = await api.runtime.nodes.invoke({ - nodeId: "mac-studio", - command: "my-plugin.command", - params: { action: "start" }, - timeoutMs: 30000, -}); -``` - -Inside the Gateway this runtime is in-process. In plugin CLI commands it calls -the configured Gateway over RPC, so commands such as `openclaw googlemeet -recover-tab` can inspect paired nodes from the terminal. Node commands still go -through normal Gateway node pairing, command allowlists, and node-local command -handling. - -### `api.runtime.taskFlow` - -Bind a Task Flow runtime to an existing OpenClaw session key or trusted tool -context, then create and manage Task Flows without passing an owner on every call. - -```typescript -const taskFlow = api.runtime.taskFlow.fromToolContext(ctx); - -const created = taskFlow.createManaged({ - controllerId: "my-plugin/review-batch", - goal: "Review new pull requests", -}); - -const child = taskFlow.runTask({ - flowId: created.flowId, - runtime: "acp", - childSessionKey: "agent:main:subagent:reviewer", - task: "Review PR #123", - status: "running", - startedAt: Date.now(), -}); - -const waiting = taskFlow.setWaiting({ - flowId: created.flowId, - expectedRevision: created.revision, - currentStep: "await-human-reply", - waitJson: { kind: "reply", channel: "telegram" }, -}); -``` - -Use `bindSession({ sessionKey, requesterOrigin })` when you already have a -trusted OpenClaw session key from your own binding layer. Do not bind from raw -user input. - -### `api.runtime.tts` - -Text-to-speech synthesis. - -```typescript -// Standard TTS -const clip = await api.runtime.tts.textToSpeech({ - text: "Hello from OpenClaw", - cfg: api.config, -}); - -// Telephony-optimized TTS -const telephonyClip = await api.runtime.tts.textToSpeechTelephony({ - text: "Hello from OpenClaw", - cfg: api.config, -}); - -// List available voices -const voices = await api.runtime.tts.listVoices({ - provider: "elevenlabs", - cfg: api.config, -}); -``` - -Uses core `messages.tts` configuration and provider selection. Returns PCM audio -buffer + sample rate. - -### `api.runtime.mediaUnderstanding` - -Image, audio, and video analysis. - -```typescript -// Describe an image -const image = await api.runtime.mediaUnderstanding.describeImageFile({ - filePath: "/tmp/inbound-photo.jpg", - cfg: api.config, - agentDir: "/tmp/agent", -}); - -// Transcribe audio -const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ - filePath: "/tmp/inbound-audio.ogg", - cfg: api.config, - mime: "audio/ogg", // optional, for when MIME cannot be inferred -}); - -// Describe a video -const video = await api.runtime.mediaUnderstanding.describeVideoFile({ - filePath: "/tmp/inbound-video.mp4", - cfg: api.config, -}); - -// Generic file analysis -const result = await api.runtime.mediaUnderstanding.runFile({ - filePath: "/tmp/inbound-file.pdf", - cfg: api.config, -}); -``` - -Returns `{ text: undefined }` when no output is produced (e.g. skipped input). - - - `api.runtime.stt.transcribeAudioFile(...)` remains as a compatibility alias - for `api.runtime.mediaUnderstanding.transcribeAudioFile(...)`. - - -### `api.runtime.imageGeneration` - -Image generation. - -```typescript -const result = await api.runtime.imageGeneration.generate({ - prompt: "A robot painting a sunset", - cfg: api.config, -}); - -const providers = api.runtime.imageGeneration.listProviders({ cfg: api.config }); -``` - -### `api.runtime.webSearch` - -Web search. - -```typescript -const providers = api.runtime.webSearch.listProviders({ config: api.config }); - -const result = await api.runtime.webSearch.search({ - config: api.config, - args: { query: "OpenClaw plugin SDK", count: 5 }, -}); -``` - -### `api.runtime.media` - -Low-level media utilities. - -```typescript -const webMedia = await api.runtime.media.loadWebMedia(url); -const mime = await api.runtime.media.detectMime(buffer); -const kind = api.runtime.media.mediaKindFromMime("image/jpeg"); // "image" -const isVoice = api.runtime.media.isVoiceCompatibleAudio(filePath); -const metadata = await api.runtime.media.getImageMetadata(filePath); -const resized = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 800 }); -const terminalQr = await api.runtime.media.renderQrTerminal("https://openclaw.ai"); -const pngQr = await api.runtime.media.renderQrPngBase64("https://openclaw.ai", { - scale: 6, // 1-12 - marginModules: 4, // 0-16 -}); -const pngQrDataUrl = await api.runtime.media.renderQrPngDataUrl("https://openclaw.ai"); -const tmpRoot = resolvePreferredOpenClawTmpDir(); -const pngQrFile = await api.runtime.media.writeQrPngTempFile("https://openclaw.ai", { - tmpRoot, - dirPrefix: "my-plugin-qr-", - fileName: "qr.png", -}); -``` - -### `api.runtime.config` - -Config load and write. - -```typescript -const cfg = await api.runtime.config.loadConfig(); -await api.runtime.config.writeConfigFile(cfg); -``` - -### `api.runtime.system` - -System-level utilities. - -```typescript -await api.runtime.system.enqueueSystemEvent(event); -api.runtime.system.requestHeartbeatNow(); -const output = await api.runtime.system.runCommandWithTimeout(cmd, args, opts); -const hint = api.runtime.system.formatNativeDependencyHint(pkg); -``` - -### `api.runtime.events` - -Event subscriptions. - -```typescript -api.runtime.events.onAgentEvent((event) => { - /* ... */ -}); -api.runtime.events.onSessionTranscriptUpdate((update) => { - /* ... */ -}); -``` - -### `api.runtime.logging` - -Logging. - -```typescript -const verbose = api.runtime.logging.shouldLogVerbose(); -const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" }, { level: "debug" }); -``` - -### `api.runtime.modelAuth` - -Model and provider auth resolution. - -```typescript -const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg }); -const providerAuth = await api.runtime.modelAuth.resolveApiKeyForProvider({ - provider: "openai", - cfg, -}); -``` - -### `api.runtime.state` - -State directory resolution. - -```typescript -const stateDir = api.runtime.state.resolveStateDir(); -``` - -### `api.runtime.tools` - -Memory tool factories and CLI. - -```typescript -const getTool = api.runtime.tools.createMemoryGetTool(/* ... */); -const searchTool = api.runtime.tools.createMemorySearchTool(/* ... */); -api.runtime.tools.registerMemoryCli(/* ... */); -``` - -### `api.runtime.channel` - -Channel-specific runtime helpers (available when a channel plugin is loaded). - -`api.runtime.channel.mentions` is the shared inbound mention-policy surface for -bundled channel plugins that use runtime injection: - -```typescript -const mentionMatch = api.runtime.channel.mentions.matchesMentionWithExplicit(text, { - mentionRegexes, - mentionPatterns, -}); - -const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({ - facts: { - canDetectMention: true, - wasMentioned: mentionMatch.matched, - implicitMentionKinds: api.runtime.channel.mentions.implicitMentionKindWhen( - "reply_to_bot", - isReplyToBot, - ), - }, - policy: { - isGroup, - requireMention, - allowTextCommands, - hasControlCommand, - commandAuthorized, - }, -}); -``` - -Available mention helpers: - -- `buildMentionRegexes` -- `matchesMentionPatterns` -- `matchesMentionWithExplicit` -- `implicitMentionKindWhen` -- `resolveInboundMentionDecision` - -`api.runtime.channel.mentions` intentionally does not expose the older -`resolveMentionGating*` compatibility helpers. Prefer the normalized -`{ facts, policy }` path. + + + Agent identity, directories, and session management. + + ```typescript + // Resolve the agent's working directory + const agentDir = api.runtime.agent.resolveAgentDir(cfg); + + // Resolve agent workspace + const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(cfg); + + // Get agent identity + const identity = api.runtime.agent.resolveAgentIdentity(cfg); + + // Get default thinking level + const thinking = api.runtime.agent.resolveThinkingDefault(cfg, provider, model); + + // Get agent timeout + const timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg); + + // Ensure workspace exists + await api.runtime.agent.ensureAgentWorkspace(cfg); + + // Run an embedded agent turn + const agentDir = api.runtime.agent.resolveAgentDir(cfg); + const result = await api.runtime.agent.runEmbeddedAgent({ + sessionId: "my-plugin:task-1", + runId: crypto.randomUUID(), + sessionFile: path.join(agentDir, "sessions", "my-plugin-task-1.jsonl"), + workspaceDir: api.runtime.agent.resolveAgentWorkspaceDir(cfg), + prompt: "Summarize the latest changes", + timeoutMs: api.runtime.agent.resolveAgentTimeoutMs(cfg), + }); + ``` + + `runEmbeddedAgent(...)` is the neutral helper for starting a normal OpenClaw agent turn from plugin code. It uses the same provider/model resolution and agent-harness selection as channel-triggered replies. + + `runEmbeddedPiAgent(...)` remains as a compatibility alias. + + **Session store helpers** are under `api.runtime.agent.session`: + + ```typescript + const storePath = api.runtime.agent.session.resolveStorePath(cfg); + const store = api.runtime.agent.session.loadSessionStore(cfg); + await api.runtime.agent.session.saveSessionStore(cfg, store); + const filePath = api.runtime.agent.session.resolveSessionFilePath(cfg, sessionId); + ``` + + + + Default model and provider constants: + + ```typescript + const model = api.runtime.agent.defaults.model; // e.g. "anthropic/claude-sonnet-4-6" + const provider = api.runtime.agent.defaults.provider; // e.g. "anthropic" + ``` + + + + Launch and manage background subagent runs. + + ```typescript + // Start a subagent run + const { runId } = await api.runtime.subagent.run({ + sessionKey: "agent:main:subagent:search-helper", + message: "Expand this query into focused follow-up searches.", + provider: "openai", // optional override + model: "gpt-4.1-mini", // optional override + deliver: false, + }); + + // Wait for completion + const result = await api.runtime.subagent.waitForRun({ runId, timeoutMs: 30000 }); + + // Read session messages + const { messages } = await api.runtime.subagent.getSessionMessages({ + sessionKey: "agent:main:subagent:search-helper", + limit: 10, + }); + + // Delete a session + await api.runtime.subagent.deleteSession({ + sessionKey: "agent:main:subagent:search-helper", + }); + ``` + + + Model overrides (`provider`/`model`) require operator opt-in via `plugins.entries..subagent.allowModelOverride: true` in config. Untrusted plugins can still run subagents, but override requests are rejected. + + + + + List connected nodes and invoke a node-host command from Gateway-loaded plugin code or from plugin CLI commands. Use this when a plugin owns local work on a paired device, for example a browser or audio bridge on another Mac. + + ```typescript + const { nodes } = await api.runtime.nodes.list({ connected: true }); + + const result = await api.runtime.nodes.invoke({ + nodeId: "mac-studio", + command: "my-plugin.command", + params: { action: "start" }, + timeoutMs: 30000, + }); + ``` + + Inside the Gateway this runtime is in-process. In plugin CLI commands it calls the configured Gateway over RPC, so commands such as `openclaw googlemeet recover-tab` can inspect paired nodes from the terminal. Node commands still go through normal Gateway node pairing, command allowlists, and node-local command handling. + + + + Bind a Task Flow runtime to an existing OpenClaw session key or trusted tool context, then create and manage Task Flows without passing an owner on every call. + + ```typescript + const taskFlow = api.runtime.taskFlow.fromToolContext(ctx); + + const created = taskFlow.createManaged({ + controllerId: "my-plugin/review-batch", + goal: "Review new pull requests", + }); + + const child = taskFlow.runTask({ + flowId: created.flowId, + runtime: "acp", + childSessionKey: "agent:main:subagent:reviewer", + task: "Review PR #123", + status: "running", + startedAt: Date.now(), + }); + + const waiting = taskFlow.setWaiting({ + flowId: created.flowId, + expectedRevision: created.revision, + currentStep: "await-human-reply", + waitJson: { kind: "reply", channel: "telegram" }, + }); + ``` + + Use `bindSession({ sessionKey, requesterOrigin })` when you already have a trusted OpenClaw session key from your own binding layer. Do not bind from raw user input. + + + + Text-to-speech synthesis. + + ```typescript + // Standard TTS + const clip = await api.runtime.tts.textToSpeech({ + text: "Hello from OpenClaw", + cfg: api.config, + }); + + // Telephony-optimized TTS + const telephonyClip = await api.runtime.tts.textToSpeechTelephony({ + text: "Hello from OpenClaw", + cfg: api.config, + }); + + // List available voices + const voices = await api.runtime.tts.listVoices({ + provider: "elevenlabs", + cfg: api.config, + }); + ``` + + Uses core `messages.tts` configuration and provider selection. Returns PCM audio buffer + sample rate. + + + + Image, audio, and video analysis. + + ```typescript + // Describe an image + const image = await api.runtime.mediaUnderstanding.describeImageFile({ + filePath: "/tmp/inbound-photo.jpg", + cfg: api.config, + agentDir: "/tmp/agent", + }); + + // Transcribe audio + const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ + filePath: "/tmp/inbound-audio.ogg", + cfg: api.config, + mime: "audio/ogg", // optional, for when MIME cannot be inferred + }); + + // Describe a video + const video = await api.runtime.mediaUnderstanding.describeVideoFile({ + filePath: "/tmp/inbound-video.mp4", + cfg: api.config, + }); + + // Generic file analysis + const result = await api.runtime.mediaUnderstanding.runFile({ + filePath: "/tmp/inbound-file.pdf", + cfg: api.config, + }); + ``` + + Returns `{ text: undefined }` when no output is produced (e.g. skipped input). + + + `api.runtime.stt.transcribeAudioFile(...)` remains as a compatibility alias for `api.runtime.mediaUnderstanding.transcribeAudioFile(...)`. + + + + + Image generation. + + ```typescript + const result = await api.runtime.imageGeneration.generate({ + prompt: "A robot painting a sunset", + cfg: api.config, + }); + + const providers = api.runtime.imageGeneration.listProviders({ cfg: api.config }); + ``` + + + + Web search. + + ```typescript + const providers = api.runtime.webSearch.listProviders({ config: api.config }); + + const result = await api.runtime.webSearch.search({ + config: api.config, + args: { query: "OpenClaw plugin SDK", count: 5 }, + }); + ``` + + + + Low-level media utilities. + + ```typescript + const webMedia = await api.runtime.media.loadWebMedia(url); + const mime = await api.runtime.media.detectMime(buffer); + const kind = api.runtime.media.mediaKindFromMime("image/jpeg"); // "image" + const isVoice = api.runtime.media.isVoiceCompatibleAudio(filePath); + const metadata = await api.runtime.media.getImageMetadata(filePath); + const resized = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 800 }); + const terminalQr = await api.runtime.media.renderQrTerminal("https://openclaw.ai"); + const pngQr = await api.runtime.media.renderQrPngBase64("https://openclaw.ai", { + scale: 6, // 1-12 + marginModules: 4, // 0-16 + }); + const pngQrDataUrl = await api.runtime.media.renderQrPngDataUrl("https://openclaw.ai"); + const tmpRoot = resolvePreferredOpenClawTmpDir(); + const pngQrFile = await api.runtime.media.writeQrPngTempFile("https://openclaw.ai", { + tmpRoot, + dirPrefix: "my-plugin-qr-", + fileName: "qr.png", + }); + ``` + + + + Config load and write. + + ```typescript + const cfg = await api.runtime.config.loadConfig(); + await api.runtime.config.writeConfigFile(cfg); + ``` + + + + System-level utilities. + + ```typescript + await api.runtime.system.enqueueSystemEvent(event); + api.runtime.system.requestHeartbeatNow(); + const output = await api.runtime.system.runCommandWithTimeout(cmd, args, opts); + const hint = api.runtime.system.formatNativeDependencyHint(pkg); + ``` + + + + Event subscriptions. + + ```typescript + api.runtime.events.onAgentEvent((event) => { + /* ... */ + }); + api.runtime.events.onSessionTranscriptUpdate((update) => { + /* ... */ + }); + ``` + + + + Logging. + + ```typescript + const verbose = api.runtime.logging.shouldLogVerbose(); + const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" }, { level: "debug" }); + ``` + + + + Model and provider auth resolution. + + ```typescript + const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg }); + const providerAuth = await api.runtime.modelAuth.resolveApiKeyForProvider({ + provider: "openai", + cfg, + }); + ``` + + + + State directory resolution. + + ```typescript + const stateDir = api.runtime.state.resolveStateDir(); + ``` + + + + Memory tool factories and CLI. + + ```typescript + const getTool = api.runtime.tools.createMemoryGetTool(/* ... */); + const searchTool = api.runtime.tools.createMemorySearchTool(/* ... */); + api.runtime.tools.registerMemoryCli(/* ... */); + ``` + + + + Channel-specific runtime helpers (available when a channel plugin is loaded). + + `api.runtime.channel.mentions` is the shared inbound mention-policy surface for bundled channel plugins that use runtime injection: + + ```typescript + const mentionMatch = api.runtime.channel.mentions.matchesMentionWithExplicit(text, { + mentionRegexes, + mentionPatterns, + }); + + const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({ + facts: { + canDetectMention: true, + wasMentioned: mentionMatch.matched, + implicitMentionKinds: api.runtime.channel.mentions.implicitMentionKindWhen( + "reply_to_bot", + isReplyToBot, + ), + }, + policy: { + isGroup, + requireMention, + allowTextCommands, + hasControlCommand, + commandAuthorized, + }, + }); + ``` + + Available mention helpers: + + - `buildMentionRegexes` + - `matchesMentionPatterns` + - `matchesMentionWithExplicit` + - `implicitMentionKindWhen` + - `resolveInboundMentionDecision` + + `api.runtime.channel.mentions` intentionally does not expose the older `resolveMentionGating*` compatibility helpers. Prefer the normalized `{ facts, policy }` path. + + + ## Storing runtime references -Use `createPluginRuntimeStore` to store the runtime reference for use outside -the `register` callback: +Use `createPluginRuntimeStore` to store the runtime reference for use outside the `register` callback: -```typescript -import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store"; -import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store"; + + + ```typescript + import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store"; + import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store"; -const store = createPluginRuntimeStore({ - pluginId: "my-plugin", - errorMessage: "my-plugin runtime not initialized", -}); + const store = createPluginRuntimeStore({ + pluginId: "my-plugin", + errorMessage: "my-plugin runtime not initialized", + }); + ``` -// In your entry point -export default defineChannelPluginEntry({ - id: "my-plugin", - name: "My Plugin", - description: "Example", - plugin: myPlugin, - setRuntime: store.setRuntime, -}); + + + ```typescript + export default defineChannelPluginEntry({ + id: "my-plugin", + name: "My Plugin", + description: "Example", + plugin: myPlugin, + setRuntime: store.setRuntime, + }); + ``` + + + ```typescript + export function getRuntime() { + return store.getRuntime(); // throws if not initialized + } -// In other files -export function getRuntime() { - return store.getRuntime(); // throws if not initialized -} + export function tryGetRuntime() { + return store.tryGetRuntime(); // returns null if not initialized + } + ``` -export function tryGetRuntime() { - return store.tryGetRuntime(); // returns null if not initialized -} -``` + + -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. + +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: -| Field | Type | Description | -| ------------------------ | ------------------------- | ------------------------------------------------------------------------------------------- | -| `api.id` | `string` | Plugin id | -| `api.name` | `string` | Plugin display name | -| `api.config` | `OpenClawConfig` | Current config snapshot (active in-memory runtime snapshot when available) | -| `api.pluginConfig` | `Record` | Plugin-specific config from `plugins.entries..config` | -| `api.logger` | `PluginLogger` | Scoped logger (`debug`, `info`, `warn`, `error`) | -| `api.registrationMode` | `PluginRegistrationMode` | Current load mode; `"setup-runtime"` is the lightweight pre-full-entry startup/setup window | -| `api.resolvePath(input)` | `(string) => string` | Resolve a path relative to the plugin root | + + Plugin id. + + + Plugin display name. + + + Current config snapshot (active in-memory runtime snapshot when available). + + + Plugin-specific config from `plugins.entries..config`. + + + Scoped logger (`debug`, `info`, `warn`, `error`). + + + Current load mode; `"setup-runtime"` is the lightweight pre-full-entry startup/setup window. + + + Resolve a path relative to the plugin root. + ## Related -- [SDK overview](/plugins/sdk-overview) — subpath reference -- [SDK entry points](/plugins/sdk-entrypoints) — `definePluginEntry` options - [Plugin internals](/plugins/architecture) — capability model and registry +- [SDK entry points](/plugins/sdk-entrypoints) — `definePluginEntry` options +- [SDK overview](/plugins/sdk-overview) — subpath reference