Plugins

ابزارهای کمکی زمان اجرای Plugin

مرجعی برای شیء api.runtime که هنگام ثبت در هر plugin تزریق می‌شود. از این helperها به‌جای import مستقیم internals میزبان استفاده کنید.

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

بارگذاری و نوشتن پیکربندی

پیکربندی‌ای را ترجیح دهید که از قبل به مسیر فراخوانی فعال پاس داده شده است، برای مثال api.config هنگام ثبت یا آرگومان cfg در callbackهای channel/provider. این کار باعث می‌شود یک snapshot فرایند در کل کار جریان پیدا کند، به‌جای اینکه پیکربندی در مسیرهای داغ دوباره parse شود.

از api.runtime.config.current() فقط زمانی استفاده کنید که یک handler بلندعمر به snapshot فعلی فرایند نیاز دارد و هیچ پیکربندی‌ای به آن تابع پاس داده نشده است. مقدار برگردانده‌شده readonly است؛ پیش از ویرایش آن را clone کنید یا از یک helper جهش استفاده کنید.

factoryهای ابزار ctx.runtimeConfig به‌همراه ctx.getRuntimeConfig() را دریافت می‌کنند. در callback execute یک ابزار بلندعمر، زمانی که پیکربندی ممکن است پس از ساخته‌شدن تعریف ابزار تغییر کند، از getter استفاده کنید.

تغییرات را با api.runtime.config.mutateConfigFile(...) یا api.runtime.config.replaceConfigFile(...) پایدار کنید. هر write باید یک سیاست صریح afterWrite انتخاب کند:

  • afterWrite: { mode: "auto" } اجازه می‌دهد تصمیم reload planner به gateway سپرده شود.
  • afterWrite: { mode: "restart", reason: "..." } زمانی که نویسنده می‌داند hot reload امن نیست، یک restart تمیز را اجبار می‌کند.
  • afterWrite: { mode: "none", reason: "..." } reload/restart خودکار را فقط زمانی سرکوب می‌کند که caller مالک پیگیری بعدی باشد.

helperهای جهش afterWrite به‌همراه یک خلاصهٔ typed به نام followUp برمی‌گردانند تا callerها بتوانند log کنند یا test کنند که آیا restart درخواست کرده‌اند. gateway همچنان مالک این است که آن restart واقعاً چه زمانی رخ دهد.

api.runtime.config.loadConfig() و api.runtime.config.writeConfigFile(...) helperهای سازگاری منسوخ‌شده زیر runtime-config-load-write هستند. آن‌ها در runtime یک‌بار هشدار می‌دهند و در پنجرهٔ migration برای pluginهای خارجی قدیمی همچنان در دسترس می‌مانند. pluginهای bundled نباید از آن‌ها استفاده کنند؛ اگر کد plugin آن‌ها را فراخوانی کند یا آن helperها را از subpathهای plugin SDK import کند، نگهبان‌های مرز پیکربندی fail می‌شوند.

برای importهای مستقیم SDK، به‌جای barrel سازگاری گستردهٔ openclaw/plugin-sdk/config-runtime از subpathهای متمرکز پیکربندی استفاده کنید: config-types برای typeها، plugin-config-runtime برای assertionهای پیکربندی از قبل بارگذاری‌شده و lookup ورودی plugin، runtime-config-snapshot برای snapshotهای فعلی فرایند، و config-mutation برای writeها. testهای pluginهای bundled باید به‌جای mock کردن barrel سازگاری گسترده، این subpathهای متمرکز را مستقیماً mock کنند.

کد runtime داخلی OpenClaw نیز همین جهت‌گیری را دارد: پیکربندی را یک‌بار در مرز CLI، gateway یا فرایند بارگذاری کنید، سپس آن مقدار را پاس دهید. writeهای جهش موفق snapshot runtime فرایند را refresh می‌کنند و revision داخلی آن را جلو می‌برند؛ cacheهای بلندعمر باید به‌جای serializing محلی پیکربندی، بر اساس cache key متعلق به runtime کلید بخورند. moduleهای runtime بلندعمر یک scanner با تحمل صفر برای فراخوانی‌های ambient loadConfig() دارند؛ از یک cfg پاس‌داده‌شده، یک request context.getRuntimeConfig()، یا getRuntimeConfig() در یک مرز صریح فرایند استفاده کنید.

