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 — Autorizarea acțiunilor pe gazdă
Revizia 17 (2026-09-09): file_organizer își închide fluxul exact — automatizarea mind devine preview-only (nu mai mută fișiere), iar skill-ul builtin file_organizer devine singurul executant, sub plan exact validat + aprobare umană; dev_task devine fail-closed implicit (deny în config). Cod local și verificare automată; fără restart sau deploy de producție.
#Fix revizia 17 — plan exact pentru file_organizer și valoarea implicită fail-closed a dev_task
file-organizer (automatizarea mind) nu a putut niciodată muta fișiere (reviziile 7/14/16: non-dryRun refuzat cu motiv exact-plan). Revizia 17 aduce executantul sigur pe care îl aștepta:
- Modul partajat
src/skills/fileOrganize.ts: categorii dinEXTENSION_MAP, arbore de destinație coerent,archiveAfterDays→_archive;computeOrganizeMoves(dir, archiveAfterDays)întoarce{ moves:[{from,to,size,archived}], skipped }cu ținte anti-coliziune(2)verificate și pe disc; fișierele neclasificate/directoare numărate și lăsate în loc. - Skill-ul builtin
file_organizer(kindfile_organizer, înregistrat însrc/skills/builtin/index.ts): ladryRun:truepregătește un plan exact (inclusiv copii de recuperare) prinprepareExactPlanși întoarce același shapedata:{ plan, exactPlan:{id,kind}, exactPlanId, backupDir }ca revizia 16; execuția cereexactPlan:{id,kind:'file_organizer'}validat prinvalidateExactPlanși ruleazăexecuteExactPlan; fără referință → motivul de blocare exact-plan. - Automatizarea mind
file-organizerv1.1.0 e preview-only: afișează output-ulcomputeOrganizeMovespentru operator și trimite întotdeauna la planul exact aprobat în chat; nu atinge discul. Politica: builtinfile_organizer→allowladryRun:true, altfelconfirm(cu ref valid) — aceeași formă deny-fără-ref ca restul planurilor exacte; automatizarea își păstrează deny pe non-dryRun. dev_taskfail-closed implicit:src/config/config.tslivrează acumsecurity.skillPolicy.dev_task:'deny'(eraconfirm);dev_status/dev_cancelrefuză ori de câte ori modul dev e oprit;DevManager.startn-are apelant activ în producție, deci niciun job de codare de fundal nu poate porni. Înlocuitorul sigur rămâne fluxul de sarcini-dispozitiv P1 / codare interactivă P2 — un ledger de mutări/ștergeri nu se potrivește editărilor interactive de cod.- Block la nivel de registru extins: pregătirea refuză un kind
file_organizernecunoscut, ca la celelalte kinduri.
Dovezi: 4 teste motor noi în tests/exact-plan.test.ts (file_organizer dryRun pregătește + arată țintele; non-dryRun fără plan refuzat; execuție cu plan real validat + aprobare rulează mutările; reverificarea pe disc rămâne) și 2 noi în tests/dev.test.ts (config implicit neagă dev_task; dev_status/dev_cancel fail-closed fără mod dev). Suita motor: 132 teste (131 trecute + 1 skip semantic opțional); mind/web 228 în 34 suite (vezi auditul); ambele typecheck-uri trecute.
#Fix revizia 16 — planuri exacte de acțiune pentru documents_organize și disk_doctor reap
documents_organize (non-dryRun) și disk_doctor reap au fost blocate (reviziile 7/10) cu motivul „plan exact aprobat". Revizia 16 înlocuiește blocul cu un mecanism persistent de planuri exacte, păstrând aprobarea umană la execuție.
Mecanismul (src/exactions/ledger.ts, nou):
- Plan imutabil:
{ id: 'ep-<timestamp36>-<hex>', kind: 'documents_organize' | 'disk_doctor_reap', ops }, operațiunile cu căi absolute +sizeșisha256(doar pentru move, fișiere ≤16 MB) — imuabilitate verificabilă. prepareExactPlan: validează fiecare operațiune (sursă există, ținta liberă,size>0), face copiile de recuperare pentru move (data/exactions/<id>/moves/N.bin, subKELVIN_DATA_DIR) și scrie ledger-uldata/exactions/ledger.jsoncustatus:'prepared'.validateExactPlan: înainte de orice execuție reverifică imuabilitatea (sha256 canonizat la{kind, ops}), prezența backup-urilor și starea de pe disc — sursa să existe în continuare, destinația să fie încă liberă.executeExactPlan: move =rename; delete =renameînbackupDir/deletes/N(fărărm, recuperabil instant). La prima eroare →status:'partial'+ raport{ok:false, done, error}; succes →status:'done'cuexecutedAt.rollbackExactPlan: restaurează din backup (idempotent); planurilerolledbacknu se pot re-încărca.describeExactPlanproduce previzualizarea pentru operator.
Politica (src/security/host-action-policy.ts):
exactPlanRefOf(args)acceptă doarexactPlan:{id, kind}.legacyActionBlockReason:documents_organizenon-dryRun șidisk_doctor reaprefuză DOAR fără referință validă; builtinfile_organizernon-dryRun fărăexactPlanși automatizareafile-organizernon-dryRun refuză cu același motiv;dev_taskrămâne refuzat necondiționat (configdeny).minimumPolicy: pregătirea rămâneallow—documents_organizecudryRun:trueșidisk_doctorcuaction:plan_reap/report; execuția cuexactPlanvalid →confirm(aprobare umană obligatorie prinConversationManager, nu auto-executare). Fără ref → deny.
Skill-urile:
documents.ts:documents_organizecudryRun:truepregătește planul (clasificare LLM, ținte cu nume anti-coliziune „ (2)"/„ (3)", copii de recuperare) și returneazădata:{ plan, exactPlan:{id,kind}, exactPlanId, backupDir }; non-dryRun cereexactPlan, validează contra ledger-ului și execută. Check-ul brain făcut doar în calea de pregătire (execuția nu are nevoie de LLM).diskDoctor.ts:action:'plan_reap'pregătește planul delete (fără ștergere);action:'reap'cereexactPlan, validează și mută folderele în backup. Ștergerea destructivă veche (rm) a fost eliminată.
Dovezi: 11 teste noi în tests/exact-plan.test.ts — plan gol/sursă lipsă refuzate; move prepare→validate→execute→rollback; invalidare la coliziune de destinație; delete reap-style cu backup+rollback; eșec parțial marchează partial; flux complet documents_organize (dryRun pregătește, execuția doar cu ref); flux disk_doctor plan_reap/reap; politică deny→confirm cu/fără ref; aprobare reală prin ConversationManager; shape validation exactPlanRefOf; block la nivel de registru fără plan. Suita motor 124 teste (123 trecute + 1 skip semantic opțional; +11); mind/web 222 în 33 suite (neschimbată); ambele typecheck-uri trecute.
Revizia 15 (2026-09-09): pluginurile de skills declară capabilitățile; fără declarație sunt oprite. Cod local și verificare automată; fără restart sau deploy de producție.
#Fix revizia 15 — capabilități declarate pentru pluginuri (fail-closed)
Revizia 14 a lăsat pluginurile fără metadata de efecte: hostSkillPolicy nu le cunoștea și cădea pe allow implicit (pluginurile nu erau în minimumPolicy), iar apelurile directe la registry.run (document skills în src/grove-api.ts:941, diagnostic CLI în src/cli/kiwi.ts:302) nu treceau prin politica completă — orice plugin arbitrar din pages/plugins putea rula fără niciun check de efecte asupra gazdei.
Correcții:
Skill.capabilitiesdeclarative (read/write/exec/network/system/media,src/skills/types.ts);plugins/example-dice.mjsdeclară['read'].hostSkillPolicyprimește un al șaselea parametrudeclared?: DeclaredSkillInfo(src/security/host-action-policy.ts);declaredSkillPolicy: niciun/fărăcapabilities→confirm, numairead→allow, orice efect mutațional →confirm. Unallowconfigurat vechi nu slăbește fail-closed-ul (compunerea reviziei 7:deny>confirm> allow rămâne, pluginul se decide după).SkillRegistry.skillDeclaration(src/skills/registry.ts) expune metadata și alimentează toate căile de politică:Agent.declaredSkill(JSON/stream/offline,src/agent/agent.ts),ConversationManager(src/conversation/manager.ts), Telegram direct (src/telegram/run.ts).- Backstop în
registry.run: pluginul înregistrat cusourceFiledar fărăcapabilitieseste refuzat înainte derun()(ok:false, motivul capabilităților), deci apelurile directe nu pot ocoli politica. Pluginurile cu declarație rămân rulabile prinagent.runSkill/aprobare (exact ca la executarea confirmată); builtin-ii și skill-urile fărăsourceFilenu sunt pluginuri și nu se schimbă. src/skills/plugins.tsloghează un avertisment la încărcarea unui plugin fărăcapabilities.
Dovezi: 8 teste noi în tests/plugin-capabilities.test.ts (fail-closed cu allow vechi; deny configurat; tool scope; skillDeclaration builtin vs plugin; backstop care nu atinge run(); registry.run direct pe custom builtin rămâne permis). Suita motor 113 teste (112 trecute + 1 skip semantic opțional; +8); mind/web 222 în 33 suite (neschimbată); ambele typecheck-uri trecute.
Revizia 14 (2026-09-09): închide automatizarea mind (mind.hear) și rădăcina documentelor. Cod local și verificare automată; fără restart sau deploy de producție. Reviziile 7 și 10 au rămas în istoricul de mai jos.
#Fix revizia 14 — politica pe automatizarea mind și rădăcina documentelor
Unic apelant de producție al mind.hear este fallback-ul brain-down din src/grove-api.ts:122 (ruta scoped dă 503 la brain indisponibil). Branch-ul de automatizare din hear() executa SkillRunner.execute cu TOATE permisiunile (FileRead/FileWrite/Network/System/Database/Notification) și dryRun:false, fără nicio politică host-action — orice apelant viitor (fereastra de racord ≤120s a brain-ului, alte căi) putea lansa aplicații prin execSync cu argumente injectabile și putea muta/scrie fișiere neaprobat.
Corrections din src/security/host-action-policy.ts + src/mind/kelvin.ts + src/mind/automation/skills/app-launcher.ts:
minimumPolicyclasifică acum skill-urile de automatizare:app-launcherșinote-taker→confirm;file-organizernon-dryRun →denycu motivul exact-plan (legacyActionBlockReason); diagnosticul read-only (web-search,system-monitor,file-finder,daily-briefing,timer,reminder) rămâneallow.- Branch-ul de automatizare din
mind.hearaplicăhostSkillPolicyînainte să ruleze orice skill:deny→ răspuns de refuz cu motivul;confirm→ refuzat ca fir intern („canal de rezervă”, nimeni nu poate aproba) cuautomationResult: 'blocked', persistat în memorie cablocked. Skill necunoscut → eroare explicită. - Privilegiu minim:
ctx.permissionseste exactskillDef.permissions(nu lista completă a tuturor permisiunilor). app-launcherhardenizat:isSafeLaunchTokenrespinge metacharacterii shell ($(){}!&|<>^%"',;) înappNameșiargsînainte deexecSync— căi/argumente nepotrivite nu ajung niciodată în shell.
Rădăcina documentelor (POST /api/documents/root în src/grove-api.ts) făcea mkdirSync(target, {recursive:true}) pe orice cale din request, apoi saveConfig + aplicare live — putea crea foldere oriunde și îndrepta indexatorul viu spre ele. resolveDocumentsRoot (src/documents/request-scope.ts) refuză acum crearea de foldere noi în afara PROJECT_ROOT (HTTP 400); selectarea unui folder existent rămâne permisă oriunde (folder administrat de proprietar — deploy-ul actual folosește C:\Users\alexm\Documents\KiwiDocs, care există și rămâne selectabil). </> de control și căile invalide primesc 400.
#Fix revizia 10 — brokerul /api/documents/organize
Inspecția suplimentară a găsit ruta motor POST /api/documents/organize care, cu dryRun=false, executa mutări reale de fișiere fără să treacă prin politica host-action (refuzul legacyActionBlockReason pentru documents_organize non-dryRun se aplica numai în registry.run). Ruta refuză acum orice dryRun !== true cu HTTP 403 și același mesaj ca registrul; web-ul nu apelează această rută. Ruta rămâne dryRun-only ca și skill-ul; mutările reale rămân în planul exact aprobat al P1.
#Defecte confirmate
Nouă teste noi au eșuat înaintea corecțiilor: modelul executa unelte în afara listei prezentate, inclusiv în streaming; lista goală nu dezactiva uneltele; allow al apelantului ocolea deny configurat; fallback-ul offline ignora politica și lista permisă; confirmarea uita politica inițială; mutațiile gazdei puteau folosi configurații vechi allow; previzualizarea tăia argumentele după 100 de caractere; registrul permitea încă operații largi în lot.
Un scenariu suplimentar verifică faptul că pașii generați ai rutinelor nu pot confirma o modificare anterioară. Inspecția Telegram direct a găsit /vm start și variante de acțiuni PC fără confirmare, /docs organize move executat direct și /dev pornind OpenCode după aprobarea generală a sarcinii.
#Comportament aplicat
src/security/host-action-policy.ts compune restricțiile: refuzul are prioritate, apoi confirmarea, apoi permisiunea. Politica apelantului poate restrânge accesul, fără a slăbi refuzul configurat sau politica obligatorie a gazdei. tools limitează execuția în buclele agentului JSON și streaming; [] nu prezintă și nu permite nicio unealtă.
Acțiunile cunoscute pentru programe, comenzi, scriere de fișiere/clipboard, alimentare, wake, modificări VM/procese, backup, programări și creare/ștergere de rutine cer confirmare chiar cu vechiul allow. Diagnosticul rămâne disponibil conform restricțiilor configurate. Este o listă explicită de unelte cunoscute; nu certifică pluginurile arbitrare ca fiind numai pentru citire.
ConversationManager aplică aceeași verificare fallback-ului offline și execuției în așteptare. Intrarea păstrează argumente copiate, identitatea originală a canalului, expirarea în cinci minute și politica inițială; la confirmare se reverifică politica inițială și cea curentă. Firele interne scheduler/routine/auto-resolve/documents refuză acțiunile care cer confirmare, fără a interpreta textul generat drept accept. Aprobările dispar în continuare la restart.
Previzualizarea generică serializează unealta și argumentele complete, identifică gazda operatorului și escapează caracterele de control, backtick și control direcțional. Peste 2.800 de caractere, răspunsul refuză explicit și nu creează aprobare; textul acceptat nu este trunchiat. Sunt prezentate argumentele cerute, nu fiecare efect ulterior al unui executabil. Diff-urile, căile/configurația rezolvate imuabil și hash-urile stării anterioare rămân în migrarea P1.
Apelurile Telegram directe către registru folosesc acum același helper de politică. Acesta copiază argumentele, cere butoanele private existente de unică folosință când este necesar și reverifică politica înainte de execuție. Refuzul explicit se aplică și diagnosticului direct. Nu s-au trimis mesaje Telegram live în teste.
#Acțiuni largi oprite până la planuri exacte
| Intrare | Rezultat curent | Înlocuire |
|---|---|---|
dev_task, Telegram /dev <sarcină> | Refuz explicit; dev.enabled=true nu poate porni un job | Modificări/comenzi revizuite prin sarcinile comune și codarea P2 (în P1) |
documents_organize | Plan exact pregătit pe dryRun:true (copii de recuperare); execuție doar cu exactPlan valid + confirmare (revizia 16) | Implementat — folderele/deploy KiwiDocs încă nu au rulat planuri |
disk_doctor cu action:reap | Non-dryRun refuzat fără ref; plan_reap pregătește plan recoverabil; reap+exactPlan mută în backup (revizia 16) | Implementat — nimic anterior de șters nu a fost șters |
file-organizer (automatizarea mind) | Preview-only din revizia 17: dry-run raportează țintele și nu atinge discul; non-dryRun refuzat cu motiv exact-plan | Plan exact file_organizer aprobat în chat, cu backup înaintea mutării (implementat — a rămas o singură cale de mutare: aprobarea operatorului) |
file_organizer (skill builtin) | dryRun:true pregătește un plan exact (allow); execuția doar cu exactPlan validat + aprobare (confirm), fără ref → deny (revizia 17) | Implementat — niciun plan rulat în producție încă |
Registrul blochează operațiile înainte de intrarea în unealtă; intrările builtin le refuză și ele. Ruta broker /api/documents/organize aplică acum același refuz (revizia 10), iar web-ul nu o cheamă. DevManager și biblioteca de documente rămân cod pentru migrare, fără apelant din aplicație care să pornească joburi DevManager. Testele cu subprocese sintetice verifică direct acel runner. dev_status și dev_cancel cu confirmare rămân disponibile pentru joburi vechi. Configurațiile existente și bibliotecile sunt păstrate.
#Dovezi și lucru rămas
tests/mind-automation-policy.test.ts(nou, revizia 14): clasificarea automatizărilor (app-launcher/note-taker→ confirm;file-organizernon-dryRun → deny, dryRun → allow;web-search/system-monitor/file-finder→ allow), motivul exact-plan alfile-organizer, și refuzul metacharacterilor înapp-launcher(appName și args) fără a apelaexecSync; dry-run rămâne permis.tests/document-scope.test.ts(extins, revizia 14):resolveDocumentsRootrefuză calea lipsă și crearea în afara folderului proiectului, permite crearea în interior și selectarea unui folder existent oriunde.- Suita motor (revizia 14): 105 teste, 104 trecute + unul semantic opțional sărit (+4 față de revizia 13). Mind/web: 222 trecute în 33 suite (neschimbat — fără cod web). Typecheck motor trecut.
- Suita motor (revizia 15): 113 teste, 112 trecute + unul semantic opțional sărit (+8 față de revizia 14,
tests/plugin-capabilities.test.ts). Mind/web: 222 trecute în 33 suite; ambele typecheck-uri trecute. - Suita motor (revizia 16): 124 teste, 123 trecute + unul semantic opțional sărit (+11 față de revizia 15,
tests/exact-plan.test.ts). Mind/web: 222 trecute în 33 suite (neschimbat); ambele typecheck-uri trecute. tests/exact-plan.test.ts(nou, revizia 16): ciclul complet move și delete cu backup/rollback, invalidare la coliziune, eșec parțial, fluxurile de skilldocuments_organizeșidisk_doctor, politica deny→confirm și aprobarea reală prin manager.tests/host-action-boundary.test.ts: 10 regresii trecute, cu contoare sintetice în locul mutațiilor reale pe gazdă; primele nouă au eșuat toate înainte de corecție.tests/mind/unit/telegram-owner-channel.test.ts: 8 cazuri trecute cu transport simulat: aprobare VM/PC, refuz schimbat după propunere și blocarea mutărilor/codării largi.- Testele cu fișiere temporare reale cer accept înainte de emiterea artefactului notiței salvate; clasificarea documentelor păstrează sursele și refuză mutarea.
Rămân în P0 (revizia 17): dev_task rămâne oprit — config implicit deny, dev_status/dev_cancel fail-closed, DevManager.start fără apelant în producție; înlocuitorul sigur e fluxul de sarcini-dispozitiv P1 / codare interactivă P2, amânat în P1. Acceptarea pe două sisteme rămâne blocată (inexistent al doilea Windows; VM-urile sunt Linux). documents_organize, disk_doctor reap și file_organizer sunt acum permise numai cu plan exact + aprobare și nu au rulat mutări/ștergeri reale în producție. Motorul rămâne al operatorului; nu devine rezervă pentru dispozitivele utilizatorilor web. Apelurile directe de nivel jos din codul de încredere nu sunt API public de autorizare.
Surse: src/exactions/ledger.ts (nou), src/skills/fileOrganize.ts (nou), src/skills/builtin/fileOrganizer.ts (nou), src/agent/agent.ts, src/conversation/manager.ts, src/skills/registry.ts, src/skills/builtin/{documents,diskDoctor}.ts, src/security/host-action-policy.ts, src/mind/kelvin.ts, src/telegram/run.ts, contractul stării, checkpoint.