---
read_when:
    - إنشاء Plugin أصلية لـ OpenClaw أو تصحيح أخطائها
    - فهم نموذج قدرات Plugin أو حدود الملكية
    - العمل على خط أنابيب تحميل Plugin أو السجل
    - تنفيذ خطافات وقت التشغيل لمزوّد الخدمة أو Plugin القنوات
sidebarTitle: Internals
summary: 'الأجزاء الداخلية لـ Plugin: نموذج القدرات، والملكية، والعقود، وخط أنابيب التحميل، ومساعدات وقت التشغيل'
title: الأجزاء الداخلية لـ Plugin
x-i18n:
    generated_at: "2026-04-23T14:01:15Z"
    model: gpt-5.4
    provider: openai
    source_hash: b5a766c267b2618140c744cbebd28f2b206568f26ce50095b898520f4663e21d
    source_path: plugins/architecture.md
    workflow: 15
---

# الأجزاء الداخلية لـ Plugin

<Info>
  هذا هو **المرجع المعماري المتعمق**. وللأدلة العملية، راجع:
  - [تثبيت Plugin واستخدامها](/ar/tools/plugin) — دليل المستخدم
  - [البدء](/ar/plugins/building-plugins) — أول درس تعليمي لـ Plugin
  - [Plugin القنوات](/ar/plugins/sdk-channel-plugins) — أنشئ قناة مراسلة
  - [Plugin مزوّد الخدمة](/ar/plugins/sdk-provider-plugins) — أنشئ مزوّد نماذج
  - [نظرة عامة على SDK](/ar/plugins/sdk-overview) — خريطة الاستيراد وواجهة برمجة تسجيل API
</Info>

تغطي هذه الصفحة البنية الداخلية لنظام Plugin في OpenClaw.

## نموذج القدرات العام

القدرات هي النموذج العام لـ **Plugin الأصلية** داخل OpenClaw. وكل
Plugin أصلية في OpenClaw تسجّل نفسها مقابل نوع واحد أو أكثر من أنواع القدرات:

| القدرة                | طريقة التسجيل                                  | أمثلة على Plugin                    |
| --------------------- | ---------------------------------------------- | ----------------------------------- |
| استدلال النص          | `api.registerProvider(...)`                    | `openai`, `anthropic`               |
| الواجهة الخلفية لاستدلال CLI | `api.registerCliBackend(...)`                  | `openai`, `anthropic`               |
| الكلام                | `api.registerSpeechProvider(...)`              | `elevenlabs`, `microsoft`           |
| النسخ الفوري          | `api.registerRealtimeTranscriptionProvider(...)` | `openai`                            |
| الصوت الفوري          | `api.registerRealtimeVoiceProvider(...)`       | `openai`                            |
| فهم الوسائط           | `api.registerMediaUnderstandingProvider(...)`  | `openai`, `google`                  |
| توليد الصور           | `api.registerImageGenerationProvider(...)`     | `openai`, `google`, `fal`, `minimax` |
| توليد الموسيقى        | `api.registerMusicGenerationProvider(...)`     | `google`, `minimax`                 |
| توليد الفيديو         | `api.registerVideoGenerationProvider(...)`     | `qwen`                              |
| جلب الويب             | `api.registerWebFetchProvider(...)`            | `firecrawl`                         |
| البحث على الويب       | `api.registerWebSearchProvider(...)`           | `google`                            |
| القناة / المراسلة     | `api.registerChannel(...)`                     | `msteams`, `matrix`                 |

Plugin التي تسجّل صفرًا من القدرات لكنها توفر خطافات أو أدوات أو
خدمات هي Plugin **قديمة تعتمد على الخطافات فقط**. ولا يزال هذا النمط مدعومًا بالكامل.

### موقف التوافق الخارجي

تم اعتماد نموذج القدرات في النواة ويُستخدم بواسطة Plugin
المضمّنة/الأصلية اليوم، لكن توافق Plugin الخارجية لا يزال يحتاج إلى معيار
أكثر إحكامًا من مجرد «إنه مُصدَّر، إذًا فهو ثابت».

الإرشادات الحالية:

- **Plugin الخارجية الحالية:** حافظ على استمرار تكاملات الخطافات في العمل؛ واعتبر
  هذا هو خط الأساس للتوافق
- **Plugin المضمّنة/الأصلية الجديدة:** فضّل التسجيل الصريح للقدرات بدلًا من
  الوصولات الخاصة بالمورّد أو التصاميم الجديدة المعتمدة على الخطافات فقط
- **Plugin الخارجية التي تعتمد تسجيل القدرات:** هذا مسموح، لكن اعتبر
  الأسطح المساعدة الخاصة بالقدرات في طور التطور ما لم توضح الوثائق صراحةً أن
  العقد ثابت

القاعدة العملية:

- واجهات API لتسجيل القدرات هي الاتجاه المقصود
- تظل الخطافات القديمة هي المسار الأكثر أمانًا لتجنب الأعطال بالنسبة إلى Plugin الخارجية أثناء
  الانتقال
- ليست كل المسارات الفرعية المساعدة المصدّرة متساوية؛ فضّل العقد الضيق
  الموثق، لا الصادرات المساعدة العرضية

### أشكال Plugin

يصنّف OpenClaw كل Plugin محمّلة إلى شكل اعتمادًا على سلوك
التسجيل الفعلي لديها (وليس فقط البيانات الوصفية الثابتة):

- **plain-capability** -- تسجّل نوع قدرة واحدًا فقط بالضبط (مثلًا
  Plugin لمزوّد فقط مثل `mistral`)
- **hybrid-capability** -- تسجّل أنواع قدرات متعددة (مثلًا
  `openai` يمتلك استدلال النص والكلام وفهم الوسائط وتوليد
  الصور)
- **hook-only** -- تسجّل خطافات فقط (مكتوبة أو مخصصة)، بلا قدرات
  أو أدوات أو أوامر أو خدمات
- **non-capability** -- تسجّل أدوات أو أوامر أو خدمات أو مسارات ولكن بلا
  قدرات

