---
read_when:
    - आप ऐसा Plugin बना रहे हैं जिसे `before_tool_call`, `before_agent_reply`, संदेश हुक या जीवनचक्र हुक की आवश्यकता है
    - आपको किसी Plugin से होने वाली टूल कॉल को अवरुद्ध करना, पुनर्लेखित करना या उनके लिए अनुमोदन आवश्यक बनाना है
    - आप आंतरिक हुक और Plugin हुक के बीच चयन कर रहे हैं
    - आप OpenClaw Cron वेक-अप को किसी बाहरी होस्ट शेड्यूलर में प्रक्षेपित कर रहे हैं
summary: 'Plugin हुक: एजेंट, टूल, संदेश, सत्र और Gateway जीवनचक्र इवेंट को इंटरसेप्ट करें'
title: Plugin हुक्स
x-i18n:
    generated_at: "2026-07-27T18:39:03Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: 95d7ea2f7bfe26b5904ea3cd8f8db85ffd8163af58e03ec56d11eee992bc13d2
    source_path: plugins/hooks.md
    workflow: 16
---

Plugin hooks, OpenClaw plugins के लिए इन-प्रोसेस विस्तार बिंदु हैं: एजेंट रन, टूल कॉल, संदेश प्रवाह, सत्र जीवनचक्र, सबएजेंट रूटिंग, इंस्टॉलेशन या Gateway स्टार्टअप का निरीक्षण करें या उन्हें बदलें।

इसके बजाय, कमांड और Gateway इवेंट, जैसे `/new`,
`/reset`, `/stop`, `agent:bootstrap` या `gateway:startup` पर प्रतिक्रिया देने वाली ऑपरेटर द्वारा इंस्टॉल की गई छोटी
`HOOK.md` स्क्रिप्ट के लिए [आंतरिक hooks](/hi/automation/hooks) का उपयोग करें।

## त्वरित शुरुआत

Plugin एंट्री से `api.on(...)` के साथ टाइप किए गए hooks पंजीकृत करें:

```typescript
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";

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 बजट सेट कर सकते हैं:

```json
{
  "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` का उपयोग करें।
अनुरोध बनाए जाने पर हुक डिस्पैच होता है; धीमे या विफल हुक हैंडलर के कारण
पेयरिंग उत्तर की चैनल डिलीवरी में विलंब नहीं होता।

```typescript
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.toolName`
- `event.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` शामिल हो सकते हैं। अनुपस्थित फ़ील्ड अप्रमाणित हैं,
  झूठे आश्वासन नहीं; नीति द्वारा आवश्यक होने पर विफलता की स्थिति में पहुँच अस्वीकार करें।

यह निम्न लौट सकता है:

```typescript
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
  स्वीकृतियों के माध्यम से उपयोगकर्ता से पूछता है। `/approve` exec और Plugin, दोनों स्वीकृतियाँ मंज़ूर कर सकता है। Codex
  app-server रिपोर्ट-मोड नेटिव `PreToolUse` रिले में, यह संबंधित
  app-server स्वीकृति अनुरोध को सौंप देता है; देखें
  [Codex हार्नेस रनटाइम](/hi/plugins/codex-harness-runtime#hook-boundaries)।
- उच्च-प्राथमिकता वाले हुक द्वारा स्वीकृति का अनुरोध किए जाने के बाद भी निम्न-प्राथमिकता वाला `block: true`
  ब्लॉक कर सकता है।
- `onResolution` को निर्धारित निर्णय प्राप्त होता है: `allow-once`, `allow-always`,
  `deny`, `timeout`, या `cancelled`।

### एक फ़ाइल में प्रेषक-जागरूक नीति

एक स्वतंत्र Plugin फ़ाइल, कोई अन्य कॉन्फ़िगरेशन स्कीमा जोड़ने के बजाय,
परिनियोजन-विशिष्ट नीति को कोड में रख सकती है। यह उदाहरण स्वामियों को प्रत्येक टूल देता है,
कॉन्फ़िगर किए गए मेंटेनर को सीमित टूल और संदेश-क्रिया सेट का उपयोग करने देता है,
और चैनल कॉन्फ़िगरेशन द्वारा पहले से अधिकृत प्रेषकों के लिए `/fix` उपलब्ध कराता है:

```typescript
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";

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 पुनः आरंभ करें:

```json5
{
  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 अनुमति अनुरोध](/hi/plugins/plugin-permission-requests) देखें।

जिन plugins को होस्ट-स्तरीय नीति चाहिए, वे
`api.registerTrustedToolPolicy(...)` के साथ विश्वसनीय टूल नीतियाँ पंजीकृत कर सकते हैं। ये सामान्य
`before_tool_call` हुक और सामान्य हुक निर्णयों से पहले चलती हैं। बंडल की गई विश्वसनीय
नीतियाँ पहले चलती हैं; इंस्टॉल किए गए Plugin की विश्वसनीय नीतियाँ Plugin-लोड
क्रम में उसके बाद चलती हैं; सामान्य `before_tool_call` हुक इनके बाद चलते हैं। बंडल किए गए plugins
मौजूदा विश्वसनीय-नीति पथ बनाए रखते हैं। इंस्टॉल किए गए plugins को स्पष्ट रूप से सक्षम करना
और `contracts.trustedToolPolicies` में प्रत्येक नीति आईडी घोषित करना आवश्यक है; अघोषित आईडी
पंजीकरण से पहले अस्वीकार कर दी जाती हैं। नीति आईडी पंजीकरण करने वाले
Plugin के स्कोप में होती हैं, इसलिए अलग-अलग plugins समान स्थानीय आईडी का पुनः उपयोग कर सकते हैं। इस स्तर का उपयोग केवल
होस्ट-विश्वसनीय गेट के लिए करें, जैसे कार्यक्षेत्र नीति, बजट प्रवर्तन, या
आरक्षित कार्यप्रवाह सुरक्षा।

### Exec परिवेश हुक

`resolve_exec_env` कमांड चलने से पहले प्लगइनों को `exec`
टूल आह्वानों में परिवेश चर जोड़ने देता है। इसे ये प्राप्त होते हैं:

- `event.sessionKey`
- `event.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` - चैनल-सीमित प्रेषक आईडी (उदा. Feishu `open_id`, Discord
  उपयोगकर्ता आईडी)। तब भरा जाता है जब रन ज्ञात
  प्रेषक मेटाडेटा वाले उपयोगकर्ता संदेश से उत्पन्न होता है।
- `ctx.chatId` - ट्रांसपोर्ट-मूल वार्तालाप पहचानकर्ता (उदा. Feishu
  `chat_id`, Telegram `chat_id`)। तब भरा जाता है जब मूल चैनल
  एक मूल वार्तालाप आईडी प्रदान करता है।
- `ctx.channelContext.sender.id` - `ctx.senderId` के समान प्रेषक आईडी,
  एक चैनल-स्वामित्व वाले ऑब्जेक्ट के अंतर्गत, जिसे Plugin चैनल-विशिष्ट फ़ील्ड से विस्तारित कर सकते हैं।
- `ctx.channelContext.chat.id` - `ctx.chatId` के समान वार्तालाप आईडी,
  एक चैनल-स्वामित्व वाले ऑब्जेक्ट के अंतर्गत, जिसे Plugin चैनल-विशिष्ट
  फ़ील्ड से विस्तारित कर सकते हैं।

कोर केवल नेस्टेड `id` फ़ील्ड परिभाषित करता है। इनबाउंड हेल्पर के माध्यम से अधिक समृद्ध
प्रेषक या चैट मेटाडेटा पास करने वाले चैनल Plugin
`openclaw/plugin-sdk/channel-inbound` से `PluginHookChannelSenderContext` या `PluginHookChannelChatContext` को
विस्तारित कर सकते हैं:

```ts
declare module "openclaw/plugin-sdk/channel-inbound" {
  interface PluginHookChannelSenderContext {
    unionId?: string;
    userId?: string;
  }
}
```

चैनल Plugin इन फ़ील्ड को इनबाउंड SDK हेल्पर के माध्यम से पास करते हैं:

```ts
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` मेटाडेटा शामिल कर सकते हैं:

```typescript
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`) की आवश्यकता है, उन्हें यह सेट करना होगा:

```json
{
  "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` केवल उस वर्कर से
प्रामाणिक इंस्टेंस दोबारा पढ़ने के लिए कहता है, इसलिए देर से आया संकेत किसी पुराने शेड्यूलर को पुनर्स्थापित नहीं कर सकता।
नया संशोधन सक्रिय होस्ट प्रयास को पुराना
स्नैपशॉट स्वीकार करने से पहले निरस्त कर देता है।

```typescript
import { setTimeout as sleep } from "node:timers/promises";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";

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](/hi/plugins/sdk-migration#active-deprecations)।
- **`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 माइग्रेशन → सक्रिय अप्रचलन](/hi/plugins/sdk-migration#active-deprecations)।

## संबंधित

- [Plugin SDK माइग्रेशन](/hi/plugins/sdk-migration) - सक्रिय अप्रचलन और हटाने की समयरेखा
- [plugins बनाना](/hi/plugins/building-plugins)
- [Plugin SDK अवलोकन](/hi/plugins/sdk-overview)
- [Plugin प्रवेश बिंदु](/hi/plugins/sdk-entrypoints)
- [आंतरिक हुक](/hi/automation/hooks)
- [Plugin आर्किटेक्चर की आंतरिक संरचना](/hi/plugins/architecture-internals)
