الانتقال إلى المحتوى الرئيسي

title: العمارة description: “كيف يعمل معالج الخطاف وتحميل الإعدادات وتقييم السياسات بشكل داخلي” icon: sitemap

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

نظرة عامة

يحتوي failproofai على نظامين فرعيين مستقلين:
  1. معالج الخطاف - عملية CLI سريعة يستدعيها Claude Code عند كل استدعاء لأداة وكيل. يقيّم السياسات ويعيد قراراً.
  2. مراقب الوكيل (لوحة المعلومات) - تطبيق ويب Next.js لمراقبة جلسات الوكيل وإدارة السياسات.
يشترك كلا النظامين الفرعيين في ملفات الإعدادات في ~/.failproofai/ وفي دليل المشروع .failproofai/، لكنهما يعملان كعمليات منفصلة ولا يتواصلان إلا من خلال نظام الملفات.

معالج الخطاف

التكامل مع Claude Code

عندما تقوم بتشغيل failproofai policies --install، يتم كتابة إدخالات مثل هذه في ~/.claude/settings.json:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
بعد ذلك يستدعي Claude Code failproofai --hook PreToolUse كعملية فرعية قبل كل استدعاء أداة، مما يمرر حمولة JSON على stdin.

تنسيق الحمولة

{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/myproject/sessions/abc123.jsonl",
  "cwd": "/home/user/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "sudo apt install nodejs" }
}
بالنسبة لأحداث PostToolUse، تحتوي الحمولة أيضاً على tool_result مع مخرجات الأداة. يفرض المعالج حداً أقصى بحجم 1 ميجابايت للـ stdin. يتم تجاهل الحمولات التي تتجاوز هذا الحد وجميع السياسات تسمح بشكل ضمني.

تنسيق الاستجابة

Deny (PreToolUse):
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
Deny (PostToolUse):
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
Instruct (أي حدث باستثناء Stop):
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
حدث Stop instruct:
  • رمز الخروج: 2
  • السبب المكتوب إلى stderr (وليس stdout)
Allow:
  • رمز الخروج: 0
  • stdout فارغ
السماح مع رسالة: allow(message) يسمح لسياسة بإرسال سياق معلوماتي مرة أخرى إلى Claude حتى عند السماح بالعملية. يكتب معالج الخطاف JSON التالي إلى stdout (وليس ملف إعدادات — هذه هي استجابة المعالج إلى Claude Code، تماماً مثل استجابات deny و instruct أعلاه):
// مكتوب إلى stdout بواسطة عملية معالج الخطاف
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
  • رمز الخروج: 0 (العملية مسموحة)
  • عندما تعيد عدة سياسات allow مع رسالة، يتم دمج رسائلهم بفاصل أسطر جديدة في سلسلة additionalContext واحدة
  • إذا لم توفر أي سياسة رسالة، يكون stdout فارغاً (نفس الحالة السابقة)

خط أنابيب المعالجة

