快速开始

插件运行时辅助工具

这是在注册期间注入到每个插件中的 api.runtime 对象参考。请使用这些辅助函数,而不是直接导入主机内部机制。

register(api) {
  const runtime = api.runtime;
}

配置加载和写入

优先使用已经传入当前调用路径的配置,例如注册期间的 api.config,或渠道/提供商回调中的 cfg 参数。这样可以让一个进程快照贯穿整个工作流,而不是在热路径上重新解析配置。

仅当长生命周期处理程序需要当前进程快照,并且没有向该函数传入配置时,才使用 api.runtime.config.current()。返回值是只读的;编辑前请克隆或使用变更辅助函数。

工具工厂会收到 ctx.runtimeConfigctx.getRuntimeConfig()。当配置可能在工具定义创建后发生变化时,请在长生命周期工具的 execute 回调中使用该 getter。

使用 api.runtime.config.mutateConfigFile(...)api.runtime.config.replaceConfigFile(...) 持久化更改。每次写入都必须选择明确的 afterWrite 策略:

  • afterWrite: { mode: "auto" } 让 Gateway 网关的重新加载规划器决定。
  • afterWrite: { mode: "restart", reason: "..." } 在写入方知道热重载不安全时强制干净重启。
  • afterWrite: { mode: "none", reason: "..." } 仅当调用方自行负责后续操作时,才抑制自动重新加载/重启。

变更辅助函数会返回 afterWrite 以及带类型的 followUp 摘要,以便调用方记录或测试它们是否请求了重启。Gateway 网关仍然负责决定重启实际发生的时机。

api.runtime.config.loadConfig()api.runtime.config.writeConfigFile(...)runtime-config-load-write 下已弃用的兼容性辅助函数。它们会在运行时警告一次,并在迁移窗口期间继续供旧的外部插件使用。内置插件不得使用它们;如果插件代码调用它们,或从插件 SDK 子路径导入这些辅助函数,配置边界保护会失败。

对于直接 SDK 导入,请使用聚焦的配置子路径,而不是宽泛的 openclaw/plugin-sdk/config-runtime 兼容性 barrel:config-types 用于 类型,plugin-config-runtime 用于已加载配置断言和插件 入口查找,runtime-config-snapshot 用于当前进程快照,以及 config-mutation 用于写入。内置插件测试应直接 mock 这些聚焦的 子路径,而不是 mock 宽泛的兼容性 barrel。

OpenClaw 内部运行时代码也遵循相同方向:在 CLI、Gateway 网关或进程边界加载一次配置,然后传递该值。成功的变更写入会刷新进程运行时快照并推进其内部修订;长生命周期缓存应基于运行时拥有的缓存键,而不是在本地序列化配置。长生命周期运行时模块对环境中的 loadConfig() 调用有零容忍扫描器;请使用传入的 cfg、请求的 context.getRuntimeConfig(),或在明确的进程边界使用 getRuntimeConfig()

提供商和渠道执行路径必须使用当前运行时配置快照,而不是用于配置读回或编辑的文件快照。文件快照会保留源值,例如用于 UI 和写入的 SecretRef 标记;提供商回调需要已解析的运行时视图。当辅助函数可能以当前源快照或当前运行时快照调用时,请在读取凭证前通过 selectApplicableRuntimeConfig() 路由。

运行时命名空间

api.runtime.agent

智能体身份、目录和会话管理。

// 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,
});

// Validate a user-provided thinking level against the active provider profile
const policy = api.runtime.agent.resolveThinkingPolicy({ provider, model });
const level = api.runtime.agent.normalizeThinkingLevel("extra high");
if (level && policy.levels.some((entry) => entry.id === level)) {
  // pass level to an embedded run
}

// 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(...) 是从插件代码启动普通 OpenClaw 智能体轮次的中立辅助函数。它使用与渠道触发回复相同的提供商/模型解析和 agent-harness 选择。

runEmbeddedPiAgent(...) 仍作为兼容性别名保留。

resolveThinkingPolicy(...) 返回该提供商/模型支持的 thinking 级别和可选默认值。提供商插件通过自己的 thinking 钩子拥有特定模型的配置文件,因此工具插件应调用这个运行时辅助函数,而不是导入或复制提供商列表。

