التخزين
جميع البيانات ذات الحالة في Triggerfish تتدفق عبر تجريد StorageProvider موحد. لا تُنشئ أي وحدة آلية تخزين خاصة بها -- كل مكون يحتاج إلى استمرارية يأخذ StorageProvider كتبعية. هذا التصميم يجعل الواجهات الخلفية قابلة للتبديل دون لمس منطق الأعمال ويبقي جميع الاختبارات سريعة وحتمية.
واجهة StorageProvider
typescript
interface StorageProvider {
/** استرجاع قيمة بالمفتاح. يُعيد null إذا لم يُوجد. */
get(key: string): Promise<StorageValue | null>;
/** تخزين قيمة في مفتاح. يكتب فوق أي قيمة موجودة. */
set(key: string, value: StorageValue): Promise<void>;
/** حذف مفتاح. لا عملية إذا لم يكن المفتاح موجوداً. */
delete(key: string): Promise<void>;
/** سرد جميع المفاتيح المطابقة لبادئة اختيارية. */
list(prefix?: string): Promise<string[]>;
/** حذف جميع المفاتيح. استخدم بحذر. */
clear(): Promise<void>;
}StorageValue هو سلسلة نصية. جميع البيانات المُهيكلة (الجلسات، سجلات
النسب، التكوين) تُسلسل إلى JSON قبل التخزين وتُعاد هيكلتها عند القراءة. هذا يبقي الواجهة بسيطة ومستقلة عن الواجهة الخلفية. :::
التنفيذات
| الواجهة الخلفية | حالة الاستخدام | الاستمرارية | التكوين |
|---|---|---|---|
MemoryStorageProvider | الاختبار، الجلسات المؤقتة | لا يوجد (تُفقد عند إعادة التشغيل) | لا حاجة لتكوين |
SqliteStorageProvider | الافتراضي للمستوى الشخصي | SQLite WAL في ~/.triggerfish/data/triggerfish.db | بدون تكوين |
| واجهات خلفية مؤسسية | المستوى المؤسسي | يديرها العميل | Postgres, S3، أو واجهات أخرى |
MemoryStorageProvider
يُستخدم في جميع الاختبارات للسرعة والحتمية. البيانات موجودة فقط في الذاكرة وتُفقد عند خروج العملية. كل مجموعة اختبار تُنشئ MemoryStorageProvider جديد، مما يضمن عزل الاختبارات وقابليتها للإعادة.
SqliteStorageProvider
الافتراضي لعمليات النشر على المستوى الشخصي. يستخدم SQLite في وضع WAL (Write-Ahead Logging) للوصول المتزامن للقراءة وأمان ضد الأعطال. تقع قاعدة البيانات في:
~/.triggerfish/data/triggerfish.dbلا يتطلب SQLite تكويناً، ولا عملية خادم، ولا شبكة. ملف واحد يخزن جميع حالة Triggerfish. توفر حزمة @db/sqlite لـ Deno الربط، الذي يتطلب صلاحية --allow-ffi.
يسمح وضع SQLite WAL لقراء متعددين بالوصول لقاعدة البيانات بشكل متزامن مع
كاتب واحد. هذا مهم لـ Gateway، الذي قد يقرأ حالة الجلسة بينما يكتب الوكيل نتائج الأداة. :::
واجهات خلفية مؤسسية
يمكن لعمليات النشر المؤسسية توصيل واجهات تخزين خلفية خارجية (Postgres, S3، إلخ.) بدون تغييرات في الكود. أي تنفيذ لواجهة StorageProvider يعمل. تُكوّن الواجهة الخلفية في triggerfish.yaml.
مفاتيح بمساحات أسماء
جميع المفاتيح في نظام التخزين منظمة بمساحات أسماء ببادئة تحدد نوع البيانات. هذا يمنع التصادمات ويجعل من الممكن الاستعلام والاحتفاظ والتطهير حسب الفئة.
| مساحة الاسم | نمط المفتاح | الوصف |
|---|---|---|
sessions: | sessions:sess_abc123 | حالة الجلسة (سجل المحادثة، البيانات الوصفية) |
taint: | taint:sess_abc123 | مستوى taint الجلسة |
lineage: | lineage:lin_789xyz | سجلات نسب البيانات (تتبع المنشأ) |
audit: | audit:2025-01-29T10:23:45Z:hook_pre_output | إدخالات سجل التدقيق |
cron: | cron:job_daily_report | حالة مهام cron وسجل التنفيذ |
notifications: | notifications:notif_456 | قائمة انتظار الإشعارات |
exec: | exec:run_789 | سجل بيئة تنفيذ الوكيل |
skills: | skills:skill_weather | بيانات وصفية للمهارات المُثبتة |
config: | config:v3 | لقطات التكوين |
سياسات الاحتفاظ
لكل مساحة اسم سياسة احتفاظ افتراضية. يمكن لعمليات النشر المؤسسية تخصيصها.
| مساحة الاسم | الاحتفاظ الافتراضي | المبرر |
|---|---|---|
sessions: | 30 يوماً | سجل المحادثة يتقادم |
taint: | يطابق احتفاظ الجلسة | Taint بلا معنى بدون جلسته |
lineage: | 90 يوماً | مدفوع بالامتثال، مسار التدقيق |
audit: | سنة واحدة | مدفوع بالامتثال، قانوني وتنظيمي |
cron: | 30 يوماً | سجل التنفيذ لتصحيح الأخطاء |
notifications: | حتى التسليم + 7 أيام | الإشعارات غير المُسلمة يجب أن تستمر |
exec: | 30 يوماً | مخرجات التنفيذ لتصحيح الأخطاء |
skills: | دائم | بيانات المهارات المُثبتة يجب ألا تنتهي |
config: | 10 نسخ | سجل تكوين متدحرج للتراجع |
مبادئ التصميم
جميع الوحدات تستخدم StorageProvider
لا تُنشئ أي وحدة في Triggerfish آلية تخزين خاصة بها. إدارة الجلسات، تتبع taint، تسجيل النسب، تسجيل التدقيق، حالة cron، قوائم انتظار الإشعارات، سجل التنفيذ، والتكوين -- كلها تتدفق عبر StorageProvider.
هذا يعني:
- تبديل الواجهات الخلفية يتطلب تغيير نقطة حقن تبعية واحدة
- الاختبارات تستخدم
MemoryStorageProviderللسرعة -- بدون إعداد SQLite، بدون نظام ملفات - يوجد مكان واحد بالضبط لتنفيذ التشفير أثناء الراحة أو النسخ الاحتياطي أو النسخ المتماثل
التسلسل
جميع البيانات المُهيكلة تُسلسل إلى سلاسل JSON قبل التخزين. طبقة التسلسل/إعادة الهيكلة تتعامل مع:
- كائنات
Date(تُسلسل كسلاسل ISO 8601 عبرtoISOString()، تُعاد هيكلتها عبرnew Date()) - الأنواع المُوسومة (تُسلسل كقيمتها النصية الأساسية)
- الكائنات والمصفوفات المتداخلة
typescript
// تخزين جلسة
const session = {
id: "sess_abc",
taint: "CONFIDENTIAL",
createdAt: new Date(),
};
await storage.set("sessions:sess_abc", JSON.stringify(session));
// استرجاع جلسة
const raw = await storage.get("sessions:sess_abc");
if (raw) {
const session = JSON.parse(raw);
session.createdAt = new Date(session.createdAt); // استعادة Date
}الثبات
عمليات الجلسة ثابتة. قراءة جلسة وتعديلها وكتابتها مرة أخرى تُنتج دائماً كائناً جديداً. لا تُعدل الدوال أبداً الكائن المُخزن في مكانه. هذا يتوافق مع مبدأ Triggerfish الأوسع أن الدوال تُعيد كائنات جديدة ولا تُعدل أبداً.
هيكل الدليل
~/.triggerfish/
config/ # تكوين الوكيل، SPINE.md، TRIGGER.md
data/ # triggerfish.db (SQLite)
workspace/ # بيئة تنفيذ الوكيل
<agent-id>/ # مساحة عمل لكل وكيل (تستمر)
background/ # مساحات عمل جلسات الخلفية
skills/ # المهارات المُثبتة
logs/ # سجلات التدقيق
secrets/ # مخزن بيانات اعتماد مُشفرSECURITY يحتوي دليل secrets/ على بيانات اعتماد مُشفرة يديرها تكامل
سلسلة مفاتيح نظام التشغيل. لا تخزن أبداً الأسرار في ملفات التكوين أو في StorageProvider. استخدم سلسلة مفاتيح نظام التشغيل (المستوى الشخصي) أو تكامل الخزنة (المستوى المؤسسي). :::
