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: "वेब खोज चलाएँ", description: `खोज क्वेरी की अनुमति दें: ${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 रनटाइम से स्टेज की गई skill या Plugin इंस्टॉल सामग्री का निरीक्षण करना |
चैनल पेयरिंग अनुरोध
जब किसी अपेयर्ड DM प्रेषक द्वारा लंबित पेयरिंग अनुरोध बनाए जाने के बाद किसी Plugin को ऑपरेटर को सूचित करना हो या
ऑडिट रिकॉर्ड लिखना हो, तब channel_pairing_requested का उपयोग करें।
अनुरोध बनाए जाने पर हुक डिस्पैच होता है; धीमे या विफल हुक हैंडलर के कारण
पेयरिंग उत्तर की चैनल डिलीवरी में विलंब नहीं होता।
api.on("channel_pairing_requested", async (event) => { await notifyOperator({ text: `नया ${event.channel} पेयरिंग अनुरोध ${event.senderId} से: ${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 कमांड चलने से पहले प्लगइनों को exec
टूल आह्वानों में परिवेश चर जोड़ने देता है। इसे ये प्राप्त होते हैं:
event.sessionKeyevent.toolName, जो वर्तमान में हमेशा"exec"होता हैevent.host, जो"gateway","sandbox", या"node"में से एक होता है- संदर्भ फ़ील्ड, जैसे
ctx.agentId,ctx.sessionKey,ctx.messageProvider, औरctx.channelId
Exec परिवेश में मर्ज करने के लिए एक Record<string, string> लौटाएँ। हैंडलर
प्राथमिकता क्रम में चलते हैं; एक ही कुंजी के लिए बाद के परिणाम पहले के
परिणामों को ओवरराइड करते हैं।
मर्ज करने से पहले हुक आउटपुट को होस्ट की Exec परिवेश कुंजी नीति के माध्यम से
फ़िल्टर किया जाता है। PATH को हमेशा हटा दिया जाता है (कमांड रिज़ॉल्यूशन और सुरक्षित-बाइन जाँचें
इस पर निर्भर करती हैं)। अमान्य कुंजियाँ और खतरनाक होस्ट ओवरराइड कुंजियाँ, जैसे LD_*,
DYLD_*, NODE_OPTIONS, प्रॉक्सी चर (HTTP_PROXY, HTTPS_PROXY,
ALL_PROXY, NO_PROXY), और TLS ओवरराइड चर (NODE_TLS_REJECT_UNAUTHORIZED,
SSL_CERT_FILE, तथा इनके समान) हटा दिए जाते हैं। फ़िल्टर किया गया Plugin परिवेश
Gateway अनुमोदन/ऑडिट मेटाडेटा में शामिल किया जाता है और Node-होस्ट निष्पादन
अनुरोधों को अग्रेषित किया जाता है।
टूल परिणाम स्थायित्व
टूल परिणामों में UI रेंडरिंग, निदान, मीडिया रूटिंग, या Plugin-स्वामित्व वाले मेटाडेटा के लिए
संरचित details शामिल हो सकता है। details को रनटाइम मेटाडेटा मानें,
प्रॉम्प्ट सामग्री नहीं:
- OpenClaw प्रदाता रीप्ले और Compaction
इनपुट से पहले
toolResult.detailsहटा देता है, ताकि मेटाडेटा मॉडल संदर्भ न बन जाए। - स्थायी सत्र प्रविष्टियाँ केवल सीमित
detailsरखती हैं। अत्यधिक बड़े विवरणों को एक संक्षिप्त सारांश औरpersistedDetailsTruncated: trueसे बदल दिया जाता है। tool_result_persistऔरbefore_message_writeअंतिम स्थायित्व सीमा से पहले चलते हैं। लौटाए गएdetailsको छोटा रखें और प्रॉम्प्ट-संबंधित पाठ को केवलdetailsमें रखने से बचें; मॉडल को दिखाई देने वाला टूल आउटपुटcontentमें रखें।
प्रॉम्प्ट और मॉडल हुक
नए प्लगइनों के लिए चरण-विशिष्ट हुक का उपयोग करें:
before_model_resolve: केवल वर्तमान प्रॉम्प्ट और अटैचमेंट मेटाडेटा प्राप्त करता है।providerOverrideयाmodelOverrideलौटाएँ।agent_turn_prepare: वर्तमान प्रॉम्प्ट, तैयार किए गए सत्र संदेश, और इस सत्र के लिए निकाले गए ठीक-एक-बार कतारबद्ध इंजेक्शन प्राप्त करता है।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 परिणाम
समर्थित हैं; असमर्थित निर्णय आकार सुरक्षित रूप से विफल होते हैं।
जब कोई रन अवरुद्ध किया जाता है, तो OpenClaw केवल प्रतिस्थापन पाठ को
message.content में और गैर-संवेदनशील अवरोध मेटाडेटा, जैसे अवरोधक
Plugin आईडी और टाइमस्टैम्प, संग्रहीत करता है। मूल उपयोगकर्ता पाठ ट्रांस्क्रिप्ट
या भावी संदर्भ में नहीं रखा जाता। आंतरिक अवरोध कारणों को संवेदनशील माना जाता है और
ट्रांस्क्रिप्ट, इतिहास, प्रसारण, लॉग तथा निदान पेलोड से बाहर रखा जाता है।
अवलोकनीयता के लिए अवरोधक आईडी, परिणाम,
टाइमस्टैम्प, या सुरक्षित श्रेणी जैसे स्वच्छ फ़ील्ड का उपयोग करना चाहिए।
agent_end सहित एजेंट-टर्न हुक में event.runId शामिल होता है, जब OpenClaw
सक्रिय रन की पहचान कर सकता है; यही मान ctx.runId पर भी होता है। Cron-संचालित
रन एजेंट-टर्न संदर्भ पर ctx.jobId (मूल Cron जॉब आईडी) भी उजागर करते हैं,
ताकि हुक मेट्रिक्स, दुष्प्रभाव, या स्थिति को किसी विशिष्ट
निर्धारित जॉब तक सीमित कर सकें। ctx.jobId, before_tool_call टूल संदर्भ का भाग नहीं है।
चैनल से उत्पन्न रन के लिए, ctx.channel और ctx.messageProvider,
discord या telegram जैसी प्रदाता सतह की पहचान करते हैं, जबकि ctx.channelId
वार्तालाप लक्ष्य पहचानकर्ता होता है, जब OpenClaw उसे
सत्र कुंजी या डिलीवरी मेटाडेटा से प्राप्त कर सकता है।
जब प्रेषक की पहचान उपलब्ध होती है, तो एजेंट हुक संदर्भों में ये भी शामिल होते हैं:
ctx.senderId- चैनल-सीमित प्रेषक आईडी (उदा. Feishuopen_id, Discord उपयोगकर्ता आईडी)। तब भरा जाता है जब रन ज्ञात प्रेषक मेटाडेटा वाले उपयोगकर्ता संदेश से उत्पन्न होता है।ctx.chatId- ट्रांसपोर्ट-मूल वार्तालाप पहचानकर्ता (उदा. Feishuchat_id, Telegramchat_id)। तब भरा जाता है जब मूल चैनल एक मूल वार्तालाप आईडी प्रदान करता है।ctx.channelContext.sender.id-ctx.senderIdके समान प्रेषक आईडी, एक चैनल-स्वामित्व वाले ऑब्जेक्ट के अंतर्गत, जिसे Plugin चैनल-विशिष्ट फ़ील्ड से विस्तारित कर सकते हैं।ctx.channelContext.chat.id-ctx.chatIdके समान वार्तालाप आईडी, एक चैनल-स्वामित्व वाले ऑब्जेक्ट के अंतर्गत, जिसे Plugin चैनल-विशिष्ट फ़ील्ड से विस्तारित कर सकते हैं।
कोर केवल नेस्टेड id फ़ील्ड परिभाषित करता है। इनबाउंड हेल्पर के माध्यम से अधिक समृद्ध
प्रेषक या चैट मेटाडेटा पास करने वाले चैनल Plugin
openclaw/plugin-sdk/channel-inbound से PluginHookChannelSenderContext या PluginHookChannelChatContext को
विस्तारित कर सकते हैं:
declare module "openclaw/plugin-sdk/channel-inbound" { interface PluginHookChannelSenderContext { unionId?: string; userId?: string; }}चैनल Plugin इन फ़ील्ड को इनबाउंड SDK हेल्पर के माध्यम से पास करते हैं:
buildChannelInboundEventContext({ // ... channelContext: { sender: { id: senderOpenId, unionId, userId }, chat: { id: chatId }, },});ये फ़ील्ड वैकल्पिक हैं और सिस्टम से उत्पन्न रन (Heartbeat, Cron, Exec-ईवेंट) में अनुपस्थित रहते हैं।
ctx.senderExternalId पुराने प्लगइनों के लिए एक बहिष्कृत स्रोत-संगतता फ़ील्ड के रूप में
बना हुआ है। कोर इसे नहीं भरता; नई चैनल-विशिष्ट प्रेषक
पहचानें मॉड्यूल ऑगमेंटेशन के माध्यम से ctx.channelContext.sender के अंतर्गत
होनी चाहिए।
agent_end एक अवलोकन हुक है। Gateway और स्थायी हार्नेस पथ इसे
टर्न के बाद बिना प्रतीक्षा किए चलाते हैं, जबकि अल्पजीवी एकबारगी CLI पथ
प्रक्रिया क्लीनअप से पहले हुक प्रॉमिस की प्रतीक्षा करते हैं, ताकि विश्वसनीय Plugin
टर्मिनल अवलोकनीयता फ़्लश कर सकें या स्थिति कैप्चर कर सकें। हुक रनर 30 सेकंड की
समय-सीमा लागू करता है, ताकि अटका हुआ Plugin या एम्बेडिंग एंडपॉइंट हुक प्रॉमिस को
हमेशा लंबित न छोड़ सके। समय-सीमा समाप्ति लॉग की जाती है और OpenClaw जारी रहता है; यह
Plugin-स्वामित्व वाले नेटवर्क कार्य को रद्द नहीं करता, जब तक Plugin अपने स्वयं के एबॉर्ट
सिग्नल का भी उपयोग न करे।
उस प्रदाता-कॉल टेलीमेट्री के लिए model_call_started और model_call_ended का उपयोग करें
जिसे रॉ प्रॉम्प्ट, इतिहास, प्रतिक्रियाएँ, हेडर, अनुरोध
बॉडी, या प्रदाता अनुरोध आईडी प्राप्त नहीं होने चाहिए। इन हुक में
runId, callId, provider, model, वैकल्पिक api/transport, अंतिम
durationMs/outcome, और upstreamRequestIdHash जैसे स्थिर मेटाडेटा शामिल होते हैं, जब OpenClaw एक
सीमित प्रदाता अनुरोध-आईडी हैश प्राप्त कर सकता है। जब रनटाइम ने
संदर्भ-विंडो मेटाडेटा रिज़ॉल्व कर लिया हो, तो हुक ईवेंट और संदर्भ में
contextTokenBudget, मॉडल/कॉन्फ़िग/एजेंट
सीमाओं के बाद प्रभावी टोकन बजट, और कम सीमा लागू होने पर
contextWindowSource तथा contextWindowReferenceTokens भी शामिल होते हैं।
before_agent_finalize केवल तब चलता है जब हार्नेस किसी स्वाभाविक
अंतिम सहायक उत्तर को स्वीकार करने वाला होता है। यह /stop रद्दीकरण पथ नहीं है और
उपयोगकर्ता द्वारा टर्न निरस्त किए जाने पर नहीं चलता। अंतिमकरण से पहले हार्नेस से
मॉडल का एक और पास माँगने के लिए { action: "revise", reason }, अंतिमकरण बाध्य करने के लिए { action: "finalize", reason? } लौटाएँ, या जारी रखने के लिए कोई परिणाम न लौटाएँ।
हैंडलरों का डिफ़ॉल्ट बजट 15s है; समय-सीमा समाप्त होने पर OpenClaw विफलता लॉग करता है और
मूल अंतिम उत्तर के साथ जारी रहता है।
Codex के मूल Stop हुक इस हुक में OpenClaw
before_agent_finalize निर्णयों के रूप में रिले किए जाते हैं।
action: "revise" लौटाते समय, Plugin अतिरिक्त मॉडल पास को सीमित और रीप्ले-सुरक्षित बनाने के लिए
retry मेटाडेटा शामिल कर सकते हैं:
type BeforeAgentFinalizeRetry = { instruction: string; idempotencyKey?: string; maxAttempts?: number;};instruction हार्नेस को भेजे गए संशोधन कारण में जोड़ा जाता है।
idempotencyKey होस्ट को समान अंतिमकरण निर्णयों में एक ही Plugin अनुरोध के
पुनःप्रयास गिनने देता है, और maxAttempts यह सीमित करता है कि स्वाभाविक अंतिम उत्तर के साथ जारी रखने से पहले
होस्ट कितने अतिरिक्त पास की अनुमति देगा।
जिन गैर-बंडल Plugin को रॉ वार्तालाप हुक (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 से अक्षम किया जा सकता है।
सत्र एक्सटेंशन और अगले-टर्न इंजेक्शन
वर्कफ़्लो Plugin 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 प्राप्त होता है। होस्ट रीसेट/हटाने/अक्षम करने पर स्वामी Plugin की स्थायी सत्र एक्सटेंशन
स्थिति और लंबित अगले-टर्न इंजेक्शन हटा देता है; पुनरारंभ
टिकाऊ सत्र स्थिति बनाए रखता है, जबकि क्लीनअप कॉलबैक Plugin को पुराने
रनटाइम जनरेशन के लिए शेड्यूलर जॉब, रन संदर्भ, और अन्य आउट-ऑफ़-बैंड संसाधन मुक्त करने देते हैं।
संदेश हुक
चैनल-स्तरीय रूटिंग और डिलीवरी नीति के लिए संदेश हुक का उपयोग करें:
message_received: इनबाउंड सामग्री, प्रेषक,threadId,messageId,senderId, वैकल्पिक रन/सत्र सहसंबंध, क्रमबद्धmedia, और मेटाडेटा का अवलोकन करता है।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 फ़ील्ड को
प्राथमिकता दें।
इनबाउंड क्लेम और संदेश-प्राप्ति इवेंट विहित अटैचमेंट API के रूप में media?: PluginHookMediaFact[] उजागर करते हैं। प्रत्येक तथ्य में
path, url, contentType, kind, transcribed, messageId, और
workspaceDir हो सकते हैं; ऐरे में स्थिति ही अटैचमेंट की पहचान है। जब किसी रिमोट अटैचमेंट को
अभी स्थानीय रूप से स्टेज नहीं किया गया हो, तो media छोड़ा जाता है,
mediaStagingPending: true, और originalMedia में प्रदाता-पक्ष के
तथ्य होते हैं। originalMedia.path को स्थानीय रूप से पठनीय न मानें, जब तक कोई बाद का
स्टेज किया हुआ इवेंट media प्रदान न करे।
एकवचन/बहुवचन mediaPath, mediaUrl, mediaType, mediaPaths,
mediaUrls, mediaTypes, और मेल खाने वाले originalMedia* मेटाडेटा गुण
बहिष्कृत संगतता उपनाम हैं। नए हुक को टाइप किए गए शीर्ष-स्तरीय
ऐरे का उपयोग करना चाहिए।
निर्णय नियम:
message_sendingके साथcancel: trueअंतिम है।message_sendingके साथcancel: falseको कोई निर्णय नहीं माना जाता है।- पुनर्लिखित
contentनिम्न-प्राथमिकता वाले हुक तक जारी रहता है, जब तक कोई बाद का हुक डिलीवरी रद्द न कर दे। reply_payload_sendingपेलोड सामान्यीकरण के बाद और चैनल डिलीवरी से पहले चलता है, जिसमें मूल चैनल पर वापस भेजे गए उत्तर भी शामिल हैं। हैंडलर क्रमिक रूप से चलते हैं और प्रत्येक हैंडलर उच्च-प्राथमिकता वाले हैंडलरों द्वारा निर्मित नवीनतम पेलोड देखता है।reply_payload_sendingपेलोडtrustedLocalMediaजैसे रनटाइम विश्वास मार्कर उजागर नहीं करते; plugins पेलोड का आकार संपादित कर सकते हैं, लेकिन स्थानीय मीडिया विश्वास प्रदान नहीं कर सकते।message_sendingरद्दीकरण के साथcancelReasonऔर सीमाबद्धmetadataलौटा सकता है। नए संदेश जीवनचक्र API इसे कारणcancelled_by_message_sending_hookवाले दबाए गए डिलीवरी परिणाम के रूप में उजागर करते हैं; लीगेसी प्रत्यक्ष डिलीवरी संगतता के लिए खाली परिणाम ऐरे लौटाती रहती है।message_sentकेवल अवलोकन के लिए है। हैंडलर विफलताएँ लॉग की जाती हैं और डिलीवरी परिणाम नहीं बदलतीं।
हुक इंस्टॉल करें
ऑपरेटर-स्वामित्व वाले अनुमति/अवरोध निर्णयों के लिए security.installPolicy का उपयोग करें। वह
नीति OpenClaw कॉन्फ़िगरेशन से चलती है, CLI इंस्टॉल और अपडेट पथों को कवर करती है, और
सक्षम लेकिन अनुपलब्ध होने पर सुरक्षित रूप से अवरुद्ध रहती है।
before_install एक plugin-रनटाइम जीवनचक्र हुक है। यह
security.installPolicy के बाद केवल उस OpenClaw प्रक्रिया में चलता है जहाँ plugin हुक
पहले ही लोड हो चुके हों, जैसे Gateway-समर्थित इंस्टॉल प्रवाह। यह
plugin-स्वामित्व वाले अवलोकनों, चेतावनियों और संगतता जाँचों के लिए उपयोगी है, लेकिन इंस्टॉल के लिए
प्राथमिक एंटरप्राइज़ या होस्ट सुरक्षा सीमा नहीं है। संगतता के लिए
builtinScan फ़ील्ड इवेंट पेलोड में बना रहता है, लेकिन
OpenClaw अब इंस्टॉल के समय अंतर्निहित खतरनाक-कोड अवरोधन नहीं चलाता, इसलिए यह
खाली ok परिणाम है। उस प्रक्रिया में इंस्टॉल रोकने के लिए अतिरिक्त निष्कर्ष या
{ block: true, blockReason } लौटाएँ।
block: true अंतिम है। block: false को कोई निर्णय नहीं माना जाता है। हैंडलर
विफलताएँ सुरक्षित रूप से इंस्टॉल अवरुद्ध करती हैं।
Gateway जीवनचक्र
सामान्य plugin सेवाएँ शुरू करने के लिए gateway_start और
लंबे समय तक चलने वाले संसाधनों को साफ़ करने के लिए gateway_stop का उपयोग करें। जब
gateway_start चलता है तब भी cron शेड्यूलर लोड हो रहा हो सकता है, इसलिए इसे किसी बाहरी
cron प्रक्षेपण के आधारभूत संकेत के रूप में उपयोग न करें।
plugin-स्वामित्व वाली रनटाइम सेवाओं के लिए आंतरिक gateway:startup हुक पर
निर्भर न रहें।
cron_reconciled Gateway cron शेड्यूलर और उसके निकास-समय
वॉचरों द्वारा अपनी टिकाऊ स्थिति का समाधान करने के बाद सक्रिय होता है। यह प्रारंभिक
स्टार्टअप और कॉन्फ़िगरेशन पुनः लोड के दौरान शेड्यूलर प्रतिस्थापन, दोनों के लिए सक्रिय होता है। इवेंट
reason (startup या reload) और प्रभावी enabled स्थिति की रिपोर्ट करता है। अक्षम
cron भी enabled: false के साथ उत्सर्जित होता है, जिससे कोई बाहरी प्रक्षेपण
पुराने वेक साफ़ कर सकता है। समाधान पूरा करने वाले सटीक शेड्यूलर इंस्टेंस के लिए
ctx.getCron?.() का उपयोग करें; बाद का पुनः लोड उस कॉलबैक का लक्ष्य नहीं बदलता।
ctx.abortSignal उसी शेड्यूलर स्नैपशॉट का स्वामी है। जैसे ही
कोई नया शेड्यूलर सक्रिय होता है या शटडाउन शुरू होता है, Gateway इसे निरस्त कर देता है। इसे प्रत्येक
टिकाऊ पार्श्व प्रभाव में आगे भेजें और निरस्त होने के बाद स्नैपशॉट स्वीकार न करें।
यह शेड्यूलर जीवनचक्र संकेत है, plugin-सक्रियण संकेत नहीं: केवल
plugin का हॉट रीलोड इसे दोबारा नहीं चलाता। नए सक्षम हुए उपभोक्ता को
अगले शेड्यूलर प्रतिस्थापन या Gateway प्रारंभ पर अपना पहला आधार मिलता है।
अन्य अवलोकन हुक की तरह, gateway_start और cron_reconciled कॉलबैक
एक-दूसरे के साथ ओवरलैप कर सकते हैं। यदि दोनों हैंडलर plugin आरंभीकरण साझा करते हैं, तो उन्हें
कॉलबैक क्रम पर निर्भर रहने के बजाय plugin-स्थानीय तत्परता प्रॉमिस से
समन्वित करें।
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(`external cron projection failed; retrying in ${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 reconciliation did not expose a scheduler"); 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 plugin हुक प्रभावों के लिए
आउटबॉक्स प्रदान नहीं करता। यदि टिकाऊ स्वीकृति से पहले प्रक्रिया बंद हो जाती है,
तो अगला Gateway प्रारंभ नया प्रामाणिक cron_reconciled स्नैपशॉट उत्सर्जित करता है।
gateway_stop प्रगति पर चल रहे होस्ट कार्य को निरस्त करता है, वर्कर के स्थिर होने की प्रतीक्षा करता है, फिर
अडैप्टर बंद करता है।
आगामी बहिष्करण
हुक से संबंधित कुछ सतहें बहिष्कृत हैं, लेकिन अभी भी समर्थित हैं। अगले प्रमुख रिलीज़ से पहले माइग्रेट करें:
inbound_claimऔरmessage_receivedहैंडलर में प्लेनटेक्स्ट चैनल एनवेलप। समतल एनवेलप टेक्स्ट को पार्स करने के बजायBodyForAgentऔर संरचित उपयोगकर्ता-संदर्भ ब्लॉक पढ़ें। देखें प्लेनटेक्स्ट चैनल एनवेलप → BodyForAgent।subagent_spawningपुराने plugins के साथ संगतता के लिए बना हुआ है, लेकिन नए plugins को इससे थ्रेड रूटिंग नहीं लौटानी चाहिए।subagent_spawnedके सक्रिय होने से पहले कोर, चैनल सत्र-बाइंडिंग अडैप्टर के माध्यम सेthread: trueसबएजेंट बाइंडिंग तैयार करता है।deactivateको 2026-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 आर्किटेक्चर की आंतरिक संरचना