normalizeThinkingLevel(...) 会在根据已解析策略检查前,将 onx-highextra high 等用户文本转换为规范存储级别。

会话存储辅助函数位于 api.runtime.agent.session 下:

const storePath = api.runtime.agent.session.resolveStorePath(cfg);
const store = api.runtime.agent.session.loadSessionStore(storePath);
await api.runtime.agent.session.updateSessionStore(storePath, (nextStore) => {
  // Patch one entry without replacing the whole file from stale state.
  nextStore[sessionKey] = { ...nextStore[sessionKey], thinkingLevel: "high" };
});
const filePath = api.runtime.agent.session.resolveSessionFilePath(cfg, sessionId);

对于运行时写入,优先使用 updateSessionStore(...)updateSessionStoreEntry(...)。它们会经过 Gateway 网关拥有的会话存储写入器,保留并发更新,并复用热缓存。saveSessionStore(...) 仍可用于兼容性和离线维护式重写。

api.runtime.agent.defaults

默认模型和提供商常量:

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

启动和管理后台 subagent 运行。

// 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",
});

deleteSession(...) 可以删除同一插件通过 api.runtime.subagent.run(...) 创建的会话。删除任意用户或操作者会话仍需要管理员范围的 Gateway 网关请求。

api.runtime.nodes

列出已连接节点,并从 Gateway 网关加载的插件代码或插件 CLI 命令调用节点主机命令。当插件拥有配对设备上的本地工作时使用它,例如另一台 Mac 上的浏览器或音频桥接器。

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,
});

在 Gateway 网关内部,此运行时在进程内。在插件 CLI 命令中,它会通过 RPC 调用已配置的 Gateway 网关,因此 openclaw googlemeet recover-tab 等命令可以从终端检查配对节点。节点命令仍会经过常规 Gateway 网关节点配对、命令允许列表、插件节点调用策略和节点本地命令处理。

暴露危险节点主机命令的插件应使用 api.registerNodeInvokePolicy(...) 注册节点调用策略。该策略在 Gateway 网关中运行,位于命令允许列表检查之后、命令转发到节点之前,因此直接 node.invoke 调用和更高层级的插件工具共享同一条执行路径。

api.runtime.tasks.managedFlows

将任务流运行时绑定到现有 OpenClaw 会话键或可信工具上下文,然后创建和管理任务流,而无需在每次调用时传入所有者。

const taskFlow = api.runtime.tasks.managedFlows.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" },
});

当你已经从自己的绑定层获得可信 OpenClaw 会话键时,请使用 bindSession({ sessionKey, requesterOrigin })。不要从原始用户输入进行绑定。

api.runtime.tts

文本转语音合成。

// 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,
});

使用核心 messages.tts 配置和提供商选择。返回 PCM 音频缓冲区和采样率。

api.runtime.mediaUnderstanding

图像、音频和视频分析。

// 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,
});

无输出时(例如跳过的输入)返回 { text: undefined }

api.runtime.imageGeneration

图像生成。

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 搜索。

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

底层媒体工具。

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

当前运行时配置快照和事务性配置写入。优先使用已传入当前活跃调用路径的配置;仅当处理程序需要直接读取进程快照时才使用 current()

const cfg = api.runtime.config.current();
await api.runtime.config.mutateConfigFile({
  afterWrite: { mode: "auto" },
  mutate(draft) {
    draft.plugins ??= {};
  },
});

mutateConfigFile(...)replaceConfigFile(...) 会返回一个 followUp 值,例如 { mode: "restart", requiresRestart: true, reason },用于记录写入方意图,同时不会从 Gateway 网关手中接管重启控制权。

api.runtime.system

系统级工具。

await api.runtime.system.enqueueSystemEvent(event);
api.runtime.system.requestHeartbeat({
  source: "other",
  intent: "event",
  reason: "plugin-event",
});
api.runtime.system.requestHeartbeatNow({ reason: "plugin-event" }); // Deprecated compatibility alias.
const output = await api.runtime.system.runCommandWithTimeout(cmd, args, opts);
const hint = api.runtime.system.formatNativeDependencyHint(pkg);
api.runtime.events

