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

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

مرّر `sessionId` و`checkpointStore` إلى `AgentExecutor.execute()` فيصمد التشغيل أمام تعطّل، أو إعادة تشغيل، أو إيقاف (abort)، أو موافقة بشرية تستغرق أيامًا. لا شيء هنا يعتمد على مستضيف بعينه: أي `CheckpointStore` (`LocalStorageCheckpointStore` على Node، أو مخزن Cloudflare KV الذي يجهّزه بناء `cloudflare-worker`، أو تنفيذك الخاص المكوَّن من ثلاث دوال) وأي `ApprovalStore` يعملان بالطريقة نفسها.

```ts theme={null}
import { AgentExecutor, LocalStorageCheckpointStore } from '@lousho/build-ai-agent';

const checkpoints = new LocalStorageCheckpointStore(storage);

// First message of a conversation.
await AgentExecutor.execute({ agent, input: 'Book a table for 2 tonight', provider, sessionId: 'chat-42', checkpointStore: checkpoints });

// Later - same process or another one - the next message of the same conversation.
await AgentExecutor.execute({ agent, input: 'Make it 3 people', provider, sessionId: 'chat-42', checkpointStore: checkpoints });
```

مع `createAgent()`، يكفي خيار `store` واحد لتجهيز كل شيء: مرّر `sessionId` إلى `send()` (أو `stream()`) فتُحفظ للتشغيل نقاط حفظ (checkpoints) في `store.checkpoints`؛ وبعد تعطّل، تُكمله `agent.resume(sessionId)` (وتعيد `null` حين لا يوجد شيء معلّق). ولا حاجة إلى `AgentExecutor`:

```ts theme={null}
import { createAgent } from '@lousho/build-ai-agent';
import { SqliteStore } from '@lousho/build-ai-agent/sqlite';

const agent = createAgent({ provider, store: new SqliteStore('./.lousho/jobs.db') });

const finished = await agent.resume('job-1'); // the interrupted run, if any
const next = await agent.send('Now write a summary', { sessionId: 'job-1' }); // continues the same run's conversation
```

