From 9111825d95778e70721a7b3e6b5a65ea4163f05e Mon Sep 17 00:00:00 2001 From: Hamza Ayed Date: Wed, 7 Oct 2026 20:21:45 +0300 Subject: [PATCH] test: add reproducible live agent workspace smoke check --- SovereignAI-Starter/README.md | 8 ++ SovereignAI-Starter/ROADMAP.md | 4 +- .../scripts/verify_agent_workspace_live.ps1 | 127 ++++++++++++++++++ 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 SovereignAI-Starter/scripts/verify_agent_workspace_live.ps1 diff --git a/SovereignAI-Starter/README.md b/SovereignAI-Starter/README.md index 5d6d8b2..29118b9 100644 --- a/SovereignAI-Starter/README.md +++ b/SovereignAI-Starter/README.md @@ -78,6 +78,14 @@ python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 - `POST /v1/chat/completions` يمرر الطلب إلى الخادم المحلي عند ضبط المتغيرين أعلاه. - يمكن التبديل في Swagger بين النموذجين بإضافة `model` إلى جسم الطلب، مثل `gemma4:e2b`. +للتحقق الحي من دورة تسجيل مجلد اختبار، وقراءة ملف عبر الوكيل، وإنشاء معاينة تعديل دون تطبيقها: + +```powershell +.\scripts\verify_agent_workspace_live.ps1 +``` + +يتطلب هذا التحقق أن يكون FastAPI وOllama والنموذج المحلي يعملون. ينشئ السكريبت ملفات مؤقتة، ثم يلغي تسجيل المشروع والجلسة ويحذف مجلد الاختبار؛ ولا يطبق معاينة التعديل. + تثبيت OCR المحلي الاختياري للصور وPDFات الممسوحة: ```powershell diff --git a/SovereignAI-Starter/ROADMAP.md b/SovereignAI-Starter/ROADMAP.md index 37ca2b6..18b0885 100644 --- a/SovereignAI-Starter/ROADMAP.md +++ b/SovereignAI-Starter/ROADMAP.md @@ -247,8 +247,8 @@ 4. [x] إضافة طبقة مزود النموذج واكتشاف النماذج (2026-10-01): عقد موحد للطلب الكامل والبث والقائمة، واستخدامه في المحادثة والوكيل وقراءة مساحة العمل والويب؛ Ollama هو المزوّد المنفذ حاليًا. تحقق حي من `/health` و`/v1/models` وطلب محادثة باستخدام Gemma. 5. [x] إكمال Markdown للمرحلة 1: جداول قابلة للتمرير، قوائم متداخلة، ومربعات مهام GFM؛ التحليل واختبارات الواجهة الثلاثة ناجحة وبناء Windows Debug نجح. 6. [x] تشغيل جلسة Windows Debug بعد إصلاح مجلدي روابط إضافات `record` المؤقتة. بناء OneDrive يفشل عند cloud placeholders وMSBuild `FTK1011`. في 2026-10-04 نجح بناء وتشغيل نسخة `%TEMP%` (PID 33624) مع API/Gemma؛ ثم أضيف `scripts/run_windows_debug_local.ps1` لمسار ثابت خارج OneDrive. `-BuildOnly` في `%LOCALAPPDATA%\SovereignAI-Starter\windows-dev\flutter_app` نجح أول مرة في 336 ثانية، وإعادة البناء بالـcache نجحت في 55 ثانية. وفي 2026-10-04 شُغلت أيضًا نسخة Debug الأحدث التي تتضمن زر إنشاء المشروع (PID 35400) وأعادت API `/health` حالة `ok` مع Gemma 4 وSQLite والوكيل. في محاولة 2026-10-04 لبناء تغييرات heartbeat/فهرسة PDF لم يكتمل البناء: إضافة `flutter_secure_storage_windows` لم تجد الرأس C++ عبر مسار الرابط الرمزي؛ ترجمت الإضافة منفردة بعد توجيه MSBuild إلى مسار الكاش الحقيقي، لكن البناء الكامل اصطدم بصلاحيات سجلات MSBuild في النسخة المؤقتة. كانت المحاولة السابقة غير ناجحة؛ لكن بناء لاحق من SDK معزول نجح، ونافذة التطبيق استجابت واتصلت بالخدمة كما هو مسجل أعلاه. تفاعل الأزرار ما زال مفتوحًا. -- [ ] تحقق مرئي ووظيفي كامل من زر فتح/إنشاء المشروع، ثم تسجيل مجلد وقراءة ملف وطلب معاينة تعديل من داخل نافذة Windows. أدلة الواجهة الآلية: اختبارات Flutter على أحدث مصدر بعد مزامنته إلى نسخة LocalAppData نجحت 22/22، واختبار `widget_test.dart` المستهدف نجح 7/7 بعد إضافة تأكيدات أن إرسال المستخدم يمرر مسار المشروع و`src/hello.py` للوكيل، وأن معاينة التعديل لا تطبق دون موافقة؛ وتشمل الاختبارات ظهور إجراء المشروع على المقاسات الضيقة والواسعة، واستمرار تسجيل المجلدات، وإنشاء مجلد مشروع آمن، ومعاينة diff؛ `flutter analyze --no-pub` بلا ملاحظات. أعيدت مزامنة أحدث المصدر وبناؤه في `LocalAppData\SovereignAI-Starter\windows-dev\ui-verify-20261004` باستخدام SDK معزول؛ نجح Windows Debug خلال 389.6 ثانية. اجتازت النسخة نفسها `flutter test --no-pub` 22/22 و`flutter analyze --no-pub` بلا ملاحظات (146 ثانية). شُغّل exe الحالي PID 55860، HWND 182911482، و`Responding=True`؛ أكد `/health` API 8000 الحالة `ok` مع Gemma 4 وSQLite. لم أتحقق بصريًا: `Computer Use list_windows` لم يعرض نافذة PID 55860، وأظهر نوافذ من مسار Debug قديم؛ التقاط النافذة القديمة انتهى بمهلة مرتين. في 2026-10-04 أضيف Widget Test يحاكي استجابة `file_selector` ثم ينقر تسجيل المشروع، يختار `src/hello.py`، ويرسل السياق للوكيل حتى معاينة diff مع تأكيد عدم التطبيق دون موافقة؛ الاختبارات الكاملة صارت 23/23 و`flutter analyze --no-pub` بلا ملاحظات. في متابعة 2026-10-04 الحالية، قائمة Computer Use ما زالت تعرض أربع نوافذ `Mithqal AI` من المسار القديم `SovereignAI-run-20260930` فقط، ولا تعرض نافذة PID 55860 من نسخة LocalAppData؛ فتح مجلد المشروع من تبويب Flutter Web 5301 لم يُظهر منتقي ملفات أو تغييرًا مرئيًا. لذلك لم أرسل إدخالًا إلى نافذة غير مؤكدة. هذا لا يغني عن اختبار النافذة الأصلية؛ ما زال فتح/تسجيل/قراءة/معاينة المشروع يدويًا على Windows مطلوبًا. - - [ ] تجربة `flutter run` التفاعلية عبر السكريبت المحلي ما زالت معلقة. أعيد بناء Debug بنجاح باستخدام SDK معزول، لكن توجد عدة عمليات `flutter_app` من جلسات ومسارات مختلفة؛ لم أغلقها، والسكريبت يرفض `flutter run` عندما تكون هذه النسخ مفتوحة. البناء والتحليل واختبارات Flutter ليست بديلًا عن التشغيل التفاعلي؛ يلزم إغلاق جلسات Debug بعد حفظ ما يلزم ثم تشغيل السكريبت. +- [ ] تحقق مرئي ووظيفي كامل من زر فتح/إنشاء المشروع، ثم تسجيل مجلد وقراءة ملف وطلب معاينة تعديل من داخل نافذة Windows. أدلة الواجهة الآلية: اختبارات Flutter على أحدث مصدر بعد مزامنته إلى نسخة LocalAppData نجحت 22/22، واختبار `widget_test.dart` المستهدف نجح 7/7 بعد إضافة تأكيدات أن إرسال المستخدم يمرر مسار المشروع و`src/hello.py` للوكيل، وأن معاينة التعديل لا تطبق دون موافقة؛ وتشمل الاختبارات ظهور إجراء المشروع على المقاسات الضيقة والواسعة، واستمرار تسجيل المجلدات، وإنشاء مجلد مشروع آمن، ومعاينة diff؛ `flutter analyze --no-pub` بلا ملاحظات. أعيدت مزامنة أحدث المصدر وبناؤه في `LocalAppData\SovereignAI-Starter\windows-dev\ui-verify-20261004` باستخدام SDK معزول؛ نجح Windows Debug خلال 389.6 ثانية. اجتازت النسخة نفسها `flutter test --no-pub` 22/22 و`flutter analyze --no-pub` بلا ملاحظات (146 ثانية). شُغّل exe الحالي PID 55860، HWND 182911482، و`Responding=True`؛ أكد `/health` API 8000 الحالة `ok` مع Gemma 4 وSQLite. لم أتحقق بصريًا: `Computer Use list_windows` لم يعرض نافذة PID 55860، وأظهر نوافذ من مسار Debug قديم؛ التقاط النافذة القديمة انتهى بمهلة مرتين. في 2026-10-04 أضيف Widget Test يحاكي استجابة `file_selector` ثم ينقر تسجيل المشروع، يختار `src/hello.py`، ويرسل السياق للوكيل حتى معاينة diff مع تأكيد عدم التطبيق دون موافقة؛ الاختبارات الكاملة صارت 23/23 و`flutter analyze --no-pub` بلا ملاحظات. في متابعة 2026-10-04 الحالية، قائمة Computer Use ما زالت تعرض أربع نوافذ `Mithqal AI` من المسار القديم `SovereignAI-run-20260930` فقط، ولا تعرض نافذة PID 55860 من نسخة LocalAppData؛ فتح مجلد المشروع من تبويب Flutter Web 5301 لم يُظهر منتقي ملفات أو تغييرًا مرئيًا. لذلك لم أرسل إدخالًا إلى نافذة غير مؤكدة. هذا لا يغني عن اختبار النافذة الأصلية؛ ما زال فتح/تسجيل/قراءة/معاينة المشروع يدويًا على Windows مطلوبًا. تحقق API حي 2026-10-07: اجتاز `scripts/verify_agent_workspace_live.ps1` دورة مصطنعة كاملة على FastAPI 8000 وGemma 4؛ سجل المشروع المؤقت، أظهر `README.md` و`src/hello.py`، قرأ الوكيل الملف واستشهد به، ثم أعاد `propose_file_change` معاينة تحديث لـ`README.md` مع بقاء الملف دون تغيير؛ ألغيت الجلسة والتسجيل وحُذف المجلد. أداة Computer Use الحالية لم تعرض أي تطبيق Windows أصلي؛ PID 1328 من مسار البناء الحالي بلا HWND، والتقاط تبويب الويب 5303 انتهى بمهلة بينما 5305 ظهر أبيض. لذلك تبقى تجربة النافذة اليدوية مفتوحة. + - [ ] تجربة `flutter run` التفاعلية عبر السكريبت المحلي ما زالت معلقة. أعيد بناء Debug بنجاح باستخدام SDK معزول، لكن توجد عدة عمليات `flutter_app` من جلسات ومسارات مختلفة؛ لم أغلقها، والسكريبت يرفض `flutter run` عندما تكون هذه النسخ مفتوحة. البناء والتحليل واختبارات Flutter ليست بديلًا عن التشغيل التفاعلي؛ يلزم إغلاق جلسات Debug بعد حفظ ما يلزم ثم تشغيل السكريبت. فحص 2026-10-07: لا يظهر في جرد Computer Use تطبيق أصلي قابل للتحديد، وتعذر التقاط نافذة Windows؛ لا أعد تشغيل API/الوكيل الحي بديلًا عن تجربة Flutter التفاعلية. 7. [x] اختيار مساحة عمل وعرض الملفات المدعومة وتحديد ملفات للسؤال؛ التحقق من المجلدات والملفات على الخادم. 8. [x] إظهار انتقالات تقدم الوكيل أثناء العمل وربطها بحالات التنفيذ الفعلية عبر SSE؛ اختبار API حي لمسار الحاسبة. (2026-10-03: عبر FastAPI على 8100 وجلسة loopback، اختار Gemma 4 أداة `calculator` لمهمة 17×23، سجّل خطوة واحدة `completed` وأعاد 391.) 9. [x] معاينة تغييرات الملفات عبر diff، ثم موافقة صريحة قبل الكتابة داخل مساحة العمل فقط (2026-10-02: endpoint ظهر في OpenAPI بعد إعادة تشغيل FastAPI؛ اختبارات Python وFlutter ناجحة، وشُغلت واجهة Windows Debug مجددًا مع مسار التأكيد.) diff --git a/SovereignAI-Starter/scripts/verify_agent_workspace_live.ps1 b/SovereignAI-Starter/scripts/verify_agent_workspace_live.ps1 new file mode 100644 index 0000000..38f7dad --- /dev/null +++ b/SovereignAI-Starter/scripts/verify_agent_workspace_live.ps1 @@ -0,0 +1,127 @@ +param( + [string]$ApiBaseUrl = 'http://127.0.0.1:8000' +) + +$ErrorActionPreference = 'Stop' +$ApiBaseUrl = $ApiBaseUrl.TrimEnd('/') +$testRoot = Join-Path ([IO.Path]::GetTempPath()) ('sovereignai-agent-smoke-' + [guid]::NewGuid().ToString('N')) +$readmePath = Join-Path $testRoot 'README.md' +$sourcePath = Join-Path $testRoot 'src/hello.py' +$token = $null +$registered = $false + +try { + $health = Invoke-RestMethod -Uri "$ApiBaseUrl/health" -TimeoutSec 10 + if ($health.status -ne 'ok') { + throw "The API is not healthy: $($health.status)" + } + + $null = New-Item -ItemType Directory -Path (Split-Path -Parent $sourcePath) -Force + Set-Content -LiteralPath $readmePath -Encoding utf8 -Value "# Agent workspace smoke test`n`nThe greet function returns a greeting containing the provided name.`n" + Set-Content -LiteralPath $sourcePath -Encoding utf8 -Value "def greet(name):`n return f'Hello, {name}!'`n" + + $session = Invoke-RestMethod -Method Post -Uri "$ApiBaseUrl/v1/auth/local-session" -TimeoutSec 10 + $token = $session.access_token + $headers = @{ Authorization = "Bearer $token" } + $projectBody = @{ workspace_path = $testRoot } | ConvertTo-Json + + $registeredProject = Invoke-RestMethod ` + -Method Post ` + -Uri "$ApiBaseUrl/v1/agent/projects" ` + -Headers $headers ` + -ContentType 'application/json' ` + -Body $projectBody ` + -TimeoutSec 10 + $registered = $true + + $listed = Invoke-RestMethod ` + -Method Post ` + -Uri "$ApiBaseUrl/v1/agent/workspace/files" ` + -Headers $headers ` + -ContentType 'application/json' ` + -Body $projectBody ` + -TimeoutSec 10 + if ($listed.files -notcontains 'src/hello.py') { + throw 'The registered project file list did not contain src/hello.py.' + } + + $readRequest = @{ + task = 'اشرح بإيجاز ماذا تفعل الدالة greet في الملف المحدد، واستشهد بمساره.' + workspace_path = $testRoot + workspace_files = @('src/hello.py') + skill_id = 'code_explain' + } | ConvertTo-Json -Depth 5 + $readResult = Invoke-RestMethod ` + -Method Post ` + -Uri "$ApiBaseUrl/v1/agent/run" ` + -Headers $headers ` + -ContentType 'application/json' ` + -Body $readRequest ` + -TimeoutSec 240 + if ($readResult.files -notcontains 'src/hello.py' -or $readResult.result -notmatch 'greet') { + throw 'The live agent response did not cite and explain src/hello.py.' + } + + $previewRequest = @{ + task = 'نفّذ تعديلًا صريحًا داخل المشروع: حدّث README.md بإضافة سطر عربي يشرح أن الدالة greet ترجع تحية تتضمن الاسم. استخدم أداة propose_file_change لإنشاء معاينة diff فقط. لا تطبق التغيير.' + workspace_path = $testRoot + workspace_files = @('README.md') + skill_id = 'code_review' + } | ConvertTo-Json -Depth 5 + $previewResult = Invoke-RestMethod ` + -Method Post ` + -Uri "$ApiBaseUrl/v1/agent/run" ` + -Headers $headers ` + -ContentType 'application/json' ` + -Body $previewRequest ` + -TimeoutSec 240 + $unchanged = [IO.File]::ReadAllText($readmePath).Contains('The greet function returns') + if ($previewResult.tool -ne 'propose_file_change' -or + $previewResult.proposal.path -ne 'README.md' -or + -not $unchanged) { + throw 'The live agent did not return a README.md diff preview while preserving the file.' + } + + [pscustomobject]@{ + status = 'passed' + model = $readResult.model + registered_project = $registeredProject.name + listed_files = @($listed.files) + read_citations = @($readResult.files) + preview_tool = $previewResult.tool + preview_path = $previewResult.proposal.path + preview_operation = $previewResult.proposal.operation + file_unchanged_without_user_approval = $unchanged + } | ConvertTo-Json -Depth 5 +} +finally { + if ($registered -and $token) { + try { + Invoke-RestMethod ` + -Method Delete ` + -Uri "$ApiBaseUrl/v1/agent/projects" ` + -Headers @{ Authorization = "Bearer $token" } ` + -ContentType 'application/json' ` + -Body (@{ workspace_path = $testRoot } | ConvertTo-Json) ` + -TimeoutSec 10 | Out-Null + } + catch { + Write-Warning 'Could not remove the temporary project registration.' + } + } + if ($token) { + try { + Invoke-RestMethod ` + -Method Post ` + -Uri "$ApiBaseUrl/v1/auth/logout" ` + -Headers @{ Authorization = "Bearer $token" } ` + -TimeoutSec 10 | Out-Null + } + catch { + Write-Warning 'Could not revoke the temporary local session.' + } + } + if (Test-Path -LiteralPath $testRoot) { + Remove-Item -LiteralPath $testRoot -Recurse -Force + } +}