事件订阅。

api.runtime.events.onAgentEvent((event) => {
  /* ... */
});
api.runtime.events.onSessionTranscriptUpdate((update) => {
  /* ... */
});
api.runtime.logging

日志记录。

const verbose = api.runtime.logging.shouldLogVerbose();
const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" }, { level: "debug" });
api.runtime.modelAuth

模型和提供商凭证解析。

const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg });
const providerAuth = await api.runtime.modelAuth.resolveApiKeyForProvider({
  provider: "openai",
  cfg,
});
api.runtime.state

状态目录解析和基于 SQLite 的键值存储。

const stateDir = api.runtime.state.resolveStateDir(process.env);
const store = api.runtime.state.openKeyedStore<MyRecord>({
  namespace: "my-feature",
  maxEntries: 200,
  defaultTtlMs: 15 * 60_000,
});

await store.register("key-1", { value: "hello" });
const claimed = await store.registerIfAbsent("dedupe-key", { value: "first" });
const value = await store.lookup("key-1");
await store.consume("key-1");
await store.clear();

键值存储会在重启后保留,并按运行时绑定的插件 id 隔离。使用 registerIfAbsent(...) 执行原子去重声明:当键缺失或已过期并完成注册时返回 true;当已有有效值存在时返回 false,且不会覆盖其值、创建时间或 TTL。限制:每个 namespace 的 maxEntries、每个插件 1,000 条有效行、JSON 值小于 64KB,以及可选 TTL 过期。

api.runtime.tools

记忆工具工厂和 CLI。

const getTool = api.runtime.tools.createMemoryGetTool(/* ... */);
const searchTool = api.runtime.tools.createMemorySearchTool(/* ... */);
api.runtime.tools.registerMemoryCli(/* ... */);
api.runtime.channel

渠道特定的运行时辅助函数(加载渠道插件时可用)。

api.runtime.channel.mentions 是使用运行时注入的内置渠道插件共享的入站提及策略表面:

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,
  },
});

可用的提及辅助函数:

  • buildMentionRegexes
  • matchesMentionPatterns
  • matchesMentionWithExplicit
  • implicitMentionKindWhen
  • resolveInboundMentionDecision

api.runtime.channel.mentions 有意不公开较旧的 resolveMentionGating* 兼容性辅助函数。优先使用规范化的 { facts, policy } 路径。

存储运行时引用

使用 createPluginRuntimeStore 存储运行时引用,以便在 register 回调之外使用:

  • 创建存储

    import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
    import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
    
    const store = createPluginRuntimeStore<PluginRuntime>({
      pluginId: "my-plugin",
      errorMessage: "my-plugin runtime not initialized",
    });
    
  • 接入入口点

    export default defineChannelPluginEntry({
      id: "my-plugin",
      name: "My Plugin",
      description: "Example",
      plugin: myPlugin,
      setRuntime: store.setRuntime,
    });
    
  • 从其他文件访问

    export function getRuntime() {
      return store.getRuntime(); // throws if not initialized
    }
    
    export function tryGetRuntime() {
      return store.tryGetRuntime(); // returns null if not initialized
    }
    
  • 其他顶层 api 字段

    api.runtime 外,API 对象还提供:

    api.idstring

    插件 id。

    api.namestring

    插件显示名称。

    api.configOpenClawConfig

    当前配置快照(可用时为活跃的内存运行时快照)。

    OPENCLAW_DOCS_MARKER:paramOpen:IHBhdGg9ImFwaS5wbHVnaW5Db25maWciIHR5cGU9IlJlY29yZDxzdHJpbmcsIHVua25vd24 "> 来自 plugins.entries.<id>.config 的插件特定配置。

    api.loggerPluginLogger

    作用域日志记录器(debuginfowarnerror)。

    api.registrationModePluginRegistrationMode

    当前加载模式;"setup-runtime" 是轻量级的完整入口前启动/设置窗口。

    api.resolvePath(input)"(string)

    相关