Technical
Memory Health
Corpus-ops dashboard for the persistent agent memory corpus: counts by type, repo, and tool; stale-note detection; a credential-pattern policy scanner; and vector-based near-duplicate detection.
Public and internal interfaces
These are the main routes, handlers, and integration surfaces used by this product area.
GET /api/portal/memory
GET /api/portal/memory/stale
GET /api/portal/memory/policy
GET /api/portal/memory/dedup
POST /api/portal/memory/archive
Architecture notes
flowchart TD
U[Owner or admin] --> P[MemoryHealth.tsx]
P --> C[lib/memoryHealth.ts client]
C --> R[/api/portal/memory routes/]
R --> H[handlers/memory.php]
H --> G{owner or admin?}
G -- no --> X[403 denied]
G -- yes --> K[memCorpusId: bnl-memory corpus]
K --> M[(client_documents MySQL)]
H -. dedup only .-> Q[ClientRagPipeline + Qdrant vectors]
- The React page is caveauai-web/src/pages/app/MemoryHealth.tsx; each tab fetches its own data on mount via lib/memoryHealth.ts.
- All routes are handled by ai-portal/api/portal/handlers/memory.php and require an owner or admin role.
- Notes live in client_documents under the per-client bnl-memory corpus; tags are a JSON array using type:, repo:, tool:, and client: prefixes.
- Overview, staleness, and policy run as MySQL queries; the duplicate scan uses ClientRagPipeline against Qdrant vectors.
- Archiving sets status to archived, deletes chunk rows, and best-effort removes Qdrant vectors, dropping the note from recall while preserving the row.
Access and permissions
App pages are authenticated through the shared bnl_session cookie unless explicitly marked public. Public docs should describe integration behavior without including secrets, private tokens, private IPs, or tenant-specific records.
Troubleshooting
- A 403 means the signed-in account is not an owner or admin.
- A 404 of "Memory corpus not provisioned" means the workspace has no bnl-memory corpus yet.
- An empty Duplicates result is normal until the corpus has enough vectorized notes to compare.