Plugins
Fonctions auxiliaires d’exécution du Plugin
Référence pour l’objet api.runtime injecté dans chaque plugin pendant l’enregistrement. Utilisez ces assistants au lieu d’importer directement les éléments internes de l’hôte.
Guide pas à pas qui utilise ces assistants en contexte pour les plugins de canal.
Guide pas à pas qui utilise ces assistants en contexte pour les plugins de fournisseur.
register(api) {
const runtime = api.runtime;
}
Chargement et écritures de configuration
Préférez la configuration qui a déjà été transmise au chemin d’appel actif, par exemple api.config pendant l’enregistrement ou un argument cfg dans les callbacks de canal/fournisseur. Cela permet de faire circuler un instantané de processus unique pendant le travail au lieu de réanalyser la configuration sur les chemins chauds.
Utilisez api.runtime.config.current() uniquement lorsqu’un gestionnaire à longue durée de vie a besoin de l’instantané de processus courant et qu’aucune configuration n’a été transmise à cette fonction. La valeur renvoyée est en lecture seule ; clonez-la ou utilisez un assistant de mutation avant de la modifier.
Les fabriques d’outils reçoivent ctx.runtimeConfig ainsi que ctx.getRuntimeConfig(). Utilisez le getter dans le callback execute d’un outil à longue durée de vie lorsque la configuration peut changer après la création de la définition de l’outil.
Persistez les changements avec api.runtime.config.mutateConfigFile(...) ou api.runtime.config.replaceConfigFile(...). Chaque écriture doit choisir une politique afterWrite explicite :
afterWrite: { mode: "auto" }laisse le planificateur de rechargement du Gateway décider.afterWrite: { mode: "restart", reason: "..." }force un redémarrage propre lorsque l’auteur de l’écriture sait que le rechargement à chaud n’est pas sûr.afterWrite: { mode: "none", reason: "..." }supprime le rechargement/redémarrage automatique uniquement lorsque l’appelant prend en charge le suivi.
Les assistants de mutation renvoient afterWrite ainsi qu’un résumé followUp typé afin que les appelants puissent journaliser ou tester s’ils ont demandé un redémarrage. Le Gateway reste responsable du moment où ce redémarrage se produit réellement.
api.runtime.config.loadConfig() et api.runtime.config.writeConfigFile(...) sont des assistants de compatibilité obsolètes sous runtime-config-load-write. Ils avertissent une fois à l’exécution et restent disponibles pour les anciens plugins externes pendant la fenêtre de migration. Les plugins fournis ne doivent pas les utiliser ; les gardes de frontière de configuration échouent si le code du Plugin les appelle ou importe ces assistants depuis les sous-chemins du SDK de Plugin.
Pour les imports directs du SDK, utilisez les sous-chemins de configuration ciblés plutôt que le barrel de compatibilité large
openclaw/plugin-sdk/config-runtime : config-types pour les
types, plugin-config-runtime pour les assertions de configuration déjà chargée et la recherche
d’entrée de Plugin, runtime-config-snapshot pour les instantanés de processus courants, et
config-mutation pour les écritures. Les tests de Plugin fournis doivent mocker directement ces
sous-chemins ciblés au lieu de mocker le barrel de compatibilité large.
Le code d’exécution interne d’OpenClaw suit la même direction : charger la configuration une fois à la frontière CLI, Gateway ou processus, puis transmettre cette valeur. Les écritures de mutation réussies actualisent l’instantané d’exécution du processus et avancent sa révision interne ; les caches à longue durée de vie doivent s’appuyer sur la clé de cache possédée par l’exécution plutôt que de sérialiser la configuration localement. Les modules d’exécution à longue durée de vie disposent d’un scanner de tolérance zéro pour les appels ambiants à loadConfig() ; utilisez un cfg transmis, un context.getRuntimeConfig() de requête, ou getRuntimeConfig() à une frontière de processus explicite.
Les chemins d’exécution des fournisseurs et des canaux doivent utiliser l’instantané de configuration d’exécution actif, et non un instantané de fichier renvoyé pour la relecture ou la modification de la configuration. Les instantanés de fichier préservent les valeurs sources comme les marqueurs SecretRef pour l’interface utilisateur et les écritures ; les callbacks de fournisseur ont besoin de la vue d’exécution résolue. Lorsqu’un assistant peut être appelé avec l’instantané source actif ou l’instantané d’exécution actif, passez par selectApplicableRuntimeConfig() avant de lire les identifiants.
Espaces de noms d’exécution
api.runtime.agent
Identité de l’agent, répertoires et gestion des sessions.
// 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(...) est l’assistant neutre permettant de démarrer un tour d’agent OpenClaw normal depuis le code d’un Plugin. Il utilise la même résolution fournisseur/modèle et la même sélection de harnais d’agent que les réponses déclenchées par canal.
runEmbeddedPiAgent(...) reste disponible comme alias de compatibilité.
resolveThinkingPolicy(...) renvoie les niveaux de réflexion pris en charge par le fournisseur/modèle et le défaut optionnel. Les plugins de fournisseur possèdent le profil spécifique au modèle via leurs hooks de réflexion, donc les plugins d’outils doivent appeler cet assistant d’exécution au lieu d’importer ou de dupliquer des listes de fournisseurs.
normalizeThinkingLevel(...) convertit le texte utilisateur comme on, x-high ou extra high vers le niveau stocké canonique avant de le vérifier par rapport à la politique résolue.
Les assistants de magasin de sessions se trouvent sous 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);
Préférez updateSessionStore(...) ou updateSessionStoreEntry(...) pour les écritures d’exécution. Ils passent par l’auteur de magasin de sessions possédé par le Gateway, préservent les mises à jour concurrentes et réutilisent le cache chaud. saveSessionStore(...) reste disponible pour la compatibilité et les réécritures de style maintenance hors ligne.
api.runtime.agent.defaults
Constantes de fournisseur et de modèle par défaut :
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
Lancez et gérez les exécutions de sous-agents en arrière-plan.
// 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(...) peut supprimer les sessions créées par le même Plugin via api.runtime.subagent.run(...). La suppression de sessions arbitraires d’utilisateur ou d’opérateur nécessite toujours une requête Gateway avec portée administrateur.
api.runtime.nodes
Listez les nœuds connectés et invoquez une commande hébergée par un nœud depuis le code de Plugin chargé par le Gateway ou depuis les commandes CLI du Plugin. Utilisez ceci lorsqu’un Plugin possède du travail local sur un appareil appairé, par exemple une passerelle de navigateur ou audio sur un autre 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,
});
À l’intérieur du Gateway, cette exécution est dans le processus. Dans les commandes CLI de Plugin, elle appelle le Gateway configuré via RPC, afin que des commandes comme openclaw googlemeet recover-tab puissent inspecter les nœuds appairés depuis le terminal. Les commandes Node passent toujours par l’appairage de nœuds normal du Gateway, les listes d’autorisation de commandes, les politiques d’invocation de nœud du Plugin et la gestion locale des commandes du nœud.
Les plugins qui exposent des commandes dangereuses hébergées par des nœuds doivent enregistrer une politique d’invocation de nœud avec api.registerNodeInvokePolicy(...). La politique s’exécute dans le Gateway après les vérifications de liste d’autorisation de commandes et avant que la commande ne soit transférée au nœud, de sorte que les appels directs node.invoke et les outils de Plugin de plus haut niveau partagent le même chemin d’application.
api.runtime.tasks.managedFlows
Liez une exécution TaskFlow à une clé de session OpenClaw existante ou à un contexte d’outil fiable, puis créez et gérez des TaskFlows sans transmettre de propriétaire à chaque appel.
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" },
});
Utilisez bindSession({ sessionKey, requesterOrigin }) lorsque vous disposez déjà d’une clé de session OpenClaw fiable issue de votre propre couche de liaison. Ne liez pas depuis une entrée utilisateur brute.
api.runtime.tts
Synthèse vocale.
// 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,
});
Utilise la configuration cœur messages.tts et la sélection de fournisseur. Renvoie un tampon audio PCM + la fréquence d’échantillonnage.
api.runtime.mediaUnderstanding
Analyse d’images, d’audio et de vidéo.
// 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,
});
Renvoie { text: undefined } lorsqu'aucune sortie n'est produite (par exemple, une entrée ignorée).
api.runtime.imageGeneration
Génération d'images.
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
Recherche 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
Utilitaires média de bas niveau.
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
Instantané de la configuration d'exécution actuelle et écritures de configuration transactionnelles. Préférez
la configuration déjà passée dans le chemin d'appel actif ; utilisez
current() uniquement lorsque le gestionnaire a directement besoin de l'instantané du processus.
const cfg = api.runtime.config.current();
await api.runtime.config.mutateConfigFile({
afterWrite: { mode: "auto" },
mutate(draft) {
draft.plugins ??= {};
},
});
mutateConfigFile(...) et replaceConfigFile(...) renvoient une valeur followUp,
par exemple { mode: "restart", requiresRestart: true, reason },
qui enregistre l'intention du rédacteur sans retirer au
Gateway le contrôle du redémarrage.
api.runtime.system
Utilitaires au niveau système.
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
Abonnements aux événements.
api.runtime.events.onAgentEvent((event) => {
/* ... */
});
api.runtime.events.onSessionTranscriptUpdate((update) => {
/* ... */
});
api.runtime.logging
Journalisation.
const verbose = api.runtime.logging.shouldLogVerbose();
const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" }, { level: "debug" });
api.runtime.modelAuth
Résolution de l'authentification du modèle et du fournisseur.
const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg });
const providerAuth = await api.runtime.modelAuth.resolveApiKeyForProvider({
provider: "openai",
cfg,
});
api.runtime.state
Résolution du répertoire d'état et stockage clé-valeur adossé à 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();
Les stockages clé-valeur survivent aux redémarrages et sont isolés par l'id de Plugin lié à l'exécution. Utilisez registerIfAbsent(...) pour les revendications de déduplication atomiques : il renvoie true lorsque la clé était absente ou expirée et a été enregistrée, ou false lorsqu'une valeur active existe déjà sans remplacer sa valeur, son heure de création ni sa durée de vie. Limites : maxEntries par espace de noms, 1 000 lignes actives par Plugin, valeurs JSON inférieures à 64 Ko, et expiration TTL facultative.
api.runtime.tools
Fabriques d'outils mémoire et CLI.
const getTool = api.runtime.tools.createMemoryGetTool(/* ... */);
const searchTool = api.runtime.tools.createMemorySearchTool(/* ... */);
api.runtime.tools.registerMemoryCli(/* ... */);
api.runtime.channel
Assistants d'exécution propres au canal (disponibles lorsqu'un Plugin de canal est chargé).
api.runtime.channel.mentions est la surface partagée de politique de mention entrante pour les Plugins de canal groupés qui utilisent l'injection d'exécution :
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,
},
});
Assistants de mention disponibles :
buildMentionRegexesmatchesMentionPatternsmatchesMentionWithExplicitimplicitMentionKindWhenresolveInboundMentionDecision
api.runtime.channel.mentions n'expose intentionnellement pas les anciens assistants de compatibilité resolveMentionGating*. Préférez le chemin normalisé { facts, policy }.
Stockage des références d'exécution
Utilisez createPluginRuntimeStore pour stocker la référence d'exécution afin de l'utiliser en dehors du rappel register :
Créer le stockage
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",
});
Raccorder au point d'entrée
export default defineChannelPluginEntry({
id: "my-plugin",
name: "My Plugin",
description: "Example",
plugin: myPlugin,
setRuntime: store.setRuntime,
});
Accéder depuis d'autres fichiers
export function getRuntime() {
return store.getRuntime(); // throws if not initialized
}
export function tryGetRuntime() {
return store.tryGetRuntime(); // returns null if not initialized
}
Autres champs api de premier niveau
Au-delà de api.runtime, l'objet API fournit aussi :
api.idstringId du Plugin.
api.namestringNom d'affichage du Plugin.
api.configOpenClawConfigInstantané de la configuration actuelle (instantané d'exécution en mémoire actif lorsqu'il est disponible).
OPENCLAW_DOCS_MARKER:paramOpen:IHBhdGg9ImFwaS5wbHVnaW5Db25maWciIHR5cGU9IlJlY29yZDxzdHJpbmcsIHVua25vd24
">
Configuration propre au Plugin depuis plugins.entries.<id>.config.
api.loggerPluginLoggerJournaliseur limité au périmètre (debug, info, warn, error).
api.registrationModePluginRegistrationModeMode de chargement actuel ; "setup-runtime" est la fenêtre légère de démarrage/configuration avant l'entrée complète.
api.resolvePath(input)"(string)Associés
- Internes des Plugins — modèle de capacités et registre
- Points d'entrée du SDK — options de
definePluginEntry - Vue d'ensemble du SDK — référence des sous-chemins