يحتفظ `store.approvals` بحالات التوقف بانتظار الموافقة، وتواصل `agent.approvals.resolve()` حفظ نقاط الحفظ للتشغيل تحت `sessionId` الخاص به. والجلسات تُحفظ لها نقاط حفظ أيضًا: `agent.session({ id })` تحفظ نقطة حفظ لكل دورة، و`agent.resume(id)` (أو `session.resume()`) تُكمل الدورة التي انقطعت، انظر [الجلسات المتينة](/ar/sessions#الجلسات-المتينة).

## ما الذي يُحفظ في نقطة الحفظ، ومتى

نقطة الحفظ هي سجل محادثة التشغيل كاملًا مع عدد خطواته، واستهلاكه، و`businessState`، وقيمة `status`. وتُكتب تحت `sessionId`:

| اللحظة | `status` |
| - | - |
| مباشرة بعد كل رد من النموذج يطلب أدوات، قبل تنفيذ أي أداة | `'in-progress'` |
| في كل مرة تُسجَّل فيها نتيجة أداة (تُسجَّل النتائج بترتيب الاستدعاء) | `'in-progress'` |
| عند إيقاف التشغيل (`signal`) | `'in-progress'` |
| عند توقف التشغيل مؤقتًا بانتظار موافقة (على استدعاء أداة خاص به، أو خاص بـ[وكيل فرعي](/ar/sub-agents)، وكذلك حين يتوقف وكيل فرعي مستأنف مرة أخرى) | `'awaiting-approval'` (مع `approvalId`) |
| عند انتهاء التشغيل (توقف النموذج عن استدعاء الأدوات، أو استُنفد `maxSteps`) | `'finished'` |

التشغيل الذي ينتهي بخطأ (خطأ من المزوّد، أو خطّاف يرمي خطأً، أو `PropagatingToolError`، أو تعطّل العملية) يحتفظ بآخر نقطة حفظ له بالحالة `'in-progress'`.

## ما الذي يحدث حين تستدعي `execute()` مرة أخرى بالـ`sessionId` نفسه

يتوقف ذلك على قيمة `status` المخزَّنة:

* **تشغيل غير مكتمل** (`'in-progress'`: تعطّل، أو أُوقف، أو فشلت كتابة في المخزن). يُستأنف التشغيل. إذا كانت دورة النموذج الأخيرة فيه تتضمن استدعاءات أدوات بلا نتيجة، تُنفَّذ تلك الاستدعاءات بعينها أولًا، عبر مسار الأدوات المعتاد (التحقق من المعطيات، والخطّافات، و`needsApproval`، و`toolConcurrency`)، **من دون استدعاء النموذج من جديد**. ثم تستمر الحلقة بما تبقّى من ميزانية `maxSteps`؛ ويستمر `usage` من الإجماليات المحفوظة في نقطة الحفظ (والأمر نفسه في `resumeAfterApproval()`، انطلاقًا من اللقطة).
  * يُضاف `input` الجديد كرسالة مستخدم بعد نتائج تلك الأدوات. ولا يوضع أبدًا بين دورة استدعاء أدوات ونتائجها، فيبقى سجل المحادثة صالحًا لدى كل مزوّد. وهذا ما يتيح للمستخدم أن يقاطع تشغيلًا (بإيقافه) ويعيد توجيهه برسالة جديدة.
  * الـ`input` الذي يعيد إرسال الرسالة التي بدأ منها التشغيل (وهو التصرف الطبيعي: «أعد الطلب نفسه بعد تعطّل») يُعامَل كإعادة محاولة ولا يُضاف مرة أخرى. و`input: []` كذلك يستأنف فحسب.
* **تشغيل منتهٍ** (`'finished'`). تستمر الجلسة كمحادثة: تبقى الرسائل المخزَّنة ويصبح `input` دورة المستخدم التالية. مرّر الرسالة أو الرسائل الجديدة فقط؛ وإذا أعدت إرسال السجل المخزَّن متبوعًا برسائل جديدة، يُتعرَّف على السجل ولا يُكرَّر. وتُحتسب `steps` و`usage` و`toolCalls` في النتيجة من الصفر للتشغيل الجديد؛ أما `messages` فهي المحادثة كاملة.
* **متوقف بانتظار موافقة** (`'awaiting-approval'`). ترمي `execute()` الخطأ `SessionAwaitingApprovalError` (مع `sessionId` و`approvalId`) ولا تستدعي النموذج ولا أي أداة: فلا يجوز أن يتجاوز مدخل جديد قرارًا معلّقًا. احسمه أولًا بـ`resumeAfterApproval()`، مع تمرير `checkpointStore` نفسه، ثم أرسل الرسالة الجديدة.

تُتجاهَل رسائل النظام في `input` حين يُضاف إلى جلسة مخزَّنة (فالجلسة لها موجّه النظام الخاص بها أصلًا). ولإنهاء جلسة والبدء من جديد بالمعرّف نفسه، استدعِ `checkpointStore.delete(sessionId)`.

```ts theme={null}
import { AgentExecutor, SessionAwaitingApprovalError } from '@lousho/build-ai-agent';

try {
  await AgentExecutor.execute({ agent, input: 'Are you done?', provider, sessionId: 'chat-42', checkpointStore });
} catch (error) {
  if (error instanceof SessionAwaitingApprovalError) {
    console.log(`Waiting on approval ${error.approvalId} - resolve it with resumeAfterApproval() first.`);
  }
}
```

## الموافقات في منتصف دفعة أدوات

حين تطلب دورة نموذج واحدة عدة أدوات وتحتاج إحداها إلى موافقة، تُنفَّذ الاستدعاءات التي تسبقها وتُسجَّل، ويتوقف التشغيل عندها. وتسجّل لقطة الموافقة (snapshot) الاستدعاءات التي تليها (`remainingToolCalls`). ثم تقوم `resumeAfterApproval()` بما يلي:

1. تسجّل نتيجة الاستدعاء المتوقف: نتيجة الأداة إن وُوفق عليه، أو نتيجة رفض منظَّمة `{ error, note }` إن رُفض؛
2. تنفّذ الاستدعاءات المتبقية بمنطق الدفعة نفسه: فقد تُنفَّذ، أو تفشل في التحقق، أو توقف التشغيل مرة أخرى عند موافقة أخرى (`approvalId` جديد؛ احسمه بالطريقة نفسها، وبعدد المرات اللازم)؛
3. تستدعي النموذج حالما يصبح لكل استدعاء في الدورة نتيجة واحدة بالضبط.

```ts theme={null}
import { AgentExecutor, resumeAfterApproval } from '@lousho/build-ai-agent';

const paused = await AgentExecutor.execute({
  agent, input, provider, toolRegistry, approvalStore, sessionId: 'chat-42', checkpointStore,
});

if (paused.finishReason === 'awaiting-approval') {
  // ...a human decides, possibly days later, in another process...
  const result = await resumeAfterApproval(
    { id: paused.approvalId!, approved: true },
    approvalStore,
    toolRegistry,
    provider,
    {},
    checkpointStore, // clears the 'awaiting-approval' mark and keeps checkpointing
  );
  console.log(result.finishReason); // 'stop', or 'awaiting-approval' again for the next gated call
}
```

يستخدم التشغيل المستأنف `approvalStore` نفسه لأي موافقة لاحقة ما لم تمرّر مخزنًا آخر في الخيارات.

## الضمانات

* **يحصل كل استدعاء أداة على نتيجة واحدة بالضبط، بترتيب الاستدعاء**، بعد أي تتابع من حالات التعطّل والإيقاف والتوقف المؤقت والاستئناف. (الاستدعاء الذي أُوقف يحصل على نتيجة «أُلغي»؛ والاستدعاء المرفوض يحصل على نتيجة رفض.)
* **النتيجة المسجَّلة لا يُعاد تنفيذها أبدًا.** تُحفظ النتائج في نقاط الحفظ كلما امتدّت سلسلة الاستدعاءات المكتملة المتتالية بترتيبها؛ والتشغيل المستأنف لا ينفّذ إلا الاستدعاءات التي ليست لها نتيجة مسجَّلة.
* **دورة النموذج المحفوظة في نقطة حفظ لا تُولَّد من جديد أبدًا.** إذا توقفت العملية بعد أن أجاب النموذج، ينفّذ التشغيل المستأنف استدعاءات الأدوات الواردة في تلك الإجابة بدل سؤال النموذج مرة أخرى (فلا تكلفة إضافية ولا خطة مختلفة).
* **يتلقى المزوّد دائمًا سجل محادثة صالحًا**: نتائج الأدوات تأتي مباشرة بعد دورة استدعاء الأدوات الخاصة بها، ومدخل المستخدم الجديد يأتي بعدها.

## الأدوات تُنفَّذ مرة واحدة على الأقل: اجعل الآثار الجانبية آمنة عند التكرار

الأداة التي كانت **قيد التنفيذ** حين توقفت العملية ليست لها نتيجة مسجَّلة، فتُنفَّذ من جديد عند الاستئناف. فتنفيذ الأدوات إذن «مرة واحدة على الأقل» (at-least-once) لا «مرة واحدة بالضبط» (exactly-once). أما الأدوات ذات الآثار الجانبية (خصم مبلغ من بطاقة، أو إرسال بريد إلكتروني) فإما أن تقيّدها بـ`needsApproval` وإما أن تجعلها آمنة عند التكرار (idempotent). يُمرَّر `toolCallId` الخاص بكل استدعاء (وهو معرّف النموذج للاستدعاء، ولا يتغير حين يُعاد تنفيذه عند الاستئناف) إلى `execute`، فيصلح مفتاحًا لمنع التكرار (idempotency key):

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

declare const payments: { charge(amount: number, opts: { idempotencyKey: string }): Promise<{ id: string }> };

const chargeCard = defineTool({
  name: 'charge_card',
  description: 'Charge the customer',
  input: z.object({ amount: z.number() }),
  execute: async ({ amount }, { toolCallId }) =>
    payments.charge(amount, { idempotencyKey: toolCallId }), // a re-run cannot charge twice
});
```

(يُعيَّن `toolCallId` في كل تنفيذ لأداة، ومنها الأدوات التي تمرّ عبر `sandboxExecute`.)

## السجل التاريخي لنقاط الحفظ

قيد نقطة الحفظ يُستبدل عند كل حفظ، فتزول خطوات التشغيل السابقة حالما يتقدّم. ويمكن للمخزن أيضًا أن يحتفظ بسجل تاريخي محدود لكل جلسة: كل `save()` يضيف القيد إلى حلقة دوّارة (أحدث `historyLimit` عملية حفظ، والافتراضي 50؛ وتُسقَط الأقدم)، وعلى ذلك تقوم إعادة تشغيل (replay) تشغيلٍ ما أو تفريعه (fork) من خطوة سابقة.

المخازن التي تحتفظ بسجل تاريخي:

| المخزن | السجل التاريخي | الخيار |
| - | - | - |
| `memoryStore()` | نعم | `memoryStore({ historyLimit })` |
| `SqliteStore` | نعم (جدول `checkpoint_history`) | `new SqliteStore(path, { historyLimit })` |
| `LocalStorageCheckpointStore` | نعم | `new LocalStorageCheckpointStore(storage, { historyLimit })` |
| `KVCheckpointStore` / `KVStore` (Cloudflare Workers KV) | نعم (انظر أدناه) | `new KVStore(kv, { historyLimit })` |
| `FileCheckpointStore` الخاص بـ Agent Forge | نعم | `new FileCheckpointStore(dir, { historyLimit })` |
| `CheckpointStore` مخصَّص | فقط إذا نفّذ `history()` | - |

مثلًا، مع المخزن الذي يعمل في الذاكرة:

```ts theme={null}
import { getCheckpointHistory, memoryStore } from '@lousho/build-ai-agent';

const { checkpoints } = memoryStore({ historyLimit: 20 }); // default 50, 0 keeps none
// new SqliteStore(path, { historyLimit }), new LocalStorageCheckpointStore(storage, { historyLimit })

// ... after a run with sessionId 'chat-42' ...
const history = await getCheckpointHistory(checkpoints, 'chat-42', { limit: 10 });
for (const { step, savedAt, status, checkpoint } of history ?? []) {
  console.log(step, status, savedAt, checkpoint.messages.length);
}

// Clear the checkpoint but keep its history (by default delete() clears both).
await checkpoints.delete('chat-42', { keepHistory: true });
```

* تعيد `history(sessionId, { limit? })` مدخلات بالشكل `{ step, savedAt, status, checkpoint }`، الأحدث أولًا (`step` هو `checkpoint.stepIndex`، و`savedAt` طابع زمني بصيغة ISO، و`status` يكون `'in-progress'` لنقطة حفظ لا تحمل حالة). يحفظ المنفِّذ بعد كل دورة نموذج وكل دفعة أدوات، فللجلسة مدخل واحد لكل عملية حفظ؛ والمدخلات تحتوي نقاط حفظ كاملة، فاخفض `historyLimit` لسجلات المحادثة الطويلة. والجلسة غير المعروفة تعطي `[]`.
* الدالة `history` اختيارية في `CheckpointStore`: المخزن المخصَّص الذي لا يوفّرها يواصل العمل، وتعيد له `getCheckpointHistory()` القيمة `undefined` بدل أن ترمي خطأً.
* يحتفظ `KVCheckpointStore` بمفتاح فهرس واحد لكل جلسة (`<prefix><sessionId>#history`، وفيه معرّفات المدخلات من الأقدم إلى الأحدث) وبمفتاح واحد لكل مدخل (`<prefix><sessionId>#history/<id>`)، فلا تكبر أي قيمة مع نمو السجل التاريخي؛ وتبقى أحدث نقطة حفظ عند `<prefix><sessionId>`، فالمخزن المكتوب قبل وجود السجل التاريخي يُحمَّل كما كان. عملية الحفظ تكتب نقطة الحفظ، ثم المدخل، ثم الفهرس، ثم تحذف المدخلات التي أسقطتها: فالتعطّل بين كتابتين يترك في أسوأ الأحوال مدخلًا غير مدرج في الفهرس (تنتهي صلاحيته مع TTL)، ولا يترك أبدًا مخزنًا تتعذّر قراءته. لا يدعم KV المعاملات (transactions)، فقد تفقد عمليتا حفظ متزامنتان لجلسة واحدة تحديثًا واحدًا للفهرس (فيغيب ذلك المدخل عن `history()`)، وقد يغيب مدخل لبعض الوقت في موقع طرفي (edge) آخر (اتساق نهائي؛ و`history()` تتخطاه). وجِّه طلبات الجلسة إلى موقع واحد حين يكون ذلك مهمًا. كل عملية حفظ تكلّف عملية `get` واحدة وعمليتي `put` إضافية؛ و`historyLimit: 0` يعطّل ذلك.
* مخزن الملفات في Agent Forge يكتب السجل التاريخي ملفًا واحدًا لكل جلسة (`.lousho/agents/<id>/checkpoint-history/<session>.json`، ويُستبدل استبدالًا ذرّيًا)؛ والملف الذي أتلفه تعطّل يُقرأ كأنه بلا سجل تاريخي ويعيد الحفظ التالي بناءه.
* يحتفظ `SqliteStore` بالسجل التاريخي في جدول جديد `checkpoint_history`، يُضاف عند فتح ملف قاعدة بيانات موجود؛ و`prune()` تزيل أيضًا مدخلات السجل التاريخي الأقدم من حدّها الزمني.

## التفريع وإعادة التشغيل

تبدأ `AgentExecutor.fork()` جلسة جديدة من خطوة في السجل التاريخي لجلسة أخرى: «ماذا لو أعادت الأداة شيئًا آخر في الخطوة 1؟» تأخذ أحدث مدخل في السجل التاريخي للخطوة `fromStep`، وتطبّق `patch`، وتحفظ الناتج نقطةَ حفظ بالحالة `'in-progress'` للجلسة الجديدة، وتعيد `{ sessionId, step, checkpoint }`. استأنف التفريعة كأي تشغيل غير مكتمل:

```ts theme={null}
import { AgentExecutor, compareTrajectories, memoryStore } from '@lousho/build-ai-agent';

const { checkpoints } = memoryStore();
await AgentExecutor.execute({ agent, provider, toolRegistry, input: 'Plan my trip', sessionId: 'trip', checkpointStore: checkpoints });

const fork = await AgentExecutor.fork({
  sessionId: 'trip',
  fromStep: 1,
  checkpointStore: checkpoints,
  patch: { toolResult: { toolCallId: 'call_weather', result: { forecast: 'rain' } } },
});
// fork.sessionId is 'trip.fork-1'; the 'trip' checkpoint and history are unchanged.
await AgentExecutor.execute({ agent, provider, toolRegistry, input: [], sessionId: fork.sessionId, checkpointStore: checkpoints });

const original = await checkpoints.load('trip');
const replayed = await checkpoints.load(fork.sessionId);
if (original && replayed) {
  const { divergedAt, a, b, drift } = compareTrajectories(original, replayed);
  console.log(divergedAt, a.length, b.length, drift); // 1, then each run's steps and the tool-call diff
}
```

مع `createAgent({ store })`، تفعل `agent.fork(sessionId, { fromStep, patch? })` الشيء نفسه على `store.checkpoints`، وتواصل `agent.resume(fork.sessionId)` التفريعة:

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

const assistant = createAgent({ provider, store: memoryStore() });
await assistant.send('Plan my trip', { sessionId: 'trip' });
const fork = await assistant.fork('trip', { fromStep: 1, patch: { appendInput: 'It will rain, plan for that.' } });
const result = await assistant.resume(fork.sessionId);
```

* يُطبَّق `patch` بهذا الترتيب: `messages(messages)` تعيد كتابة سجل المحادثة؛ و`businessState` يحلّ محلّ القيمة المحفوظة؛ و`toolResult: { toolCallId, result }` يستبدل نتيجة ذلك الاستدعاء في مكانها (الموضع نفسه ومعرّف الاستدعاء نفسه واسم الأداة نفسه، فيبقى سجل المحادثة صالحًا لدى كل مزوّد)، أو يسجّلها حين لا تكون للاستدعاء نتيجة بعد؛ و`appendInput` يضع رسالة مستخدم في الانتظار، تُرسل بعد أي استدعاءات أدوات ما زالت معلّقة.
* تحتفظ التفريعة بعدد خطوات نقطة الحفظ المصدر وباستهلاكها، فينطبق عليها ما تبقّى من ميزانية `maxSteps`. نتائج الأدوات المسجَّلة لا يُعاد تنفيذها؛ واستدعاءات الأدوات التي بلا نتيجة تُنفَّذ أولًا، كما في أي استئناف (فتفريعة من مدخل بالحالة `'awaiting-approval'` تمرّ عبر `needsApproval` من جديد؛ ولا يُنقل `approvalId` القديم).
* يسمّي `newSessionId` التفريعة؛ والافتراضي `<sessionId>.fork-<n>` حيث `n` أول عدد ليست له نقطة حفظ. ويُرفض `newSessionId` الذي له نقطة حفظ أصلًا.
* الخطوة غير الموجودة في السجل التاريخي (الجلسة غير معروفة، أو الخطوة لم تُنفَّذ قط، أو أُسقطت لتجاوزها `historyLimit`) ترمي `SDKError` برمز الخطأ [`LOUSHO_CHECKPOINT_NOT_FOUND`](/ar/errors#lousho_checkpoint_not_found)، ورسالته تسرد الخطوات المحفوظة. والمخزن الذي بلا `history()` لا يمكن التفريع منه (`ConfigurationError`).
* تأخذ `compareTrajectories(a, b)` نقطتي حفظ أو سجلَّي محادثة وتعيد خطوات كل تشغيل (خطوة لكل دورة للمساعد: نصها واستدعاءات أدواتها مع نتائجها)، و`divergedAt` (أول خطوة تختلف، بصرف النظر عن معرّفات استدعاءات الأدوات)، و`drift`، وهو الفروق في ترتيب الأدوات والمعطيات وعدد الخطوات وسبب الانتهاء، بالبنية نفسها التي يخرجها `lousho eval --drift`.

## الاستئناف بوكيل تغيَّر

كل نقطة حفظ وكل لقطة موافقة تحمل **بصمة الوكيل** (agent fingerprint): معرّف النموذج، واسم كل أداة مع تجزئة (hash) لمخطط JSON الخاص بمدخلها، وتجزئة لتعليمات النظام (إضافةً إلى SHA-256 قصير محسوب عليها كلها باستخدام Web Crypto). الدوال والقيم الأخرى التي لا يمكن تحويلها إلى بيانات متسلسلة (`execute` الخاصة بالأداة، والخطّافات، ودوال رد النداء) ليست جزءًا منها، فتغيير الشيفرة وحدها لا يُعدّ تغييرًا أبدًا. وتُخزَّن الأجزاء كلٌّ على حدة ليتمكن عدم التطابق من بيان ما الذي تغيّر.

حين يُستأنف تشغيل (`agent.resume(id)`، أو `session.resume()`، أو استدعاء لاحق لـ`execute()` على `sessionId` غير مكتمل، أو `agent.approvals.resolve()`، والصيغ المبثوثة منها)، تُقارَن بصمة الوكيل المستأنِف بالبصمة المحفوظة، قبل أي استدعاء للنموذج أو تنفيذ لأداة. ويحدّد `onAgentDrift` في `createAgent()` (وفي `AgentExecutor.execute()` / `resumeAfterApproval()`) ما يترتب على الاختلاف:

| `onAgentDrift` | عند اختلاف النموذج أو الأدوات أو التعليمات |
| - | - |
| `'warn'` (الافتراضي) | تحذير عبر `console.warn` يسمّي ما تغيّر وحدث `agent.drift` (`model`، `toolsAdded`، `toolsRemoved`، `toolsChanged`، `instructions`)؛ ويستمر التشغيل. |
| `'error'` | يُرفض بالخطأ [`LOUSHO_AGENT_DRIFT`](/ar/errors#lousho_agent_drift). تبقى نقطة الحفظ كما كانت؛ وتُعاد الموافقة إلى مكانها، فيستطيع الوكيل الصحيح أن يحسمها لاحقًا. |
| `'ignore'` | لا شيء. |

استدعاء الأداة المعلّق الذي لم تعد أداته موجودة (استدعتها دورة النموذج الأخيرة وليست لها نتيجة بعد، أو وافق عليها الإنسان) يُرفض دائمًا بالخطأ [`LOUSHO_RESUME_TOOL_MISSING`](/ar/errors#lousho_resume_tool_missing)، أيًّا كان الخيار.

```ts theme={null}
import { createAgent, memoryStore } from '@lousho/build-ai-agent';
import type { LLMProvider } from '@lousho/build-ai-agent';

declare const provider: LLMProvider;

// After a deploy that renamed a tool or changed the model, refuse to continue old runs.
const agent = createAgent({ provider, store: memoryStore(), onAgentDrift: 'error' });
const result = await agent.resume('job-1');
```

ملاحظات:

* نقاط الحفظ واللقطات المكتوبة قبل وجود هذه الميزة ليست لها بصمة، وتُستأنف تمامًا كما في السابق، بلا تحذير.
* تشغيل [وكيل ديناميكي](/ar/api-overview#الإعداد-الديناميكي) يحفظ `ctx` والنموذج الذي اختاره في نقطة الحفظ (`runConfig`)، ويستخدمهما الاستئناف بعد تعطّل: النموذج المحفوظ، وأدوات وتعليمات تُحسب من جديد بالـ`ctx` المحفوظ (وكان الحساب قبل ذلك يجري بـ`input: []` وبلا `metadata`).
* تغطي البصمة أدوات الوكيل نفسه ونموذجه وتعليماته، لا الأدوات التي تضيفها `subagents` و`skills`. والوكيل الفرعي الذي يتوقف بانتظار موافقة له بصمته الخاصة في لقطته المتداخلة، لكن الاستئناف لا يقارن إلا وكيل المستوى الأعلى.
* التشغيل المستأنف بعد موافقة يواصل بالتعليمات التي بدأ بها (فهي في سجل محادثته)، ولذلك تسجّل نقاط حفظه اللاحقة تلك التعليمات.

## التوافق مع البيانات المخزَّنة

* نقاط الحفظ المكتوبة قبل وجود `status` تُعامَل على أنها `'in-progress'` (فقبل هذا التغيير كانت التشغيلات المنتهية تحذف نقطة حفظها، فلم يكن يُخزَّن إلا التشغيلات غير المكتملة).
* الجلسات المحفوظة قبل أن يحتفظ المخزن بسجل تاريخي يبقى سجلها التاريخي فارغًا حتى الحفظ التالي.
* لقطات الموافقة المكتوبة قبل وجود `remainingToolCalls` تُستأنف كما كانت تُستأنف: لا تُنفَّذ الاستدعاءات التي تلي الاستدعاء المتوقف. ويحصل كل منها على نتيجة خطأ تفيد بأنه لم يُنفَّذ، فيبقى سجل المحادثة صالحًا.

## القيود

* يعرّف `sessionId` محادثة منطقية واحدة؛ وتشغيل استدعاءين لـ`execute()` على المعرّف نفسه في الوقت نفسه غير مدعوم.
* إذا استدعيت `resumeAfterApproval()` من دون `checkpointStore`، تحتفظ الجلسة بعلامة `'awaiting-approval'` وتظل استدعاءات `execute()` اللاحقة ترمي `SessionAwaitingApprovalError`؛ مرّر المخزن (أو احذف نقطة الحفظ).
* Workers KV متّسق اتساقًا نهائيًا (eventual consistency) عبر المواقع؛ انظر [النشر](/ar/deployment) لمعرفة تفاصيل هذا التحفّظ.


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