Skip to main content
يضع وضع الصلاحيات الوكيل في حالة مسمّاة بدل كتابة القواعد: انظر ولا تلمس (plan)، أو عدّل الملفات من دون سؤال (acceptEdits)، أو لا تتوقف أبدًا (dontAsk). بدّله في منتصف الجلسة كما تعمل مع وكيل برمجة: خطّط أولًا، ثم دعه يعدّل.
الأوضاع إعدادات مسبقة فوق قواعد الصلاحيات وneedsApproval، وليست نظامًا ثانيًا: تُطبَّق في النهاية، على ما قرّرته القواعد وحواجز الحماية وneedsApproval الخاص بالأداة.

الأوضاع

  • plan: يُرفض استدعاء أي أداة ليست للقراءة فقط مع kind: 'denied' والسبب The agent is in plan mode: it may read but not change anything. Describe the change instead.، وذلك حتى لو طابقته قاعدة allow. والتشغيل الذي يبدأ في وضع plan يحصل أيضًا على فقرة واحدة في موجّه النظام تخبر النموذج بذلك. أما استدعاء أداة غير معروفة فيحصل على خطأ عدم العثور المعتاد، فيرى النموذج المشكلة الحقيقية.
  • acceptEdits: الاستدعاء الذي كان سيتوقف لطلب موافقة يعمل من دون سؤال إذا كانت أداته تعديلًا للملفات (editsFiles، أدناه). ويبقى كل ما عدا ذلك على حاله.
  • dontAsk: الاستدعاء الذي كان سيتوقف لطلب موافقة - قاعدة ask أو needsApproval أو ask_question - يُرفض مع السبب The agent is in dontAsk mode: calls that need approval are refused. أما الاستدعاءات التي وافقت عليها قاعدة allow والاستدعاءات التي لا تحتاج موافقة فتعمل. ولا شيء يتوقف، لذلك لا يُستدعى approve أبدًا.

أي الأدوات للقراءة فقط

لا يسمح وضع plan بتشغيل أداة إلا إذا أعلنت أنها لا تغيّر شيئًا. والأداة التي لا تعلن شيئًا تُعامل كأداة لها آثار جانبية وتُرفض.
  • أداة لها annotations: { readOnlyHint: true }. من المدمجة: read_file وlist_dir وglob وgrep وtodo_read وcurrent_date وday_name وweb_fetch وload_skill وكل أداة ذاكرة recall_<name>، وagent_status / agent_await (تراقبان الوكلاء الفرعيين في الخلفية). ولـأداة OpenAPI لعملية GET التلميح نفسه، وتحتفظ أداة MCP بالتلميح الذي أرسله خادمها: وهو كلمة الخادم لا ضمانًا، فلا تربط إلا خوادم تثق بها.
  • الأداة المدمجة ask_question (هي تسأل فقط؛ ويتوقف الاستدعاء بانتظار الجواب) والأداة task (يرث وكيلها الفرعي وضع plan، انظر أدناه).
  • أدوات المزوّد المستضافة تعمل داخل طلب المزوّد، فلا يمكن رفض أي استدعاء. يرسل وضع plan الأداتين webSearch() وfileSearch() (فهما تقرآن) ويترك codeInterpreter() وكل hostedTool() خارج طلب النموذج؛ وترسلها الأوضاع الأخرى كلها.
أعلن أداة مخصّصة للقراءة فقط هكذا:
التلميح وعد تقطعه عن أداتك: يثق به وضع plan.

تعديل الملفات: العلامة editsFiles

يوافق acceptEdits فقط على الأدوات الموسومة بأنها تعديل للملفات: write_file وedit_file من createFsTools()، وأي أداة معرَّفة بـ editsFiles: true (تُخزَّن في metadata.editsFiles). لا تُعدّ أداة تعديلًا للملفات باسمها وحده أبدًا، فأداة من صنعك اسمها write_file لا يُوافَق عليها بصمت.

ترتيب التقييم

لكل استدعاء أداة، بهذا الترتيب:
  1. التحقق من الوسائط، ثم خطّافات preToolCall. رفض الخطّاف ينهي الأمر هنا.
  2. قواعد الصلاحيات (permissions). قاعدة deny تنهي الأمر هنا.
  3. حواجز حماية الأداة. الحجب ينهي التشغيل.
  4. needsApproval الخاص بالأداة. الرفض ينهي الأمر هنا؛ وقاعدة allow أو ask تحلّ محل طلبها.
  5. وضع الصلاحيات، على ما تبقّى.