src/hooks/handler.ts ينفذ خط الأنابيب الكامل:
stdin JSON
  → parse payload (max 1 MB)
  → extract session metadata (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← merges project + local + global config
  → register enabled builtin policies with resolved params
  → load custom policies from customPoliciesPath (if set)
  → register custom policies into policy registry
  → evaluate all policies (builtins first, then custom)
      → first deny short-circuits
      → instruct decisions accumulate
      → allow messages accumulate
  → write JSON decision to stdout
  → persist event to ~/.failproofai/hook-activity.jsonl
  → exit
تعمل العملية بأكملها في أقل من 100ms للحمولات النموذجية بدون استدعاءات LLM.

تحميل الإعدادات

src/hooks/hooks-config.ts ينفذ تحميل إعدادات بثلاث نطاقات.
[1] {cwd}/.failproofai/policies-config.json        ← project  (highest priority)
[2] {cwd}/.failproofai/policies-config.local.json  ← local
[3] ~/.failproofai/policies-config.json             ← global   (lowest priority)
منطق الدمج:
  • enabledPolicies - اتحاد منقح الازدواجية عبر جميع الملفات الثلاثة
  • policyParams - لكل سياسة، الملف الأول الذي يعرّفها يفوز بالكامل
  • customPoliciesPath - الملف الأول الذي يعرّفه يفوز
  • llm - الملف الأول الذي يعرّفه يفوز
تستخدم لوحة المعلومات الويب readHooksConfig() (عام فقط) للقراءة والكتابة، لأنه لا يتم استدعاؤها مع مسار cwd للمشروع.

تقييم السياسة

src/hooks/policy-evaluator.ts ينفذ السياسات بالترتيب. لكل سياسة:
  1. ابحث عن مخطط params للسياسة (إن وجد).
  2. اقرأ policyParams[policy.name] من الإعدادات المدمجة.
  3. ادمج القيم المقدمة من المستخدم فوق الافتراضيات المخطط لها لإنتاج ctx.params.
  4. استدعِ policy.fn(ctx) مع السياق المحلول.
  5. إذا كانت النتيجة deny، توقف على الفور وأرجع هذا القرار.
  6. إذا كانت النتيجة instruct، اجمع الرسالة وتابع.
  7. إذا كانت النتيجة allow، انتقل إلى السياسة التالية.
بعد تشغيل جميع السياسات:
  • إذا تم إرجاع أي deny، أرسل استجابة deny.
  • إذا تم جمع أي استجابات instruct، أرسل استجابة instruct واحدة مع دمج جميع الرسائل.
  • بخلاف ذلك، أرسل استجابة allow (stdout فارغ، خروج 0).

السياسات المدمجة

src/hooks/builtin-policies.ts يعرّف جميع السياسات المدمجة الـ 26 كائنات BuiltinPolicyDefinition:
interface BuiltinPolicyDefinition {
  name: string;
  description: string;
  fn: (ctx: PolicyContext) => PolicyResult;
  match: {
    events: HookEventType[];
    tools?: string[];
  };
  defaultEnabled: boolean;
  category: string;
  beta?: boolean;
  params?: PolicyParamsSchema;
}
السياسات التي تقبل params تعلن عن PolicyParamsSchema مع أنواع وافتراضيات لكل معامل. يُدرج مُقيّم السياسة القيم المحلولة في ctx.params قبل استدعاء fn. تقرأ دوال السياسة ctx.params دون حماية null لأن الافتراضيات يتم تطبيقها دائماً أولاً. يستخدم مطابقة الأنماط داخل السياسات رموز الأوامر المحللة (argv)، وليس مطابقة السلاسل الخام. هذا يمنع التجاوز عبر حقن مشغل shell (على سبيل المثال، نمط لـ sudo systemctl status * لا يمكن تجاوزه بإضافة ; rm -rf / للأمر).

السياسات المخصصة

src/hooks/custom-hooks-registry.ts ينفذ سجل مدعوم بـ globalThis:
const REGISTRY_KEY = "__failproofai_custom_hooks__";

export const customPolicies = {
  add(hook: CustomHook): void { ... }
};

export function getCustomHooks(): CustomHook[] { ... }
export function clearCustomHooks(): void { ... }  // used in tests
src/hooks/custom-hooks-loader.ts يحمّل ملف السياسة الخاص بالمستخدم:
  1. اقرأ customPoliciesPath من الإعدادات؛ تخطّ إذا كان غائباً.
  2. حل إلى مسار مطلق؛ تحقق من وجود الملف.
  3. أعد كتابة جميع استيرادات from "failproofai" إلى مسار dist الفعلي بحيث تحل customPolicies إلى سجل globalThis نفسه.
  4. أعد كتابة الاستيرادات المحلية الانتقالية بشكل متكرر لضمان التوافق مع ESM.
  5. اكتب ملفات .mjs مؤقتة و import() ملف الإدخال.
  6. استدعِ getCustomHooks() لاسترجاع الخطاطيف المسجلة.
  7. نظف جميع الملفات المؤقتة في كتلة finally.
عند حدوث أي خطأ (ملف غير موجود، خطأ في الصيغة، فشل الاستيراد)، يتم تسجيل الخطأ إلى ~/.failproofai/hook.log والمحمّل يعيد مصفوفة فارغة. السياسات المدمجة لا تتأثر. يتم تقييم السياسات المخصصة بعد جميع السياسات المدمجة. رفض السياسة المخصصة deny يوقف السياسات المخصصة الأخرى (لكن جميع البرامج المدمجة قد عملت بالفعل عند هذه النقطة).

تسجيل النشاط

بعد كل حدث خطاف، يُلحق المعالج سطر JSONL بـ ~/.failproofai/hook-activity.jsonl:
{
  "timestamp": "2026-04-06T12:34:56.789Z",
  "sessionId": "abc123",
  "eventType": "PreToolUse",
  "toolName": "Bash",
  "policyName": "block-sudo",
  "decision": "deny",
  "reason": "sudo command blocked by failproofai",
  "durationMs": 12
}
سطر واحد لكل سياسة اتخذت قراراً غير allow. قرارات Allow لا يتم تسجيلها (للحفاظ على صغر حجم الملف).

معمارية لوحة المعلومات

لوحة المعلومات هي تطبيق Next.js 16 يستخدم App Router مع React Server Components و Server Actions.
app/
  layout.tsx                  ← Root layout (theme, telemetry, nav)
  projects/page.tsx           ← Server component: list all Claude projects
  project/[name]/page.tsx     ← Server component: list sessions in a project
  project/[name]/session/
    [sessionId]/page.tsx      ← Server component: render session viewer
  policies/page.tsx           ← Client component: policy management + activity log
  actions/
    get-hooks-config.ts       ← Read config + policy list
    update-hooks-config.ts    ← Toggle policy on/off
    update-policy-params.ts   ← Update policy parameters
    get-hook-activity.ts      ← Paginate/search activity log
    install-hooks-web.ts      ← Install/remove hooks from the browser
  api/
    download/[project]/[session]/route.ts   ← Export session as ZIP/JSONL
تدفق البيانات:
  • مكونات الصفحة استدعاء lib/projects.ts و lib/log-entries.ts لقراءة بيانات المشروع/الجلسة مباشرة من نظام الملفات (لا طبقة API للقراءات).
  • صفحة السياسات استخدام Server Actions لجميع الطفرات (تبديل، تحديث المعاملات، التثبيت/الإزالة).
  • عارض الجلسة يحلل تنسيق نسخة Claude JSONL ويعرض جدول زمني للرسائل واستدعاءات الأدوات.
قرارات التصميم الرئيسية:
  • لا توجد قاعدة بيانات - جميع الحالة المستمرة موجودة في ملفات عادية (~/.failproofai/, ~/.claude/projects/).
  • Server Actions للطفرات - لا حاجة لـ REST API لعمليات CRUD.
  • React Server Components لصفحات القراءة - حمل أولي أسرع، بدون حزمة عميل لجلب البيانات.
  • مكونات العميل فقط حيث تكون التفاعلية مطلوبة (تبديل السياسة، بحث النشاط، عارض السجل).

تخطيط الملفات

failproofai/
├── bin/
│   └── failproofai.mjs           # CLI router (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Hook event pipeline
│   ├── builtin-policies.ts       # 26 policy definitions
│   ├── policy-evaluator.ts       # Policy execution engine
│   ├── policy-registry.ts        # Policy registration and lookup
│   ├── policy-types.ts           # TypeScript interfaces
│   ├── hooks-config.ts           # Multi-scope config loading
│   ├── custom-hooks-registry.ts  # globalThis-backed hook registry
│   ├── custom-hooks-loader.ts    # ESM loader for user JS hooks
│   ├── manager.ts                # install / remove / list operations
│   ├── install-prompt.ts         # Interactive policy selection prompt
│   ├── hook-logger.ts            # Logging to hook.log
│   ├── hook-activity-store.ts    # Persist activity to hook-activity.jsonl
│   └── llm-client.ts             # LLM API client (for AI-powered policies)
├── app/                          # Next.js dashboard (pages + server actions)
├── lib/                          # Shared utilities
│   ├── projects.ts               # Enumerate Claude projects from filesystem
│   ├── log-entries.ts            # Parse Claude transcript JSONL format
│   ├── paths.ts                  # Resolve system paths
│   └── ...
├── components/                   # Shared React UI components
├── contexts/                     # React context providers (theme, auto-refresh, telemetry)
├── examples/                     # Example custom hook files
└── __tests__/                    # Unit and E2E tests