مسیرهای اجرای provider و channel باید از snapshot فعال runtime config استفاده کنند، نه snapshot فایل که برای readback یا ویرایش پیکربندی برگردانده شده است. snapshotهای فایل مقدارهای source مانند markerهای SecretRef را برای UI و writeها حفظ می‌کنند؛ callbackهای provider به نمای runtime resolve‌شده نیاز دارند. وقتی ممکن است یک helper با snapshot فعال source یا snapshot فعال runtime فراخوانی شود، پیش از خواندن credentials از مسیر selectApplicableRuntimeConfig() عبور دهید.

namespaceهای runtime

api.runtime.agent

هویت agent، directoryها، و مدیریت session.

// 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(...) helper خنثی برای شروع یک turn معمولی OpenClaw agent از کد plugin است. از همان resolution provider/model و انتخاب agent-harness استفاده می‌کند که replyهای channel-triggered استفاده می‌کنند.

runEmbeddedPiAgent(...) به‌عنوان alias سازگاری باقی می‌ماند.

resolveThinkingPolicy(...) سطح‌های thinking پشتیبانی‌شدهٔ provider/model و default اختیاری را برمی‌گرداند. provider pluginها profile اختصاصی مدل را از طریق hookهای thinking خود مالک هستند، بنابراین tool pluginها باید به‌جای import یا duplicate کردن فهرست‌های provider، این helper runtime را فراخوانی کنند.

normalizeThinkingLevel(...) متن کاربر مانند on، x-high، یا extra high را پیش از بررسی آن در برابر policy resolve‌شده، به سطح ذخیره‌شدهٔ canonical تبدیل می‌کند.

helperهای session store زیر 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);

برای writeهای runtime، updateSessionStore(...) یا updateSessionStoreEntry(...) را ترجیح دهید. آن‌ها از مسیر writer session-store متعلق به Gateway عبور می‌کنند، updateهای همزمان را حفظ می‌کنند، و hot cache را دوباره استفاده می‌کنند. saveSessionStore(...) برای سازگاری و rewriteهای سبک maintenance آفلاین همچنان در دسترس می‌ماند.

api.runtime.agent.defaults

ثابت‌های default model و provider:

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های پس‌زمینه را launch و مدیریت کنید.

// 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(...) می‌تواند sessionهایی را حذف کند که همان plugin از طریق api.runtime.subagent.run(...) ساخته است. حذف sessionهای دلخواه کاربر یا اپراتور همچنان به یک درخواست Gateway با scope ادمین نیاز دارد.

api.runtime.nodes

nodeهای متصل را فهرست کنید و یک command میزبان node را از کد plugin بارگذاری‌شده در Gateway یا از commandهای CLI plugin فراخوانی کنید. زمانی از این استفاده کنید که یک plugin مالک کار محلی روی دستگاه paired باشد، برای مثال browser یا audio bridge روی 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 این runtime درون‌فرایندی است. در commandهای CLI plugin، Gateway پیکربندی‌شده را از طریق RPC فراخوانی می‌کند، بنابراین commandهایی مانند openclaw googlemeet recover-tab می‌توانند nodeهای paired را از terminal بررسی کنند. commandهای Node همچنان از مسیر pairing معمول node در Gateway، allowlistهای command، policyهای node-invoke plugin، و handling command محلی node عبور می‌کنند.

pluginهایی که commandهای خطرناک میزبان node را expose می‌کنند باید یک policy node-invoke با api.registerNodeInvokePolicy(...) ثبت کنند. policy پس از بررسی‌های allowlist command و پیش از forward شدن command به node در Gateway اجرا می‌شود، بنابراین فراخوانی‌های مستقیم node.invoke و ابزارهای سطح‌بالاتر plugin مسیر enforcement یکسانی را به اشتراک می‌گذارند.

api.runtime.tasks.managedFlows

یک runtime Task Flow را به یک session key موجود OpenClaw یا context ابزار trusted متصل کنید، سپس بدون پاس دادن owner در هر فراخوانی، Task Flowها را بسازید و مدیریت کنید.

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

