> ## Documentation Index
> Fetch the complete documentation index at: https://lousho.com/llms.txt
> Use this file to discover all available pages before exploring further.

# وضع الشيفرة

مع وضع الشيفرة (code mode) يحصل النموذج على أداة إضافية واحدة هي `run_code`.
فبدلًا من استدعاء الأدوات واحدة تلو أخرى، مع ذهاب وإياب إلى النموذج بعد كل
منها، يكتب برنامج JavaScript قصيرًا يستدعي أدوات الوكيل كدوال غير متزامنة،
ويكرّر ويرشّح ويجمع نتائجها، ويعيد قيمة واحدة. فتكلّف استدعاءات الأدوات
الكثيرة ذهابًا وإيابًا واحدًا إلى النموذج، ولا تدخل النتائج الوسيطة الكبيرة
إلى السياق: لا تدخله إلا القيمة التي يعيدها البرنامج.

يعمل البرنامج في عزل (isolate) QuickJS على WebAssembly داخل عمليتك، لا في
Docker. وكل استدعاء أداة يجريه يمرّ بالفحوص نفسها التي يمرّ بها استدعاء
يجريه النموذج مباشرة: التحقق من الوسائط، والخطّافات، وقواعد الصلاحيات
وأوضاعها، وحواجز الحماية، و`needsApproval`.

## متى تستخدمه

* تحتاج الإجابة إلى عدة استدعاءات تعتمد مدخلاتها على نتائج سابقة (ابحث عن
  ثلاثة أسعار، ثم حوّل المجموع).
* تعيد أداة أكثر بكثير مما يحتاج إليه النموذج (قائمة طويلة للترشيح، أو
  تقرير للعدّ).
* يُجرى الاستدعاء نفسه على قائمة من العناصر.

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

## تفعيله

ثبّت الاعتمادية النظيرة الاختيارية `quickjs-emscripten` واضبط `codeMode` في
`createAgent()`:

```bash theme={null}
npm install quickjs-emscripten@^0.32.0
```

```ts theme={null}
import { createAgent, defineTool } from '@lousho/build-ai-agent';
import { z } from 'zod';

const prices: Record<string, number> = { apple: 1.2, pear: 0.8, plum: 2 };

const getPrice = defineTool({
  name: 'get_price',
  description: 'The price of one fruit in USD',
  input: z.object({ item: z.string() }),
  execute: async ({ item }) => ({ item, usd: prices[item] ?? null }),
});

const convert = defineTool({
  name: 'convert',
  description: 'Converts an amount in USD to another currency',
  input: z.object({ amount: z.number(), to: z.enum(['EUR', 'GBP']) }),
  execute: async ({ amount, to }) => ({ amount: amount * (to === 'EUR' ? 0.9 : 0.8), currency: to }),
});

const agent = createAgent({
  model: 'openai/gpt-4o-mini',
  tools: [getPrice, convert],
  codeMode: { exclusive: true },
});

const { text } = await agent.send('What do an apple, a pear and a plum cost together in EUR?');
```

برنامج قد يكتبه النموذج لهذا السؤال:

```js theme={null}
let usd = 0;
for (const item of ['apple', 'pear', 'plum']) {
  usd += (await tools.get_price({ item })).usd;
}
const eur = await tools.convert({ amount: usd, to: 'EUR' });
return { usd, eur: eur.amount };
```

تعيد `run_code` الكائن `{ result, logs, toolCalls }`: أي قيمة البرنامج
المعادة، والأسطر التي سجّلها بـ `console.log()` (وبـ `console.error()` /
`warn()`، مسبوقة بمستواها)، وعدد استدعاءات الأدوات التي أجراها.

إذا لم تكن `quickjs-emscripten` مثبّتة، يفشل أول تشغيل لوكيل مفعَّل فيه
`codeMode` بالخطأ `MissingPeerDependencyError` الذي يذكر اسم الحزمة وأمر
التثبيت.

## الخيارات

`codeMode: true` يستخدم القيم الافتراضية. ويمكن لكائن أن يضبط أيًّا مما يلي:

| الخيار | القيمة الافتراضية | المعنى |
| - | - | - |
| `tools` | انظر أدناه | أسماء الأدوات التي يجوز للبرنامج استدعاؤها. الأسماء التي لا أداة لها في التشغيل تُتخطّى. و`run_code` نفسها مرفوضة. |
| `exclusive` | `false` | القيمة `true` تسحب تلك الأدوات من قائمة أدوات النموذج، فيجب أن يستدعيها عبر `run_code`. وتبقى البرامج قادرة على استدعائها. |
| `timeoutMs` | `30000` | مهلة البرنامج كله، بما فيها استدعاءات الأدوات التي ينتظرها. |
| `memoryLimitBytes` | `67108864` (64 MiB) | ذاكرة العزل. |
| `maxToolCalls` | `50` | عدد استدعاءات الأدوات في البرنامج الواحد. |
| `maxOutputChars` | `20000` | عدد أحرف القيمة المعادة (بصيغة JSON) مع السجلات. |

