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()خارج طلب النموذج؛ وترسلها الأوضاع الأخرى كلها.
تعديل الملفات: العلامة editsFiles
يوافق acceptEdits فقط على الأدوات الموسومة بأنها تعديل للملفات: write_file وedit_file من createFsTools()، وأي أداة معرَّفة بـ editsFiles: true (تُخزَّن في metadata.editsFiles). لا تُعدّ أداة تعديلًا للملفات باسمها وحده أبدًا، فأداة من صنعك اسمها write_file لا يُوافَق عليها بصمت.
ترتيب التقييم
لكل استدعاء أداة، بهذا الترتيب:- التحقق من الوسائط، ثم خطّافات
preToolCall. رفض الخطّاف ينهي الأمر هنا. - قواعد الصلاحيات (
permissions). قاعدةdenyتنهي الأمر هنا. - حواجز حماية الأداة. الحجب ينهي التشغيل.
needsApprovalالخاص بالأداة. الرفض ينهي الأمر هنا؛ وقاعدةallowأوaskتحلّ محل طلبها.- وضع الصلاحيات، على ما تبقّى.
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 }:
سجل التدقيق
ما دام وضع غير'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 يُرفض الاستدعاء الموافَق عليه نفسه أيضًا إذا لم تكن أداته للقراءة فقط.
الاستئناف في عملية أخرى يستخدم الوضع الذي يملكه الوكيل أو الجلسة المستأنِفة.