لذلك لا يحوّل أي وضع الرفضَ إلى تشغيل: رفض الخطّاف وقاعدة deny ورفض needsApproval تبقى رفضًا في كل وضع، ولا يوافق acceptEdits أبدًا على استدعاء حجبه حاجز حماية.

ضبط الوضع وتبديله

  • createAgent({ permissionMode }): وضع الوكيل. الدالة تُقرأ عند كل استدعاء أداة، فالوضع الذي تبدّله أثناء تشغيل جارٍ يسري على استدعائه التالي.
  • agent.send(message, { permissionMode }) / agent.stream(...): الوضع لذلك التشغيل وحده.
  • agent.session({ permissionMode })، ثم session.setPermissionMode(mode) والمُجلِب session.permissionMode. تقرأ الجلسة وضعها عند كل استدعاء أداة في الدورة، فإن استُدعيت setPermissionMode() أثناء دورة (من مستمع tool.start مثلًا) سرت من استدعاء الأداة التالي في تلك الدورة. وافتراضيًا تأخذ الجلسة وضع الوكيل.
التبديل دائمًا استدعاء صريح لـ setPermissionMode(). ويُبلَّغ كل تبديل إلى onPermissionModeChange مع { sessionId, from, to, at }:
يُضبط موجّه النظام عند بدء التشغيل: لا يُخبَر بوضع plan إلا التشغيل (أو دورة الجلسة) الذي يبدأ فيه. أما التبديل أثناء التشغيل فتفرضه بوابة استدعاء الأداة وحدها، وهذا كافٍ لأن سبب الاستدعاء المرفوض يُعلم النموذج.

سجل التدقيق

ما دام وضع غير 'default' مضبوطًا، يُدقَّق قرار كل استدعاء أداة حتى لو لم يضبط الوكيل permissions ولا onPermissionDecision: يذهب إلى onPermissionDecision (إن ضُبط) ويُبثّ كحدث permission.decision. ويُضبط الحقل mode في الإدخال حين غيّر الوضع نتيجة الاستدعاء: 'plan' أو 'dontAsk' مع decision: 'deny' وreason الوضع، و'acceptEdits' مع decision: 'allow'. وحين يرفض الوضع استدعاءً طابقته قاعدة allow يحتفظ الإدخال بـ rule.index لتلك القاعدة. وتذهب تبديلات الوضع إلى onPermissionModeChange. ويستلم onPermissionDecision من يعمل التشغيل نيابةً عنه بوصفه وسيطه الثاني، { principal }؛ أما الإدخال والحدث فلا يحملان هوية المستدعي (انظر auth).

الوكلاء الفرعيون

يعمل الوكيل الفرعي (الأداة task أو مهمة في الخلفية أو createDelegateTool()) بوضع الوكيل الرئيسي ما دام وضعه ليس 'default'، وبـ permissionMode الخاص به فيما عدا ذلك. والوكيل الفرعي المنشأ بـ permissionMode: 'plan' يبقى في وضع plan أيًّا كان وضع الوكيل الرئيسي. ويُقرأ الوضع عند كل استدعاء أداة للوكيل الفرعي، فتبديل وضع جلسة الوكيل الرئيسي يسري على وكيل فرعي قيد التشغيل. وهذا ما يجعل task آمنة في وضع plan: فلا يستطيع الوكيل الفرعي تغيير شيء بدوره. أما الوكيل الفرعي البعيد (remoteAgent()) فيعمل على خادم آخر بصلاحيات ذلك الخادم، فلا يمكن إلزامه بوضع الوكيل الرئيسي: في وضع plan يُرفض استدعاء task إليه من دون الاتصال به، وفي وضع dontAsk تُفشل الموافقة البعيدة المهمة بدل أن توقف الوكيل الرئيسي مؤقتًا.

الاستئناف بعد موافقة

لا يُحفظ الوضع في نقاط الحفظ ولا في لقطات الموافقات. التشغيل الذي يتابعه agent.approvals.resolve() يستخدم الوضع الحالي للجلسة المتوقفة إذا كان التشغيل المتوقف تابعًا لجلسة، وإلا وضع الوكيل (لا الوضع الذي مرّره استدعاء send()). فالدورة التي توقفت في 'default' وحُسمت بعد session.setPermissionMode('dontAsk') تشغّل الاستدعاء الموافَق عليه ثم ترفض الاستدعاءات التي تليه وكانت ستطلب موافقة. وفي وضع plan يُرفض الاستدعاء الموافَق عليه نفسه أيضًا إذا لم تكن أداته للقراءة فقط. الاستئناف في عملية أخرى يستخدم الوضع الذي يملكه الوكيل أو الجلسة المستأنِفة.