بلا `tools`، يجوز للبرنامج استدعاء كل أدوات التشغيل ما عدا `run_code`
و`ask_question` وأدوات الوكلاء الفرعيين (`task` و`agent_status` و`agent_await`
و`agent_cancel` و`delegate_to_*`) و`tool_search` و`load_skill` والأدوات التي
يؤجّلها [البحث عن الأدوات](/ar/tool-search). سمِّ أداة في `tools` لتسمح بها
رغم ذلك: الأداة المؤجَّلة المسمّاة هناك يمكن استدعاؤها من البرامج ويظهر
توقيعها في وصف `run_code` (فلا تعود محجوبة عن السياق في وضع الشيفرة)، بينما
تبقى خارج قائمة أدوات النموذج نفسه إلى أن تحمّلها `tool_search`.

يعرف النموذج ما يستطيع استدعاءه من وصف `run_code`: توقيع شبيه بـ TypeScript
لكل أداة مسموحة، مبني من مخطط مدخلها، ووصفها تعليقًا:

```text theme={null}
// The price of one fruit in USD
tools.get_price(args: { item: string }): Promise<unknown>
```

ليس للنتائج نوع مصرَّح به، لذا حين يفشل برنامج يسرد الخطأ أول خمسة
استدعاءات أدوات أجراها البرنامج مع وسائطها ونتائجها (يُقتطع كل منها عند 200
حرف). ويستطيع النموذج قراءة الأشكال هناك وإصلاح البرنامج.

## ما يستطيع البرنامج فعله وما لا يستطيع

الشيفرة هي متن دالة `async`. يمكنها استخدام لغة JavaScript ومكوّناتها
المضمَّنة (`JSON` و`Math` و`Date` و`Promise` والمصفوفات والخرائط والتعابير
النمطية)، واستدعاء الأدوات، والتسجيل، وإعادة قيمة.

* `await tools.<name>(args)` تنتهي بنتيجة الأداة، أو ترمي `Error`
  (`name: 'ToolError'`) رسالته هي رسالة خطأ الأداة. ولا يحتوي `tools` إلا على
  الأدوات المسموحة ولا يمكن تعديله.
* تعبر الوسائط والنتائج الحدّ نسخًا بصيغة JSON: فـ `Date` تصل نصًّا، وتُحذف
  الدوال والحقول `undefined`، وتعديل نتيجة داخل البرنامج لا يغيّر شيئًا
  خارجه.
* `Promise.all` تشغّل الاستدعاءات في وقت واحد، بحد أقصى `toolConcurrency`
  منها (إعداد الوكيل) في البرنامج الواحد.
* يجب أن تكون القيمة المعادة قابلة للتحويل إلى JSON؛ و`undefined` تصبح
  `null`.

لا يوجد `require` ولا `import` ولا `process` ولا `fetch` ولا نظام ملفات ولا
شبكة ولا `setTimeout` ولا أي مؤقّت آخر، ولا أي كائن من المضيف. ولا يستطيع
البرنامج بلوغ العالم الخارجي إلا عبر الأدوات الممنوحة له.

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

* **العزل.** يحصل كل برنامج على بيئة تشغيل وسياق QuickJS جديدين في
  WebAssembly، بلا شيء من المضيف فيهما سوى دالتين (استدعاء أداة، وسطر
  سجل) تسحبهما شيفرة الإعداد الخاصة بـ SDK من الكائن العام قبل أن يعمل
  البرنامج. ولا يُستخدم `node:vm`: فهو ليس حدًّا أمنيًا.
* **الحدود.** يفرض العزل حدّ الذاكرة وحدّ المكدّس البالغ 512 KiB. ويفحص
  معالج مقاطعة الموعد النهائي أثناء حساب البرنامج، فتُوقَف الحلقة التي لا
  تنتهي عند `timeoutMs` (ولا يستطيع `try` / `catch` اعتراض هذا الإيقاف)،
  ويفحصه مؤقّت أثناء انتظاره أداة. وكثرة استدعاءات الأدوات توقف البرنامج
  أيضًا، حتى لو التقط الخطأ.
* **البوابة لكل استدعاء.** كل `tools.x(args)` استدعاء أداة داخلي في التشغيل:
  يُتحقَّق منه مقابل مخطط الأداة، ثم تأتي خطّافات ما قبل الأداة وقواعد
  الصلاحيات و[وضع الصلاحيات](/ar/permission-modes) و[حواجز حماية الأدوات](/ar/guardrails)
  و`needsApproval`، ثم تعمل الأداة (عبر البيئة المعزولة للتشغيل إن كانت
  `requiresSandbox`) بمُنفِّذ (principal) التشغيل، ثم خطّافات ما بعد الأداة.
  والاستدعاء المرفوض يرمي خطأً في البرنامج مع سبب الرفض. وحاجز الحماية الذي
  يمنع استدعاءً داخليًا يوقف التشغيل كله، كما يفعل مع استدعاء مباشر.
* **وضع التخطيط.** `run_code` معلَّمة للقراءة فقط: فهي لا تغيّر شيئًا بنفسها.
  وفي وضع التخطيط يُفحص كل استدعاء داخلي على حدة، فيستطيع البرنامج استدعاء
  أدوات القراءة فقط، أما استدعاء أي أداة أخرى فيرمي `denied by plan mode`.
