Compare commits

..

4 Commits

Author SHA1 Message Date
adb9586055 fix(ops): שרת ה-MCP עובר ל-S3 — סוגר את דריפט האחסון מול הקונטיינר
All checks were successful
INV-AG3 Agent Tool Grants / agent-tool-grants (pull_request) Successful in 5s
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
Lint — undefined names / undefined-names (pull_request) Successful in 12s
הקונטיינר רץ `STORAGE_BACKEND=s3` ושרת ה-MCP נפל ל-`filesystem`. כלומר
מסמך שסוכן כתב דרך MCP נחת על הדיסק בעוד ה-web חיפש אותו ב-S3 — שני
מסלולים לאותו נכס, שמתפצלים בשקט (G2 / INV-STG1).

מה נדרש כדי לסגור, ומה נמצא בדרך
- **הסודות לא היו קיימים במקור-האמת.** `MINIO_ENDPOINT`/`ACCESS_KEY`/
  `SECRET_KEY` חיו רק ב-env של Coolify. נוצרו ב-Infisical תחת
  `/apps/legal-ai` (tag `credentials`, באישור חיים) ונטענים
  ל-`~/.legal-mcp-http.env` — אותו דפוס כמו MCP_HTTP_SHARED_SECRET.
  הערכים עברו ב-stdin דרך infisical-set.sh; לא ב-argv ולא בהיסטוריה.
- **`aioboto3` לא היה מותקן ב-venv המקומי.** מוצהר ב-pyproject אך חסר.
  מסלול ה-S3 מייבא אותו **עצלנית**, ולכן השירות היה עולה בהצלחה ונשבר
  רק בפעולת-הקובץ הראשונה — כשל שקט עד לשימוש. זו בדיוק הסיבה שהבדיקה
  רצה לפני ההפעלה ולא אחריה.

אימות מהמארח לפני ההפעלה: put/get/delete מלא מול
https://s3.nautilus.marcusgroup.org, וקריאת בלוב-ייצור קיים
(573KB) מתוך 2,283 אובייקטים בדלי.

ה-runbook עודכן: בלוק-האזהרה הוחלף בתיעוד המצב הסגור, כולל מלכודת
הייבוא-העצל ופקודת-אימות שאפשר להעתיק.