استخدم `openclaw plugins inspect <id>` لمعرفة شكل Plugin وتفصيل
القدرات فيها. راجع [مرجع CLI](/ar/cli/plugins#inspect) للتفاصيل.

### الخطافات القديمة

لا يزال الخطاف `before_agent_start` مدعومًا كمسار توافق لـ Plugin
المعتمدة على الخطافات فقط. ولا تزال Plugin القديمة الواقعية تعتمد عليه.

الاتجاه:

- حافظ على عمله
- وثّقه باعتباره قديمًا
- فضّل `before_model_resolve` لأعمال تجاوز النموذج/مزوّد الخدمة
- فضّل `before_prompt_build` لأعمال تعديل المطالبة
- أزله فقط بعد انخفاض الاستخدام الفعلي وتأكيد تغطية الثوابت أمان الانتقال

### إشارات التوافق

عند تشغيل `openclaw doctor` أو `openclaw plugins inspect <id>`، قد ترى
إحدى هذه التسميات:

| الإشارة                    | المعنى                                                     |
| -------------------------- | ---------------------------------------------------------- |
| **config valid**           | يتم تحليل الإعدادات بشكل سليم ويتم حل Plugin               |
| **compatibility advisory** | تستخدم Plugin نمطًا مدعومًا لكنه أقدم (مثل `hook-only`) |
| **legacy warning**         | تستخدم Plugin `before_agent_start`، وهو مهجور            |
| **hard error**             | الإعدادات غير صالحة أو فشل تحميل Plugin                   |

لن يؤدي `hook-only` ولا `before_agent_start` إلى تعطيل Plugin الخاصة بك اليوم --
فـ `hook-only` مجرد تنبيه، و`before_agent_start` لا يؤدي إلا إلى تحذير. كما
تظهر هذه الإشارات أيضًا في `openclaw status --all` و`openclaw plugins doctor`.

## نظرة عامة على البنية

يتكون نظام Plugin في OpenClaw من أربع طبقات:

1. **البيان + الاكتشاف**
   يعثر OpenClaw على Plugin المرشحة من المسارات المضبوطة وجذور
   مساحات العمل وجذور Plugin العامة وPlugin المضمّنة. ويقرأ الاكتشاف
   بيانات `openclaw.plugin.json` الأصلية بالإضافة إلى بيانات الحِزم المدعومة أولًا.
2. **التمكين + التحقق**
   تقرر النواة ما إذا كانت Plugin المكتشفة مفعلة أو معطلة أو محظورة أو
   محددة لفتحة حصرية مثل الذاكرة.
3. **التحميل في وقت التشغيل**
   يتم تحميل Plugin الأصلية في OpenClaw داخل العملية عبر jiti وتقوم بتسجيل
   القدرات في سجل مركزي. وتتم تسوية الحِزم المتوافقة إلى
   سجلات في السجل من دون استيراد كود وقت التشغيل.
4. **استهلاك السطح**
   يقرأ بقية OpenClaw السجل لكشف الأدوات والقنوات وإعداد
   مزوّد الخدمة والخطافات ومسارات HTTP وأوامر CLI والخدمات.

وبالنسبة إلى Plugin الخاصة بـ CLI تحديدًا، فإن اكتشاف الأمر الجذري ينقسم إلى
مرحلتين:

- تأتي البيانات الوصفية وقت التحليل من `registerCli(..., { descriptors: [...] })`
- يمكن أن تبقى وحدة CLI الحقيقية الخاصة بـ Plugin مؤجلة وتُسجَّل عند أول استدعاء

وهذا يبقي كود CLI المملوك لـ Plugin داخل Plugin مع السماح لـ OpenClaw
بحجز أسماء الأوامر الجذرية قبل التحليل.

الحد التصميمي المهم:

- يجب أن يعمل الاكتشاف + التحقق من الإعدادات انطلاقًا من **بيانات البيان/المخطط**
  من دون تنفيذ كود Plugin
- يأتي سلوك وقت التشغيل الأصلي من مسار `register(api)` في وحدة Plugin

يتيح هذا الفصل لـ OpenClaw التحقق من الإعدادات وشرح Plugin المفقودة/المعطلة و
بناء تلميحات الواجهة/المخطط قبل أن يصبح وقت التشغيل الكامل نشطًا.

### Plugin القنوات وأداة الرسائل المشتركة

لا تحتاج Plugin القنوات إلى تسجيل أداة منفصلة للإرسال/التحرير/التفاعل
لإجراءات الدردشة العادية. يحتفظ OpenClaw بأداة `message` مشتركة واحدة في النواة،
وتملك Plugin القنوات الاكتشاف والتنفيذ الخاصين بالقناة خلفها.

الحد الحالي هو:

- تملك النواة مضيف أداة `message` المشتركة، وربط المطالبات، ومسك
  دفاتر الجلسات/السلاسل، وتوزيع التنفيذ
- تملك Plugin القنوات اكتشاف الإجراءات المحددة النطاق، واكتشاف القدرات،
  وأي أجزاء مخطط خاصة بالقناة
- تملك Plugin القنوات قواعد المحادثات الخاصة بالجلسات لدى مزوّد الخدمة، مثل
  كيفية ترميز معرّفات المحادثة لمعرّفات السلاسل أو وراثتها من المحادثات الأم
- تنفّذ Plugin القنوات الإجراء النهائي عبر محول الإجراءات الخاص بها

بالنسبة إلى Plugin القنوات، فإن سطح SDK هو
`ChannelMessageActionAdapter.describeMessageTool(...)`. ويتيح هذا الاستدعاء
الموحّد للاكتشاف أن تعيد Plugin الإجراءات المرئية والقدرات ومساهمات المخطط
معًا حتى لا تنجرف هذه الأجزاء بعيدًا عن بعضها.

وعندما تحمل وسيطة أداة الرسائل الخاصة بالقناة مصدر وسائط مثل
مسار محلي أو عنوان URL لوسائط بعيدة، يجب على Plugin أيضًا أن تعيد
`mediaSourceParams` من `describeMessageTool(...)`. وتستخدم النواة هذه القائمة
الصريحة لتطبيق تسوية مسارات sandbox وتلميحات وصول الوسائط الصادرة من دون
تضمين أسماء المعاملات المملوكة لـ Plugin بشكل ثابت.
وفضّل هناك الخرائط المحددة لكل إجراء، لا قائمة مسطحة واحدة على مستوى
القناة بالكامل، حتى لا تتم تسوية وسيطة وسائط خاصة بالملف الشخصي في
إجراءات غير مرتبطة مثل `send`.

تمرّر النواة نطاق وقت التشغيل إلى خطوة الاكتشاف هذه. وتشمل الحقول المهمة:

- `accountId`
- `currentChannelId`
- `currentThreadTs`
- `currentMessageId`
- `sessionKey`
- `sessionId`
- `agentId`
- `requesterSenderId` الوارد الموثوق

وهذا مهم بالنسبة إلى Plugin الحساسة للسياق. إذ يمكن للقناة إخفاء أو إظهار
إجراءات الرسائل استنادًا إلى الحساب النشط أو الغرفة/السلسلة/الرسالة الحالية أو
هوية الطالب الموثوق بها من دون تضمين تفرعات خاصة بالقناة داخل أداة
`message` في النواة.

ولهذا السبب تظل تغييرات التوجيه في embedded-runner من عمل Plugin: إذ تقع على
عاتق المشغّل مسؤولية تمرير هوية الدردشة/الجلسة الحالية إلى حد
الاكتشاف الخاص بـ Plugin حتى تعرض أداة `message` المشتركة السطح
المملوك للقناة الصحيح في الدور الحالي.

وبالنسبة إلى مساعدات التنفيذ المملوكة للقناة، يجب على Plugin المضمّنة الاحتفاظ
بوقت تشغيل التنفيذ داخل وحدات الامتداد الخاصة بها. لم تعد النواة تملك
أوقات تشغيل إجراءات الرسائل الخاصة بـ Discord أو Slack أو Telegram أو WhatsApp
تحت `src/agents/tools`.
نحن لا ننشر مسارات فرعية منفصلة `plugin-sdk/*-action-runtime`، ويجب على
Plugin المضمّنة استيراد كود وقت التشغيل المحلي الخاص بها مباشرةً من
الوحدات المملوكة لامتدادها.

وينطبق الحد نفسه عمومًا على مسارات SDK المسماة بأسماء مزوّدي الخدمة: يجب ألا
تستورد النواة حِزم تسهيلية خاصة بالقنوات مثل Slack أو Discord أو Signal أو
WhatsApp أو امتدادات مشابهة. وإذا احتاجت النواة إلى سلوك ما، فإما أن
تستهلك الحزمة `api.ts` / `runtime-api.ts` الخاصة بـ Plugin المضمّنة نفسها أو
ترقّي الحاجة إلى قدرة عامة ضيقة في SDK المشتركة.

وبالنسبة إلى الاستطلاعات تحديدًا، هناك مساران للتنفيذ:

- `outbound.sendPoll` هو خط الأساس المشترك للقنوات التي تناسب نموذج
  الاستطلاع العام
- `actions.handleAction("poll")` هو المسار المفضّل لدلالات الاستطلاع
  الخاصة بالقناة أو لمعاملات الاستطلاع الإضافية

وتؤجّل النواة الآن تحليل الاستطلاعات المشتركة إلى ما بعد رفض توزيع
الاستطلاع الخاص بـ Plugin للإجراء، بحيث يمكن لمعالجات الاستطلاع المملوكة لـ Plugin
قبول حقول استطلاع خاصة بالقناة من دون أن يعيقها محلل الاستطلاع العام أولًا.

راجع [خط أنابيب التحميل](#load-pipeline) لمعرفة تسلسل بدء التشغيل الكامل.

## نموذج ملكية القدرات

يتعامل OpenClaw مع Plugin الأصلية باعتبارها حد الملكية الخاص بـ **شركة**
أو **ميزة**، وليس كمجموعة عشوائية من تكاملات غير مترابطة.

وهذا يعني:

- ينبغي عادةً أن تملك Plugin الخاصة بشركة ما كل الأسطح
  المواجهة لـ OpenClaw التابعة لتلك الشركة
- ينبغي عادةً أن تملك Plugin الخاصة بميزة ما السطح الكامل للميزة التي تقدمها
- ينبغي أن تستهلك القنوات قدرات النواة المشتركة بدلًا من إعادة تنفيذ
  سلوك مزوّد الخدمة بشكل مخصص

أمثلة:

- تملك Plugin المضمّنة `openai` سلوك مزوّد نماذج OpenAI وسلوك OpenAI
  للكلام + الصوت الفوري + فهم الوسائط + توليد الصور
- تملك Plugin المضمّنة `elevenlabs` سلوك الكلام الخاص بـ ElevenLabs
- تملك Plugin المضمّنة `microsoft` سلوك الكلام الخاص بـ Microsoft
- تملك Plugin المضمّنة `google` سلوك مزوّد نماذج Google بالإضافة إلى سلوك Google
  لفهم الوسائط + توليد الصور + البحث على الويب
- تملك Plugin المضمّنة `firecrawl` سلوك جلب الويب الخاص بـ Firecrawl
- تملك Plugin المضمّنة `minimax` و`mistral` و`moonshot` و`zai`
  الواجهات الخلفية الخاصة بها لفهم الوسائط
- تملك Plugin المضمّنة `qwen` سلوك مزوّد النصوص Qwen بالإضافة إلى
  سلوك فهم الوسائط وتوليد الفيديو
- Plugin `voice-call` هي Plugin ميزات: فهي تملك نقل المكالمات والأدوات
  وCLI والمسارات وربط تدفقات وسائط Twilio، لكنها تستهلك قدرات الكلام
  المشتركة بالإضافة إلى النسخ الفوري والصوت الفوري بدلًا من
  استيراد Plugin المورّد مباشرة

الحالة النهائية المقصودة هي:

- يبقى OpenAI داخل Plugin واحدة حتى لو امتد عبر نماذج النصوص والكلام والصور
  والفيديو مستقبلاً
- يمكن لمورّد آخر أن يفعل الشيء نفسه لسطحه الخاص
- لا تهتم القنوات بأي Plugin مورّد تملك مزوّد الخدمة؛ فهي تستهلك
  عقد القدرة المشتركة التي تكشفها النواة

وهذا هو التمييز الأساسي:

- **plugin** = حد الملكية
- **capability** = عقد النواة الذي يمكن أن تنفذه أو تستهلكه عدة Plugin

لذا إذا أضاف OpenClaw مجالًا جديدًا مثل الفيديو، فإن السؤال الأول ليس
«أي مزوّد خدمة يجب أن يضمّن معالجة الفيديو بشكل ثابت؟». بل السؤال الأول هو
«ما هو عقد قدرة الفيديو في النواة؟». وبمجرد وجود ذلك العقد، يمكن لـ Plugin المورّد
أن تسجل نفسها مقابله، ويمكن لـ Plugin القنوات/الميزات أن تستهلكه.

إذا لم تكن القدرة موجودة بعد، فإن الخطوة الصحيحة تكون عادة:

1. تعريف القدرة المفقودة في النواة
2. كشفها عبر API/وقت التشغيل الخاص بـ Plugin بطريقة مكتوبة
3. توصيل القنوات/الميزات بتلك القدرة
4. السماح لـ Plugin المورّد بتسجيل التنفيذات

وهذا يحافظ على وضوح الملكية مع تجنب سلوك في النواة يعتمد على
مورّد واحد أو على مسار كود خاص بـ Plugin بعينها.

### طبقات القدرات

استخدم هذا النموذج الذهني عند تحديد موضع الكود:

- **طبقة قدرات النواة**: التنسيق المشترك، والسياسات، والاحتياط،
  وقواعد دمج الإعدادات، ودلالات التسليم، والعقود المكتوبة
- **طبقة Plugin المورّد**: واجهات API الخاصة بالمورّد، والتوثيق، وكتالوجات النماذج، والكلام
  الاصطناعي، وتوليد الصور، والواجهات الخلفية المستقبلية للفيديو، ونقاط نهاية الاستخدام
- **طبقة Plugin القنوات/الميزات**: تكامل Slack/Discord/voice-call/إلخ
  الذي يستهلك قدرات النواة ويعرضها على سطح ما

على سبيل المثال، يتبع TTS هذا الشكل:

- تملك النواة سياسة TTS وقت الرد، وترتيب الاحتياط، والتفضيلات، وتسليم القناة
- تملك `openai` و`elevenlabs` و`microsoft` تنفيذات التوليف
- تستهلك `voice-call` مساعد وقت تشغيل TTS الخاص بالاتصالات الهاتفية

ويجب تفضيل النمط نفسه للقدرات المستقبلية.

### مثال على Plugin شركة متعددة القدرات

ينبغي أن تبدو Plugin الشركة متماسكة من الخارج. فإذا كان لدى OpenClaw عقود
مشتركة للنماذج والكلام والنسخ الفوري والصوت الفوري وفهم الوسائط
وتوليد الصور وتوليد الفيديو وجلب الويب والبحث على الويب،
فيمكن للمورّد أن يملك جميع أسطحه في مكان واحد:

```ts
import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry";
import {
  describeImageWithModel,
  transcribeOpenAiCompatibleAudio,
} from "openclaw/plugin-sdk/media-understanding";

const plugin: OpenClawPluginDefinition = {
  id: "exampleai",
  name: "ExampleAI",
  register(api) {
    api.registerProvider({
      id: "exampleai",
      // auth/model catalog/runtime hooks
    });

    api.registerSpeechProvider({
      id: "exampleai",
      // vendor speech config — implement the SpeechProviderPlugin interface directly
    });

    api.registerMediaUnderstandingProvider({
      id: "exampleai",
      capabilities: ["image", "audio", "video"],
      async describeImage(req) {
        return describeImageWithModel({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
      async transcribeAudio(req) {
        return transcribeOpenAiCompatibleAudio({
          provider: "exampleai",
          model: req.model,
          input: req.input,
        });
      },
    });

    api.registerWebSearchProvider(
      createPluginBackedWebSearchProvider({
        id: "exampleai-search",
        // credential + fetch logic
      }),
    );
  },
};

export default plugin;
```

المهم ليس أسماء المساعدات الدقيقة. المهم هو الشكل:

- Plugin واحدة تملك سطح المورّد
- لا تزال النواة تملك عقود القدرات
- تستهلك Plugin القنوات والميزات مساعدات `api.runtime.*`، لا كود المورّد
- يمكن لاختبارات العقود أن تؤكد أن Plugin سجلت القدرات التي
  تدّعي امتلاكها

### مثال على القدرة: فهم الفيديو

يتعامل OpenClaw بالفعل مع فهم الصور/الصوت/الفيديو باعتباره قدرة
مشتركة واحدة. وينطبق نموذج الملكية نفسه هنا:

1. تحدد النواة عقد فهم الوسائط
2. تسجل Plugin المورّد `describeImage` و`transcribeAudio` و
   `describeVideo` حسب الاقتضاء
3. تستهلك Plugin القنوات والميزات سلوك النواة المشترك بدلًا من
   الربط المباشر بكود المورّد

وهذا يتجنب تضمين افتراضات الفيديو الخاصة بمزوّد واحد داخل النواة. فـ Plugin تملك
سطح المورّد؛ بينما تملك النواة عقد القدرة وسلوك الاحتياط.

ويستخدم توليد الفيديو بالفعل هذا التسلسل نفسه: إذ تملك النواة عقد
القدرة المكتوب ومساعد وقت التشغيل، وتسجل Plugin المورّد
تنفيذات `api.registerVideoGenerationProvider(...)` مقابله.

هل تحتاج إلى قائمة تحقق عملية لطرح ذلك؟ راجع
[كتاب وصفات القدرات](/ar/plugins/architecture).

## العقود وفرضها

يكون سطح API الخاص بـ Plugin مكتوبًا ومركزيًا عمدًا في
`OpenClawPluginApi`. ويحدد هذا العقد نقاط التسجيل المدعومة
ومساعدات وقت التشغيل التي يمكن أن تعتمد عليها Plugin.

سبب أهمية ذلك:

- يحصل مؤلفو Plugin على معيار داخلي ثابت واحد
- يمكن للنواة رفض الملكية المكررة مثل قيام Plugin اثنتين بتسجيل
  معرّف مزوّد الخدمة نفسه
- يمكن لبدء التشغيل إظهار تشخيصات عملية للتسجيلات المشوهة
- يمكن لاختبارات العقود فرض ملكية Plugin المضمّنة ومنع الانجراف الصامت

هناك طبقتان من الفرض:

1. **فرض التسجيل في وقت التشغيل**
   يتحقق سجل Plugin من التسجيلات أثناء تحميل Plugin. أمثلة:
   معرّفات مزوّدي الخدمة المكررة، ومعرّفات مزوّدي الكلام المكررة، والتسجيلات
   المشوهة تنتج تشخيصات Plugin بدلًا من سلوك غير معرّف.
2. **اختبارات العقود**
   يتم التقاط Plugin المضمّنة في سجلات عقود أثناء تشغيل الاختبارات حتى
   يتمكن OpenClaw من تأكيد الملكية صراحةً. ويُستخدم هذا اليوم لمزوّدي
   النماذج، ومزوّدي الكلام، ومزوّدي البحث على الويب، وملكية
   التسجيل الخاصة بـ Plugin المضمّنة.

والأثر العملي هو أن OpenClaw يعرف مسبقًا أي Plugin تملك أي
سطح. وهذا يتيح للنواة والقنوات أن تركّب بسلاسة لأن الملكية
معلنة ومكتوبة وقابلة للاختبار بدلًا من أن تكون ضمنية.

### ما الذي ينتمي إلى العقد

عقود Plugin الجيدة تكون:

- مكتوبة
- صغيرة
- خاصة بالقدرة
- مملوكة للنواة
- قابلة لإعادة الاستخدام بواسطة عدة Plugin
- قابلة للاستهلاك من القنوات/الميزات من دون معرفة بالمورّد

أما عقود Plugin السيئة فهي:

- سياسات خاصة بالمورّد مخفية داخل النواة
- منافذ هروب خاصة بـ Plugin واحدة تتجاوز السجل
- كود القناة الذي يصل مباشرة إلى تنفيذ المورّد
- كائنات وقت تشغيل مخصصة ليست جزءًا من `OpenClawPluginApi` أو
  `api.runtime`

وعند الشك، ارفع مستوى التجريد: عرّف القدرة أولًا، ثم
دع Plugin تتصل بها.

## نموذج التنفيذ

تعمل Plugin الأصلية في OpenClaw **داخل العملية** مع Gateway. وهي ليست
ضمن sandbox. وتمتلك Plugin الأصلية المحمّلة نفس حد الثقة على مستوى العملية
مثل كود النواة.

الآثار المترتبة:

- يمكن لـ Plugin الأصلية تسجيل أدوات ومعالجات شبكية وخطافات وخدمات
- يمكن لخطأ في Plugin أصلية أن يعطّل البوابة أو يزعزع استقرارها
- تعادل Plugin الأصلية الخبيثة تنفيذ كود عشوائي داخل
  عملية OpenClaw

تكون الحِزم المتوافقة أكثر أمانًا افتراضيًا لأن OpenClaw يتعامل معها حاليًا
كحزم بيانات/محتوى. وفي الإصدارات الحالية، يعني ذلك غالبًا
Skills المضمّنة.

استخدم قوائم السماح ومسارات التثبيت/التحميل الصريحة بالنسبة إلى Plugin غير المضمّنة. وتعامل
مع Plugin مساحات العمل باعتبارها كودًا لوقت التطوير، لا كافتراضات للإنتاج.

وبالنسبة إلى أسماء حزم مساحات العمل المضمّنة، أبقِ معرّف Plugin مرتبطًا باسم
npm: `@openclaw/<id>` افتراضيًا، أو لاحقة مكتوبة معتمدة مثل
`-provider` أو `-plugin` أو `-speech` أو `-sandbox` أو `-media-understanding` عندما
تكشف الحزمة عمدًا دور Plugin أضيق.

ملاحظة ثقة مهمة:

- يثق `plugins.allow` في **معرّفات plugin**، لا في مصدر المنشأ.
- تقوم Plugin مساحة عمل لها نفس معرّف Plugin مضمّنة بتظليل
  النسخة المضمّنة عمدًا عندما تكون Plugin مساحة العمل هذه مفعلة/في قائمة السماح.
- هذا سلوك طبيعي ومفيد للتطوير المحلي واختبار التصحيحات والإصلاحات العاجلة.
- يتم تحليل الثقة في Plugin المضمّنة من لقطة المصدر — البيان والكود
  الموجودان على القرص وقت التحميل — لا من بيانات التثبيت الوصفية. ولا يمكن لسجل تثبيت
  تالف أو مستبدل أن يوسّع سطح الثقة الخاص بـ Plugin المضمّنة بصمت
  إلى ما هو أبعد مما يصرّح به المصدر الفعلي.

## حد التصدير

يصدر OpenClaw القدرات، لا وسائل الراحة التنفيذية.

أبقِ تسجيل القدرات عامًا. وقلّص صادرات المساعدات غير التعاقدية:

- المسارات الفرعية الخاصة بمساعدات Plugin المضمّنة
- المسارات الفرعية الخاصة ببنية وقت التشغيل غير المقصودة كواجهة API عامة
- المساعدات المريحة الخاصة بالمورّد
- مساعدات الإعداد/التهيئة التي تُعد تفاصيل تنفيذ

ولا تزال بعض المسارات الفرعية المساعدة الخاصة بـ Plugin المضمّنة موجودة في
خريطة تصدير SDK المولدة من أجل التوافق وصيانة Plugin المضمّنة. ومن الأمثلة
الحالية: `plugin-sdk/feishu` و`plugin-sdk/feishu-setup` و`plugin-sdk/zalo`
و`plugin-sdk/zalo-setup` وعدة مسارات `plugin-sdk/matrix*`. تعامل مع هذه
على أنها صادرات محجوزة لتفاصيل التنفيذ، لا باعتبارها النمط الموصى به في SDK
بالنسبة إلى Plugin الخارجية الجديدة.

## خط أنابيب التحميل

عند بدء التشغيل، يقوم OpenClaw تقريبًا بما يلي:

1. اكتشاف جذور Plugin المرشحة
2. قراءة البيانات الأصلية أو بيانات الحِزم المتوافقة وبيانات الحزم الوصفية
3. رفض المرشحين غير الآمنين
4. تسوية إعدادات Plugin (`plugins.enabled`, `allow`, `deny`, `entries`,
   `slots`, `load.paths`)
5. تقرير التمكين لكل مرشح
6. تحميل الوحدات الأصلية المفعلة: تستخدم الوحدات المضمّنة المبنية محمّلًا أصليًا؛
   بينما تستخدم Plugin الأصلية غير المبنية jiti
7. استدعاء خطافات `register(api)` الأصلية وجمع التسجيلات في سجل Plugin
8. كشف السجل إلى أسطح الأوامر/وقت التشغيل

<Note>
`activate` هو اسم بديل قديم لـ `register` — ويحل المحمّل أيّهما موجود (`def.register ?? def.activate`) ويستدعيه في النقطة نفسها. تستخدم جميع Plugin المضمّنة `register`؛ ويفضّل استخدام `register` في Plugin الجديدة.
</Note>

تحدث بوابات الأمان **قبل** تنفيذ وقت التشغيل. ويتم حظر المرشحين
عندما يخرج الإدخال من جذر Plugin، أو يكون المسار قابلاً للكتابة من الجميع، أو
تبدو ملكية المسار مريبة بالنسبة إلى Plugin غير المضمّنة.

### السلوك المعتمد على البيان أولًا

البيان هو مصدر الحقيقة الخاص بمستوى التحكم. ويستخدمه OpenClaw من أجل:

- تحديد Plugin
- اكتشاف القنوات/Skills/مخطط الإعدادات أو قدرات الحِزم المُعلنة
- التحقق من `plugins.entries.<id>.config`
- تعزيز تسميات/عناصر نائبة في واجهة التحكم
- عرض بيانات التثبيت/الفهرس الوصفية
- الحفاظ على واصفات رخيصة للتفعيل والإعداد من دون تحميل وقت تشغيل Plugin

أما بالنسبة إلى Plugin الأصلية، فإن وحدة وقت التشغيل هي جزء مستوى البيانات. فهي تسجل
السلوك الفعلي مثل الخطافات أو الأدوات أو الأوامر أو تدفقات مزوّد الخدمة.

تظل كتلتا `activation` و`setup` الاختياريتان في البيان ضمن مستوى التحكم.
فهما واصفتان للبيانات الوصفية فقط لتخطيط التفعيل واكتشاف الإعداد؛
ولا تستبدلان تسجيل وقت التشغيل أو `register(...)` أو `setupEntry`.
ويستخدم أول مستهلكي التفعيل الحي الآن تلميحات الأوامر والقنوات ومزوّد الخدمة في البيان
لتضييق تحميل Plugin قبل تشكيل السجل على نطاق أوسع:

- يضيق تحميل CLI إلى Plugin التي تملك الأمر الأساسي المطلوب
- يضيق إعداد القناة/تحليل Plugin إلى Plugin التي تملك
  معرّف القناة المطلوب
- يضيق إعداد/تحليل وقت التشغيل الصريح لمزوّد الخدمة إلى Plugin التي تملك
  معرّف مزوّد الخدمة المطلوب

أصبح اكتشاف الإعداد يفضّل الآن المعرّفات المملوكة للواصفات مثل
`setup.providers` و`setup.cliBackends` لتضييق Plugin المرشحة قبل
الرجوع إلى `setup-api` بالنسبة إلى Plugin التي ما زالت تحتاج إلى خطافات وقت تشغيل
في وقت الإعداد. وإذا ادعت أكثر من Plugin مكتشفة واحدة امتلاك معرّف مزوّد إعداد
أو واجهة خلفية CLI بعد التطبيع، فإن البحث في الإعداد يرفض هذا المالك
الملتبس بدلًا من الاعتماد على ترتيب الاكتشاف.

### ما الذي يخزنه المحمّل مؤقتًا

يحتفظ OpenClaw بمخابئ قصيرة داخل العملية من أجل:

- نتائج الاكتشاف
- بيانات سجل البيان
- سجلات Plugin المحمّلة

تقلل هذه المخابئ من الزيادات المفاجئة عند بدء التشغيل ومن حمل الأوامر المتكررة.
ومن الآمن اعتبارها مخابئ أداء قصيرة العمر، لا آلية استمرارية.

ملاحظة أداء:

- اضبط `OPENCLAW_DISABLE_PLUGIN_DISCOVERY_CACHE=1` أو
  `OPENCLAW_DISABLE_PLUGIN_MANIFEST_CACHE=1` لتعطيل هذه المخابئ.
- اضبط نوافذ المخابئ باستخدام `OPENCLAW_PLUGIN_DISCOVERY_CACHE_MS` و
  `OPENCLAW_PLUGIN_MANIFEST_CACHE_MS`.

## نموذج السجل

لا تقوم Plugin المحمّلة بتعديل المتغيرات العامة العشوائية في النواة مباشرة. بل تسجّل في
سجل Plugin مركزي.

يتتبع السجل:

- سجلات Plugin (الهوية، والمصدر، والمنشأ، والحالة، والتشخيصات)
- الأدوات
- الخطافات القديمة والخطافات المكتوبة
- القنوات
- مزوّدي الخدمة
- معالجات Gateway RPC
- مسارات HTTP
- مسجّلات CLI
- الخدمات الخلفية
- الأوامر المملوكة لـ Plugin

ثم تقرأ ميزات النواة من هذا السجل بدلًا من التحدث مباشرةً إلى وحدات Plugin.
وهذا يبقي التحميل في اتجاه واحد:

- وحدة Plugin -> التسجيل في السجل
- وقت تشغيل النواة -> استهلاك السجل

وهذا الفصل مهم لقابلية الصيانة. فهو يعني أن معظم أسطح النواة تحتاج فقط إلى
نقطة تكامل واحدة: «اقرأ السجل»، لا «أضف حالة خاصة لكل وحدة Plugin».

## نداءات ربط المحادثة

يمكن لـ Plugin التي تربط محادثة أن تتفاعل عند حل الموافقة.

استخدم `api.onConversationBindingResolved(...)` لتلقي نداء بعد الموافقة على
طلب الربط أو رفضه:

```ts
export default {
  id: "my-plugin",
  register(api) {
    api.onConversationBindingResolved(async (event) => {
      if (event.status === "approved") {
        // A binding now exists for this plugin + conversation.
        console.log(event.binding?.conversationId);
        return;
      }

      // The request was denied; clear any local pending state.
      console.log(event.request.conversation.conversationId);
    });
  },
};
```

حقول حمولة النداء:

- `status`: `"approved"` أو `"denied"`
- `decision`: `"allow-once"` أو `"allow-always"` أو `"deny"`
- `binding`: الربط المحلول للطلبات الموافق عليها
- `request`: ملخص الطلب الأصلي، وتلميح الفصل، ومعرّف المرسل، و
  البيانات الوصفية للمحادثة

هذا النداء مخصص للإشعار فقط. فهو لا يغيّر من يُسمح له بربط
محادثة، ويعمل بعد انتهاء معالجة الموافقة في النواة.

## خطافات وقت التشغيل الخاصة بمزوّد الخدمة

أصبحت Plugin مزوّد الخدمة الآن تتكون من طبقتين:

- بيانات البيان الوصفية: `providerAuthEnvVars` للبحث الرخيص عن توثيق مزوّد الخدمة من البيئة
  قبل تحميل وقت التشغيل، و`providerAuthAliases` لمتغيرات مزوّد الخدمة التي تشترك
  في التوثيق، و`channelEnvVars` للبحث الرخيص عن بيئة/إعداد القناة قبل
  تحميل وقت التشغيل، بالإضافة إلى `providerAuthChoices` لملصقات
  الإعداد/اختيار التوثيق الرخيصة وبيانات أعلام CLI الوصفية قبل تحميل وقت التشغيل
- خطافات وقت الإعداد: `catalog` / `discovery` القديم بالإضافة إلى `applyConfigDefaults`
- خطافات وقت التشغيل: `normalizeModelId`, `normalizeTransport`,
  `normalizeConfig`,
  `applyNativeStreamingUsageCompat`, `resolveConfigApiKey`,
  `resolveSyntheticAuth`, `resolveExternalAuthProfiles`,
  `shouldDeferSyntheticProfileAuth`,
  `resolveDynamicModel`, `prepareDynamicModel`, `normalizeResolvedModel`,
  `contributeResolvedModelCompat`, `capabilities`,
  `normalizeToolSchemas`, `inspectToolSchemas`,
  `resolveReasoningOutputMode`, `prepareExtraParams`, `createStreamFn`,
  `wrapStreamFn`, `resolveTransportTurnState`,
  `resolveWebSocketSessionPolicy`, `formatApiKey`, `refreshOAuth`,
  `buildAuthDoctorHint`, `matchesContextOverflowError`,
  `classifyFailoverReason`, `isCacheTtlEligible`,
  `buildMissingAuthMessage`, `suppressBuiltInModel`, `augmentModelCatalog`,
  `resolveThinkingProfile`, `isBinaryThinking`, `supportsXHighThinking`,
  `resolveDefaultThinkingLevel`, `isModernModelRef`, `prepareRuntimeAuth`,
  `resolveUsageAuth`, `fetchUsageSnapshot`, `createEmbeddingProvider`,
  `buildReplayPolicy`,
  `sanitizeReplayHistory`, `validateReplayTurns`, `onModelSelected`

ولا يزال OpenClaw يملك حلقة الوكيل العامة، والتحويل الاحتياطي، ومعالجة
النصوص، وسياسة الأدوات. وتمثل هذه الخطافات سطح الامتداد للسلوك الخاص بمزوّد الخدمة من دون
الحاجة إلى نقل استدلال مخصص بالكامل.

استخدم `providerAuthEnvVars` في البيان عندما يكون لدى مزوّد الخدمة بيانات اعتماد مستندة إلى البيئة
وينبغي أن تراها مسارات التوثيق/الحالة/منتقي النماذج العامة من دون تحميل وقت تشغيل Plugin.
واستخدم `providerAuthAliases` في البيان عندما ينبغي أن يعيد معرّف مزوّد خدمة ما
استخدام متغيرات البيئة، وملفات تعريف التوثيق، والتوثيق المعتمد على الإعدادات، وخيار
إعداد API-key الخاص بمعرّف مزوّد خدمة آخر. واستخدم `providerAuthChoices` في البيان عندما ينبغي لأسطح
CLI الخاصة بالإعداد/اختيار التوثيق أن تعرف معرّف الخيار الخاص بالمزوّد، وتسميات المجموعات،
وربط التوثيق البسيط بعلم واحد من دون تحميل وقت تشغيل مزوّد الخدمة. وأبقِ
`envVars` الخاصة بوقت تشغيل مزوّد الخدمة لتلميحات المشغّل مثل تسميات الإعداد أو متغيرات
إعداد client-id/client-secret الخاصة بـ OAuth.

استخدم `channelEnvVars` في البيان عندما تمتلك القناة توثيقًا أو إعدادًا مستندًا إلى البيئة
وينبغي أن تراه آلية الاحتياط العامة لبيئة shell أو فحوصات الإعدادات/الحالة أو
مطالبات الإعداد من دون تحميل وقت تشغيل القناة.

### ترتيب الخطافات واستخدامها

بالنسبة إلى Plugin النماذج/مزوّد الخدمة، يستدعي OpenClaw الخطافات بهذا الترتيب التقريبي.
ويمثل عمود "متى يُستخدم" دليل القرار السريع.

| #   | الخطاف                             | ما الذي يفعله                                                                                                   | متى يُستخدم                                                                                                                                    |
| --- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | `catalog`                          | ينشر إعدادات مزوّد الخدمة داخل `models.providers` أثناء توليد `models.json`                                     | عندما يملك مزوّد الخدمة فهرسًا أو قيمًا افتراضية لـ base URL                                                                                   |
| 2   | `applyConfigDefaults`              | يطبق القيم الافتراضية العامة المملوكة لمزوّد الخدمة أثناء تشكيل الإعدادات                                        | عندما تعتمد القيم الافتراضية على وضع التوثيق أو البيئة أو دلالات عائلة نماذج مزوّد الخدمة                                                       |
| --  | _(البحث المضمّن عن النموذج)_        | يحاول OpenClaw أولًا مسار السجل/الفهرس العادي                                                                     | _(ليس خطاف Plugin)_                                                                                                                            |
| 3   | `normalizeModelId`                 | يطبّع الأسماء البديلة القديمة أو التجريبية لمعرّف النموذج قبل البحث                                              | عندما يملك مزوّد الخدمة تنظيف الأسماء البديلة قبل تحليل النموذج القانوني                                                                        |
| 4   | `normalizeTransport`               | يطبّع `api` / `baseUrl` الخاصين بعائلة مزوّد الخدمة قبل تجميع النموذج العام                                      | عندما يملك مزوّد الخدمة تنظيف النقل لمعرّفات مزوّد خدمة مخصصة ضمن عائلة النقل نفسها                                                            |
| 5   | `normalizeConfig`                  | يطبّع `models.providers.<id>` قبل تحليل وقت التشغيل/مزوّد الخدمة                                                 | عندما يحتاج مزوّد الخدمة إلى تنظيف الإعدادات بطريقة ينبغي أن تبقى مع Plugin؛ كما تدعم مساعدات Google-family المضمّنة إدخالات Google المدعومة |
| 6   | `applyNativeStreamingUsageCompat`  | يطبق إعادة كتابة توافق native streaming usage على مزوّدي الخدمة في الإعدادات                                    | عندما يحتاج مزوّد الخدمة إلى إصلاحات لبيانات usage الوصفية الأصلية المعتمدة على نقطة النهاية                                                   |
| 7   | `resolveConfigApiKey`              | يحل توثيق env-marker لمزوّدي الخدمة في الإعدادات قبل تحميل توثيق وقت التشغيل                                      | عندما يملك مزوّد الخدمة تحليل API-key خاصًا به عبر env-marker؛ كما يملك `amazon-bedrock` أيضًا محلل AWS env-marker مضمّنًا هنا               |
| 8   | `resolveSyntheticAuth`             | يكشف التوثيق المحلي/المستضاف ذاتيًا أو التوثيق المعتمد على الإعدادات من دون حفظ نص صريح                          | عندما يمكن لمزوّد الخدمة العمل باستخدام علامة بيانات اعتماد تركيبية/محلية                                                                       |
| 9   | `resolveExternalAuthProfiles`      | يركب ملفات تعريف التوثيق الخارجية المملوكة لمزوّد الخدمة؛ والقيمة الافتراضية لـ `persistence` هي `runtime-only` لبيانات الاعتماد المملوكة لـ CLI/app | عندما يعيد مزوّد الخدمة استخدام بيانات اعتماد توثيق خارجية من دون حفظ refresh tokens المنسوخة؛ صرّح بـ `contracts.externalAuthProviders` في البيان |
| 10  | `shouldDeferSyntheticProfileAuth`  | يخفض أولوية العناصر النائبة التركيبية المخزنة لملف التعريف خلف التوثيق المعتمد على البيئة/الإعدادات             | عندما يخزّن مزوّد الخدمة ملفات تعريف تركيبية نائبة ينبغي ألا تتقدم في الأولوية                                                                  |
| 11  | `resolveDynamicModel`              | احتياط متزامن لمعرّفات نماذج يملكها مزوّد الخدمة ولم توجد بعد في السجل المحلي                                     | عندما يقبل مزوّد الخدمة معرّفات نماذج عشوائية من المصدر العلوي                                                                                  |
| 12  | `prepareDynamicModel`              | تهيئة غير متزامنة، ثم يُعاد تشغيل `resolveDynamicModel`                                                          | عندما يحتاج مزوّد الخدمة إلى بيانات وصفية من الشبكة قبل تحليل المعرّفات غير المعروفة                                                            |
| 13  | `normalizeResolvedModel`           | إعادة كتابة نهائية قبل أن يستخدم embedded runner النموذج المحلول                                                 | عندما يحتاج مزوّد الخدمة إلى إعادة كتابة النقل مع الاستمرار في استخدام نقل من النواة                                                            |
| 14  | `contributeResolvedModelCompat`    | يضيف إشارات توافق لنماذج المورّد خلف نقل متوافق آخر                                                               | عندما يتعرف مزوّد الخدمة على نماذجه الخاصة في عمليات نقل الوكيل من دون الاستيلاء على مزوّد الخدمة نفسه                                          |
| 15  | `capabilities`                     | بيانات وصفية مملوكة لمزوّد الخدمة خاصة بالنصوص/الأدوات وتستخدمها منطق النواة المشتركة                           | عندما يحتاج مزوّد الخدمة إلى خصائص شاذة خاصة بالنصوص أو عائلة مزوّد الخدمة                                                                       |
| 16  | `normalizeToolSchemas`             | يطبّع مخططات الأدوات قبل أن يراها embedded runner                                                                  | عندما يحتاج مزوّد الخدمة إلى تنظيف مخططات خاص بعائلة النقل                                                                                      |
| 17  | `inspectToolSchemas`               | يكشف تشخيصات المخطط المملوكة لمزوّد الخدمة بعد التطبيع                                                           | عندما يريد مزوّد الخدمة تحذيرات الكلمات المفتاحية من دون تعليم النواة قواعد خاصة به                                                              |
| 18  | `resolveReasoningOutputMode`       | يختار بين عقد مخرجات الاستدلال الأصلية أو الموسومة                                                               | عندما يحتاج مزوّد الخدمة إلى مخرجات reasoning/final موسومة بدلًا من الحقول الأصلية                                                              |
| 19  | `prepareExtraParams`               | تطبيع معلمات الطلب قبل أغلفة خيارات البث العامة                                                                  | عندما يحتاج مزوّد الخدمة إلى معلمات طلب افتراضية أو تنظيف معلمات خاص به                                                                         |
| 20  | `createStreamFn`                   | يستبدل مسار البث العادي بالكامل بنقل مخصص                                                                          | عندما يحتاج مزوّد الخدمة إلى بروتوكول سلكي مخصص، لا مجرد غلاف                                                                                   |
| 21  | `wrapStreamFn`                     | غلاف للبث بعد تطبيق الأغلفة العامة                                                                                 | عندما يحتاج مزوّد الخدمة إلى أغلفة توافق لطلبات الرؤوس/الجسم/النموذج من دون نقل مخصص                                                            |
| 22  | `resolveTransportTurnState`        | يرفق رؤوسًا أو بيانات وصفية أصلية لكل دور                                                                          | عندما يريد مزوّد الخدمة من عمليات النقل العامة إرسال هوية الدور الأصلية الخاصة به                                                               |
| 23  | `resolveWebSocketSessionPolicy`    | يرفق رؤوس WebSocket أصلية أو سياسة تهدئة للجلسة                                                                   | عندما يريد مزوّد الخدمة من عمليات نقل WS العامة ضبط رؤوس الجلسة أو سياسة الاحتياط                                                                |
| 24  | `formatApiKey`                     | منسق ملف تعريف التوثيق: يتحول ملف التعريف المخزن إلى سلسلة `apiKey` وقت التشغيل                                   | عندما يخزّن مزوّد الخدمة بيانات توثيق إضافية ويحتاج إلى شكل رمز مخصص في وقت التشغيل                                                              |
| 25  | `refreshOAuth`                     | تجاوز لتحديث OAuth لنقاط نهاية تحديث مخصصة أو لسياسة فشل التحديث                                                  | عندما لا يتوافق مزوّد الخدمة مع آليات التحديث المشتركة في `pi-ai`                                                                                |
| 26  | `buildAuthDoctorHint`              | تلميح إصلاح يُضاف عند فشل تحديث OAuth                                                                             | عندما يحتاج مزوّد الخدمة إلى إرشاد إصلاح توثيق خاص به بعد فشل التحديث                                                                            |
| 27  | `matchesContextOverflowError`      | مطابق مملوك لمزوّد الخدمة لحالات تجاوز نافذة السياق                                                               | عندما تكون لدى مزوّد الخدمة أخطاء تجاوز خام لا تلتقطها الإرشادات العامة                                                                           |
| 28  | `classifyFailoverReason`           | تصنيف سبب التحويل الاحتياطي المملوك لمزوّد الخدمة                                                                  | عندما يستطيع مزوّد الخدمة ربط أخطاء API/النقل الخام بالحدود المعدلية/التحميل الزائد/إلخ                                                         |
| 29  | `isCacheTtlEligible`               | سياسة prompt-cache لمزوّدي الخدمة الوكيلين/backhaul                                                               | عندما يحتاج مزوّد الخدمة إلى ضبط TTL خاص بالوكيل                                                                                                 |
| 30  | `buildMissingAuthMessage`          | بديل للرسالة العامة الخاصة باسترداد التوثيق المفقود                                                                 | عندما يحتاج مزوّد الخدمة إلى تلميح استرداد خاص بحالة التوثيق المفقود                                                                             |
| 31  | `suppressBuiltInModel`             | إخفاء النماذج القديمة من المصدر العلوي مع تلميح خطأ اختياري موجّه للمستخدم                                        | عندما يحتاج مزوّد الخدمة إلى إخفاء صفوف قديمة من المصدر العلوي أو استبدالها بتلميح من المورّد                                                    |
| 32  | `augmentModelCatalog`              | صفوف فهرس تركيبية/نهائية تُضاف بعد الاكتشاف                                                                       | عندما يحتاج مزوّد الخدمة إلى صفوف توافق مستقبلي تركيبية في `models list` وأدوات الاختيار                                                       |
| 33  | `resolveThinkingProfile`           | مجموعة مستوى `/think` الخاصة بالنموذج، وتسميات العرض، والقيمة الافتراضية                                          | عندما يكشف مزوّد الخدمة سلم thinking مخصصًا أو تسمية ثنائية لبعض النماذج                                                                          |
| 34  | `isBinaryThinking`                 | خطاف توافق لتبديل الاستدلال تشغيل/إيقاف                                                                             | عندما يكشف مزوّد الخدمة عن thinking ثنائي فقط تشغيل/إيقاف                                                                                        |
| 35  | `supportsXHighThinking`            | خطاف توافق لدعم reasoning من نوع `xhigh`                                                                          | عندما يريد مزوّد الخدمة تمكين `xhigh` فقط على مجموعة فرعية من النماذج                                                                            |
| 36  | `resolveDefaultThinkingLevel`      | خطاف توافق لمستوى `/think` الافتراضي                                                                               | عندما يملك مزوّد الخدمة سياسة `/think` الافتراضية لعائلة نماذج                                                                                   |
| 37  | `isModernModelRef`                | مطابق modern-model لمرشحات ملفات التعريف الحية واختيار smoke                                                 | عندما يملك مزوّد الخدمة مطابقة preferred-model الخاصة بالاختبارات الحية/smoke                                                                |
| 38  | `prepareRuntimeAuth`              | يستبدل بيانات اعتماد مضبوطة بالرمز/المفتاح الفعلي لوقت التشغيل قبل الاستدلال مباشرةً                         | عندما يحتاج مزوّد الخدمة إلى تبادل رمز أو بيانات اعتماد طلب قصيرة العمر                                                                        |
| 39  | `resolveUsageAuth`                | يحل بيانات اعتماد الاستخدام/الفوترة لـ `/usage` والأسطح الأخرى المرتبطة بالحالة                              | عندما يحتاج مزوّد الخدمة إلى تحليل مخصص لرمز الاستخدام/الحصة أو إلى بيانات اعتماد استخدام مختلفة                                              |
| 40  | `fetchUsageSnapshot`              | يجلب ويطبّع لقطات الاستخدام/الحصة الخاصة بمزوّد الخدمة بعد حل التوثيق                                         | عندما يحتاج مزوّد الخدمة إلى نقطة نهاية استخدام خاصة به أو إلى محلل حمولة خاص به                                                               |
| 41  | `createEmbeddingProvider`         | يبني محول embedding مملوكًا لمزوّد الخدمة من أجل الذاكرة/البحث                                                 | عندما ينتمي سلوك تضمين الذاكرة إلى Plugin مزوّد الخدمة                                                                                         |
| 42  | `buildReplayPolicy`               | يعيد سياسة replay تتحكم في معالجة النصوص لمزوّد الخدمة                                                        | عندما يحتاج مزوّد الخدمة إلى سياسة نصوص مخصصة (مثل إزالة كتل thinking)                                                                        |
| 43  | `sanitizeReplayHistory`           | يعيد كتابة سجل replay بعد التنظيف العام للنصوص                                                                | عندما يحتاج مزوّد الخدمة إلى إعادة كتابة خاصة به في replay تتجاوز مساعدات Compaction المشتركة                                                 |
| 44  | `validateReplayTurns`             | التحقق النهائي من أدوار replay أو إعادة تشكيلها قبل embedded runner                                           | عندما يحتاج نقل مزوّد الخدمة إلى تحقق أشد صرامة من الأدوار بعد التنقية العامة                                                                  |
| 45  | `onModelSelected`                 | يشغّل آثارًا جانبية بعد الاختيار مملوكة لمزوّد الخدمة                                                         | عندما يحتاج مزوّد الخدمة إلى telemetry أو حالة مملوكة له عندما يصبح نموذج ما نشطًا                                                             |

يتحقق `normalizeModelId` و`normalizeTransport` و`normalizeConfig` أولًا من
Plugin مزوّد الخدمة المطابقة، ثم ينتقل إلى بقية Plugin مزوّد الخدمة القادرة على استخدام الخطافات
حتى تقوم إحداها فعليًا بتغيير معرّف النموذج أو النقل/الإعدادات. وهذا يحافظ على
عمل الطبقات الوسيطة للأسماء البديلة/التوافق الخاصة بمزوّد الخدمة من دون
إلزام المستدعي بمعرفة أي Plugin مضمّنة تملك إعادة الكتابة. وإذا لم يُعِد
أي خطاف مزوّد خدمة كتابة إدخال إعدادات مدعوم من Google-family،
فسيستمر مُطبّع إعدادات Google المضمّن في تطبيق تنظيف التوافق هذا.

إذا كان مزوّد الخدمة يحتاج إلى بروتوكول سلكي مخصص بالكامل أو منفذ طلبات مخصص،
فهذه فئة مختلفة من الامتدادات. وهذه الخطافات مخصصة لسلوك مزوّد الخدمة
الذي لا يزال يعمل ضمن حلقة الاستدلال العادية في OpenClaw.

### مثال على مزوّد خدمة

```ts
api.registerProvider({
  id: "example-proxy",
  label: "Example Proxy",
  auth: [],
  catalog: {
    order: "simple",
    run: async (ctx) => {
      const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey;
      if (!apiKey) {
        return null;
      }
      return {
        provider: {
          baseUrl: "https://proxy.example.com/v1",
          apiKey,
          api: "openai-completions",
          models: [{ id: "auto", name: "Auto" }],
        },
      };
    },
  },
  resolveDynamicModel: (ctx) => ({
    id: ctx.modelId,
    name: ctx.modelId,
    provider: "example-proxy",
    api: "openai-completions",
    baseUrl: "https://proxy.example.com/v1",
    reasoning: false,
    input: ["text"],
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
    contextWindow: 128000,
    maxTokens: 8192,
  }),
  prepareRuntimeAuth: async (ctx) => {
    const exchanged = await exchangeToken(ctx.apiKey);
    return {
      apiKey: exchanged.token,
      baseUrl: exchanged.baseUrl,
      expiresAt: exchanged.expiresAt,
    };
  },
  resolveUsageAuth: async (ctx) => {
    const auth = await ctx.resolveOAuthToken();
    return auth ? { token: auth.token } : null;
  },
  fetchUsageSnapshot: async (ctx) => {
    return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn);
  },
});
```

### أمثلة مضمّنة

تستخدم Plugin مزوّد الخدمة المضمّنة الخطافات المذكورة أعلاه في تركيبات
مفصلة وفق احتياجات كل مورّد في الفهرسة والتوثيق وthinking وreplay
وتتبع الاستخدام. وتوجد المجموعة الدقيقة للخطافات لكل مزوّد خدمة مع
مصدر Plugin تحت `extensions/`؛ وتعامل مع ذلك باعتباره القائمة المرجعية
بدلًا من تكرارها هنا.

أنماط توضيحية:

- **مزوّدو الفهرسة الممرِّرة** (OpenRouter وKilocode وZ.AI وxAI) يسجلون
  `catalog` بالإضافة إلى `resolveDynamicModel`/`prepareDynamicModel` حتى يتمكنوا من
  إظهار معرّفات النماذج من المصدر العلوي قبل الفهرس الثابت في OpenClaw.
- **مزوّدو OAuth + نقاط نهاية الاستخدام** (GitHub Copilot وGemini CLI وChatGPT
  Codex وMiniMax وXiaomi وz.ai) يقرنون `prepareRuntimeAuth` أو `formatApiKey`
  مع `resolveUsageAuth` + `fetchUsageSnapshot` لامتلاك تبادل الرموز و
  تكامل `/usage`.
- **تنظيف replay / النصوص** مشترك عبر عائلات مسماة:
  `google-gemini` و`passthrough-gemini` و`anthropic-by-model` و
  `hybrid-anthropic-openai`. وتختار مزوّدو الخدمة هذا عبر `buildReplayPolicy`
  بدلًا من أن ينفذ كل واحد منهم تنظيف النصوص بنفسه.
- **مزوّدو الخدمة المضمّنة الخاصة بالفهرسة فقط** (`byteplus` و`cloudflare-ai-gateway` و
  `huggingface` و`kimi-coding` و`nvidia` و`qianfan` و`synthetic` و`together` و
  `venice` و`vercel-ai-gateway` و`volcengine`) تسجل `catalog` فقط وتستخدم
  حلقة الاستدلال المشتركة.
- **مساعدات البث الخاصة بـ Anthropic** (رؤوس beta، و`/fast`/`serviceTier`،
  و`context1m`) تعيش داخل الوصلة العامة `api.ts` /
  `contract-api.ts` الخاصة بـ Plugin المضمّنة Anthropic (`wrapAnthropicProviderStream`, `resolveAnthropicBetas`,
  `resolveAnthropicFastMode`, `resolveAnthropicServiceTier`) وليس في
  SDK العامة.

## مساعدات وقت التشغيل

يمكن لـ Plugin الوصول إلى مساعدات مختارة من النواة عبر `api.runtime`. بالنسبة إلى TTS:

```ts
const clip = await api.runtime.tts.textToSpeech({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

const result = await api.runtime.tts.textToSpeechTelephony({
  text: "Hello from OpenClaw",
  cfg: api.config,
});

const voices = await api.runtime.tts.listVoices({
  provider: "elevenlabs",
  cfg: api.config,
});
```

ملاحظات:

- يعيد `textToSpeech` حمولة مخرجات TTS الأساسية العادية لأسطح الملفات/الملاحظات الصوتية.
- يستخدم إعدادات `messages.tts` الأساسية واختيار مزوّد الخدمة.
- يعيد مخزنًا مؤقتًا للصوت PCM + معدل عينة. ويجب على Plugin إعادة أخذ العينات/الترميز من أجل مزوّدي الخدمة.
- تكون `listVoices` اختيارية لكل مزوّد خدمة. استخدمها لمنتقيات الأصوات أو تدفقات الإعداد المملوكة للمورّد.
- يمكن أن تتضمن قوائم الأصوات بيانات وصفية أغنى مثل اللغة والمنطقة والنوع الاجتماعي ووسوم الشخصية من أجل المنتقيات الواعية بالمزوّد.
- يدعم OpenAI وElevenLabs الاتصالات الهاتفية اليوم. أما Microsoft فلا يدعمها.

يمكن لـ Plugin أيضًا تسجيل مزوّدي الكلام عبر `api.registerSpeechProvider(...)`.

```ts
api.registerSpeechProvider({
  id: "acme-speech",
  label: "Acme Speech",
  isConfigured: ({ config }) => Boolean(config.messages?.tts),
  synthesize: async (req) => {
    return {
      audioBuffer: Buffer.from([]),
      outputFormat: "mp3",
      fileExtension: ".mp3",
      voiceCompatible: false,
    };
  },
});
```

ملاحظات:

- أبقِ سياسة TTS والاحتياط وتسليم الردود في النواة.
- استخدم مزوّدي الكلام لسلوك التوليف المملوك للمورّد.
- يتم تطبيع الإدخال القديم `edge` الخاص بـ Microsoft إلى معرّف مزوّد الخدمة `microsoft`.
- نموذج الملكية المفضّل موجّه حسب الشركة: يمكن لـ Plugin مورّد واحدة أن تملك
  النصوص والكلام والصور ومزوّدي الوسائط مستقبلاً كلما أضاف OpenClaw
  عقود تلك القدرات.

بالنسبة إلى فهم الصور/الصوت/الفيديو، تسجّل Plugin مزوّد فهم وسائط
واحدًا مكتوبًا بدلًا من حقيبة عامة من المفاتيح/القيم:

```ts
api.registerMediaUnderstandingProvider({
  id: "google",
  capabilities: ["image", "audio", "video"],
  describeImage: async (req) => ({ text: "..." }),
  transcribeAudio: async (req) => ({ text: "..." }),
  describeVideo: async (req) => ({ text: "..." }),
});
```

ملاحظات:

- أبقِ التنسيق والاحتياط والإعدادات وربط القنوات في النواة.
- أبقِ سلوك المورّد في Plugin مزوّد الخدمة.
- ينبغي أن يظل التوسع الإضافي مكتوبًا: طرق اختيارية جديدة، وحقول نتائج
  اختيارية جديدة، وقدرات اختيارية جديدة.
- يتبع توليد الفيديو بالفعل النمط نفسه:
  - تملك النواة عقد القدرة ومساعد وقت التشغيل
  - تسجّل Plugin المورّد `api.registerVideoGenerationProvider(...)`
  - تستهلك Plugin الميزات/القنوات `api.runtime.videoGeneration.*`

وبالنسبة إلى مساعدات وقت التشغيل لفهم الوسائط، يمكن لـ Plugin استدعاء:

```ts
const image = await api.runtime.mediaUnderstanding.describeImageFile({
  filePath: "/tmp/inbound-photo.jpg",
  cfg: api.config,
  agentDir: "/tmp/agent",
});

const video = await api.runtime.mediaUnderstanding.describeVideoFile({
  filePath: "/tmp/inbound-video.mp4",
  cfg: api.config,
});
```

وبالنسبة إلى نسخ الصوت، يمكن لـ Plugin استخدام وقت تشغيل فهم الوسائط
أو الاسم البديل الأقدم STT:

```ts
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  // Optional when MIME cannot be inferred reliably:
  mime: "audio/ogg",
});
```

ملاحظات:

- `api.runtime.mediaUnderstanding.*` هو السطح المشترك المفضّل من أجل
  فهم الصور/الصوت/الفيديو.
- يستخدم إعدادات الصوت الأساسية الخاصة بفهم الوسائط (`tools.media.audio`) وترتيب الاحتياط لمزوّد الخدمة.
- يعيد `{ text: undefined }` عندما لا يتم إنتاج مخرجات نسخ (مثلًا عند تخطي الإدخال أو عدم دعمه).
- يظل `api.runtime.stt.transcribeAudioFile(...)` اسمًا بديلًا من أجل التوافق.

يمكن لـ Plugin أيضًا إطلاق تشغيلات وكلاء فرعيين في الخلفية عبر `api.runtime.subagent`:

```ts
const result = await api.runtime.subagent.run({
  sessionKey: "agent:main:subagent:search-helper",
  message: "Expand this query into focused follow-up searches.",
  provider: "openai",
  model: "gpt-4.1-mini",
  deliver: false,
});
```

ملاحظات:

- `provider` و`model` تجاوزات اختيارية لكل تشغيل، وليست تغييرات دائمة للجلسة.
- لا يلتزم OpenClaw بحقول التجاوز هذه إلا للمستدعين الموثوقين.
- بالنسبة إلى التشغيلات الاحتياطية المملوكة لـ Plugin، يجب على المشغّلين تفعيل ذلك صراحةً باستخدام `plugins.entries.<id>.subagent.allowModelOverride: true`.
- استخدم `plugins.entries.<id>.subagent.allowedModels` لتقييد Plugin الموثوقة بأهداف قانونية محددة من `provider/model`، أو `"*"` للسماح صراحةً بأي هدف.
- تظل تشغيلات الوكيل الفرعي لـ Plugin غير الموثوقة تعمل، لكن يتم رفض طلبات التجاوز بدلًا من الرجوع الاحتياطي بصمت.

وبالنسبة إلى البحث على الويب، يمكن لـ Plugin استهلاك مساعد وقت التشغيل المشترك بدلًا من
الوصول إلى ربط أداة الوكيل:

```ts
const providers = api.runtime.webSearch.listProviders({
  config: api.config,
});

const result = await api.runtime.webSearch.search({
  config: api.config,
  args: {
    query: "OpenClaw plugin runtime helpers",
    count: 5,
  },
});
```

يمكن لـ Plugin أيضًا تسجيل مزوّدي البحث على الويب عبر
`api.registerWebSearchProvider(...)`.

ملاحظات:

- أبقِ اختيار مزوّد الخدمة، وتحليل بيانات الاعتماد، ودلالات الطلب المشتركة في النواة.
- استخدم مزوّدي البحث على الويب لعمليات نقل البحث الخاصة بالمورّد.
- `api.runtime.webSearch.*` هو السطح المشترك المفضّل لـ Plugin الميزات/القنوات التي تحتاج إلى سلوك بحث من دون الاعتماد على غلاف أداة الوكيل.

### `api.runtime.imageGeneration`

```ts
const result = await api.runtime.imageGeneration.generate({
  config: api.config,
  args: { prompt: "A friendly lobster mascot", size: "1024x1024" },
});

const providers = api.runtime.imageGeneration.listProviders({
  config: api.config,
});
```

- `generate(...)`: إنشاء صورة باستخدام سلسلة مزوّدي توليد الصور المضبوطة.
- `listProviders(...)`: إدراج مزوّدي توليد الصور المتاحين وقدراتهم.

## مسارات Gateway HTTP

يمكن لـ Plugin كشف نقاط نهاية HTTP باستخدام `api.registerHttpRoute(...)`.

```ts
api.registerHttpRoute({
  path: "/acme/webhook",
  auth: "plugin",
  match: "exact",
  handler: async (_req, res) => {
    res.statusCode = 200;
    res.end("ok");
    return true;
  },
});
```

حقول المسار:

- `path`: مسار التوجيه ضمن خادم HTTP الخاص بـ Gateway.
- `auth`: مطلوب. استخدم `"gateway"` لطلب توثيق Gateway العادي، أو `"plugin"` للتوثيق/التحقق من Webhook الذي تديره Plugin.
- `match`: اختياري. `"exact"` (الافتراضي) أو `"prefix"`.
- `replaceExisting`: اختياري. يتيح لنفس Plugin استبدال تسجيل المسار الحالي الخاص بها.
- `handler`: أعد `true` عندما يكون المسار قد عالج الطلب.

ملاحظات:

- تمت إزالة `api.registerHttpHandler(...)` وسيؤدي إلى خطأ عند تحميل Plugin. استخدم `api.registerHttpRoute(...)` بدلًا منه.
- يجب أن تعلن مسارات Plugin عن `auth` صراحةً.
- يتم رفض تعارضات `path + match` الدقيقة ما لم يكن `replaceExisting: true`، ولا يمكن لـ Plugin واحدة استبدال مسار Plugin أخرى.
- يتم رفض المسارات المتداخلة ذات مستويات `auth` المختلفة. أبقِ سلاسل السقوط `exact`/`prefix` على مستوى التوثيق نفسه فقط.
- لا تتلقى مسارات `auth: "plugin"` نطاقات وقت تشغيل المشغّل تلقائيًا. فهي مخصصة لـ Webhook/التحقق من التوقيع الذي تديره Plugin، لا لاستدعاءات مساعدات Gateway ذات الامتيازات.
- تعمل مسارات `auth: "gateway"` داخل نطاق وقت تشغيل طلب Gateway، لكن هذا النطاق محافظ عمدًا:
  - يحافظ توثيق bearer بالمفتاح السري المشترك (`gateway.auth.mode = "token"` / `"password"`) على تثبيت نطاقات وقت تشغيل مسارات Plugin إلى `operator.write`، حتى لو أرسل المستدعي `x-openclaw-scopes`
  - أوضاع HTTP الموثوقة الحاملة للهوية (مثل `trusted-proxy` أو `gateway.auth.mode = "none"` على مدخل خاص) تلتزم بـ `x-openclaw-scopes` فقط عندما يكون الرأس موجودًا صراحةً
  - إذا كان `x-openclaw-scopes` غائبًا في طلبات مسارات Plugin الحاملة للهوية، فإن نطاق وقت التشغيل يعود إلى `operator.write`
- القاعدة العملية: لا تفترض أن مسار Plugin الموثق عبر gateway هو سطح إدارة ضمني. وإذا كان مسارك يحتاج إلى سلوك خاص بالمسؤول فقط، فاطلب وضع توثيق حاملًا للهوية ووثّق عقد الرأس `x-openclaw-scopes` الصريح.

## مسارات استيراد Plugin SDK

استخدم مسارات SDK الفرعية الضيقة بدلًا من برميل الجذر الأحادي `openclaw/plugin-sdk`
عند تأليف Plugin جديدة. المسارات الفرعية الأساسية:

| المسار الفرعي                         | الغرض                                              |
| ------------------------------------ | -------------------------------------------------- |
| `openclaw/plugin-sdk/plugin-entry`   | بدائيات تسجيل Plugin                               |
| `openclaw/plugin-sdk/channel-core`   | مساعدات إدخال/بناء القنوات                         |
| `openclaw/plugin-sdk/core`           | المساعدات العامة المشتركة والعقد الجامع            |
| `openclaw/plugin-sdk/config-schema`  | مخطط Zod الجذري لـ `openclaw.json` (`OpenClawSchema`) |

تختار Plugin القنوات من عائلة من الوصلات الضيقة — `channel-setup`,
و`setup-runtime`، و`setup-adapter-runtime`، و`setup-tools`، و`channel-pairing`,
و`channel-contract`، و`channel-feedback`، و`channel-inbound`، و`channel-lifecycle`,
و`channel-reply-pipeline`، و`command-auth`، و`secret-input`، و`webhook-ingress`,
و`channel-targets`، و`channel-actions`. ويجب أن يتوحد سلوك الموافقات
على عقد `approvalCapability` واحد بدلًا من خلطه عبر حقول Plugin
غير المرتبطة. راجع [Plugin القنوات](/ar/plugins/sdk-channel-plugins).

توجد مساعدات وقت التشغيل والإعدادات تحت مسارات فرعية `*-runtime` مطابقة
(`approval-runtime`, `config-runtime`, `infra-runtime`, `agent-runtime`,
`lazy-runtime`, `directory-runtime`, `text-runtime`, `runtime-store`, وغيرها).

<Info>
`openclaw/plugin-sdk/channel-runtime` مهجور — وهو طبقة توافقية لـ
Plugin الأقدم. يجب على الكود الجديد استيراد البدائيات العامة الأضيق بدلًا من ذلك.
</Info>

نقاط الإدخال الداخلية للمستودع (لكل جذر حزمة Plugin مضمّنة):

- `index.js` — إدخال Plugin المضمّنة
- `api.js` — برميل المساعدات/الأنواع
- `runtime-api.js` — برميل مخصص لوقت التشغيل فقط
- `setup-entry.js` — إدخال إعداد Plugin

يجب على Plugin الخارجية الاستيراد فقط من المسارات الفرعية `openclaw/plugin-sdk/*`. ويُمنع
استيراد `src/*` الخاص بحزمة Plugin أخرى من النواة أو من Plugin أخرى.
وتفضّل نقاط الإدخال المحمّلة عبر الواجهة التكوينية لقطة إعدادات وقت التشغيل النشطة
عند وجودها، ثم تعود إلى ملف الإعدادات المحلول على القرص.

توجد مسارات فرعية خاصة بالقدرات مثل `image-generation` و`media-understanding`
و`speech` لأن Plugin المضمّنة تستخدمها اليوم. وهي ليست تلقائيًا عقودًا
خارجية مجمدة على المدى الطويل — تحقّق من صفحة مرجع SDK ذات الصلة عند الاعتماد عليها.

## مخططات أداة الرسائل

يجب أن تملك Plugin مساهمات مخطط `describeMessageTool(...)` الخاصة بالقناة
للأدوات غير الرسائلية مثل التفاعلات والقراءات والاستطلاعات.
ويجب أن يستخدم عرض الإرسال المشترك عقد `MessagePresentation` العام
بدلًا من حقول الأزرار أو المكوّنات أو الكتل أو البطاقات الأصلية الخاصة بمزوّد الخدمة.
راجع [عرض الرسائل](/ar/plugins/message-presentation) للاطلاع على العقد
وقواعد الاحتياط والتعيين إلى مزوّد الخدمة وقائمة التحقق لمؤلفي Plugin.

تعلن Plugin القادرة على الإرسال ما يمكنها عرضه من خلال قدرات الرسائل:

- `presentation` لكتل العرض الدلالية (`text`, `context`, `divider`, `buttons`, `select`)
- `delivery-pin` لطلبات التسليم المثبّتة

تقرر النواة ما إذا كانت ستعرض العرض بشكل أصلي أم ستخفضه إلى نص.
ولا تكشف منافذ هروب لواجهة مستخدم أصلية خاصة بمزوّد الخدمة من أداة الرسائل العامة.
وتظل مساعدات SDK المهجورة الخاصة بالمخططات الأصلية القديمة مُصدّرة من أجل
Plugin الخارجية الحالية، لكن Plugin الجديدة يجب ألا تستخدمها.

## تحليل أهداف القنوات

يجب أن تملك Plugin القنوات دلالات الأهداف الخاصة بالقناة. وأبقِ
مضيف الإرسال المشترك عامًا واستخدم سطح محول المراسلة لقواعد مزوّد الخدمة:

- يقرر `messaging.inferTargetChatType({ to })` ما إذا كان الهدف المطبع
  يجب التعامل معه باعتباره `direct` أو `group` أو `channel` قبل البحث في الدليل.
- يخبر `messaging.targetResolver.looksLikeId(raw, normalized)` النواة ما إذا كان
  ينبغي لإدخال ما أن يتجاوز مباشرة إلى تحليل يشبه المعرّفات بدلًا من البحث في الدليل.
- يكون `messaging.targetResolver.resolveTarget(...)` احتياط Plugin عندما
  تحتاج النواة إلى تحليل نهائي يملكه مزوّد الخدمة بعد التطبيع أو بعد الإخفاق في الدليل.
- تملك `messaging.resolveOutboundSessionRoute(...)` إنشاء مسار الجلسة
  الخاص بمزوّد الخدمة بمجرد تحليل الهدف.

التقسيم الموصى به:

- استخدم `inferTargetChatType` لقرارات الفئات التي ينبغي أن تحدث قبل
  البحث في الأقران/المجموعات.
- استخدم `looksLikeId` لفحوصات من نوع «تعامل مع هذا باعتباره معرّف هدف صريح/أصلي».
- استخدم `resolveTarget` لاحتياط التطبيع الخاص بمزوّد الخدمة، لا
  للبحث الواسع في الدليل.
- أبقِ المعرّفات الأصلية الخاصة بمزوّد الخدمة مثل معرّفات الدردشة ومعرّفات السلاسل وJIDs والمعرّفات والرموز ومعرّفات الغرف
  داخل قيم `target` أو المعاملات الخاصة بمزوّد الخدمة، لا في حقول SDK العامة.

## الأدلة المعتمدة على الإعدادات

Plugin التي تشتق إدخالات الدليل من الإعدادات يجب أن تبقي هذا المنطق داخل
Plugin وأن تعيد استخدام المساعدات المشتركة من
`openclaw/plugin-sdk/directory-runtime`.

استخدم هذا عندما تحتاج قناة إلى أقران/مجموعات معتمدة على الإعدادات مثل:

- أقران الرسائل الخاصة المعتمدين على قائمة السماح
- خرائط القنوات/المجموعات المضبوطة
- احتياطات الدليل الثابتة ضمن نطاق الحساب

لا تتعامل المساعدات المشتركة في `directory-runtime` إلا مع العمليات العامة:

- ترشيح الاستعلام
- تطبيق الحدود
- مساعدات إزالة التكرار/التطبيع
- بناء `ChannelDirectoryEntry[]`

أما فحص الحساب الخاص بالقناة وتطبيع المعرّفات فيجب أن يبقيا في
تنفيذ Plugin.

## فهارس مزوّدي الخدمة

يمكن لـ Plugin مزوّد الخدمة تعريف فهارس النماذج الخاصة بالاستدلال باستخدام
`registerProvider({ catalog: { run(...) { ... } } })`.

تعيد `catalog.run(...)` الشكل نفسه الذي يكتبه OpenClaw في
`models.providers`:

- `{ provider }` لإدخال مزوّد خدمة واحد
- `{ providers }` لإدخالات مزوّدي خدمة متعددة

استخدم `catalog` عندما تملك Plugin معرّفات نماذج خاصة بمزوّد الخدمة، أو قيمًا
افتراضية لـ base URL، أو بيانات وصفية للنماذج محكومة بالتوثيق.

يتحكم `catalog.order` في توقيت دمج فهرس Plugin نسبةً إلى مزوّدي الخدمة
الضمنيين المضمّنين في OpenClaw:

- `simple`: مزوّدو الخدمة العاديون القائمون على API-key أو البيئة
- `profile`: مزوّدو الخدمة الذين يظهرون عند وجود ملفات تعريف توثيق
- `paired`: مزوّدو الخدمة الذين يركبون إدخالات مزوّد خدمة متعددة مترابطة
- `late`: المرور الأخير بعد مزوّدي الخدمة الضمنيين الآخرين

يفوز مزوّدو الخدمة اللاحقون عند تعارض المفاتيح، لذا يمكن لـ Plugin عمدًا تجاوز
إدخال مزوّد خدمة مضمّن له معرّف مزوّد الخدمة نفسه.

التوافق:

- لا يزال `discovery` يعمل باعتباره اسمًا بديلًا قديمًا
- إذا تم تسجيل `catalog` و`discovery` معًا، يستخدم OpenClaw `catalog`

## فحص القنوات للقراءة فقط

إذا كانت Plugin الخاصة بك تسجّل قناة، ففضّل تنفيذ
`plugin.config.inspectAccount(cfg, accountId)` إلى جانب `resolveAccount(...)`.

السبب:

- `resolveAccount(...)` هو مسار وقت التشغيل. ويُسمح له بافتراض أن بيانات الاعتماد
  قد تم تشكيلها بالكامل، ويمكنه الفشل سريعًا عند غياب الأسرار المطلوبة.
- لا ينبغي لمسارات الأوامر الخاصة بالقراءة فقط مثل `openclaw status` و`openclaw status --all`،
  و`openclaw channels status`، و`openclaw channels resolve`، وتدفقات doctor/config
  repair أن تحتاج إلى تشكيل بيانات اعتماد وقت التشغيل فقط من أجل
  وصف الإعدادات.

السلوك الموصى به لـ `inspectAccount(...)`:

- أعد فقط حالة وصفية للحساب.
- حافظ على `enabled` و`configured`.
- ضمّن حقول مصدر/حالة بيانات الاعتماد عند الاقتضاء، مثل:
  - `tokenSource`, `tokenStatus`
  - `botTokenSource`, `botTokenStatus`
  - `appTokenSource`, `appTokenStatus`
  - `signingSecretSource`, `signingSecretStatus`
- لا تحتاج إلى إعادة قيم الرموز الخام فقط للإبلاغ عن
  التوفر للقراءة فقط. فيكفي إرجاع `tokenStatus: "available"` (وحقل المصدر المطابق)
  لأوامر من نمط الحالة.
- استخدم `configured_unavailable` عندما تكون بيانات الاعتماد مضبوطة عبر SecretRef لكن
  غير متاحة في مسار الأوامر الحالي.

وهذا يتيح لأوامر القراءة فقط الإبلاغ عن «مضبوط لكن غير متاح في مسار الأوامر هذا»
بدلًا من الانهيار أو الإبلاغ خطأً أن الحساب غير مضبوط.

## حِزم package packs

يمكن أن يتضمن دليل Plugin ملف `package.json` مع `openclaw.extensions`:

```json
{
  "name": "my-pack",
  "openclaw": {
    "extensions": ["./src/safety.ts", "./src/tools.ts"],
    "setupEntry": "./src/setup-entry.ts"
  }
}
```

يصبح كل إدخال Plugin. وإذا كانت الحزمة تسرد عدة امتدادات، فإن معرّف Plugin
يصبح `name/<fileBase>`.

إذا كانت Plugin الخاصة بك تستورد تبعيات npm، فثبّتها في ذلك الدليل بحيث
يكون `node_modules` متاحًا (`npm install` / `pnpm install`).

حاجز أمان: يجب أن يبقى كل إدخال `openclaw.extensions` داخل دليل Plugin
بعد تحليل الروابط الرمزية. وتُرفض الإدخالات التي تخرج من دليل الحزمة.

ملاحظة أمان: يقوم `openclaw plugins install` بتثبيت تبعيات Plugin باستخدام
`npm install --omit=dev --ignore-scripts` (من دون نصوص lifecycle، ومن دون تبعيات dev وقت التشغيل). حافظ على أشجار تبعيات Plugin
«JS/TS خالصة» وتجنب الحزم التي تتطلب بناء `postinstall`.

اختياري: يمكن لـ `openclaw.setupEntry` أن يشير إلى وحدة خفيفة مخصصة للإعداد فقط.
وعندما يحتاج OpenClaw إلى أسطح إعداد لـ Plugin قناة معطلة، أو
عندما تكون Plugin القناة مفعلة لكنها لا تزال غير مضبوطة، فإنه يحمّل `setupEntry`
بدلًا من إدخال Plugin الكامل. وهذا يُبقي بدء التشغيل والإعداد أخف
عندما يكون إدخال Plugin الرئيسي لديك يربط أيضًا أدوات أو خطافات أو كودًا آخر
مخصصًا لوقت التشغيل فقط.

اختياري: يمكن لـ `openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen`
أن يجعل Plugin القناة تستخدم مسار `setupEntry` نفسه أثناء مرحلة
بدء التشغيل قبل الاستماع في Gateway، حتى عندما تكون القناة مضبوطة بالفعل.

استخدم هذا فقط عندما يغطي `setupEntry` بالكامل سطح بدء التشغيل الذي يجب أن يوجد
قبل أن تبدأ Gateway في الاستماع. وعمليًا، يعني ذلك أن إدخال الإعداد
يجب أن يسجل كل قدرة مملوكة للقناة يعتمد عليها بدء التشغيل، مثل:

- تسجيل القناة نفسها
- أي مسارات HTTP يجب أن تكون متاحة قبل أن تبدأ Gateway في الاستماع
- أي طرق Gateway أو أدوات أو خدمات يجب أن توجد خلال تلك النافذة نفسها

إذا كان الإدخال الكامل لديك لا يزال يملك أي قدرة مطلوبة عند بدء التشغيل، فلا تفعّل
هذا العلم. وأبقِ Plugin على السلوك الافتراضي ودع OpenClaw يحمّل
الإدخال الكامل أثناء بدء التشغيل.

يمكن للقنوات المضمّنة أيضًا نشر مساعدات لسطح العقد مخصصة للإعداد فقط بحيث
تتمكن النواة من الرجوع إليها قبل تحميل وقت تشغيل القناة الكامل. وسطح
ترقية الإعداد الحالي هو:

- `singleAccountKeysToMove`
- `namedAccountPromotionKeys`
- `resolveSingleAccountPromotionTarget(...)`

وتستخدم النواة هذا السطح عندما تحتاج إلى ترقية إعداد قناة قديم خاص
بحساب واحد إلى `channels.<id>.accounts.*` من دون تحميل إدخال Plugin الكامل.
وMatrix هو المثال المضمّن الحالي: فهو ينقل فقط مفاتيح
التوثيق/التمهيد إلى حساب مسمّى مُرقّى عندما تكون الحسابات المسمّاة موجودة بالفعل، ويمكنه
الحفاظ على مفتاح حساب افتراضي مضبوط وغير قانوني بدلًا من إنشاء
`accounts.default` دائمًا.

وتُبقي محولات تصحيح الإعداد هذه اكتشاف سطح العقد الخاصة بالقنوات المضمّنة كسولًا.
ويبقى وقت الاستيراد خفيفًا؛ ويتم تحميل سطح الترقية عند أول استخدام فقط بدلًا من
إعادة الدخول إلى بدء تشغيل القناة المضمّنة عند استيراد الوحدة.

وعندما تتضمن أسطح بدء التشغيل هذه طرق Gateway RPC، فأبقها تحت
بادئة خاصة بـ Plugin. وتظل مساحات أسماء الإدارة الأساسية (`config.*`,
`exec.approvals.*`, `wizard.*`, `update.*`) محجوزة وتُحل دائمًا
إلى `operator.admin`، حتى إذا طلبت Plugin نطاقًا أضيق.

مثال:

```json
{
  "name": "@scope/my-channel",
  "openclaw": {
    "extensions": ["./index.ts"],
    "setupEntry": "./setup-entry.ts",
    "startup": {
      "deferConfiguredChannelFullLoadUntilAfterListen": true
    }
  }
}
```

### البيانات الوصفية لفهرس القنوات

يمكن لـ Plugin القنوات الإعلان عن بيانات وصفية للإعداد/الاكتشاف عبر `openclaw.channel` و
تلميحات التثبيت عبر `openclaw.install`. وهذا يُبقي بيانات فهرس النواة خالية من البيانات.

مثال:

```json
{
  "name": "@openclaw/nextcloud-talk",
  "openclaw": {
    "extensions": ["./index.ts"],
    "channel": {
      "id": "nextcloud-talk",
      "label": "Nextcloud Talk",
      "selectionLabel": "Nextcloud Talk (self-hosted)",
      "docsPath": "/channels/nextcloud-talk",
      "docsLabel": "nextcloud-talk",
      "blurb": "Self-hosted chat via Nextcloud Talk webhook bots.",
      "order": 65,
      "aliases": ["nc-talk", "nc"]
    },
    "install": {
      "npmSpec": "@openclaw/nextcloud-talk",
      "localPath": "<bundled-plugin-local-path>",
      "defaultChoice": "npm"
    }
  }
}
```

الحقول المفيدة في `openclaw.channel` بالإضافة إلى المثال الأدنى:

- `detailLabel`: تسمية ثانوية لأسطح الفهرسة/الحالة الأكثر ثراءً
- `docsLabel`: تجاوز نص الرابط الخاص برابط الوثائق
- `preferOver`: معرّفات Plugin/قنوات أقل أولوية ينبغي أن تتفوق عليها خانة الفهرس هذه
- `selectionDocsPrefix`, `selectionDocsOmitLabel`, `selectionExtras`: عناصر تحكم في نص سطح الاختيار
- `markdownCapable`: يعلّم القناة بأنها قادرة على Markdown لقرارات التنسيق الصادر
- `exposure.configured`: إخفاء القناة من أسطح عرض القنوات المضبوطة عند ضبطها على `false`
- `exposure.setup`: إخفاء القناة من منتقيات الإعداد/التهيئة التفاعلية عند ضبطها على `false`
- `exposure.docs`: تعليم القناة بأنها داخلية/خاصة لأسطح التنقل في الوثائق
- `showConfigured` / `showInSetup`: أسماء بديلة قديمة لا تزال مقبولة من أجل التوافق؛ ويفضّل `exposure`
- `quickstartAllowFrom`: يُدخل القناة في تدفق `allowFrom` القياسي للبداية السريعة
- `forceAccountBinding`: يفرض ربط الحساب صراحةً حتى عند وجود حساب واحد فقط
- `preferSessionLookupForAnnounceTarget`: يفضّل البحث في الجلسة عند تحليل أهداف الإعلان

يمكن لـ OpenClaw أيضًا دمج **فهارس قنوات خارجية** (مثل تصدير سجل
MPM). ضع ملف JSON في أحد المواقع التالية:

- `~/.openclaw/mpm/plugins.json`
- `~/.openclaw/mpm/catalog.json`
- `~/.openclaw/plugins/catalog.json`

أو وجّه `OPENCLAW_PLUGIN_CATALOG_PATHS` (أو `OPENCLAW_MPM_CATALOG_PATHS`) إلى
ملف JSON واحد أو أكثر (مفصولة بفواصل/فواصل منقوطة/محدّد `PATH`). ويجب أن يحتوي كل ملف
على `{ "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }`. ويقبل المحلل أيضًا
`"packages"` أو `"plugins"` كأسماء بديلة قديمة للمفتاح `"entries"`.

## Plugin محرك السياق

تملك Plugin محرك السياق تنسيق سياق الجلسة لعمليات الإدخال والتجميع
وCompaction. سجّلها من Plugin الخاصة بك باستخدام
`api.registerContextEngine(id, factory)`، ثم اختر المحرك النشط باستخدام
`plugins.slots.contextEngine`.

استخدم هذا عندما تحتاج Plugin الخاصة بك إلى استبدال خط أنابيب
السياق الافتراضي أو توسيعه بدلًا من مجرد إضافة بحث في الذاكرة أو خطافات.

```ts
import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";

export default function (api) {
  api.registerContextEngine("lossless-claw", () => ({
    info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages, availableTools, citationsMode }) {
      return {
        messages,
        estimatedTokens: 0,
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
        }),
      };
    },
    async compact() {
      return { ok: true, compacted: false };
    },
  }));
}
```

إذا كان محركك **لا** يملك خوارزمية Compaction، فأبقِ `compact()`
منفذًا وفوّضه صراحةً:

```ts
import {
  buildMemorySystemPromptAddition,
  delegateCompactionToRuntime,
} from "openclaw/plugin-sdk/core";

export default function (api) {
  api.registerContextEngine("my-memory-engine", () => ({
    info: {
      id: "my-memory-engine",
      name: "My Memory Engine",
      ownsCompaction: false,
    },
    async ingest() {
      return { ingested: true };
    },
    async assemble({ messages, availableTools, citationsMode }) {
      return {
        messages,
        estimatedTokens: 0,
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
        }),
      };
    },
    async compact(params) {
      return await delegateCompactionToRuntime(params);
    },
  }));
}
```

## إضافة قدرة جديدة

عندما تحتاج Plugin إلى سلوك لا يناسب API الحالية، فلا تتجاوز
نظام Plugin من خلال وصول خاص. بل أضف القدرة المفقودة.

التسلسل الموصى به:

1. حدّد عقد النواة
   قرر ما هو السلوك المشترك الذي ينبغي أن تملكه النواة: السياسة، والاحتياط، ودمج الإعدادات،
   ودورة الحياة، والدلالات الموجّهة للقنوات، وشكل مساعد وقت التشغيل.
2. أضف أسطح تسجيل/وقت تشغيل مكتوبة لـ Plugin
   وسّع `OpenClawPluginApi` و/أو `api.runtime` بأصغر سطح قدرة مكتوب
   مفيد.
3. وصّل مستهلكي النواة + القنوات/الميزات
   يجب أن تستهلك القنوات وPlugin الميزات القدرة الجديدة عبر النواة،
   لا من خلال استيراد تنفيذ مورّد مباشرة.
4. سجّل تنفيذات المورّد
   بعد ذلك تسجّل Plugin المورّد واجهاتها الخلفية مقابل القدرة.
5. أضف تغطية للعقد
   أضف اختبارات حتى تبقى الملكية وشكل التسجيل واضحين مع مرور الوقت.

بهذه الطريقة يبقى OpenClaw ذا رأي واضح من دون أن يصبح مضمّنًا
وفق رؤية مزوّد واحد للعالم. راجع [كتاب وصفات القدرات](/ar/plugins/architecture)
للحصول على قائمة ملفات عملية ومثال منجز.

### قائمة تحقق القدرة

عندما تضيف قدرة جديدة، ينبغي عادةً أن يلمس التنفيذ هذه
الأسطح معًا:

- أنواع عقد النواة في `src/<capability>/types.ts`
- مساعد التشغيل/وقت التشغيل في النواة في `src/<capability>/runtime.ts`
- سطح تسجيل API الخاص بـ Plugin في `src/plugins/types.ts`
- ربط سجل Plugin في `src/plugins/registry.ts`
- كشف وقت تشغيل Plugin في `src/plugins/runtime/*` عندما تحتاج Plugin الميزات/القنوات
  إلى استهلاكه
- مساعدات الالتقاط/الاختبار في `src/test-utils/plugin-registration.ts`
- تأكيدات الملكية/العقد في `src/plugins/contracts/registry.ts`
- وثائق المشغّل/Plugin في `docs/`

إذا كان أحد هذه الأسطح مفقودًا، فغالبًا ما يكون ذلك علامة على أن القدرة
لم تُدمج بالكامل بعد.

### قالب القدرة

النمط الأدنى:

```ts
// core contract
export type VideoGenerationProviderPlugin = {
  id: string;
  label: string;
  generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;
};

// plugin API
api.registerVideoGenerationProvider({
  id: "openai",
  label: "OpenAI",
  async generateVideo(req) {
    return await generateOpenAiVideo(req);
  },
});

// shared runtime helper for feature/channel plugins
const clip = await api.runtime.videoGeneration.generate({
  prompt: "Show the robot walking through the lab.",
  cfg,
});
```

نمط اختبار العقد:

```ts
expect(findVideoGenerationProviderIdsForPlugin("openai")).toEqual(["openai"]);
```

وهذا يبقي القاعدة بسيطة:

- تملك النواة عقد القدرة + التنسيق
- تملك Plugin المورّد تنفيذات المورّد
- تستهلك Plugin الميزات/القنوات مساعدات وقت التشغيل
- تُبقي اختبارات العقود الملكية واضحة