* **الأخطاء.** يصل خطأ الأداة إلى البرنامج رسالةً فقط، لا أثر مكدّس للمضيف
  أبدًا. والرموز التي حصلت عليها الأداة من `ctx.getToken()` تُحجب من نتيجتها،
  كما في الاستدعاء المباشر.

البرنامج الذي يحسب دون انتظار يعمل على خيط عمليتك: فيحجب حلقة الأحداث إلى أن
ينتهي أو يوقفه `timeoutMs`. وعلى خادم يعالج طلبات أخرى، أبقِ `timeoutMs`
منخفضًا.

## الموافقات وتسجيل الدخول وحالات التوقف الأخرى

لا يستطيع البرنامج أن يتوقف. فعزل QuickJS لا يمكن حفظه واستئنافه لاحقًا،
لذا فإن الاستدعاء الداخلي الذي كان سيوقف التشغيل يرمي خطأً في البرنامج بدلًا
من ذلك، ولا يتوقف التشغيل:

| الاستدعاء الداخلي | ما يحصل عليه البرنامج |
| - | - |
| يحتاج إلى موافقة (`needsApproval`، أو قاعدة `ask`) | `Tool x needs approval; call it directly, not from run_code.` |
| يحتاج إلى تسجيل دخول (`ctx.getToken()` دون رمز) | `Tool x needs the user to sign in to <provider>; call it directly, not from run_code.` |
| يبدأ وكيلًا فرعيًا يحتاج إلى موافقة | `Tool x started a sub-agent that needs approval; call it directly, not from run_code.` |

يستطيع النموذج بعدها استدعاء تلك الأداة مباشرة، فيتوقف التشغيل كالمعتاد.
وقد تحتاج `run_code` نفسها إلى موافقة (مثلًا مع `permissions: [ask('*')]`):
فيتوقف التشغيل قبل أن يعمل البرنامج، ويعمل البرنامج بعد البتّ في القرار، مع
فحص كل استدعاء داخلي.

## الأحداث والتتبّع

تُصدر الاستدعاءات الداخلية [أحداث](/ar/stream-events) `tool.start` و`tool.partial`
و`tool.done` و`tool.error` المعتادة، مع ضبط `parentToolCallId` على معرّف
استدعاء `run_code`. ومعرّفاتها `<run_code call id>:<n>`، مرقّمة بترتيب
استدعاءات البرنامج. ويأتي `tool.done` أو `tool.error` لكل استدعاء داخلي قبل
الخاص بـ `run_code` نفسها: فحين ينتهي البرنامج (أو يُوقَف) وما زالت استدعاءات
بدأها تعمل، يُلغى `abortSignal` الخاص بها وتنتظر `run_code` حتى تستقر. كما
تنتج الاستدعاءات الداخلية أحداث `permission.decision`، وتراها الخطّافات
بمعرّفاتها الخاصة.

تذهب النتائج الداخلية إلى الأحداث وإلى البرنامج فقط. أما السجل والجلسة
والنموذج فلا يحصلون إلا على نتيجة `run_code` وحدها.

مع التتبّع، يكون لكل استدعاء داخلي مقطع تتبّع (span) خاص `execute_tool`، ابنٌ
لمقطع `run_code`، مع السمة `lousho.tool.parent_call_id`.

## القيود والمتانة

* **لا تقدّم جزئي.** حالة البرنامج تعيش في العزل فقط. وبعد تعطل، يستدعي
  التشغيل المستأنف `run_code` من جديد فيعمل البرنامج كله مرة أخرى، بما فيه
  استدعاءات الأدوات التي كان قد أجراها. اجعل الأدوات التي يستدعيها البرنامج
  متساوية الأثر (idempotent) (قيمة `ctx.toolCallId` لكل استدعاء داخلي هي نفسها
  عند إعادة التشغيل: استخدمها مفتاحًا لتساوي الأثر)، أو استدعِ الأدوات ذات
  الآثار الجانبية مباشرة.
* **JavaScript فقط.** يكتب النموذج JavaScript؛ ولا يُحوَّل TypeScript.
* **أين يعمل.** تشغيلات وكيل `createAgent()`: `send()` و`stream()` والجلسات
  والاستئناف والموافقات، على Node. أما الهدف `cloudflare-worker` في
  `lousho build` فلا يضمّ QuickJS: وتشغيل مفعَّل فيه `codeMode` يفشل هناك عند
  بدايته. والوكيل الذي يبدأ وكيلًا فرعيًا (أداة `task` أو أداة التفويض) يعمل
  بلا وضع الشيفرة، و`codeMode` الخاص بالوكيل الرئيسي لا يصل إلى أدوات الوكيل
  الفرعي.
* **الأخطاء نتائج.** خطأ في البرنامج، أو انتهاء المهلة، أو بلوغ حدّ الذاكرة،
  أو كثرة استدعاءات الأدوات، أو قيمة معادة تتجاوز `maxOutputChars`، هو خطأ أداة
  في `run_code` يسمّي السبب؛ يحصل عليه النموذج ويتابع التشغيل.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.