agent.send(text) أحادي الدورة: كل استدعاء يبدأ بسجل فارغ.
أما الجلسة (session) فمحادثة متعددة الدورات: تحتفظ بسجل المحادثة (transcript) وتمرّره
إلى النموذج عند كل send().
كائن الجلسة
استدعاءات
send() المتزامنة على جلسة واحدة توضع في طابور وتُنفَّذ واحدًا تلو الآخر
بترتيب استدعائها، فلا تتداخل الرسائل في سجل المحادثة أبدًا.
agent.session({ id, turnPolicy }) يحدد ما يفعله send() أو stream()
حين تكون دورة قيد التنفيذ أو تنتظر البدء:
'wait'(الافتراضي): ينتظر ثم يُنفَّذ دورةً مستقلة، كما سبق.'queue': ينضم مُدخَله إلى تلك الدورة، مثلrun.enqueue(): يُضاف بعد نتائج أدوات الخطوة الحالية، فيراه استدعاء النموذج التالي في الدورة. يُنجَز الاستدعاء بنتيجة تلك الدورة (وsignalالخاص به لا يسري على الدورة)، وسجل المحادثة المحفوظ عند انتهاء الدورة يحوي رسالة المستخدم المضافة إلى الطابور في ترتيبها. وstream()المنضم لا يُخرج إلاrun.doneالخاص بالدورة؛ أما أحداث الدورة، ومنهاinput.queuedوinput.applied، فتُبث على التشغيل الذي بدأها. وإذا انتهت الدورة قبل أن تأخذ المُدخَل (اكتملت، أو توقفت مؤقتًا، أو أُلغيت)، نُفِّذ الاستدعاء دورةً تالية في نهاية المطاف. وإذا فشلت الدورة رُفض الاستدعاء بالخطأ نفسه؛ وفي الجلسة المتينة يبقى المُدخَل في نقطة حفظ الدورة ويطبّقهresume().'steer': مثل'queue'، لكن المُدخَل ينضم عبرrun.steer(): إذا لم يكن استدعاء النموذج في الدورة قد أصدر شيئًا بعد، أُلغي وأُعيد بالرسالة الجديدة، ولا تُنفَّذ استدعاءات الأدوات التي لم تبدأ في الدورة. وإلا انتظر النقطة الآمنة التالية، كما في'queue'. يُنجَز الاستدعاء بنتيجة الدورة، وتسري البدائل نفسها.
send() وstream() نصًا، أو أجزاء محتوى ([{ type: 'text', ... }, { type: 'image', ... }]، وهي رسالة مستخدم واحدة)، أو Message[]؛ وتحتفظ المخازن بالأجزاء، انظر المدخلات متعددة الوسائط.
send() الذي يرمي خطأً (خطأ من المزوّد، أو أداة ترمي
PropagatingToolError) أو يُلغى عبر signal يترك سجل المحادثة
كما كان تمامًا قبل ذلك الاستدعاء. وsend() المُلغى يُنجَز بـ
finishReason: 'aborted'، كما يفعل agent.send(). ولا يحوي سجل المحادثة المخزَّن أبدًا
دورة للمساعد فيها استدعاءات أدوات دون نتائج الأدوات المقابلة لها.
send() الذي يتوقف مؤقتًا عند أداة needsApproval يُنجَز بـ
finishReason: 'awaiting-approval'. ثم يتابع agent.approvals.resolve({ id, approved })
التشغيل بوصفه الدورة التالية في الجلسة، فينضم استدعاء الأداة ونتيجته
والإجابة النهائية إلى سجل المحادثة (انظر
الموافقات).
agent.session({ id, limits }) يضبط ميزانيات تشمل دورات الجلسة كلها
(مثل { maxCostUsd: 1 })، وتُحسب من الاستهلاك المحفوظ مع
سجل المحادثة؛ أما createAgent({ limits }) فيقيّد كل دورة على حدة. انظر
الميزانيات.
بث دورة في جلسة
session.stream(input, { signal }) هو agent.stream() لكن لمحادثة:
يُرجع AgentRun نفسه (أحداث محددة الأنواع مع وعد result، انظر
البث)، غير أن الدورة ترى سجل المحادثة حتى اللحظة وتنضم
إليه.
- يبني قائمة الرسائل نفسها التي يبنيها
send()، ويصطف خلف الاستدعاءات السابقة على الجلسة (يمكن الجمع بينsend()وstream()). - حين ينتهي التشغيل، تُحفظ رسالة المستخدم الجديدة ومخرجات التشغيل
تمامًا كما يحفظها
send()، وبعد ذلك فقط يُسلَّمrun.done. فحين تنتهي حلقةfor await، أو يُنجَزrun.result، يكون سجل المحادثة مكتملًا. وتحمل الأحداثrunIdالخاص بالمقبض المُرجَع. - التشغيل المُلغى (عبر
signal، أو بالخروج من الحلقة مبكرًا) أو الفاشل يترك سجل المحادثة كما كان قبل الاستدعاء، مثلsend(). والتشغيل الذي كان قد انتهى فعلًا عند مغادرة الحلقة يُحفظ. والتشغيل الفاشل يُنهي البث بـerrorثمrun.done(finishReason: 'error')، ويُرفضrun.result. ويشمل ذلك مخزنًا يفشل في التحميل أو الحفظ: وعندها لا يكون للدورةrun.start. - التشغيل الذي يتوقف مؤقتًا عند أداة
needsApprovalينتهي بـapproval.requestedثمrun.done('awaiting-approval')، تمامًا كما يُنجَزsend()، ويحوي سجل المحادثة الدورة حتى موضع التوقف. وagent.approvals.resolve()يتابعه بوصفه الدورة التالية في الجلسة. ودالةapproveلا تسري على البث، كما فيagent.stream().
الجلسات المتينة
أعطِ الوكيلstore فيه نقاط حفظ (checkpoints)، فتُسجَّل لكل دورة في الجلسة
نقطة حفظ بعد كل استجابة من النموذج وكل نتيجة أداة، باستخدام
آلية التنفيذ المتين. والدورة التي قطعها
انهيار، أو فشل في كتابة نقطة حفظ، أو PropagatingToolError، يمكن عندئذ
إتمامها لاحقًا، في عملية أخرى:
createAgent({ store })يقبل أيAgentStore({ sessions?, checkpoints?, approvals? }، انظر اختيار المخزن):agent.session({ id })يحفظ سجل محادثته فيstore.sessionsونقاط حفظه فيstore.checkpoints، ويحفظstore.approvalsتوقفات الموافقة. وstoreالخاص بالجلسة (وهوSessionStore، أو كائن{ sessions, checkpoints }) وcheckpointStoreيغلبان مخزن الوكيل، جزءًا جزءًا.agent.resume(id)هوagent.session({ id }).resume()، إلا أنه يُنهي أولًا تشغيلًا بدأ بـagent.send(message, { sessionId: id })(انظر التنفيذ المتين).- كل دورة تُنفَّذ بـ
sessionId: '<session id>.turn-<n>'(وnهو طول سجل المحادثة لحظة بدء الدورة)، فتجد العملية الجديدة الدورة المنقطعة دون أي تتبّع إضافي. والدورة المنتهية تنضم إلى سجل المحادثة، تمامًا كما فيsend()العادي، وتُحذف نقطة حفظها. resume()يتابع الدورة عبر مسار الاستئناف في المنفّذ (input: []): استدعاءات الأدوات التي سُجّلت نتائجها لا تُنفَّذ من جديد، واستجابة النموذج المسجّلة لا تُطلب من جديد. أما الأداة التي كانت قيد التنفيذ حين ماتت العملية فتُنفَّذ من جديد (مرة واحدة على الأقل، انظر التنفيذ المتين).send()وstream()يستأنفان الدورة المعلّقة أولًا، ثم يرسلان الرسالة الجديدة، فيرى النموذج الدورة بعد إتمامها (وأحداث الدورة المستأنفة لا تُبث). استخدمpending()للتحقق أولًا، أوdiscardPending()للتخلي عن الدورة غير المنتهية.- الدورة التي تتوقف مؤقتًا عند أداة
needsApprovalتبقى في نقطة حفظها، لا في سجل المحادثة، إلى أن تنتهي. وما دامت تنتظر، يرميresume()وsend()وstream()الخطأSessionAwaitingApprovalError(ومعهapprovalId)، ويتابعagent.approvals.resolve({ id, approved })الدورة في هذه الجلسة. بعد إعادة التشغيل، افتح الجلسة واستدعِresume()(أوsend()) مرة واحدة قبل البتّ، ليعرف الوكيل إلى أي جلسة تنتمي الموافقة؛ وأعطِ الوكيلين كليهماstoreالدائم نفسه (أوapprovalStore). - الدورة المُلغاة يُتخلى عنها (وتُحذف نقطة حفظها)، كما هو الحال من دون
مخزن نقاط حفظ. و
clear()يحذف الدورة المعلّقة أيضًا.
المخازن
SessionStore يحفظ سجلات المحادثات بين الاستدعاءات:
MemorySessionStore(الافتراضي، مخزن جديد لكل جلسة) يعيش ما عاشت العملية. شارك نسخة واحدة بين الجلسات لتجدها بمعرّفاتها.FileSessionStore(dir)يكتب ملف JSON واحدًا لكل جلسة (<dir>/<id>.json)، كتابةً ذرّية (ملف مؤقت ثم إعادة تسمية)، وينشئdirعند أول حفظ.
^[A-Za-z0-9_-]{1,128}$ (فهي تصير أسماء ملفات، ولذلك
يُرفض ../x وa/b بخطأ يوضّح السبب). نفّذ
SessionStore بنفسك لحفظ سجلات المحادثات في قاعدة بيانات أو في Redis.
اختيار المخزن
للجلسات ولنقاط حفظ التنفيذ المتين وللموافقات واجهة مخزن لكلٍّ منها (SessionStore وCheckpointStore وApprovalStore). اختر
التنفيذ المناسب بحسب المكان الذي تعمل فيه العملية:
كل واحد منها جزء من
AgentStore: مرّرها معًا هكذا
createAgent({ store: { sessions, checkpoints, approvals } }). وmemoryStore()
وSqliteStore كائنا AgentStore جاهزان؛ وللملفات العادية، اجمع
مخازن الملفات:
SessionStore
على Redis، أو CheckpointStore يعتمد على KV في Cloudflare Workers (والـ
Worker المولَّد يستخدم KVCheckpointStore، انظر النشر).
SqliteStore يحفظ الثلاثة في ملف قاعدة بيانات واحد، باستخدام
node:sqlite المضمّن في Node (دون اعتمادية أصلية (native)؛ يتطلب Node 22.13 أو أحدث، وهو غير
مُعاد تصديره من نقطة الدخول الجذرية، فاستيراد SDK لا يحمّله أبدًا):
- يُنشأ المجلد إن لم يكن موجودًا. والمخطط مُرقَّم الإصدار عبر
PRAGMA user_versionويُرحَّل عند الفتح؛ وقاعدة البيانات التي كتبها إصدار أحدث تُرفض. - وضع WAL ومهلة انشغال (busy timeout) مدتها 5 ثوانٍ يتيحان لعمليتين تشارك الملف. ولا يبتّ في الموافقة إلا واحدة منهما.
- نقاط الحفظ ولقطات الموافقة تُخزَّن JSON معتمًا لا يُفسَّر محتواه، فالحقول الجديدة تُكتب وتُقرأ دون تغيير.
prune()يزيل الجلسات ونقاط الحفظ التي لم تُحدَّث خلالolderThanMs، والموافقات التي بُتّ فيها قبل تلك المدة؛ أما الموافقات التي لم يُبتّ فيها فتبقى.- الملف الذي ليس قاعدة بيانات SQLite يفشل بخطأ يذكر المسار؛
واستخدام المخزن بعد
close()يرمي خطأً واضحًا.
تعليمات المشروع
ليست جزءًا من الجلسات، لكنها تُطلب معها كثيرًا: انظر “تعليمات المشروع” في الإعدادات لتعطي الوكيل ملفAGENTS.md الخاص بمستودعك.