feat(org): model grants, org memory, MCP gateway (v2 phases A1-3..5)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mjalmoudy94
2026-10-14 15:51:44 +03:30
co-authored by Claude Sonnet 5.5
parent c93b25741e
commit dee2f72c46
39 changed files with 9060 additions and 95 deletions
+121
View File
@@ -0,0 +1,121 @@
# Agent 1 — final report (org platform)
Branch: `dev`. Phases 1, 2 (previously merged) plus 3, 4 and 5 (implemented now,
reconstructed from the sibling `agent1/org-platform` branch and reconciled onto
`dev`'s shapes).
## Summary
The org-platform workstream is complete on `dev`: a platform owner invites a
manager, the manager builds one organization with one application through a
five-step wizard, the owner decides which models that organization may use, the
organization builds a memory of its own rules / preferences / lessons / code map,
and an external AI agent can act inside it through a scoped MCP token the
organization's own admin minted. Everything is tenant-scoped, audited,
Persian/RTL on the UI side, and no secret reaches a response, a draft, an audit
row, a log or an LLM prompt.
```
apps/api: pytest -q → 587 passed, 1 skipped
apps/api: ruff check . → All checks passed!
apps/api: mypy app → Success: no issues found in 147 source files
apps/api: alembic upgrade head / downgrade -1 / upgrade head / check → clean, single head
apps/web: tsc --noEmit → clean · next lint → No ESLint warnings or errors
apps/web: vitest run → 99 passed (13 files)
```
Migrations added on `dev` (the branch's single `a1b2c3d4e5f1` was split into one
owned migration per phase, chained off `dev`'s own head):
| revision | phase | tables |
|---|---|---|
| `e6f7a8b9c0d1` | 3 | `default_model_grants`, `org_policies` |
| `f7a8b9c0d1e2` | 4 | `org_memories` |
| `a8b9c0d1e2f3` | 5 | `mcp_tokens` |
(Phases 1 and 2 were already on `dev`: `a1b2c3d4e5f1` platform invites,
`d5e6f7a8b9c0` org setup drafts.)
## Endpoints added (all under `/api`, roles as enforced by the router)
### Phase 1 — invites, roles, sharing
`POST/GET /admin/platform-invites`, `POST /admin/platform-invites/{id}/revoke`,
`GET /invites/{token}`, `POST /invites/accept`, `GET /auth/me`
(`needs_onboarding`), `POST /organizations/{id}/transfer-ownership`,
`POST /organizations/{id}/leave`, `GET/DELETE /organizations/{id}/invites[/{id}]`,
and the `/admin/users` lifecycle.
### Phase 2 — wizard and app profile
`GET /setup/catalog`, `GET/PUT /setup/draft`, `POST /setup/check-git`,
`POST /setup/check-deploy`, `POST /setup/complete`,
`GET/PATCH /organizations/{org}/app/profile`,
`PUT /organizations/{org}/app/deploy-target`,
`PUT /organizations/{org}/app/git`.
### Phase 3 — owner grants and policy
`GET /admin/model-grants/{org}`, `PUT/DELETE /admin/model-grants/{org}/models/{model}`,
`GET /admin/model-grants/{org}/active`,
`GET/PUT/DELETE /admin/default-model-grants[/{model}]`,
`GET/PUT /admin/org-policies/{org}`.
### Phase 4 — memory
`GET /organizations/{org}/memory`, `POST /organizations/{org}/memory`,
`PATCH /organizations/{org}/memory/{id}`,
`POST /organizations/{org}/memory/{id}/pin`, `.../confirm`,
`DELETE /organizations/{org}/memory/{id}`.
### Phase 5 — MCP
`GET /organizations/{org}/mcp-tokens`, `POST .../mcp-tokens`,
`DELETE .../mcp-tokens/{id}`, `GET .../mcp-tokens/settings`,
`GET/PUT /admin/mcp`, `PUT /admin/mcp/organizations/{id}`, and the MCP server at
`/mcp` (`python -m app.interfaces.mcp.server`, `pnpm mcp:dev`).
MCP tools: `app_get_profile`, `tasks_list`, `tasks_get`, `tasks_create`,
`memory_search`, `memory_write`, `knowledge_search`, `pipeline_status`
(`{"available": false}` until Agent 3's tables are wired).
## Tables added (all in `db/models_org.py`)
`platform_invites` (1) · `org_setup_drafts` (2) · `default_model_grants` (3) ·
`org_policies` (3) · `org_memories` (4) · `mcp_tokens` (5). `org_model_grants`,
which the grants engine reads, is foundation-owned.
## Manual QA — the wizard
1. As the owner, `/admin/users` → «دعوت مدیر جدید» → copy the link.
2. Open it in a private window → «ساخت حساب مدیر» → set a password → land on
`/onboarding`.
3. Step through the five steps; leave at step 2 and reload to confirm the draft
resumes. Choose «Git پلتفرم (پیشنهادی)», skip production, create.
4. The success screen shows the demo URL; «برو به مدیر AI» → `/manager`. The DB
has exactly one project with a full profile, and the Null hooks recorded one
`bootstrap_team` and one `on_app_created`.
5. `/admin/model-grants`: grant a model to the new org, set its daily cap, revoke.
6. `/memory`: teach a rule, pin it, confirm a lesson, keep the page open while
another tab changes memory — the list refreshes on `memory.changed`.
7. `/settings` → «یکپارچه‌سازی (MCP)»: mint a token, copy it once, revoke it.
`/admin/mcp`: flip the global and per-org switches.
## CHANGE REQUESTs still open
1. **`ModelGrantPort` should expose `get_org_policy(organization_id)`** (and
`.mcp_enabled`) so Agent 2 reads the policy through the port it already holds.
2. **`expire_memories()`** needs a worker hook (billing worker or a new tick in
`workers/__main__.py`).
3. **`pipeline_status`** should be pointed at Agent 3's delivery/deploy tables.
4. **`@agy/contracts`** lacks `memory.changed` and the MCP event types; the web
compares raw strings.
5. **Post-login redirect**: an existing manager with `needs_onboarding` still
lands on `/dashboard` (`login/page.tsx` and `AppShell.tsx` are locked);
`useNeedsOnboarding()` is ready to use.
6. **`build_memory_service` / `build_model_grant_service`** live in `container_org`
only; re-export from `container.py` for one import surface.
## Known limitations
- A reflection is not grant-checked (uses `pick_for_mode(CHEAP)` like the engine).
- `expire_memories()` has no caller yet.
- `pipeline_status` is a stub until Agent 3's tables are wired.
- The MCP rate limiter is process-local.
- `mcp_gateway_enabled` is a process-local setting mutation; a restart re-reads
it from the environment.