invariants: G2 (מסלול-אחסון אחד לשתי הדלתות) · INV-STG1 · §6
2026-08-05 14:03:15 +00:00
09c737bddf Merge pull request 'fix(ops): יישור MULTIMODAL_ENABLED בשרת ה-MCP + תיעוד דריפט האחסון שנותר' (#466) from worktree-mcp-env-align into main
All checks were successful
INV-AG3 Agent Tool Grants / agent-tool-grants (push) Successful in 7s
Build & Deploy / build-and-deploy (push) Successful in 10s
G12 Leak-Guard / leak-guard (push) Successful in 5s
Lint — undefined names / undefined-names (push) Successful in 13s
2026-08-05 13:58:41 +00:00
95571e0f05 fix(ops): יישור MULTIMODAL_ENABLED בשרת ה-MCP + תיעוד דריפט האחסון שנותר
All checks were successful
INV-AG3 Agent Tool Grants / agent-tool-grants (pull_request) Successful in 6s
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
Lint — undefined names / undefined-names (pull_request) Successful in 14s
הקונטיינר והשרת ה-HTTP הם שתי דלתות לאותו קורפוס, ולכן חייבים לדרג
אותו. הם לא עשו זאת: הקונטיינר רץ `MULTIMODAL_ENABLED=true`, בעוד שרת
ה-MCP נפל לברירת-המחדל `false` — `~/.env` אינו מגדיר את הדגל כלל. סוכן
ו-UI יכלו לענות על אותה שאלה מתוך קבוצות-תוצאות שונות, בלי שאיש ידע
(G2 — מסלול מקביל שמתפצל בשקט).

מה שונה
- `MULTIMODAL_ENABLED: "true"` בקונפיג ה-pm2 של legal-mcp-http.
- תיעוד השירות ב-operations-runbook (פורט, שער-Bearer, ו**האזהרה
  שהחשובה מכולן**: קוד ב-`mcp-server/` לא נכנס לתוקף עד restart — זהו
  תהליך ארוך-חיים, בניגוד ל-stdio שנטען בכל סשן. שלושה מיזוגים היום
  רצו על קוד ישן עד שהבחנתי).

מה **לא** שונה, ולמה
`STORAGE_BACKEND` נשאר לא-מוגדר. הקונטיינר רץ `s3`, השרת נופל
ל-`filesystem`, וזה דריפט אמיתי — סוכן שכותב דרך MCP כותב לדיסק בעוד
ה-web קורא מ-S3. אבל הפיכת הדגל דורשת `MINIO_ENDPOINT`/`ACCESS_KEY`/
`SECRET_KEY`, ואלה **אינם ב-Infisical** — רק ב-env של Coolify. בלעדיהם
`MINIO_ENDPOINT` נפתר לברירת-המחדל `http://minio:9000` (שם-רשת פנימי של
דוקר, לא נגיש מהמארח) וכל פעולת-בלוב נשברת.

יצירת סוד חדש מחייבת אישור חיים לשם/תיקייה/תגית, ולכן הדריפט מתועד
בבירור ב-runbook עם בדיוק מה שצריך כדי לסגור אותו — במקום להישאר
ידיעה שבעל-פה. MinIO **כן** נגיש מהמארח: `https://s3.nautilus.marcusgroup.org`
מחזיר 200 ב-`/minio/health/live` (אומת), כך שהחסם היחיד הוא הסודות.

invariants: G2 (שתי דלתות, דירוג אחד) · §6 (הדריפט הנותר מתועד, לא נבלע)
2026-08-05 13:58:08 +00:00
1f2ff4f1ec Merge pull request 'perf(citation-view): cache מבוסס-טביעה — 24.6 שנ׳ לטעינה חוזרת → 0.01 שנ׳' (#464) from worktree-citation-view-cache into main
All checks were successful
INV-AG3 Agent Tool Grants / agent-tool-grants (push) Successful in 4s
Build & Deploy / build-and-deploy (push) Successful in 1m38s
G12 Leak-Guard / leak-guard (push) Successful in 6s
Lint — undefined names / undefined-names (push) Successful in 12s
2026-08-05 13:36:11 +00:00
2 changed files with 55 additions and 0 deletions

View File

@@ -52,6 +52,41 @@
- **אפס עלות API** — claude CLI משתמש ב-claude.ai subscription של chaim. הנחת היסוד של `claude_session.py` (claude CLI מקומי בלבד) נשמרת.
- Coolify dependency: ה-Service Definition של legal-ai חייב להכיל `extra_hosts: host.docker.internal:host-gateway` (אחרת ה-proxy יקבל ConnectError).
### legal-mcp-http — שרת ה-MCP ב-HTTP (מאוגוסט 2026)
- פורט: `127.0.0.1:8790` (loopback בלבד) · קונפיג: [`scripts/legal-mcp-http.config.cjs`](../scripts/legal-mcp-http.config.cjs)
- **הדלת של סוכני הפלטפורמה** אל 108 כלי ה-MCP. סשן אינטראקטיבי מגיע לאותו שרת ב-stdio דרך `~/.claude.json`**אותו קוד, שתי תחבורות** (G2).
- שער Bearer (`MCP_HTTP_SHARED_SECRET`); השרת **מסרב לעלות** בלי טוקן. בדיקה: ללא טוקן → `401`, עם טוקן → `200`.
- **⚠️ שינוי קוד ב-`mcp-server/` לא נכנס לתוקף עד `pm2 restart legal-mcp-http`.** זה תהליך ארוך-חיים, בניגוד ל-stdio שנטען מחדש בכל סשן. אחרי מיזוג שנוגע ב-`mcp-server/` — להפעיל מחדש, אחרת הסוכנים ירוצו על קוד ישן בשקט.
> #### ✅ תצורת האחסון והאחזור מיושרת לקונטיינר (2026-08-05)
> `STORAGE_BACKEND=s3` ו-`MULTIMODAL_ENABLED=true` — זהים לקונטיינר, כך שמסמך
> שסוכן כותב דרך MCP הוא זה שה-web קורא, ושתי הדלתות מדרגות את אותו קורפוס
> באותה צורה (G2).
>
> **הסודות:** `MINIO_ENDPOINT` · `MINIO_ACCESS_KEY` · `MINIO_SECRET_KEY` נוצרו
> ב-Infisical תחת `/apps/legal-ai` (tag `credentials`), ונטענים ל-`~/.legal-mcp-http.env`
> (chmod 600) — אותו דפוס כמו `MCP_HTTP_SHARED_SECRET`. **רוטציה:** לעדכן
> ב-Infisical → למשוך מחדש לקובץ → `pm2 restart legal-mcp-http`.
>
> **⚠️ מלכודת — `aioboto3` מיובא עצלנית.** מסלול ה-S3 מייבא אותו רק בקריאה
> הראשונה, ולכן venv בלי החבילה **עולה בהצלחה** ונשבר רק בפעולת-הקובץ הראשונה
> — כשל שקט עד לשימוש. הוא מוצהר ב-`mcp-server/pyproject.toml`, אבל לא היה
> מותקן ב-venv המקומי. אחרי כל בנייה מחדש של ה-venv:
> `~/legal-ai/mcp-server/.venv/bin/pip install -e ~/legal-ai/mcp-server`.
>
> **אימות לפני הפעלה** (הרץ מהמארח, לא מהקונטיינר):
> ```bash
> cd ~/legal-ai/mcp-server && set -a && . ~/.legal-mcp-http.env && set +a
> STORAGE_BACKEND=s3 HOME=/home/chaim DOTENV_PATH=/home/chaim/.env \
> .venv/bin/python -c "import asyncio;from legal_mcp.services import storage
> async def m():
> st=storage.get_storage()
> print(len(await st.list_keys('', bucket=storage.Bucket.DOCUMENTS)))
> asyncio.run(m())"
> ```
> מצופה: מספר האובייקטים בדלי (היו 2,283 ב-2026-08-05). שגיאת
> `ModuleNotFoundError: aioboto3` = ה-venv חסר את התלות.
---
## מבנה תיקיות

View File

@@ -56,6 +56,26 @@ const env = {
// transports read exactly the same corpus.
DOTENV_PATH: "/home/chaim/.env",
DATA_DIR: "/home/chaim/legal-ai/data",
// Retrieval flags must match the container's, or the two doors into the same
// corpus rank differently (G2 — a parallel path that drifts). The container
// runs MULTIMODAL_ENABLED=true; without this the MCP door skipped the
// page-image merge entirely, so an agent and the web UI could answer the same
// question from different result sets. ~/.env sets neither, so both fell to
// the code defaults (false) — the drift was silent.
//
// Blob storage must match the container too, for the same reason: a document
// an agent writes through MCP has to be the one the web reads back. The MinIO
// credentials now live in Infisical (/apps/legal-ai) and are loaded from the
// runtime file below, exactly like MCP_HTTP_SHARED_SECRET.
//
// Verified from the host before enabling: put/get/delete round-trip against
// https://s3.nautilus.marcusgroup.org, plus a read of an existing production
// blob out of 2,283 objects. Note the S3 path imports aioboto3 LAZILY, so a
// missing dependency does not fail at boot — it fails on the first blob
// operation. It is declared in mcp-server/pyproject.toml; make sure the venv
// actually has it (`.venv/bin/pip install -e mcp-server`).
STORAGE_BACKEND: "s3",
MULTIMODAL_ENABLED: "true",
MCP_TRANSPORT: "streamable-http",
MCP_HTTP_HOST: "127.0.0.1",
MCP_HTTP_PORT: "8790",