KIWI
Autonomous local AI agent and community of voices living in The Grove with private memory.
The Problem
Autonomous AI agents often suffer from leaky memory isolation, unpredictable side-effects on host systems, and lack of human-in-the-loop governance for high-stakes file operations and external device actions.
My Role
Architected and implemented the cognitive engine ("The Grove"), fail-closed host action safety policies, immutable exact-action plan ledgers, multi-device agent companion protocol, and unified multi-channel gateways.
Highlights
- Community of six voices living in The Grove with local memory running on the user's machine
- Local cognitive core ("The Grove") separating owner memory from credentialed client sessions
- Fail-closed security policy requiring immutable exact plans with rollback for all destructive actions
- DPAPI-secured Windows companion agent with local execution receipts and anti-re-execution guarantees
- Multi-modal interaction surface: Next.js Web UI, Voice HUD (:4321), and Telegram bot gateway with inline approvals
- Deterministic audit ledger tracking every state mutation, file movement, and memory association
Stack
Constraints
- Host safety is non-negotiable: no autonomous file deletion or batch reorganization without human-confirmed exact plans.
- Offline-first engine: core cognitive loops and memory databases must run purely locally without cloud dependencies.
- Multi-tenant memory isolation: administrative sessions and API token holders must never access the owner private mind context.
System Architecture
Key Trade-offs
The decisions worth defending — what I chose, what I turned down, and why.
File management execution model
Chose
Immutable two-phase exact-action plans with hash verification and dry-run preview
Rejected
Direct agent tool execution with broad shell permissions
Agents making speculative shell or file modifications risk catastrophic data loss; two-phase plans guarantee human oversight and rollback capability.
Engine-to-web communication architecture
Chose
Dedicated loopback HTTP/SSE gateway with bearer token authentication
Rejected
Monolithic single-process Next.js application
Decoupling the AI engine process from the Next.js frontend ensures background monitoring, telegram polling, and long-running skills survive web redeployments.
Device pairing security model
Chose
Windows DPAPI machine-bound tokens with polling architecture
Rejected
Inbound SSH or remote socket servers on client machines
Inbound listening ports expose clients to local network scans; outbound HTTPS polling behind DPAPI keeps remote companion devices secure.
What I'd Do Differently
An honest retrospective — the stuff I'd change with more time, more users, or a second pass.
- 1Implement automated regression tests for memory graph decay sweeps earlier in the development lifecycle.
- 2Consolidate SQLite schema migrations between better-sqlite3 and pg-schema under a single unified migration runner.
- 3Add fine-grained telemetry dashboards to observe cognitive latency across multi-turn reasoning chains.
Interactive Autonomous Agent Shell
Test live KIWI autonomous agent supervisor routines (agent status, grove ping, mcp tools, help) in the emulated shell below.
Technical Deep-Dive
Architecture, specifications, and implementation details.
P0 — Contractul proprietății stării
Revizia 14: rândul sarcinilor pilotului Windows capătă paritate de schemă Drizzle/PG și intră acum în ștergerea contului (GDPR) (device_tasks/user_devices/device_pairings, șterse după ID-ul utilizatorului). Corectarea a scos la iveală un defect preexistent fără legătură: tranzacția rutei de ștergere GDPR folosea un callback async, pe care driverul sincron better-sqlite3 îl refuză direct — orice cerere de ștergere a contului eșua cu HTTP 500 pe toate tabelele, nu doar pe cele noi de dispozitiv, fără nicio acoperire de test anterioară. Verificări: 132 teste motor neschimbat; 230 teste mind/web în 35 suite (+2); ambele typecheck-uri trecute. Fără lansare în producție. Detalii: pilot Windows.
Revizia 13: reconcilierea după cădere adaugă o starere citibilă nouă la contract. Memoria firului scoped al proprietarului este deținută de motor (ConversationManager, getTurns copie read-only), iar web-ul o folosește la GET /api/chat/history?channel=web&sessionId=... pentru admin (reconcileOwnerChat) ca să repare doar ultimul mesaj: dacă ultimul mesaj web este user fără assistant după el și ultima rundă engine completă are același conținut user, se adaugă răspunsul lipsă. Motorul rămâne sursa canonica a rundei completate; SQLite-ul web rămâne autoritar pentru istoricul afișat. Best-effort (eroarea motorului nu blochează citirea) și idempotent (max 1 append). Nu închide persistența personală embedded sau unirea stocărilor istorice. Verificări: 101 teste motor trecute, unul semantic opțional sărit; 222 teste mind/web în 33 suite; ambele typecheck-uri trecute. Fără lansare în producție. Detalii: chat, acțiuni gazdă.
Revizia 12: deduplicarea update_id Telegram și consumul atomic al cotei sunt acum înregistrate persistent. Webhook-ul înregistrează (chat_id, update_id) în telegram_webhook_updates (cheie compusă; 200 la duplicat) și consumă cota printr-un singur UPDATE condiționat (consumeMessageIfUnder), iar motorul persistează offset-ul update_id în data/telegram-owner-offset.json, reluând de acolo după restart — stare durabilă pentru livrarea Telegram, fără dublă procesare și fără depășire la concurență. Verificări: 97 teste motor trecute, unul semantic opțional sărit; 210 teste mind/web în 31 suite; ambele typecheck-uri trecute. Fără lansare în producție. Detalii: chat, acțiuni gazdă.
Revizia 11: rutele de chat (web și motor) impun limite de corp și de mesaj. Web-ul citește corpul prin web/src/lib/chat-body.ts cu un cap de 500.000 de octeți (citire în flux), validează JSON-ul (400 pentru corp lipsă/invalid/mesaj gol) și respinge mesajele peste 100.000 de caractere cu 413 pe ambele rute (/api/chat JSON și /api/chat/stream SSE). fileIds rămâne brut, iar resolveChatAttachments respinge valorile malformate (non-array, ID-uri non-șir). Motorul (/api/chat/scoped) respinge mesajele non-string sau peste 100.000 de caractere cu 413, fără a contacta managerul. Verificări: 97 teste motor trecute, unul semantic opțional sărit; 205 teste mind/web în 30 suite; ambele typecheck-uri trecute. Fără lansare în producție.
Plan revizia 13, 2026-09-09. Comportament inspectat și limite de migrare; consolidarea memoriei nu este finalizată. Revizia 13 a adăugat recuperarea după cădere a chatului proprietarului (thread scoped read-only din motor + reconciliere tail-only în web); revizia 12 a adăugat stare durabilă pentru livrarea Telegram (dedup update_id + cotă atomică + offset motor); revizia 11 a impus limitele de corp/mesaj înaintea oricărui apel către brain sau motor. Revizia 10: ConversationManager serializează livrările per fir (runSerialized), motorul expune DELETE /api/chat/scoped (validează identitatea owner-web:v1:<sha256> și așteaptă runda în curs înainte de a uita firul), iar web-ul, la ștergerea unei sesiuni browser de admin, cere motorului să uite firul scoped corespunzător, fără să se blocheze la eroare; POST /api/documents/organize refuză orice non-dryRun. Verificări (rev 11): 96 teste motor trecute, unul semantic opțional sărit; 200 teste mind/web în 29 suite; ambele typecheck-uri trecute.
Revizia 8: continuitatea transportului chat trimite chatul JSON și SSE al sesiunii browser admin către același endpoint al proprietarului. Eroarea motorului este explicită și nu schimbă mintea cu cea embedded; browserul nu repetă automat prin JSON cererea eșuată sau incompletă. SSE refuză Authorization, inclusiv împreună cu un cookie admin. Răspunsul proprietarului este livrat integral, fără streaming incremental al modelului. Verificare curentă: 192 teste mind/web în 28 suite, typecheck web, build de producție izolat și probă chat în Edge trecute. Persistența personală, fallback-ul intern al motorului și delimitarea conversațiilor rămân deschise. Fără deploy de producție.
Revizia 7: autorizarea acțiunilor pe gazdă aplică lista permisă în JSON/stream/offline, compune restricțiile configurate și ale apelantului, păstrează politica inițială a aprobării și împiedică textul rutinelor interne să autorizeze modificări. Mutațiile cunoscute cer confirmare; previzualizările sunt complete sau refuzate. Telegram direct reverifică politica. Pornirea dev autonomă, mutările documentelor în lot și curățarea distructivă a discului sunt oprite până la planuri exacte. Verificare în revizia 7: 90 teste motor trecute, unul semantic opțional sărit; 180 teste mind/web în 27 suite trecute. P0 rămâne deschis pentru continuitatea memoriei, automatizări/pluginuri și migrarea țintelor exacte. Fără lansare în producție.
| Stare | Proprietar și stocare actuală | Limită / lucru rămas |
|---|---|---|
| Identitatea contului web | Sesiune NextAuth sau cheie API validată a contului | currentUserIdFrom refuză rezerva cookie când Authorization există, dar este invalid; descărcarea ISO aplică aceeași regulă. Tokenurile companion nu aprobă acțiuni browser. Celelalte rute exclusiv pe sesiune necesită review. |
| Atașamente chat | chat_files, conversație proprie și rădăcină de upload administrată | Cotă tranzacțională, ștergere limitată și expirare explicită; fișiere necunoscute/vechi păstrate. Expirarea nu șterge textul deja copiat în memorie/istoric. Ciclu. |
| Mintea personală web | web/src/lib/kiwi/state.ts, Map în proces, indexat după utilizator | Atitudinea, relația, personalitatea activă și ultimele 200 de memorii sunt volatile. La restart se reîncarcă numai ultimele 40 de chat_messages, filtrate după proprietar. Conversațiile aceluiași cont sunt momentan amestecate. |
| Observații personale | nature_observations, interogată după user_id | Interpretările nu sunt fapte verificate; proveniența/corectarea și evaluările după restart rămân în P4. |
| Mintea veche a gazdei | src/kiwi.ts, src/kelvin.ts, memoriile motorului și managerul conversațiilor | Aparține operatorului, nu tuturor utilizatorilor web. Persoanele autorizate în Telegram direct administrează aceeași minte/gazdă. |
| Continuitatea admin în web | Endpoint scoped cu hash cont/conversație validată; fără unirea istoricului recent și aprobărilor | Memoria semantică, persona și starea de lungă durată a operatorului rămân comune. Ruta veche fără scope păstrează fallback-ul. Recuperarea după cădere (revizia 13): firul scoped al motorului este sursă read-only pentru reconciliere; SQLite-ul web rămâne autoritar pentru afișare. Contract chat. |
| Telegram web | telegram_links și chat_sessions/chat_messages filtrate după proprietar | Chatul privat trebuie să corespundă solicitantului; asocierea este atomică și expiră. Sesiunile vechi se refolosesc numai pentru același proprietar; ID-urile noi includ hash-ul proprietarului și chatului Telegram. Legăturile active ambigue sunt refuzate. |
| Asistentul documentelor | Sandbox-ul contului rezolvat de server; context local cererii | Chatul folosește numai documentele cererii, fără memoria/uneltele/statusul proprietarului. Continuitatea istoricului necesită implementare per cont. |
| Sarcini pilot Windows | device_pairings, user_devices, device_tasks în SQLite web, cu paritate de schemă Drizzle/PG (revizia 14) | Serverul deține contul/dispozitivul/amprenta acțiunii și expirarea; agentul reverifică și păstrează dovadă locală a execuției. Nu depind de memoria RAM a vreunui brain. Șterse la ștergerea contului (GDPR) (revizia 14). |
#Aplicat acum
/api/kiwi/status și /api/kiwi/memory, asemenea chatului JSON, rezervă motorul proprietarului sesiunii browser admin fără Authorization. Celelalte cereri primesc starea embedded personală. Fiecare răspuns de memorie — source: embedded (personal) sau source: engine (proprietar) — are doar memories/total. [Actualizat rev 69] Subsistemul pe niveluri STM/MTM/LTM a fost șters (nu persista nimic niciodată); ramura proprietarului citește acum tabelul real, plat, de memorie din motor, în loc să întoarcă mereu stm/mtm/ltm goale. Vezi docs/adr/0009-three-tier-memory.md.
Confirmările conversaționale sunt legate de identitatea originală a canalului, chiar dacă istoricul este comun. Păstrează o copie a acțiunii, expiră în cinci minute și reverifică politica înainte de execuție. Asocierea Telegram web cere identitate privată corespunzătoare și nu regenerează coduri expirate la mesaje externe.
Gateway-ul uneltelor pentru documente refuză unelte arbitrare de gazdă, suprascrierea userId și mutările reale de organizare. Motorul clonează contextul cererii, fără schimbarea directorului global. Asistentul documentelor nu mai apelează managerul conversațional comun sau fallback-ul proprietarului. Execuția uneltelor și memoria între ture sunt eliminate din această cale până la o înlocuire izolată.
Auto-resolve verifică expirarea la decizie/execuție și limitează uneltele modelului la diagnostic; o aprobare largă „fix” nu mai autorizează modificări arbitrare alese de model. Codarea veche/OpenCode și alte mutații directe ale gazdei rămân în afara contractului nou de dispozitive.
#Migrarea și acceptarea următoare
Ținta este o minte personală logică persistentă per cont autentificat, cu ID-uri explicite de conversație, proveniență și stare versionată. Dispozitivele execută sarcini aprobate; nu devin surse autoritative concurente ale memoriei. Camerele comune primesc numai lecții anonimizate, evaluate și partajate voluntar, niciodată copii ale stării conturilor.
Înaintea migrării: inventariem mutațiile gazdei rămase, delimităm conversațiile și fallback-ul intern al motorului, implementăm persistența tranzacțională și ștergerea/exportul per cont, apoi comparăm teste de restart, concurență și izolare. Păstrăm mind-library/ și toate PDF-urile sursă. Această schimbare nu a șters sau importat PDF-uri ori conversații istorice.
Dovezi: audit P0, pilot Windows, checkpoint. Testele confirmă limitele enumerate, nu auditarea completă a proiectului.