Building plugins
Plugin हुक्स
Plugin hooks, OpenClaw plugins के लिए इन-प्रोसेस एक्सटेंशन पॉइंट हैं: एजेंट रन, टूल कॉल, संदेश प्रवाह, सत्र जीवनचक्र, सबएजेंट रूटिंग, इंस्टॉलेशन या Gateway स्टार्टअप का निरीक्षण करें या उन्हें बदलें।
इसके बजाय, कमांड और Gateway इवेंट्स जैसे /new,
/reset, /stop, agent:bootstrap, या gateway:startup पर प्रतिक्रिया देने वाली, ऑपरेटर द्वारा इंस्टॉल की गई छोटी
HOOK.md स्क्रिप्ट के लिए आंतरिक hooks का उपयोग करें।
त्वरित शुरुआत
Plugin एंट्री से api.on(...) के साथ टाइप किए गए hooks पंजीकृत करें:
export default definePluginEntry({ id: "tool-preflight", name: "Tool Preflight", register(api) { api.on( "before_tool_call", async (event) => { if (event.toolName !== "web_search") { return; } return { requireApproval: { title: "Run web search", description: `Allow search query: ${String(event.params.query ?? "")}`, severity: "info", timeoutMs: 60_000, }, }; }, { priority: 50 }, ); },});निर्णय या संशोधन लौटा सकने वाले हैंडलर, घटते हुए
priority क्रम में क्रमिक रूप से चलते हैं; समान प्राथमिकता वाले हैंडलर पंजीकरण क्रम बनाए रखते हैं।
केवल-अवलोकन हैंडलर समानांतर रूप से चलते हैं, और फायर-एंड-फॉरगेट अवलोकन
डिस्पैच बाद के इवेंट्स के साथ ओवरलैप हो सकते हैं। अवलोकन के साइड इफ़ेक्ट्स को क्रमबद्ध करने के लिए प्राथमिकता का उपयोग न करें।
api.on(name, handler, opts?) यह स्वीकार करता है:
| विकल्प | प्रभाव |
|---|---|
priority |
क्रम निर्धारण; अधिक मान वाला पहले चलता है। |
timeoutMs |
प्रति-hook प्रतीक्षा बजट। इसकी अवधि समाप्त होने पर OpenClaw उस हैंडलर की प्रतीक्षा बंद करके आगे बढ़ जाता है। यह हैंडलर या उसके साइड इफ़ेक्ट्स को रद्द नहीं करता। रनर की डिफ़ॉल्ट प्रति-hook टाइमआउट अवधि का उपयोग करने के लिए इसे छोड़ दें। |
ऑपरेटर Plugin कोड पैच किए बिना hook बजट सेट कर सकते हैं:
{ "plugins": { "entries": { "my-plugin": { "hooks": { "timeoutMs": 30000, "timeouts": { "before_prompt_build": 90000, "agent_end": 60000 } } } } }}hooks.timeouts.<hookName>, hooks.timeoutMs को ओवरराइड करता है, जो बदले में
Plugin द्वारा लिखे गए api.on(..., { timeoutMs }) मान को ओवरराइड करता है। प्रत्येक मान
600000 ms तक का धनात्मक पूर्णांक होना चाहिए। ज्ञात रूप से धीमे hooks के लिए प्रति-hook ओवरराइड को प्राथमिकता दें, ताकि किसी एक Plugin को हर जगह लंबा बजट न मिले।
टाइमआउट हो चुका हैंडलर प्रॉमिस चलता रहता है, क्योंकि hook कॉलबैक को रद्दीकरण सिग्नल नहीं मिलता। जब उस Plugin का कार्य अभी भी जारी हो, तब hook डिस्पैच अपना Gateway एडमिशन मुक्त कर सकता है। लंबे समय तक चलने वाले कार्य के स्वामी Plugins को अपना रद्दीकरण और शटडाउन जीवनचक्र उपलब्ध कराना होगा।
आउटबाउंड संशोधन hooks message_sending और reply_payload_sending प्रत्येक हैंडलर के लिए
15-सेकंड की डिफ़ॉल्ट अवधि का उपयोग करते हैं। यदि किसी का टाइमआउट होता है, तो OpenClaw Plugin त्रुटि लॉग करता है
और नवीनतम पेलोड के साथ जारी रहता है, ताकि क्रमबद्ध डिलीवरी लेन
स्थिर हो सके। डिलीवरी से पहले जानबूझकर धीमा कार्य करने वाले Plugins के लिए अधिक बड़ा प्रति-hook बजट सेट करें।
createReplyDispatcher का उपयोग करने वाले चैनल Plugins भी beforeDeliverOptions: { timeoutMs } के साथ,
या dispatcher.appendBeforeDeliver(handler, { timeoutMs }) के ज़रिए कार्य जोड़ते समय, प्रत्येक चरण के लिए अधिक बड़ा
धनात्मक बजट घोषित कर सकते हैं।
स्वामी द्वारा घोषित बजट के बिना, वे कॉलबैक भी उसी 15-सेकंड
डिफ़ॉल्ट अवधि का उपयोग करते हैं, ताकि अटका हुआ कॉलबैक क्रमबद्ध डिलीवरी लेन को रोके न रख सके।
प्रत्येक hook को event.context.pluginConfig मिलता है, जो उस
Plugin का रिज़ॉल्व किया गया कॉन्फ़िगरेशन है जिसने उस हैंडलर को पंजीकृत किया था। OpenClaw इसे साझा इवेंट ऑब्जेक्ट को बदले बिना प्रत्येक हैंडलर में इंजेक्ट करता है, जिसे अन्य Plugins देखते हैं।
Hook सूची
Hooks को उनके द्वारा विस्तारित सतह के अनुसार समूहीकृत किया गया है। मोटे अक्षरों वाले नाम निर्णय परिणाम स्वीकार करते हैं (ब्लॉक करना, रद्द करना, ओवरराइड करना या अनुमोदन आवश्यक करना); शेष केवल-अवलोकन के लिए हैं।
एजेंट टर्न
| Hook | उद्देश्य |
|---|---|
before_model_resolve |
सत्र संदेश लोड होने से पहले प्रदाता या मॉडल को ओवरराइड करना |
agent_turn_prepare |
कतारबद्ध Plugin टर्न इंजेक्शन का उपयोग करना और प्रॉम्प्ट hooks से पहले उसी टर्न का संदर्भ जोड़ना |
before_prompt_build |
मॉडल कॉल से पहले डायनेमिक संदर्भ या सिस्टम-प्रॉम्प्ट टेक्स्ट जोड़ना |
before_agent_run |
मॉडल सबमिशन से पहले अंतिम प्रॉम्प्ट और सत्र संदेशों का निरीक्षण करना; रन को ब्लॉक कर सकता है |
before_agent_reply |
सिंथेटिक उत्तर या मौन के साथ मॉडल टर्न को शॉर्ट-सर्किट करना |
before_agent_finalize |
स्वाभाविक अंतिम उत्तर का निरीक्षण करना और मॉडल के एक और पास का अनुरोध करना |
agent_end |
अंतिम संदेशों, सफलता स्थिति और रन अवधि का अवलोकन करना |
heartbeat_prompt_contribution |
बैकग्राउंड मॉनिटर और जीवनचक्र Plugins के लिए केवल-Heartbeat संदर्भ जोड़ना |
वार्तालाप अवलोकन
| Hook | उद्देश्य |
|---|---|
model_call_started / model_call_ended |
स्वच्छ किए गए प्रदाता/मॉडल कॉल मेटाडेटा: समय, परिणाम, सीमित अनुरोध-ID हैश। कोई प्रॉम्प्ट या प्रतिक्रिया सामग्री नहीं। |
llm_input |
प्रदाता इनपुट: सिस्टम प्रॉम्प्ट, प्रॉम्प्ट, इतिहास |
llm_output |
प्रदाता आउटपुट, उपयोग और उपलब्ध होने पर रिज़ॉल्व किया गया contextTokenBudget |
टूल्स
| Hook | उद्देश्य |
|---|---|
before_tool_call |
टूल पैरामीटर फिर से लिखना, निष्पादन ब्लॉक करना या अनुमोदन आवश्यक करना |
after_tool_call |
टूल परिणामों, त्रुटियों और अवधि का अवलोकन करना |
resolve_exec_env |
exec में Plugin के स्वामित्व वाले पर्यावरण चर प्रदान करना |
tool_result_persist |
टूल परिणाम से बने सहायक संदेश को फिर से लिखना |
before_message_write |
जारी संदेश लेखन का निरीक्षण करना या उसे ब्लॉक करना (दुर्लभ) |
संदेश और डिलीवरी
| Hook | उद्देश्य |
|---|---|
inbound_claim |
एजेंट रूटिंग से पहले इनबाउंड संदेश का दावा करना (सिंथेटिक उत्तर) |
channel_pairing_requested |
नए बनाए गए DM पेयरिंग अनुरोधों का अवलोकन करना |
message_received |
इनबाउंड सामग्री, प्रेषक, थ्रेड और मेटाडेटा का अवलोकन करना |
message_sending |
आउटबाउंड सामग्री को फिर से लिखना या डिलीवरी रद्द करना |
reply_payload_sending |
डिलीवरी से पहले सामान्यीकृत उत्तर पेलोड को बदलना या रद्द करना |
message_sent |
आउटबाउंड डिलीवरी की सफलता या विफलता का अवलोकन करना |
before_dispatch |
चैनल हैंडऑफ़ से पहले आउटबाउंड डिस्पैच का निरीक्षण करना या उसे फिर से लिखना |
reply_dispatch |
अंतिम उत्तर-डिस्पैच पाइपलाइन में भाग लेना |
सत्र और Compaction
| Hook | उद्देश्य |
|---|---|
session_start / session_end |
सत्र जीवनचक्र सीमाओं को ट्रैक करना। reason, new, reset, idle, daily, compaction, deleted, shutdown, restart, या unknown में से एक है। सक्रिय सत्रों के साथ प्रक्रिया के रुकने या पुनः प्रारंभ होने पर shutdown/restart, Gateway शटडाउन फ़ाइनलाइज़र से सक्रिय होते हैं, ताकि Plugins (मेमोरी, ट्रांसक्रिप्ट स्टोर) घोस्ट पंक्तियों को पुनः प्रारंभों के बीच खुला छोड़ने के बजाय अंतिम रूप दे सकें। फ़ाइनलाइज़र सीमित अवधि वाला है, ताकि धीमा Plugin SIGTERM/SIGINT को ब्लॉक न कर सके। |
before_compaction / after_compaction |
Compaction चक्रों का अवलोकन करना या उन पर टिप्पणी जोड़ना |
before_reset |
सत्र-रीसेट इवेंट्स (/reset, प्रोग्रामेटिक रीसेट) का अवलोकन करना |
parentSessionKey और emitCommandHooks: true वाली sessions.create कॉल्स के लिए, एक अलग चाइल्ड को हमेशा session_start मिलता है। कॉलर succeedsParent के साथ यह घोषित करते हैं कि पैरेंट को भी टर्मिनल session_end मिलता है या नहीं: true का अर्थ उत्तराधिकारी है, false का अर्थ समानांतर चाइल्ड है। इसे छोड़ने पर लेगेसी पैरेंट-रोलओवर व्यवहार बना रहता है। command:new और before_reset hooks दोनों मामलों में अनुरोधित /new कार्रवाई का वर्णन करते रहते हैं।
सबएजेंट्स
subagent_spawned/subagent_ended- सबएजेंट के प्रारंभ और पूर्ण होने का अवलोकन करें।subagent_delivery_target- जब कोई कोर सत्र बाइंडिंग किसी रूट को प्रक्षेपित नहीं कर सकती, तब पूर्णता डिलीवरी के लिए संगतता हुक।subagent_spawning- बहिष्कृत संगतता हुक। अब कोर,subagent_spawnedके सक्रिय होने से पहले चैनल सत्र-बाइंडिंग अडैप्टर के माध्यम सेthread: trueसबएजेंट बाइंडिंग तैयार करता है।- जब OpenClaw ने प्रारंभ से पहले चाइल्ड सत्र का नेटिव मॉडल निर्धारित कर लिया हो, तब
subagent_spawnedमेंresolvedModelऔरresolvedProviderशामिल होते हैं। subagent_endedमेंtargetSessionKey(पहचान -subagent_spawned.childSessionKeyसे मेल खाती है),targetKind("subagent"या"acp"),reason, वैकल्पिकoutcome("ok","error","timeout","killed","reset", या"deleted"), वैकल्पिकerror,runId,endedAt,accountId, औरsendFarewellहोते हैं। इसमेंagentIdयाchildSessionKeyशामिल नहीं होते; संबंधितsubagent_spawnedइवेंट से सहसंबंध स्थापित करने के लिएtargetSessionKeyका उपयोग करें।
जीवनचक्र
| हुक | उद्देश्य |
|---|---|
gateway_start / gateway_stop |
Gateway के साथ Plugin के स्वामित्व वाली सेवाएँ प्रारंभ या बंद करना |
deactivate |
gateway_stop के लिए बहिष्कृत संगतता उपनाम; नए Plugins में gateway_stop का उपयोग करें |
cron_reconciled |
स्टार्टअप या रीलोड के बाद संपूर्ण Gateway Cron स्थिति से मिलान करना |
cron_changed |
Gateway के स्वामित्व वाले Cron जीवनचक्र परिवर्तनों (जोड़ा गया, अपडेट किया गया, हटाया गया, प्रारंभ हुआ, समाप्त हुआ, शेड्यूल किया गया) का अवलोकन करना |
before_install |
लोड किए गए Plugin रनटाइम से चरणबद्ध Skills या Plugin इंस्टॉलेशन सामग्री का निरीक्षण करना |
चैनल पेयरिंग अनुरोध
जब किसी Plugin को किसी अनपेयर किए गए DM प्रेषक द्वारा लंबित पेयरिंग
अनुरोध बनाने के बाद ऑपरेटर को सूचित करने या ऑडिट रिकॉर्ड लिखने की आवश्यकता हो,
तब channel_pairing_requested का उपयोग करें। अनुरोध बनते ही हुक डिस्पैच होता है; धीमे या विफल
हुक हैंडलर के कारण पेयरिंग उत्तर की चैनल डिलीवरी में विलंब नहीं होता।
api.on("channel_pairing_requested", async (event) => { await notifyOperator({ text: `${event.senderId} से नया ${event.channel} पेयरिंग अनुरोध: ${event.code}`, });});यह हुक केवल अवलोकन के लिए है। यह पेयरिंग उत्तर को स्वीकृत, अस्वीकृत, दबाता या पुनर्लिखित
नहीं करता। पेलोड में चैनल, वैकल्पिक accountId,
चैनल-स्कोप वाला senderId, पेयरिंग code, और चैनल मेटाडेटा शामिल हैं। पेयरिंग
कोड को सक्रिय, एकल-उपयोग स्वीकृति क्रेडेंशियल मानें और इसे केवल किसी
विश्वसनीय ऑपरेटर सिंक तक पहुँचाएँ। metadata को प्रेषक द्वारा दिया गया अविश्वसनीय पहचान
टेक्स्ट मानें। हुक में आने वाले संदेश का मुख्य भाग या मीडिया शामिल नहीं होता।
डीबग रनटाइम हुक
एजेंट टर्न के लिए प्रदाता या मॉडल बदलने हेतु before_model_resolve का उपयोग करें - यह
मॉडल रिज़ॉल्यूशन से पहले चलता है। llm_output केवल तब चलता है, जब मॉडल प्रयास
सहायक आउटपुट उत्पन्न करता है।
प्रभावी सत्र मॉडल के प्रमाण के लिए, रनटाइम पंजीकरणों का निरीक्षण करें, फिर
openclaw sessions या Gateway सत्र/स्थिति सतहों का उपयोग करें। प्रदाता
पेलोड डीबग करने के लिए, कच्चे मॉडल स्ट्रीम इवेंट को jsonl फ़ाइल में लिखने हेतु Gateway को
--raw-stream और --raw-stream-path <path> के साथ प्रारंभ करें।
टूल कॉल नीति
before_tool_call को ये प्राप्त होते हैं:
event.toolNameevent.params- वैकल्पिक
event.toolKindऔरevent.toolInputKind, ऐसे टूल के लिए होस्ट-प्रामाणिक विभेदक जो जानबूझकर समान नाम साझा करते हैं; उदाहरण के लिए, बाहरी कोड-मोडexecकॉलtoolKind: "code_mode_exec"का उपयोग करते हैं और इनपुट भाषा ज्ञात होने परtoolInputKind: "javascript" | "typescript"शामिल करते हैं - वैकल्पिक
event.derivedPaths,apply_patchजैसे प्रसिद्ध टूल एनवेलप के लिए होस्ट से प्राप्त सर्वोत्तम-प्रयास लक्ष्य पथ संकेत; ये पथ अधूरे हो सकते हैं या टूल वास्तव में जिन चीज़ों को प्रभावित करेगा उनका अधिक व्यापक अनुमान लगा सकते हैं (उदाहरण के लिए, विकृत या आंशिक इनपुट के साथ) - वैकल्पिक
event.runId - वैकल्पिक
event.toolCallId - संदर्भ फ़ील्ड, जैसे
ctx.agentId,ctx.sessionKey,ctx.sessionId,ctx.runId,ctx.toolKind,ctx.toolInputKind, और डायग्नोस्टिकctx.trace - वैकल्पिक
ctx.requester, होस्ट से प्राप्त वह अनुरोधकर्ता जिसने वर्तमान संदेश रन प्रारंभ किया। इसमेंchannel,accountId,senderId,senderIsOwner, और प्रदाता-नेटिवroleIdsशामिल हो सकते हैं। अनुपस्थित फ़ील्ड अप्रमाणित हैं, झूठे आश्वासन नहीं; जहाँ नीति को उनकी आवश्यकता हो, वहाँ सुरक्षित रूप से अस्वीकार करें।
यह इन्हें लौटा सकता है:
type BeforeToolCallResult = { params?: Record<string, unknown>; block?: boolean; blockReason?: string; requireApproval?: { title: string; description: string; severity?: "info" | "warning" | "critical"; timeoutMs?: number; /** @deprecated अनसुलझी स्वीकृतियाँ हमेशा अस्वीकार होती हैं। */ timeoutBehavior?: "allow" | "deny"; allowedDecisions?: Array<"allow-once" | "allow-always" | "deny">; pluginId?: string; onResolution?: ( decision: "allow-once" | "allow-always" | "deny" | "timeout" | "cancelled", ) => Promise<void> | void; };};टाइप किए गए जीवनचक्र हुक के लिए गार्ड व्यवहार:
block: trueअंतिम है और निम्न-प्राथमिकता हैंडलर को छोड़ देता है।block: falseको कोई निर्णय नहीं माना जाता।paramsनिष्पादन के लिए टूल पैरामीटर पुनर्लिखित करता है।requireApprovalएजेंट रन को रोकता है और Plugin स्वीकृतियों के माध्यम से उपयोगकर्ता से पूछता है।/approveexec और Plugin, दोनों स्वीकृतियों को मंज़ूरी दे सकता है। Codex app-server रिपोर्ट-मोड नेटिवPreToolUseरिले में, यह संबंधित app-server स्वीकृति अनुरोध को सौंप देता है; देखें Codex हार्नेस रनटाइम।- उच्च-प्राथमिकता हुक द्वारा स्वीकृति का अनुरोध किए जाने के बाद भी निम्न-प्राथमिकता वाला
block: trueब्लॉक कर सकता है। onResolutionको निर्धारित निर्णय प्राप्त होता है:allow-once,allow-always,deny,timeout, याcancelled।
एक फ़ाइल में प्रेषक-जागरूक नीति
एक स्वतंत्र Plugin फ़ाइल एक और कॉन्फ़िगरेशन स्कीमा जोड़ने के बजाय
परिनियोजन-विशिष्ट नीति को कोड में रख सकती है। यह उदाहरण स्वामियों को हर टूल देता है,
कॉन्फ़िगर किए गए मेंटेनर को एक रूढ़िवादी टूल और संदेश-क्रिया सेट का उपयोग करने देता है,
और चैनल कॉन्फ़िगरेशन द्वारा पहले से अधिकृत प्रेषकों को /fix उपलब्ध कराता है:
const AGENT_ID = "maintenance-agent";const MAINTAINER_SCOPES = [ { channel: "discord", accountId: "operations", senderIds: new Set(["maintainer-user-id"]), roleIds: new Set(["maintainer-role-id"]), },];const MAINTAINER_TOOLS = new Set(["read", "web_fetch", "web_search", "session_status", "message"]);const MAINTAINER_MESSAGE_ACTIONS = new Set(["react", "reply", "thread-create", "thread-reply"]); export default definePluginEntry({ id: "maintenance-access", name: "रखरखाव पहुँच", description: "रखरखाव एजेंट पर प्रेषक-जागरूक टूल नीति लागू करें।", register(api) { api.on("before_tool_call", (event, ctx) => { if (ctx.agentId !== AGENT_ID) { return; } const requester = ctx.requester; if (requester?.senderIsOwner === true) { return; } const maintainerScope = requester ? MAINTAINER_SCOPES.find( (scope) => scope.channel === requester.channel && scope.accountId === requester.accountId, ) : undefined; const isMaintainer = maintainerScope !== undefined && ((requester?.senderId !== undefined && maintainerScope.senderIds.has(requester.senderId)) || requester?.roleIds?.some((roleId) => maintainerScope.roleIds.has(roleId)) === true); if (!isMaintainer) { return { block: true, blockReason: "मेंटेनर पहुँच आवश्यक है।" }; } if (event.toolName === "message") { const action = typeof event.params.action === "string" ? event.params.action : ""; if (MAINTAINER_MESSAGE_ACTIONS.has(action)) { return; } return { block: true, blockReason: `message.${action || "unknown"} के लिए स्वामी आवश्यक है।` }; } if (MAINTAINER_TOOLS.has(event.toolName)) { return; } return { block: true, blockReason: `${event.toolName} के लिए स्वामी आवश्यक है।` }; }); api.registerCommand({ name: "fix", description: "रखरखाव एजेंट से किसी समस्या की जाँच और समाधान करने को कहें।", acceptsArgs: true, requireAuth: true, handler: async (ctx) => ctx.agentId === AGENT_ID ? { continueAgent: true } : { text: "यह कमांड केवल रखरखाव वार्तालाप में उपलब्ध है।" }, }); },});फ़ाइल को सीधे लोड करें और Gateway पुनः प्रारंभ करें:
{ agents: { list: [ { id: "maintenance-agent", workspace: "~/.openclaw/workspace-maintenance", }, ], }, bindings: [ { agentId: "maintenance-agent", match: { channel: "discord", accountId: "operations", peer: { kind: "channel", id: "maintenance-channel-id" }, }, }, ], plugins: { load: { paths: ["~/.openclaw/policies/maintenance-access.ts"] }, },}AGENT_ID में रखरखाव वार्तालाप से बाइंड किए गए एजेंट का नाम होना आवश्यक है।
बाइंडिंग सामान्य संदेशों और /fix के लिए उस एजेंट को चुनती है; स्वतंत्र फ़ाइल
स्वामी-बनाम-मेंटेनर टूल नीति की एकमात्र स्वामी बनी रहती है।
requireAuth: true प्रत्येक चैनल के मौजूदा प्रेषक प्रवेश का पुनः उपयोग करता है।
Discord के लिए, गिल्ड या चैनल users/roles अनुमतिसूची
रखरखाव दर्शकों को अधिकृत कर सकती है। अन्य चैनल स्थिर प्रेषक आईडी का उपयोग कर सकते हैं। इसके बाद हुक
रन में हर टूल कॉल पर अधिक सूक्ष्म प्रति-टूल निर्णय लागू करता है, जिसमें
Codex नेटिव PreToolUse कॉल भी शामिल हैं। यह मॉडल को दिखाई देने वाले टूल को वीटो कर सकता है, लेकिन
होस्ट द्वारा छोड़े गए टूल को जोड़ नहीं सकता। मौजूदा सैंडबॉक्स, exec स्वीकृति, केवल-स्वामी
कोर-टूल, और चैनल नीतियाँ फिर भी लागू होती हैं; हुक उन्हें पार करके अनुमति नहीं दे सकता।
जैसा दिखाया गया है, प्रेषक और भूमिका आईडी को सटीक चैनल/अकाउंट जोड़ी तक सीमित रखें; दोनों
प्रदाता-स्थानीय नेमस्पेस हैं। अनुमतिसूचियों को रूढ़िवादी रखें। लेखन या
निष्पादन टूल केवल तभी जोड़ें, जब परिनियोजन की सैंडबॉक्स और स्वीकृति नीति
उसे सुरक्षित बनाती हो। स्वचालित या सिस्टम रन के लिए स्पष्ट रूप से तय करें कि अनुपस्थित
ctx.requester को पास होना चाहिए या नहीं; उदाहरण स्कोप किए गए एजेंट के लिए इसे अस्वीकार करता है।
स्वीकृति रूटिंग, निर्णय व्यवहार, और वैकल्पिक टूल या exec स्वीकृतियों के बजाय
requireApproval का उपयोग कब करना है, इसके लिए
Plugin अनुमति अनुरोध देखें।
जिन Plugins को होस्ट-स्तरीय नीति की आवश्यकता है, वे
api.registerTrustedToolPolicy(...) के साथ विश्वसनीय टूल नीतियाँ पंजीकृत कर सकते हैं। ये सामान्य
before_tool_call हुक और सामान्य हुक निर्णयों से पहले चलते हैं। बंडल की गई विश्वसनीय
नीतियाँ पहले चलती हैं; इंस्टॉल किए गए Plugin की विश्वसनीय नीतियाँ Plugin-लोड
क्रम में अगली चलती हैं; सामान्य before_tool_call हुक उनके बाद चलते हैं। बंडल किए गए Plugins
मौजूदा विश्वसनीय-नीति पथ बनाए रखते हैं। इंस्टॉल किए गए Plugins स्पष्ट रूप से सक्षम होने चाहिए
और उन्हें contracts.trustedToolPolicies में प्रत्येक नीति आईडी घोषित करनी होगी; अघोषित आईडी
पंजीकरण से पहले अस्वीकार कर दी जाती हैं। नीति आईडी पंजीकरण करने वाले
Plugin के स्कोप में होती हैं, इसलिए अलग-अलग Plugins समान स्थानीय आईडी का पुनः उपयोग कर सकते हैं। इस स्तर का उपयोग केवल
होस्ट-विश्वसनीय गेट, जैसे कार्यस्थान नीति, बजट प्रवर्तन, या
आरक्षित वर्कफ़्लो सुरक्षा के लिए करें।
Exec परिवेश हुक
resolve_exec_env कमांड चलने से पहले plugins को exec
टूल आह्वानों में परिवेश चर देने देता है। इसे ये प्राप्त होते हैं:
event.sessionKeyevent.toolName, वर्तमान में हमेशा"exec"event.host, जो"gateway","sandbox", या"node"में से एक है- संदर्भ फ़ील्ड, जैसे
ctx.agentId,ctx.sessionKey,ctx.messageProvider, औरctx.channelId
Exec परिवेश में मर्ज करने के लिए एक Record<string, string> लौटाएँ। हैंडलर
प्राथमिकता क्रम में चलते हैं; समान कुंजी के लिए बाद के परिणाम पहले के परिणामों को
ओवरराइड करते हैं।
मर्ज करने से पहले हुक आउटपुट को होस्ट की exec परिवेश कुंजी नीति से फ़िल्टर किया जाता है।
PATH हमेशा हटा दिया जाता है (कमांड रिज़ॉल्यूशन और safe-bin जाँच
इस पर निर्भर करती हैं)। अमान्य कुंजियाँ और खतरनाक होस्ट ओवरराइड कुंजियाँ, जैसे LD_*,
DYLD_*, NODE_OPTIONS, प्रॉक्सी चर (HTTP_PROXY, HTTPS_PROXY,
ALL_PROXY, NO_PROXY), और TLS ओवरराइड चर (NODE_TLS_REJECT_UNAUTHORIZED,
SSL_CERT_FILE, और इसी प्रकार के अन्य) हटा दिए जाते हैं। फ़िल्टर किया गया plugin परिवेश
Gateway अनुमोदन/ऑडिट मेटाडेटा में शामिल किया जाता है और node-host निष्पादन
अनुरोधों में अग्रेषित किया जाता है।
टूल परिणाम स्थायित्व
टूल परिणामों में UI रेंडरिंग, निदान, मीडिया रूटिंग या plugin-स्वामित्व वाले मेटाडेटा के लिए
संरचित details शामिल हो सकता है। details को रनटाइम मेटाडेटा मानें,
प्रॉम्प्ट सामग्री नहीं:
- OpenClaw प्रदाता रीप्ले और Compaction इनपुट से पहले
toolResult.detailsहटा देता है, ताकि मेटाडेटा मॉडल संदर्भ न बने। - स्थायी सत्र प्रविष्टियाँ केवल सीमित
detailsरखती हैं। बहुत बड़े विवरणों को एक संक्षिप्त सारांश औरpersistedDetailsTruncated: trueसे बदल दिया जाता है। tool_result_persistऔरbefore_message_writeअंतिम स्थायित्व सीमा से पहले चलते हैं। लौटाया गयाdetailsछोटा रखें और प्रॉम्प्ट-संबंधित टेक्स्ट केवलdetailsमें रखने से बचें; मॉडल को दिखाई देने वाला टूल आउटपुटcontentमें रखें।
प्रॉम्प्ट और मॉडल हुक
नए plugins के लिए चरण-विशिष्ट हुक का उपयोग करें:
before_model_resolve: केवल वर्तमान प्रॉम्प्ट और अटैचमेंट मेटाडेटा प्राप्त करता है।providerOverrideयाmodelOverrideलौटाएँ।agent_turn_prepare: वर्तमान प्रॉम्प्ट, तैयार सत्र संदेश और इस सत्र के लिए निकाले गए exactly-once कतारबद्ध इंजेक्शन प्राप्त करता है।prependContextयाappendContextलौटाएँ।before_prompt_build: वर्तमान प्रॉम्प्ट और सत्र संदेश प्राप्त करता है।prependContext,appendContext,systemPrompt,prependSystemContext, याappendSystemContextलौटाएँ।heartbeat_prompt_contribution: केवल Heartbeat टर्न के लिए चलता है औरprependContextयाappendContextलौटाता है। यह ऐसे पृष्ठभूमि मॉनिटरों के लिए है जिन्हें उपयोगकर्ता द्वारा आरंभ किए गए टर्न बदले बिना वर्तमान स्थिति का सारांश देना होता है।
before_agent_run प्रॉम्प्ट निर्माण के बाद और किसी भी मॉडल इनपुट से पहले चलता है,
जिसमें प्रॉम्प्ट-स्थानीय छवि लोडिंग और llm_input अवलोकन शामिल हैं। इसे
वर्तमान उपयोगकर्ता इनपुट prompt के रूप में, साथ ही messages में लोड किया गया सत्र इतिहास
और सक्रिय सिस्टम प्रॉम्प्ट प्राप्त होते हैं। मॉडल द्वारा प्रॉम्प्ट पढ़े जाने से पहले रन रोकने के लिए
{ outcome: "block", reason, message? } लौटाएँ। reason आंतरिक है;
message उपयोगकर्ता को दिखाई देने वाला प्रतिस्थापन है। केवल pass और block परिणाम
समर्थित हैं; असमर्थित निर्णय संरचनाएँ fail closed होती हैं।
जब कोई रन अवरुद्ध होता है, तो OpenClaw केवल प्रतिस्थापन टेक्स्ट को
message.content में तथा गैर-संवेदनशील ब्लॉक मेटाडेटा, जैसे अवरोधक
plugin id और टाइमस्टैम्प, संग्रहीत करता है। मूल उपयोगकर्ता टेक्स्ट ट्रांसक्रिप्ट
या भविष्य के संदर्भ में नहीं रखा जाता। आंतरिक ब्लॉक कारणों को संवेदनशील माना जाता है और
ट्रांसक्रिप्ट, इतिहास, प्रसारण, लॉग तथा निदान पेलोड से बाहर रखा जाता है।
अवलोकनीयता के लिए अवरोधक id, परिणाम,
टाइमस्टैम्प या सुरक्षित श्रेणी जैसे स्वच्छ किए गए फ़ील्ड का उपयोग करना चाहिए।
agent_end सहित एजेंट-टर्न हुक में event.runId शामिल होता है, जब OpenClaw
सक्रिय रन की पहचान कर सकता है; वही मान ctx.runId पर भी होता है। Cron-संचालित
रन एजेंट-टर्न संदर्भ पर ctx.jobId (मूल Cron जॉब id) भी उपलब्ध कराते हैं,
ताकि हुक मेट्रिक्स, साइड इफ़ेक्ट या स्थिति को किसी विशिष्ट
निर्धारित जॉब तक सीमित कर सकें। ctx.jobId, before_tool_call टूल संदर्भ का भाग नहीं है।
चैनल से आरंभ हुए रन के लिए, ctx.channel और ctx.messageProvider
discord या telegram जैसी प्रदाता सतह की पहचान करते हैं, जबकि ctx.channelId
वार्तालाप लक्ष्य पहचानकर्ता है, जब OpenClaw सत्र कुंजी या डिलीवरी मेटाडेटा से इसे
प्राप्त कर सकता है।
जब प्रेषक की पहचान उपलब्ध होती है, तो एजेंट हुक संदर्भों में ये भी शामिल होते हैं:
ctx.senderId- चैनल-सीमित प्रेषक ID (जैसे Feishuopen_id, Discord उपयोगकर्ता ID)। जब रन ज्ञात प्रेषक मेटाडेटा वाले उपयोगकर्ता संदेश से आरंभ होता है, तब भरा जाता है।ctx.chatId- परिवहन-मूल वार्तालाप पहचानकर्ता (जैसे Feishuchat_id, Telegramchat_id)। जब मूल चैनल मूल वार्तालाप ID प्रदान करता है, तब भरा जाता है।ctx.channelContext.sender.id-ctx.senderIdवाला वही प्रेषक ID, चैनल-स्वामित्व वाली उस ऑब्जेक्ट के अंतर्गत जिसे plugins चैनल-विशिष्ट फ़ील्ड से विस्तारित कर सकते हैं।ctx.channelContext.chat.id-ctx.chatIdवाली वही वार्तालाप ID, चैनल-स्वामित्व वाली उस ऑब्जेक्ट के अंतर्गत जिसे plugins चैनल-विशिष्ट फ़ील्ड से विस्तारित कर सकते हैं।
कोर केवल नेस्टेड id फ़ील्ड परिभाषित करता है। इनबाउंड सहायक के माध्यम से अधिक समृद्ध
प्रेषक या चैट मेटाडेटा भेजने वाले चैनल plugins
openclaw/plugin-sdk/channel-inbound से PluginHookChannelSenderContext या PluginHookChannelChatContext को
विस्तारित कर सकते हैं:
declare module "openclaw/plugin-sdk/channel-inbound" { interface PluginHookChannelSenderContext { unionId?: string; userId?: string; }}चैनल plugins इनबाउंड SDK सहायक के माध्यम से वे फ़ील्ड भेजते हैं:
buildChannelInboundEventContext({ // ... channelContext: { sender: { id: senderOpenId, unionId, userId }, chat: { id: chatId }, },});ये फ़ील्ड वैकल्पिक हैं और सिस्टम से आरंभ हुए रन (Heartbeat, Cron, exec-event) में अनुपस्थित रहते हैं।
ctx.senderExternalId पुराने plugins के लिए बहिष्कृत स्रोत-संगतता फ़ील्ड के रूप में
बना हुआ है। कोर इसे नहीं भरता; नए चैनल-विशिष्ट प्रेषक
पहचान मान मॉड्यूल विस्तार के माध्यम से ctx.channelContext.sender के अंतर्गत
रहने चाहिए।
agent_end एक अवलोकन हुक है। Gateway और स्थायी हार्नेस पथ
टर्न के बाद इसे fire-and-forget रूप में चलाते हैं, जबकि अल्पकालिक one-shot CLI पथ
प्रोसेस क्लीनअप से पहले हुक प्रॉमिस की प्रतीक्षा करते हैं, ताकि विश्वसनीय plugins
टर्मिनल अवलोकनीयता फ़्लश कर सकें या स्थिति कैप्चर कर सकें। हुक रनर 30 सेकंड
का टाइमआउट लागू करता है, ताकि अटका हुआ plugin या एम्बेडिंग एंडपॉइंट हुक प्रॉमिस को
हमेशा के लिए लंबित न छोड़ सके। टाइमआउट लॉग किया जाता है और OpenClaw जारी रहता है; यह
plugin-स्वामित्व वाले नेटवर्क कार्य को रद्द नहीं करता, जब तक plugin अपने स्वयं के abort
सिग्नल का भी उपयोग न करे।
ऐसी प्रदाता-कॉल टेलीमेट्री के लिए model_call_started और model_call_ended का उपयोग करें,
जिसे रॉ प्रॉम्प्ट, इतिहास, प्रतिक्रियाएँ, हेडर, अनुरोध
बॉडी या प्रदाता अनुरोध ID प्राप्त नहीं होने चाहिए। इन हुक में स्थिर मेटाडेटा, जैसे
runId, callId, provider, model, वैकल्पिक api/transport, टर्मिनल
durationMs/outcome, और upstreamRequestIdHash शामिल होते हैं, जब OpenClaw एक
सीमित प्रदाता request-id हैश प्राप्त कर सकता है। जब रनटाइम ने
context-window मेटाडेटा रिज़ॉल्व कर लिया हो, तो हुक इवेंट और संदर्भ में
contextTokenBudget, यानी मॉडल/config/एजेंट
सीमाओं के बाद प्रभावी टोकन बजट, और कम सीमा लागू होने पर
contextWindowSource तथा contextWindowReferenceTokens भी शामिल होते हैं।
before_agent_finalize केवल तब चलता है, जब कोई हार्नेस स्वाभाविक
अंतिम सहायक उत्तर स्वीकार करने वाला हो। यह /stop रद्दीकरण पथ नहीं है और
उपयोगकर्ता द्वारा टर्न रद्द करने पर नहीं चलता। अंतिम रूप देने से पहले हार्नेस से
एक और मॉडल पास माँगने के लिए { action: "revise", reason }, अंतिम रूप देना बाध्य करने के लिए { action: "finalize", reason? } लौटाएँ, या जारी रखने के लिए परिणाम छोड़ दें।
हैंडलरों का डिफ़ॉल्ट बजट 15s है; टाइमआउट होने पर OpenClaw विफलता लॉग करता है और
मूल अंतिम उत्तर के साथ जारी रहता है।
Codex के मूल Stop हुक इस हुक में OpenClaw
before_agent_finalize निर्णयों के रूप में रिले किए जाते हैं।
action: "revise" लौटाते समय, plugins अतिरिक्त मॉडल पास को सीमित और रीप्ले-सुरक्षित बनाने के लिए
retry मेटाडेटा शामिल कर सकते हैं:
type BeforeAgentFinalizeRetry = { instruction: string; idempotencyKey?: string; maxAttempts?: number;};instruction को हार्नेस को भेजे गए संशोधन कारण में जोड़ा जाता है।
idempotencyKey होस्ट को समतुल्य अंतिमकरण निर्णयों में समान plugin अनुरोध के लिए
पुनः प्रयास गिनने देता है, और maxAttempts यह सीमा तय करता है कि स्वाभाविक अंतिम उत्तर के साथ
जारी रहने से पहले होस्ट कितने अतिरिक्त पास की अनुमति देगा।
जिन गैर-बंडल plugins को रॉ वार्तालाप हुक (before_model_resolve,
before_agent_reply, llm_input, llm_output, before_agent_finalize,
agent_end, या before_agent_run) चाहिए, उन्हें यह सेट करना होगा:
{ "plugins": { "entries": { "my-plugin": { "hooks": { "allowConversationAccess": true } } } }}प्रॉम्प्ट बदलने वाले हुक और स्थायी अगले-टर्न इंजेक्शन को प्रत्येक
plugin के लिए plugins.entries.<id>.hooks.allowPromptInjection=false से अक्षम किया जा सकता है।
सत्र विस्तार और अगले-टर्न इंजेक्शन
वर्कफ़्लो plugins api.session.state.registerSessionExtension(...) के साथ छोटी JSON-संगत सत्र स्थिति को स्थायी कर सकते हैं
और Gateway sessions.pluginPatch विधि के माध्यम से उसे अपडेट कर सकते हैं।
सत्र पंक्तियाँ पंजीकृत विस्तार स्थिति को pluginExtensions के माध्यम से प्रोजेक्ट करती हैं,
जिससे Control UI और अन्य क्लाइंट plugin की आंतरिक जानकारी जाने बिना
plugin-स्वामित्व वाली स्थिति रेंडर कर सकते हैं।
api.registerSessionExtension(...) अब भी काम करता है, लेकिन
api.session.state नेमस्पेस के पक्ष में बहिष्कृत है।
जब किसी plugin को अगले मॉडल टर्न तक स्थायी संदर्भ ठीक एक बार पहुँचाना हो, तब
api.session.workflow.enqueueNextTurnInjection(...) का उपयोग करें
(शीर्ष-स्तरीय api.enqueueNextTurnInjection(...) समान व्यवहार वाला एक बहिष्कृत उपनाम है)।
OpenClaw प्रॉम्प्ट हुक से पहले कतारबद्ध इंजेक्शन निकालता है, समाप्त हो चुके
इंजेक्शन हटाता है और प्रत्येक plugin के लिए idempotencyKey के अनुसार डुप्लिकेट हटाता है। यह
अनुमोदन पुनरारंभ, नीति सारांश, पृष्ठभूमि मॉनिटर
अंतर और कमांड निरंतरताओं के लिए सही सीम है, जिन्हें अगले टर्न पर मॉडल को दिखाई देना चाहिए,
लेकिन स्थायी सिस्टम प्रॉम्प्ट टेक्स्ट नहीं बनना चाहिए।
क्लीनअप अर्थविज्ञान अनुबंध का भाग हैं। सत्र विस्तार क्लीनअप और
रनटाइम जीवनचक्र क्लीनअप कॉलबैक को reset, delete, disable, या
restart प्राप्त होते हैं। होस्ट reset/delete/disable के लिए स्वामी plugin की स्थायी सत्र विस्तार
स्थिति और लंबित अगले-टर्न इंजेक्शन हटा देता है; restart
स्थायी सत्र स्थिति बनाए रखता है, जबकि क्लीनअप कॉलबैक plugins को पुरानी
रनटाइम पीढ़ी के शेड्यूलर जॉब, रन संदर्भ और अन्य आउट-ऑफ़-बैंड संसाधन
रिलीज़ करने देते हैं।
संदेश हुक
चैनल-स्तरीय रूटिंग और डिलीवरी नीति के लिए संदेश हुक का उपयोग करें:
message_received: इनबाउंड सामग्री, प्रेषक,threadId,messageId,senderId, वैकल्पिक रन/सत्र सहसंबंध और मेटाडेटा का अवलोकन करता है।message_sending:contentको फिर से लिखें या{ cancel: true }लौटाएँ।reply_payload_sending: सामान्यीकृतReplyPayloadऑब्जेक्ट (presentation,delivery, मीडिया संदर्भ और टेक्स्ट सहित) को फिर से लिखें या{ cancel: true }लौटाएँ।message_sent: अंतिम सफलता या विफलता का अवलोकन करता है।
केवल-ऑडियो TTS उत्तरों के लिए, content में छिपी हुई बोली गई
ट्रांसक्रिप्ट शामिल हो सकती है, भले ही चैनल पेलोड में कोई दृश्यमान टेक्स्ट/कैप्शन न हो।
उस content को फिर से लिखने से केवल हुक को दिखाई देने वाली ट्रांसक्रिप्ट अपडेट होती है; इसे
मीडिया कैप्शन के रूप में रेंडर नहीं किया जाता।
reply_payload_sending इवेंट में usageState, यानी सर्वोत्तम-प्रयास वाला लाइव
प्रति-टर्न मॉडल/उपयोग/संदर्भ स्नैपशॉट शामिल हो सकता है। स्थायी डिलीवरी, पुनर्प्राप्त रीप्ले और
सटीक रन सहसंबंध के बिना उत्तरों में यह शामिल नहीं होता।
उपलब्ध होने पर संदेश हुक संदर्भ स्थिर सहसंबंध फ़ील्ड उजागर करते हैं:
ctx.sessionKey, ctx.runId, ctx.messageId, ctx.senderId, ctx.trace,
ctx.traceId, ctx.spanId, ctx.parentSpanId, और ctx.callDepth। इनबाउंड
और before_dispatch संदर्भ उत्तर मेटाडेटा भी उजागर करते हैं, जब चैनल के पास
दृश्यता-फ़िल्टर किया हुआ उद्धृत संदेश डेटा हो: replyToId, replyToIdFull,
replyToBody, replyToSender, और replyToIsQuote। विरासती मेटाडेटा पढ़ने से पहले
इन प्रथम-श्रेणी फ़ील्ड को प्राथमिकता दें।
चैनल-विशिष्ट मेटाडेटा का उपयोग करने से पहले टाइप किए गए threadId और replyToId
फ़ील्ड को प्राथमिकता दें।
निर्णय नियम:
message_sendingके साथcancel: trueअंतिम है।message_sendingके साथcancel: falseको कोई निर्णय नहीं माना जाता है।- पुनर्लिखित
contentनिम्न-प्राथमिकता वाले हुक तक जारी रहता है, जब तक कि कोई बाद का हुक डिलीवरी रद्द न कर दे। reply_payload_sendingपेलोड सामान्यीकरण के बाद और चैनल डिलीवरी से पहले चलता है, जिसमें मूल चैनल पर वापस रूट किए गए उत्तर भी शामिल हैं। हैंडलर क्रमिक रूप से चलते हैं और प्रत्येक हैंडलर उच्च-प्राथमिकता वाले हैंडलरों द्वारा निर्मित नवीनतम पेलोड देखता है।reply_payload_sendingपेलोडtrustedLocalMediaजैसे रनटाइम विश्वास चिह्न उजागर नहीं करते; प्लगइन पेलोड का आकार संपादित कर सकते हैं, लेकिन स्थानीय मीडिया को विश्वास प्रदान नहीं कर सकते।message_sendingरद्दीकरण के साथcancelReasonऔर सीमितmetadataलौटा सकता है। नए संदेश जीवनचक्र API इसे कारणcancelled_by_message_sending_hookवाले दबाए गए डिलीवरी परिणाम के रूप में उजागर करते हैं; विरासती प्रत्यक्ष डिलीवरी संगतता के लिए खाली परिणाम सरणी लौटाती रहती है।message_sentकेवल अवलोकन के लिए है। हैंडलर विफलताएँ लॉग की जाती हैं और डिलीवरी परिणाम नहीं बदलतीं।
इंस्टॉल हुक
ऑपरेटर-स्वामित्व वाले अनुमति/अवरोध निर्णयों के लिए security.installPolicy का उपयोग करें। वह
नीति OpenClaw कॉन्फ़िगरेशन से चलती है, CLI इंस्टॉल और अपडेट पथों को कवर करती है, और
सक्षम लेकिन अनुपलब्ध होने पर सुरक्षित रूप से अवरोधित करती है।
before_install एक प्लगइन-रनटाइम जीवनचक्र हुक है। यह केवल उस OpenClaw प्रक्रिया में
security.installPolicy के बाद चलता है जहाँ प्लगइन हुक पहले ही
लोड किए जा चुके हों, जैसे Gateway-समर्थित इंस्टॉल प्रवाह। यह
प्लगइन-स्वामित्व वाले अवलोकनों, चेतावनियों और संगतता जाँचों के लिए उपयोगी है, लेकिन इंस्टॉल के लिए
प्राथमिक एंटरप्राइज़ या होस्ट सुरक्षा सीमा नहीं है। संगतता के लिए
builtinScan फ़ील्ड इवेंट पेलोड में बना रहता है, लेकिन
OpenClaw अब अंतर्निहित इंस्टॉल-समय खतरनाक-कोड अवरोधन नहीं चलाता, इसलिए यह
एक खाली ok परिणाम है। उस प्रक्रिया में इंस्टॉल रोकने के लिए अतिरिक्त निष्कर्ष या
{ block: true, blockReason } लौटाएँ।
block: true अंतिम है। block: false को कोई निर्णय नहीं माना जाता है। हैंडलर
विफलताएँ सुरक्षित रूप से इंस्टॉल को अवरोधित करती हैं।
Gateway जीवनचक्र
सामान्य प्लगइन सेवाएँ शुरू करने के लिए gateway_start और
लंबे समय तक चलने वाले संसाधनों की सफ़ाई के लिए gateway_stop का उपयोग करें। जब
gateway_start चलता है, तब भी Cron शेड्यूलर लोड हो रहा हो सकता है, इसलिए किसी बाहरी
Cron प्रक्षेपण के आधारभूत संकेत के रूप में इसका उपयोग न करें।
प्लगइन-स्वामित्व वाली रनटाइम सेवाओं के लिए आंतरिक gateway:startup हुक पर
निर्भर न रहें।
cron_reconciled Gateway Cron शेड्यूलर और उसके निकास-समय
निगरानीकर्ताओं द्वारा अपनी टिकाऊ स्थिति का सामंजस्य करने के बाद सक्रिय होता है। यह प्रारंभिक
स्टार्टअप और कॉन्फ़िगरेशन पुनः लोड के दौरान शेड्यूलर प्रतिस्थापन, दोनों के लिए सक्रिय होता है। इवेंट
reason (startup या reload) और प्रभावी enabled स्थिति की रिपोर्ट करता है। अक्षम
Cron भी enabled: false के साथ उत्सर्जित होता है, जिससे कोई बाहरी प्रक्षेपण
पुराने वेक साफ़ कर सकता है। सामंजस्य पूरा करने वाले सटीक शेड्यूलर इंस्टेंस के लिए
ctx.getCron?.() का उपयोग करें; बाद का पुनः लोड उस कॉलबैक को पुनर्लक्षित नहीं करता।
ctx.abortSignal उसी शेड्यूलर स्नैपशॉट का स्वामी है। जैसे ही कोई नया शेड्यूलर
सक्रिय होता है या शटडाउन शुरू होता है, Gateway इसे निरस्त कर देता है। इसे प्रत्येक
टिकाऊ दुष्प्रभाव तक पहुँचाएँ और इसके निरस्त होने के बाद स्नैपशॉट स्वीकार न करें।
यह शेड्यूलर जीवनचक्र संकेत है, प्लगइन-सक्रियण संकेत नहीं: केवल-प्लगइन
हॉट रीलोड इसे दोबारा नहीं चलाता। नया सक्षम हुआ उपभोक्ता अपना पहला आधारभूत मान
अगले शेड्यूलर प्रतिस्थापन या Gateway प्रारंभ पर प्राप्त करता है।
अन्य अवलोकन हुक की तरह, gateway_start और cron_reconciled कॉलबैक
एक-दूसरे से ओवरलैप हो सकते हैं। यदि दोनों हैंडलर प्लगइन आरंभीकरण साझा करते हैं, तो कॉलबैक क्रम पर
निर्भर रहने के बजाय प्लगइन-स्थानीय तत्परता प्रॉमिस से उनका समन्वय करें।
cron_changed टाइप किए गए इवेंट पेलोड के साथ Gateway-स्वामित्व वाले Cron जीवनचक्र इवेंट के लिए सक्रिय होता है,
जिसमें added, updated, removed, started, finished,
और scheduled कारण शामिल हैं। इवेंट एक PluginHookGatewayCronJob
स्नैपशॉट (उपस्थित होने पर state.nextRunAtMs, state.lastRunStatus, और
state.lastError सहित) तथा not-requested | delivered | not-delivered | unknown
का एक PluginHookGatewayCronDeliveryStatus वहन करता है। हटाए गए इवेंट
कमिट-पश्चात होते हैं: वे केवल टिकाऊ विलोपन सफल होने के बाद सक्रिय होते हैं और फिर भी
हटाए गए जॉब का स्नैपशॉट रखते हैं, ताकि बाहरी शेड्यूलर स्थिति का सामंजस्य कर सकें।
scheduled इवेंट कमिट-पश्चात है: यह केवल तब सक्रिय होता है जब कोई सफल टिकाऊ
लेखन मौजूदा जॉब के प्रभावी nextRunAtMs को बदलता है, और उस जॉब के
स्पष्ट added, updated, या removed जीवनचक्र इवेंट को छोड़ देता है। शीर्ष-स्तरीय
event.nextRunAtMs कमिट किया गया अगला वेक है; इसके अनुपस्थित होने पर जॉब का
कोई अगला वेक नहीं है। इन इवेंट को क्रमबद्ध डेल्टा लॉग नहीं, बल्कि सामंजस्य संकेत
मानें। इन्हें एकत्र किए जा सकने वाले संकेतों की तरह उपयोग करके cron_reconciled द्वारा अंतिम बार कैप्चर किए गए
शेड्यूलर को दोबारा पढ़ें; cron_changed संदर्भ से शेड्यूलर न अपनाएँ।
देयता जाँच और निष्पादन के लिए OpenClaw को सत्य का स्रोत बनाए रखें।
सुरक्षित बाहरी Cron प्रक्षेपण
Cron इवेंट डेल्टा अग्रेषित करने के बजाय एक पूर्ण वेक स्नैपशॉट प्रक्षेपित करें।
बाहरी अडैप्टर का replaceAll ऑपरेशन परमाण्विक और समशक्तिशाली होना चाहिए, और इसे
केवल होस्ट द्वारा स्नैपशॉट को टिकाऊ रूप से स्वीकार करने के बाद ही पूर्ण होना चाहिए। इसे
प्रदान किए गए निरस्तीकरण संकेत का भी पालन करना चाहिए: यदि टिकाऊ स्वीकृति से पहले संकेत निरस्त हो जाता है,
तो अडैप्टर को वह स्नैपशॉट स्वीकार नहीं करना चाहिए।
यह पैटर्न नवीनतम स्थिति वाला केवल एक वर्कर सक्रिय रखता है। केवल cron_reconciled
शेड्यूलर इंस्टेंस अपनाता है; cron_changed केवल उस वर्कर से
प्रामाणिक इंस्टेंस को दोबारा पढ़ने का अनुरोध करता है, इसलिए देर से आया संकेत किसी पुराने शेड्यूलर को पुनर्स्थापित नहीं कर सकता।
कोई नया संशोधन सक्रिय होस्ट प्रयास को पुराना स्नैपशॉट स्वीकार करने से पहले निरस्त कर देता है।
type ExternalWake = { jobId: string; runAtMs: number }; type ExternalWakeHost = { replaceAll(wakes: readonly ExternalWake[], options: { signal: AbortSignal }): Promise<void>; close(): Promise<void>;}; type CronReader = { list(options: { includeDisabled: true }): Promise< Array<{ id: string; enabled?: boolean; state?: { nextRunAtMs?: number }; }> >;}; export function registerCronProjection(api: OpenClawPluginApi, host: ExternalWakeHost) { const lifecycle = new AbortController(); let cron: CronReader | undefined; let enabled = false; let hasBaseline = false; let reconciliationSignal: AbortSignal | undefined; let requestedRevision = 0; let appliedRevision = 0; let worker = Promise.resolve(); let activeAttempt: AbortController | undefined; const projectLatest = async () => { let retryMs = 1_000; while (!lifecycle.signal.aborted && appliedRevision < requestedRevision) { const ownerSignal = reconciliationSignal; if (!ownerSignal || ownerSignal.aborted) { return; } const targetRevision = requestedRevision; const attempt = new AbortController(); const signal = AbortSignal.any([lifecycle.signal, ownerSignal, attempt.signal]); activeAttempt = attempt; try { const jobs = enabled && cron ? await cron.list({ includeDisabled: true }) : []; if (signal.aborted || targetRevision !== requestedRevision) { continue; } const wakes = jobs .flatMap((job): ExternalWake[] => { const runAtMs = job.enabled === false ? undefined : job.state?.nextRunAtMs; return runAtMs === undefined ? [] : [{ jobId: job.id, runAtMs }]; }) .sort((a, b) => a.runAtMs - b.runAtMs || a.jobId.localeCompare(b.jobId)); await host.replaceAll(wakes, { signal }); if (signal.aborted || targetRevision !== requestedRevision) { continue; } appliedRevision = targetRevision; retryMs = 1_000; } catch { if (lifecycle.signal.aborted || ownerSignal.aborted) { return; } if (attempt.signal.aborted) { continue; } api.logger.warn(`बाहरी Cron प्रक्षेपण विफल हुआ; ${retryMs}ms में पुनः प्रयास किया जा रहा है`); try { await sleep(retryMs, undefined, { signal }); } catch { if (lifecycle.signal.aborted) { return; } if (attempt.signal.aborted) { continue; } } retryMs = Math.min(retryMs * 2, 30_000); } finally { if (activeAttempt === attempt) { activeAttempt = undefined; } } } }; const requestProjection = () => { const targetRevision = ++requestedRevision; activeAttempt?.abort(); worker = worker.then(async () => { if (!lifecycle.signal.aborted && appliedRevision < targetRevision) { await projectLatest(); } }); return worker; }; api.on("cron_reconciled", (event, ctx) => { const reconciledCron = ctx.getCron?.(); if (event.enabled && !reconciledCron) { api.logger.warn("Cron सामंजस्य ने कोई शेड्यूलर उजागर नहीं किया"); return; } cron = reconciledCron; enabled = event.enabled; hasBaseline = true; reconciliationSignal = ctx.abortSignal; return requestProjection(); }); api.on("cron_changed", () => { if (hasBaseline) { return requestProjection(); } }); api.on("gateway_stop", async () => { lifecycle.abort(); await worker; await host.close(); });}जब cron_reconciled, enabled: false की रिपोर्ट करता है, तो वही पथ
replaceAll([]) को कॉल करता है और पुराने बाहरी वेक साफ़ करता है। इस उदाहरण में पुनः प्रयास/बैकऑफ़
प्रक्रिया-स्थानीय है और रनटाइम अडैप्टर विफलताओं को अस्थायी मानता है; पंजीकरण से पहले
ऐसे कॉन्फ़िगरेशन को सत्यापित करें जिन पर पुनः प्रयास नहीं किया जा सकता। OpenClaw प्लगइन हुक प्रभावों के लिए
आउटबॉक्स प्रदान नहीं करता। यदि टिकाऊ स्वीकृति से पहले प्रक्रिया समाप्त हो जाती है,
तो अगला Gateway प्रारंभ एक नया प्रामाणिक cron_reconciled स्नैपशॉट उत्सर्जित करता है।
gateway_stop सक्रिय होस्ट कार्य को निरस्त करता है, वर्कर के स्थिर होने की प्रतीक्षा करता है, फिर
अडैप्टर बंद करता है।
आगामी अप्रचलन
हुक से सटे कुछ सतह अप्रचलित हैं, लेकिन अभी भी समर्थित हैं। अगले प्रमुख रिलीज़ से पहले माइग्रेट करें:
- प्लेनटेक्स्ट चैनल एनवेलप
inbound_claimऔरmessage_receivedहैंडलर में। समतल एनवेलप टेक्स्ट को पार्स करने के बजायBodyForAgentऔर संरचित उपयोगकर्ता-संदर्भ ब्लॉक पढ़ें। देखें प्लेनटेक्स्ट चैनल एनवेलप → BodyForAgent। subagent_spawningपुराने plugins के साथ संगतता के लिए बना हुआ है, लेकिन नए plugins को इससे थ्रेड रूटिंग वापस नहीं करनी चाहिए। Core,subagent_spawnedके सक्रिय होने से पहले चैनल सेशन-बाइंडिंग अडैप्टर के माध्यम सेthread: trueसबएजेंट बाइंडिंग तैयार करता है।deactivate2026-08-16 के बाद तक अप्रचलित क्लीनअप संगतता उपनाम के रूप में बना हुआ है। नए plugins कोgateway_stopका उपयोग करना चाहिए।before_tool_callमेंonResolutionअब मुक्त-रूपstringके बजाय टाइप किए गएPluginApprovalResolutionयूनियन (allow-once/allow-always/deny/timeout/cancelled) का उपयोग करता है।api.registerSessionExtension/api.enqueueNextTurnInjectionशीर्ष-स्तरीय संगतता उपनामों के रूप में बने हुए हैं। नए plugins कोapi.session.state.registerSessionExtension(...)औरapi.session.workflow.enqueueNextTurnInjection(...)का उपयोग करना चाहिए।
पूरी सूची—मेमोरी क्षमता पंजीकरण, प्रदाता थिंकिंग
प्रोफ़ाइल, बाहरी प्रमाणीकरण प्रदाता, प्रदाता खोज प्रकार, टास्क रनटाइम
एक्सेसर और command-auth → command-status नाम-परिवर्तन—के लिए
Plugin SDK माइग्रेशन → सक्रिय अप्रचलन देखें।
संबंधित
- Plugin SDK माइग्रेशन - सक्रिय अप्रचलन और हटाने की समयरेखा
- plugins बनाना
- Plugin SDK का अवलोकन
- Plugin प्रवेश बिंदु
- आंतरिक हुक
- Plugin आर्किटेक्चर के आंतरिक भाग