زمانی از bindSession({ sessionKey, requesterOrigin }) استفاده کنید که از لایهٔ binding خودتان یک session key trusted OpenClaw در اختیار دارید. از input خام کاربر bind نکنید.

api.runtime.tts

synthesis متن به گفتار.

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

از پیکربندی core messages.tts و انتخاب provider استفاده می‌کند. audio buffer PCM به‌همراه sample rate برمی‌گرداند.

api.runtime.mediaUnderstanding

تحلیل image، audio، و video.

// 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

جست‌وجوی وب.

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

تصویر لحظه‌ای پیکربندی runtime فعلی و نوشتن‌های تراکنشی پیکربندی. پیکربندی‌ای را ترجیح دهید که از قبل به مسیر فراخوانی فعال پاس داده شده است؛ فقط زمانی از current() استفاده کنید که handler مستقیماً به تصویر لحظه‌ای فرایند نیاز دارد.

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 }، که قصد نویسنده را بدون گرفتن کنترل restart از 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

تشخیص احراز هویت مدل و provider.

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

تشخیص دایرکتوری 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();

ذخیره‌گاه‌های کلیددار پس از restart باقی می‌مانند و با شناسه Plugin متصل به runtime ایزوله می‌شوند. برای ادعاهای dedupe اتمیک از registerIfAbsent(...) استفاده کنید: وقتی کلید وجود نداشته یا منقضی شده و ثبت شده باشد true برمی‌گرداند، یا وقتی یک مقدار زنده از قبل وجود دارد، بدون بازنویسی مقدار، زمان ایجاد یا TTL آن، false برمی‌گرداند. محدودیت‌ها: maxEntries برای هر namespace، ۱٬۰۰۰ ردیف زنده برای هر Plugin، مقادیر JSON کمتر از ۶۴KB، و انقضای TTL اختیاری.

api.runtime.tools

کارخانه‌های ابزار حافظه و CLI.

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

helperهای runtime مخصوص کانال (وقتی یک Plugin کانال بارگذاری شده باشد در دسترس‌اند).

api.runtime.channel.mentions سطح مشترک سیاست mention ورودی برای Pluginهای کانال همراه‌شده‌ای است که از runtime injection استفاده می‌کنند:

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

helperهای mention موجود:

  • buildMentionRegexes
  • matchesMentionPatterns
  • matchesMentionWithExplicit
  • implicitMentionKindWhen
  • resolveInboundMentionDecision

api.runtime.channel.mentions عمداً helperهای سازگاری قدیمی‌تر resolveMentionGating* را expose نمی‌کند. مسیر نرمال‌شده { facts, policy } را ترجیح دهید.

ذخیره‌سازی ارجاع‌های runtime

برای ذخیره کردن ارجاع runtime جهت استفاده خارج از callback register، از createPluginRuntimeStore استفاده کنید:

  • Create the store

    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",
    });
    
  • Wire into the entry point

    export default defineChannelPluginEntry({
      id: "my-plugin",
      name: "My Plugin",
      description: "Example",
      plugin: myPlugin,
      setRuntime: store.setRuntime,
    });
    
  • Access from other files

    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

    شناسه Plugin.

    api.namestring

    نام نمایشی Plugin.

    api.configOpenClawConfig

    تصویر لحظه‌ای پیکربندی فعلی (در صورت وجود، تصویر لحظه‌ای runtime درون‌حافظه‌ای فعال).

    OPENCLAW_DOCS_MARKER:paramOpen:IHBhdGg9ImFwaS5wbHVnaW5Db25maWciIHR5cGU9IlJlY29yZDxzdHJpbmcsIHVua25vd24 "> پیکربندی مخصوص Plugin از plugins.entries.<id>.config.

    api.loggerPluginLogger

    logger دارای scope (debug، info، warn، error).

    api.registrationModePluginRegistrationMode

    حالت بارگذاری فعلی؛ "setup-runtime" پنجره سبک راه‌اندازی/setup پیش از ورود کامل است.

    api.resolvePath(input)"(string)

    مرتبط