287 Commits

Author SHA1 Message Date
Claude Agent
26e4ba46d9 docs(ups): scoate recomandarea de rotire a parolei
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:53:22 +00:00
Claude Agent
41779b97a6 docs(dashboard): permisiunea Pin Messages data si verificata
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:51:01 +00:00
Claude Agent
b44b204734 docs: WoL prin 10.0.20.36 testat real pe pveelite
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:44:31 +00:00
Claude Agent
ab9dbef998 docs: infrastructura din dashboard si /infra, acces agenti pe statia de birou si 10.0.20.36
Lantul de pornire nou: JuiceSSH -> 10.0.20.36 -> C:\wolcluster.bat. Cheia moltbot
in docs/chei-publice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:19:06 +00:00
Claude Agent
c87fa06b55 feat(discord-bridge): comanda /infra cu confirmare prin butoane
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:18:12 +00:00
Claude Agent
56027e2e6c feat(dashboard): tab Infrastructura, panouri colapsabile si compacte
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:18:12 +00:00
Claude Agent
dc61c43b32 feat(discord-bridge): registru infra_actions (stare cluster + actiuni de mentenanta)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:18:12 +00:00
Claude Agent
d82ac4e9c3 chore(failover): arhiveaza scripturile VM 201 (VM 201 e in HA)
Alerta pvemini-down nu mai recomanda failover manual pe pveelite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:18:12 +00:00
Claude Agent
b32392aa7e fix(ups): parola UPS citita din /etc/nut/ups-shutdown.conf
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:18:12 +00:00
Claude Agent
0104c255a5 feat(cluster): cluster-shutdown/startup --allow-on-node pentru rulare pe pvemini
Rulare detasata din dashboard/Discord: nssh local fara ssh la sine, CT 171
ramane pornit si moare odata cu pvemini (HA freeze il reporneste), email cu
instructiunile de pornire, pauza si pentru testul DR si fereastra de patch.
Garda CT 171 nu mai depinde de /proc/1/environ (era sarita fara root).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
2026-09-13 10:18:12 +00:00
Claude Agent
0ac1cdd45f docs: acces de la distanta la statia de birou (WOL + SSH) spre tunel Oracle client
Documenteaza pasul lipsa fata de diagnostic-spatiu-clienti.md sectiunea 6: cum ajunge
agentul de pe LXC 171 pe statia de birou (WOL pe acelasi L2, apoi SSH ca mmari), plus
capcana job object-ului Windows care omoara tunelul stnlc daca nu ruleaza in aceeasi
sesiune SSH cu sqlplus. Prima aplicare: PACK_CONTAFIN la ROMCONSTRUCT, 2026-09-10.

Co-Authored-By: Claude Agent <noreply@anthropic.com>
2026-09-10 08:54:39 +00:00
Claude Agent
32ff462291 feat(maria-dashboard): reset pentru firele de discutie active
Nu exista nicio cale sa resetezi manual un fir blocat pe o discutie
veche (nici comanda de chat, nici buton) — un test in grupul "Maria
Test" a ramas ancorat pe un subiect vechi fara sa poata fi curatat
fara interventie directa pe server. Sectiune noua in dashboard, intre
"Trimise la suport" si "Clienti cunoscuti": listeaza firele active
din ~/.maria-bridge/conversations/ si le sterge la cerere (rutele
GET/POST /api/maria/fire), cu aceeasi sanitizare de nume de fisier ca
rag/fir.py (oglinda, nu import — coliziune de modul intre proiecte).

11/11 teste (6 noi + cele de clienti), fara regresii.

Co-Authored-By: Claude Agent <noreply@anthropic.com>
2026-09-08 11:36:54 +00:00
Claude Agent
610638ad30 feat(maria): profil de client (firma), asociat cu mai multe numere
Un client poate avea mai multi angajati care scriu pe WhatsApp de pe
numere diferite; toti gasesc acum acelasi profil (rag/client.py,
~/.maria-bridge/clients.json). Profilul intra in prompt sub o sectiune
DESPRE CLIENT, la fiecare mesaj, stateless ca restul Mariei — dar nu
atinge cautarea/scorul din rag/rank.py, ca sa nu strice pragurile deja
calibrate. Administrare din dashboard (sectiune noua "Clienti cunoscuti"),
nu din chat, ca sa ramana pe canalul autentificat.

117/117 teste maria-whatsapp-bridge + teste noi pentru rutele de dashboard.

Co-Authored-By: Claude Agent <noreply@anthropic.com>
2026-09-08 10:35:30 +00:00
Claude Agent
f25a8157fa docs: acordare drepturi utilizator pe firme in ROA la un client
Conectare headless (fara RDP) prin Bitvise de pe VM 303/304, modelul de
drepturi CONTAFIN_ORACLE (NOM_FIRME/DEF_UTIL_GRUP/PACK_DREPTURI), cu
precedentul Conpress/Sofronie Mihaela ca exemplu lucrat.

Co-Authored-By: Claude Agent <noreply@anthropic.com>
2026-09-03 16:45:07 +00:00
Claude Agent
f46410eb58 feat(maria): ALLOW_SELF_CHAT — Maria raspunde doar in grupul de test, nu si in self-chat
`TEST_MODE_SELF_CHAT_ONLY` insemna „self-chat SI grupurile permise"; nu exista
niciun fel de a spune „doar grupurile". `ALLOW_SELF_CHAT=false` (nou, implicit
`true`) scoate chatul cu sine insusi din filtrul puntii.

De ce: `SUPPORT_JID` e tot numarul propriu, deci escaladarile, reamintirile si
raspunsurile suportului cadeau in acelasi chat cu intrebarile — Maria isi citea
propriul canal de suport ca pe o discutie cu un client.

Pus pe `false` in `~/.maria-bridge/env`, puntea repornita: raspunde acum doar in
`120363409761730101@g.us` („Maria Test"). Se vede in `/status` (`allowSelfChat`)
si in linia de conectare din log.

Trimiterea catre suport nu e afectata — filtrul e doar pe mesajele primite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 21:12:25 +00:00
Marius
dee3d1d2f5 docs(chei-ssh): procedura de mutare a profilelor pe VM 303/304 si cheile de admin
Sectiunea 7 acopera tot drumul: ce contine de fapt fiecare profil (parola in
fisier, keypair de profil, keypair global — se afla cu -noRegistry=y, fara GUI),
folderul de pregatire, conversia la publickey cu -storePw=n / -pk=a, verificarea
care prinde un Ctrl+S uitat, si importul cheii pe VM 303. Include lista celor 11
profile cu numele scurtate la copiere si serverul fiecaruia.

Sectiunea 6 tine acum toate cele 5 chei publice (doi angajati, trei ale statiei
de birou) si tabelul pe servere, verificat pe amprenta, nu pe comentariu.

Capcane platite pe drum, notate ca atare:
- stergerea keypair-urilor GLOBALE din User keypair manager lasa fara
  credentiala orice profil care se baza pe ele — asa a ramas mut
  romfast_bitvise.tlp;
- vadeco.tlp avea cheia privata salvata in profil, deci s-ar fi dus cu fisierul;
- abcval (Bitvise 9.32) respinge Ed25519, acolo intra doar RSA;
- conpress blocheaza IP-ul la conexiuni repetate: se exclude in Access control,
  nu doar se sterge din lista de blocati.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FnMP82sffxQYxWRjy5sfKo
2026-09-02 00:08:46 +03:00
Claude Agent
a4fe127926 feat(maria): raspunsul omului de la suport se pastreaza in jurnalul escaladarii
Jurnalul pastra doar intrebarile FARA raspuns. Replica omului care prelua
discutia se scria numai in firul din `conversations/`, care se sterge dupa
FIR_TTL_MIN — deci exact rezolvarea disparea, iar perechea eroare + rezolvare
trebuia recuperata manual din WhatsApp.

Acum, cat tine preluarea, fiecare mesaj al omului se adauga la fisierul
escaladarii (`completari`, `"fel": "raspuns_om"`, cu cine a scris). La fiecare
mesaj, nu doar la primul: rezolvarea vine de obicei in doua-trei replici. De aici
se pot extrage erorile si rezolvarile pentru `document_store` — „CUI cumparator
incorect" ajunge in documente doar daca o scrie cineva, si cel mai ieftin loc de
unde poate fi scrisa e ce a raspuns deja omul o data.

Limita, documentata in README: preluarea se detecteaza doar in grupuri cu cel
putin FIR_PRELUARE_MIN_PARTICIPANTI participanti, deci nu in self-chat si nu in
grupul de test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 20:04:14 +00:00
Claude Agent
6686a5406c docs(maria): marcheaza handover-ul rezolvat, cu cauza reala
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:58:12 +00:00
Claude Agent
653f5cbba9 fix(maria): acoperirea se judeca pe ce a intrebat omul acum, si pe radacina cuvantului
Doua reparatii pe poarta de acoperire, amandoua masurate pe setul de calibrare
extins (31 de cazuri, de la 26).

**Termenii distinctivi se comparau pe cuvantul intreg.** Documentatia si clientul
nu folosesc aceleasi forme: documentul scrie „token" si „tokenuri", omul scrie
„tokenul"; documentul „Generare", omul „generez". Trei continuari legitime din
cinci escaladau desi chunk-ul cu raspunsul era chiar in context. Comparatia se
face acum pe primele 5 litere; codurile raman intregi (ORA-01722 si ORA-01720 au
aceleasi cinci).

**Interogarea avea doua roluri amestecate.** Pe un fir, regasirea are nevoie de
ancora („da, ma blocheaza" singur nu gaseste nimic), dar acoperirea nu: judecata
pe ancora, ea cere sa apara in documente si salutul, si numele firmei, si luna,
deci raportul scade la fiecare replica si firul lung escaladeaza degeaba.
`rank.assess` primeste `dovada` — mesajul nou, plus OCR-ul capturii lui; cand
acesta n-are termeni proprii, se cade inapoi pe interogarea intreaga.

Setul de calibrare: 31/31 cu reparatia, 30/31 fara.

Ce NU s-a facut, desi era propus in handover: lista de formule de politete in
`rare_terms`. Masuratoarea nu o justifica — cu potrivirea pe radacina, cazurile cu
ancora politicoasa trec si fara ea, iar intr-una din configuratii facea rau.
Cauza reala nu era politetea din ancora, ci morfologia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:57:49 +00:00
Claude Agent
89145d437e docs(maria): handover — cazuri de continuare in calibrate si termenii distinctivi
Ce ramane deschis dupa sesiunea de azi: pe un fir, acoperirea se judeca pe
interogarea combinata (ancora + mesaj nou), deci cuvintele de politete din primul
mesaj ajung "termeni distinctivi" si nu apar niciodata in documente. Efectul e
escaladare in plus pe discutiile lungi — directie sigura, dar inutila.

Fisierul spune si de ce nu se incepe cu reparatia: continuarile scurte n-au termeni
distinctivi proprii, deci reparatia poate strica exact ce a construit firul. Intai
cazuri de continuare fara cod de eroare in ops/calibrate-rank.py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:47:35 +00:00
Claude Agent
af04d97de9 fix(maria): „am trimis la suport" doar cand puntea chiar a trimis, si un log care nu minte
Gasite citind logul rularii de test de azi.

1. `requests` NU ridica exceptie la 4xx/5xx, iar puntea raspunde 503 cand nu e
   conectata la WhatsApp si 500 cand `sendMessage` cade. send_reply/send_image
   ignorau codul, deci escaladarea se inregistra `notified: true` si omul primea
   „te contacteaza cineva" pentru un mesaj care nu plecase nicaieri — exact
   promisiunea pentru care exista ESCALATED_RECORDED. Acum trimiterea intoarce
   motivul esecului ("" la reusita), iar `notified` si `notify_error` vin de acolo.

2. Puntea nu loga nimic la trimitere: o escaladare nu lasa nicio urma pe partea de
   WhatsApp, deci nu se poate verifica daca captura chiar a ajuns la suport.
   /send si /send-image logheaza acum destinatarul si inceputul mesajului.

3. Mesajele primite se logau taiate la 80 de caractere, fara semn ca sunt taiate.
   Un mesaj de exact 80 arata ca unul intreg — asa am ajuns azi la concluzia
   gresita ca gardul de „mesaj prea vag" nu functioneaza, cand de fapt mesajul era
   mai lung decat parea. 200 de caractere si „… (+N)".

4. La o captura pe un fir deschis se logau doua linii „caut dupa" diferite, iar
   prima nu era interogarea folosita. Prima zice acum „din captura, retin".

Teste: 103 pass (una noua: puntea respinge cu 503 -> notified false).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:44:24 +00:00
Claude Agent
47110d71bf fix(maria): mesajul si captura sunt acelasi lucru, iar cosinusul singur nu mai e dovada
Doua greseli din grupul "Maria Test", 2026-09-01, pe care 27409fb le-a atenuat dar
nu le-a rezolvat.

1. La o reclamatie vaga Maria cauta, in loc sa intrebe. "Buna . Am si eu o factura
   pe luna august cu eroare in spv la trimitere AUTO SULE" nu spune CE eroare e —
   n-are ce cauta in documente si n-are ce trimite la suport, fiindca programatorul
   ar pune exact aceeasi intrebare. Gardul (triaj.prea_vag) exista, dar cerea text
   sub 12 cuvinte; mesajul are 14. Gresea si invers: "nu pot incarca factura in SPV,
   imi da eroare de certificat" are 9 cuvinte si ARE raspuns in documente, si era
   oprita degeaba. Lungimea nu masoara cat de precis e mesajul.
   -> gardul nu mai numara cuvinte si se aplica DUPA cautare, doar cand nu exista
      acoperire. O singura data pe fir: daca nici intrebat omul nu spune mai mult,
      mesajul pleaca la suport.

2. Textul si captura, trimise una dupa alta, erau tratate ca doua probleme fara
   legatura. `este_continuare` rupea firul la ORICE imagine — dar cazul frecvent e
   tocmai omul care scrie problema si trimite captura imediat dupa, sau care
   raspunde la "trimite-mi o captura". Textul se pierdea, captura se cauta doar pe
   OCR-ul ei, se deschideau doua fire si se puteau deschide doua escaladari pentru
   aceeasi problema.
   -> o captura in primele FIR_IMAGINE_MIN (5) minute continua firul: textul citit
      din ea intra in ancora (cu tot cu coduri), cautarea se face pe mesaj +
      captura, iar pe o escaladare deschisa pleaca la aceeasi referinta — cu
      imaginea, nu doar cu textul citit din ea (_notifica_suport ia si media).

Si, ca urmare a lui (2): RANK_STRONG_COSINE dispare. Interogarea pe un fir e ancora
plus mesajul nou, deci cosinusul urca la fiecare replica fara sa apara vreo dovada
noua — aceeasi captura da 0,778 singura si 0,836 cu mesajul de dinainte, adica peste
0,80 pus ieri. Orice prag fix de sus e trecut de o discutie destul de lunga. Acoperirea
ramane pe dovada lexicala (termenii distinctivi ai intrebarii chiar in chunk-uri),
care e stabila la lungime. Niciun caz cu raspuns in documente nu avea nevoie de
scurtatura.

ops/calibrate-rank.py: 26/26, cu interogarea combinata adaugata la set. Teste: 102 pass.
Verificat end-to-end pe scenariul real (mesaj, apoi captura la 10 secunde): intrebare
de detalii, apoi o singura escaladare care poarta si textul si captura.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:44:24 +00:00
Claude Agent
45b36591c9 fix(maria): pragul de acoperire la 0,80 si temperatura 0 — doua raspunsuri inventate
Grupul "Maria Test", 2026-09-01, ambele mesaje au primit raspunsuri inventate:

- "am si eu o factura pe luna august cu eroare in spv la trimitere AUTO SULE"
  (cosinus 0,768) — mesajul nu spune CE eroare e; Maria a explicat cum se reface
  o factura stearsa si a trimis omul sa verifice drepturile pe CIF in SPV.
- captura cu `<Error errorMessage="CUI cumparator incorect"/>` (0,778) — eroarea
  nu exista in niciun document; Maria a inventat o procedura cu "ID de descarcare",
  Borderou eFactura si retrimitere manuala.

Cauza: `RANK_STRONG_COSINE=0,70` inseamna "cosinusul singur e destul", dar toate
chunk-urile eFactura seamana intre ele — la 0,7x cosinusul nu mai distinge "e in
documente" de "e despre eFactura". Aceeasi greseala ca la codurile Oracle
(ORA-06550 vs ORA-12541, b7d75f0), doar ca fara cod pe care sa te sprijini.

- RANK_STRONG_COSINE 0,70 -> 0,80: peste atat trec doar potrivirile aproape
  textuale, restul cad pe dovada lexicala (termenii rari ai intrebarii chiar in
  chunk-uri). Ambele mesaje escaladeaza acum la suport.
- LLM_TEMPERATURE=0 la apelul modelului. Fara el llama.cpp foloseste 0,8 —
  creativitate exact acolo unde vrem doar ce scrie in context.
- Cele doua cazuri reale sunt adaugate in ops/calibrate-rank.py: 25/25.

Teste: 99 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:44:24 +00:00
Marius
5e60394c20 feat(chei-ssh): inventar de chei publice si script de adaugare/revocare in masa
Cheile publice ale angajatilor (VM 303, VM 304) se tin versionate in
docs/chei-publice/ — acelasi fisier serveste si pentru "Add File", si pentru
"Remove File", deci revocarea nu mai depinde de un fingerprint copiat de mana.

scripts/bitvise-chei.ps1 tine lista celor 11 servere Bitvise valide si ruleaza
spksc cu -unat=y (List / Add / Remove) fara sa se blocheze in prompturi.

Doua lucruri descoperite si notate in doc:
- romfast_bitvise.tlp si 10.0.20.36:22122 sunt acelasi server (liste de chei
  identice); intrarea a doua ramane ca ruta de rezerva, fiindca parola din
  bitvise_10.0.20.36.bscp a expirat si se conecteaza cu -keypairFile;
- abcval (Bitvise SSH Server 9.32) respinge Ed25519 cu KeyNotSupported — acolo
  e nevoie de o pereche RSA.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FnMP82sffxQYxWRjy5sfKo
2026-09-01 22:39:58 +03:00
Claude Agent
a01cac0657 feat(analiza): consum real Claude Code si dimensionarea suplimentului GLM
Plafonul general saptamanal Max 5x se consuma in ~2 zile, urmate de ~5 zile
blocate. Masurat pe 3 masini (Windows, claude-agent, moltbot), 2026-07-29 ->
2026-09-01: 11,15 miliarde tokeni, 435 sesiuni, 64.211 cereri API, dedup pe
message.id + requestId. Deficit +32% (~570 prompturi/saptamana).

Concluzia nu e cea asteptata: bugetele plafonate in dolari (OpenCode Zen $20,
OpenCode Go) se evapora la acest volum -- Go a tinut 1-2 zile in practica.
Planurile Z.AI se contorizeaza in prompturi, nu in dolari, si rezista la un
workload cu 7,1 cereri API per prompt. Z.AI Coding Lite ($18/luna) acopera 70%
din deficit; Pro ($72) il acopera integral.

Verificat empiric ca subagentii nu pot rula pe GLM (in proces, moștenesc auth),
dar sesiunile separate pot: endpoint fals local a primit cererea cu tokenul
alternativ, fara sa atinga OAuth-ul Max. Masurat: ~186MB RSS si 20-60s pornire
per sesiune -- de aici regulile de orchestrare (sarcini mari, max 4 procese,
fan-out in interiorul unei sesiuni GLM).

Ramane neverificat multiplicatorul de credite pentru GLM-5.3-Flash; la 3x in
loc de 1x, Lite pica si decizia se muta pe Pro.

- docs/supliment-glm-zai.md: rezumat de implementare (indexat in CLAUDE.md)
- claude_usage_report.md + CSV-uri: analiza completa si datele brute
- tools/claude_usage_*.py: pipeline de masurare read-only, re-rulabil remote
- tools/claude-glm.sh: wrapper de sesiune GLM (task/resume/shell)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 11:32:56 +00:00
Claude Agent
ed7eca4fc7 fix(discord): watchdog de gateway — procesul viu pe un gateway mort nu mai trece neobservat
Puntea a tacut ~7 ore fara ca nimic sa semnaleze: discord.py memoreaza
`resume_gateway_url` primit la ultimul READY si il refoloseste la fiecare
reconectare. Cand gatewayul regional (gateway-us-east-1a) a inceput sa dea
503 la handshake, botul a reincercat la infinit acelasi host mort, cu backoff
pana la ~15 minute. Procesul era viu, deci nici systemd nici dashboardul nu
aveau ce vedea, iar `gateway.discord.gg` raspundea normal tot timpul.

GatewayWatchdog numara de cat timp e legatura jos (on_connect / on_resumed /
on_ready o ridica, on_disconnect o coboara, iar reincercarile esuate nu
reseteaza ceasul). Peste `GATEWAY_WATCHDOG_S` (implicit 300s) alerteaza, iese
cu codul 3 si lasa systemd sa reporneasca — restartul e singurul lucru care
forteaza un IDENTIFY nou pe gateway.discord.gg. Un prag <= 0 il dezactiveaza.

O iesire la 300s ramane sub StartLimitBurst=5/5min, deci bucla de restart nu
poate ajunge sa lase unitul `failed`. `_hard_exit_after` acopera cazul in care
`close()` se blocheaza pe socketul mort.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 07:37:38 +00:00
Claude Agent
0ace9f97ac fix(maria): fara "caut informatia" la completari, si trei feluri de raspuns
Din firul testat in grup: la "Urgent este", "Nu m-a contactat nimeni. Cat mai
dureaza?" si "Tot nimic", Maria trimitea de fiecare data "Caut informatia, revin
imediat...", calcula un embedding pe care il arunca, si raspundea acelasi sablon
("am adaugat si asta la M-..."). La intrebarea "cat mai dureaza" nu raspundea deloc,
iar "urgent" nu schimba nimic nicaieri.

- Completarea se decide INAINTEA confirmarii si a cautarii: raspunsul vine instant,
  deci nici promisiune, nici embedding degeaba (~1-2s per mesaj).
- triaj.fel_completare: urgenta / stare / detaliu.
  - urgenta -> escaladarea se marcheaza si suportul primeste un mesaj cu URGENT;
  - stare -> raspuns cu ce stim (de cat timp asteapta) si reamintire catre echipa,
    cel mult una la 15 minute, ca sa nu sune telefonul la fiecare "tot nimic";
  - detaliu -> confirmare scurta, cu formularea rotita.
- Peste 30 de minute fara raspuns Maria o spune pe fata si indruma spre telefon
  (SUPPORT_PHONE), in loc sa repete ca "echipa vede detaliul".
- Gramatica: "acum 10 minute", nu "acum 10 de minute".

Teste: 99 pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 07:19:06 +00:00
Claude Agent
4d6ff1940f feat(maria): fir de discutie — continuarea nu mai e tratata ca eroare noua
Fiecare mesaj pornea de la zero. Masurat pe indexul viu, dupa o escaladare pentru
ORA-06550: "da, ma blocheaza complet" primea raspuns despre ordinul de plata la
Trezorerie (cosinus 0,642, "acoperit"), "eram la salvarea unei facturi" despre
corectia unei eFacturi (0,742), iar "am incercat si tot nu merge" cerea din nou
detaliile tocmai date. Patru cuvinte fara context seamana cu ceva din documente,
iar cautarea nu avea de unde sti ca sunt raspunsul la intrebarea Mariei.

rag/fir.py tine ancora (textul erorii), codurile ei, referinta escaladarii si
ultimele 6 schimburi, in ~/.maria-bridge/conversations/ (expira, ca si capturile).
La o continuare: cautarea e pe ancora + mesajul nou, modelul primeste istoricul,
mesajul se adauga la escaladarea deschisa de cate ori e nevoie, iar "mesaj prea
vag" nu se mai aplica. Escaladarea duce firul intreg la suport, nu un mesaj rupt
din context.

Firul se rupe doar la o captura noua sau un cod de eroare diferit — schimbarea
subiectului in cuvinte e prea usor de confundat cu o continuare.

In plus:
- Maria tace 60 min cand preia un om, dar automat doar in grupuri cu >= 2
  participanti: in self-chat totul e `fromMe`. Comenzi explicite oriunde
  ("Maria, stop" / "Maria, continua").
- ALLOWED_GROUP_JIDS: testarea se muta in grupul "Maria Test"
  (120363409761730101@g.us), ca sa nu mai poluam chatul "Eu". Filtru si in punte,
  si in consumer. NU echo-test: puntea lui Echo nu filtreaza fromMe in grupuri,
  deci cei doi boti ar intra in bucla.

Teste: 94 pass. Calibrare 23/23, cu doua cazuri de continuare.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 06:16:21 +00:00
Claude Agent
5342875aa2 feat(maria): triaj — raspuns pentru utilizatori, escaladare automata la programatori
Pe captura cu ORA-06550, avand in context chiar intrarea din dictionar care spune
"nu se rezolva din aplicatie", modelul i-a raspuns unui contabil sa verifice
schema de date si sa foloseasca "procedura alternativa MI_pack_parteneri_old" —
inventata. Si nu escalada, fiindca escaladarea se declansa doar cand NU exista
acoperire in documente, iar aici exista.

rag/triaj.py, trei decizii inaintea modelului:

- **Mesaj prea vag.** "Am o eroare" -> Maria cere operatiunea, ecranul si textul
  erorii, in loc sa ghiceasca. Regula e ingusta: text scurt, fara captura, fara cod.
- **Erori marcate "Intotdeauna"/"Imediat" in dictionar** (9 din 29) -> raspunsul se
  compune din campurile dictionarului, fara model, si pleaca automat la suport cu
  referinta si captura. Textul de acolo e scris pentru clienti; parafrazarea lui nu
  adauga nimic si poate strica tot.
- **Urgenta se intreaba, nu se deduce.** Raspunsul ("ma blocheaza" / "pot continua")
  se ataseaza escaladarii deschise, nu deschide alta referinta. Fereastra 30 min.

In plus: SYSTEM_PROMPT interzice explicit vocabularul tehnic (procedura, schema,
tabela, PL/SQL, compilare, sesiune) si propunerile de verificat baza de date; cele
mai tehnice cinci intrari din dictionar rescrise in limbaj de client.

Teste: 85 pass. Calibrare 21/21. Reindexare: 5 embeddings noi, 164 refolosite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 05:26:33 +00:00
Claude Agent
b7d75f05ee fix(maria): codul de eroare decide acoperirea, nu cosinusul
O captura cu ORA-06550 / PLS-00906 primea raspunsul despre ORA-12541 (TNS no
listener) si nu pleca la suport. Cauza: doua erori Oracle diferite se scriu
aproape la fel ("[Oracle][ODBC][Ora]ORA-..."), asa ca chunk-ul gresit a iesit la
cosinus 0,736 — peste RANK_STRONG_COSINE, care scurtcircuita orice alta dovada.
Chunk-ul corect din dictionar exista, dar statea pe pozitia 4, in afara TOP_K:
OCR-ul unei ferestre intregi aduce zeci de tokeni de zgomot, iar BM25 ineca tocmai
codul.

- `rank.codes()` scoate codurile de oriunde din text, nu doar ca token intreg —
  OCR-ul le lipeste de ce e langa ("OraJORA-06550").
- `rank.by_codes()` e un al treilea clasament in fuziune: chunk-ul care poarta
  codul cerut urca peste cele care doar seamana. Acum iese primul.
- `rank.assess()`: cand intrebarea are coduri, decid doar ele. Niciun cod gasit =
  escaladare la suport, oricat de mare ar fi cosinusul.
- `consumer.search()` judeca acoperirea pe cosinusul chunk-urilor date chiar
  modelului, nu pe cel mai bun din tot indexul.

Calibrare 21/21 (cele 19 cazuri + captura reala + ORA-00600, cod care nu e in
documente si trebuie sa plece la suport). Teste: 79 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 22:25:45 +00:00
Claude Agent
44763869ce feat(maria): modelul de embedding in cheia de cache si listarea grupurilor WhatsApp
Cheia de refolosire a embeddings-urilor era doar textul chunk-ului. La schimbarea
lui EMBED_MODEL indexul ar fi ramas un amestec de vectori din doua modele, iar
cautarea ar fi dat rezultate aiurea fara nici un mesaj de eroare. Acum fiecare
intrare poarta modelul cu care a fost calculata si se refolosesc doar cele cu
modelul curent; intrarile vechi, fara camp, se recalculeaza o singura data.
Indexul de pe container a fost stampilat manual cu `nomic-embed-text` (singurul
folosit pana acum), deci nu s-au recalculat cele 169 de chunk-uri.

`GET /groups` listeaza grupurile contului cu JID, nume si numar de participanti.
JID-ul unui grup ("120363...@g.us") nu se vede nicaieri in WhatsApp, iar el e
singurul mod de a scrie SUPPORT_JID pentru un grup de suport.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 22:20:08 +00:00
Claude Agent
52d1e9f205 docs: context handover — de ce reindexarea refacea toate embeddings-urile
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 22:15:55 +00:00
Claude Agent
c514818ebe feat(maria): depozit local, in afara oglinzii Drive
Dictionarul de erori Oracle pus in `documents/` ar fi disparut in 10 minute:
`rclone sync` sterge de acolo tot ce nu exista in Drive. Aceeasi problema o avea
si butonul de adaugare document din dashboard — documentul traia pana la
urmatorul tur de sincronizare, ceea ce README-ul mentiona ca pe o ciudatenie, nu
ca pe un bug.

Acum sunt doua directoare cu un singur spatiu de nume: `documents/` (oglinda
Drive) si `documents-local/` (ce nu vine din Drive). La acelasi nume castiga
Drive-ul — e sursa comuna a echipei, iar copia locala poate fi o versiune veche
uitata acolo. Dashboard-ul scrie in cel local si marcheaza documentele „local".

Recalibrat cu dictionarul indexat (169 chunk-uri): 19/19, cele patru intrebari
Oracle noi ies la 0,73-0,75. Reindexarea a durat cat cele 29 de chunk-uri noi,
nu cat toate 169 — refolosirea vectorilor isi face treaba (log: "29 embeddings
noi, 140 refolosite").

Verificat pe canalul viu: o escaladare cu captura chiar pleaca pe WhatsApp,
imagine + rezumat + referinta, si apare in dashboard.

73 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 22:13:09 +00:00
Claude Agent
ca6b53e0ca feat(maria): rank hibrid, escaladare la suport si dictionar de erori Oracle
Trei probleme legate, gasite testand cu capturi reale.

1. RASPUNSURI GENERICE. Cand raspunsul nu era in documente, modelul primea
   oricum top-3 chunk-uri si compunea ceva plauzibil — utilizatorul pleca cu
   impresia ca a primit ajutor. Acum, daca nu avem acoperire, modelul nu mai e
   intrebat deloc: intrebarea pleaca la suport, cu referinta (M-260831-A1B2) si
   confirmare onesta — "am trimis" doar daca notificarea chiar a plecat, altfel
   "am inregistrat, dar nu am putut trimite". Jurnalul se scrie intotdeauna, in
   ~/.maria-bridge/escalations/, si se vede in dashboard. Daca a fost o captura,
   imaginea insasi se retrimite la suport (endpoint nou /send-image, limitat la
   MEDIA_DIR).

2. ORDONARE (rank.py). Un prag pe cosinus nu putea decide "avem raspunsul?":
   masurat pe indexul viu, intrebarile bune dau 0,600-0,816 si cele straine
   0,534-0,685 — intervale care SE SUPRAPUN. Lipseau codurile: ORA-01722, D406,
   CIF sunt tokeni exacti pe care cautarea semantica ii topeste. Acum se ordoneaza
   de doua ori — embeddings si BM25 — si se fuzioneaza clasamentele (RRF), iar
   decizia de acoperire se ia pe dovezi: cosinus mare, sau cosinus de mijloc cu o
   fractiune din termenii distinctivi gasita chiar in chunk-uri. Fractiunea conteaza:
   cu un singur termen, "cum imi resetez parola de la Windows" trecea drept
   acoperita fiindca "parola" apare in documente.
   Pragurile sunt masurate, nu alese: ops/calibrate-rank.py ruleaza un set de cazuri
   pe indexul real si matura grila. 15/15 la strong=0.70 weak=0.58 ratio=0.5.

3. DICTIONAR DE ERORI ORACLE. 29 de erori uzuale, scrise pentru utilizatorul din
   fata aplicatiei: ce inseamna, ce poate incerca singur, cand sa sune. Fara nume
   de servere, IP-uri sau pasi de administrare — Maria e chatbot pentru clienti.
   Fisierul din git e SAMANTA: depozitul e oglinda Drive-ului, deci trebuie pus in
   document_store ca sa nu dispara la sincronizare.

Pe drum, doua lucruri gasite in log:
- fiecare mesaj din self-chat sosea de DOUA ori, pe @s.whatsapp.net si pe @lid,
  deci Maria raspundea de doua ori. Dedup pe key.id in punte.
- reindexarea reface toate embeddings-urile (~7 s fiecare pe CPU): adaugarea unui
  document la 170 de chunk-uri insemna 20 de minute. Acum refoloseste vectorii
  chunk-urilor nemodificate din indexul precedent, fara vreun fisier nou de stare.

Textul OCR se logheaza acum INTEGRAL, cu interogarea de cautare si sursele alese
cu scorurile lor — fara asta un raspuns gresit nu se poate explica: nu stiai daca
a citit prost captura sau a cautat prost in documente.

6 fisiere de test noi/extinse, 68 pass (de la 42).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 22:08:50 +00:00
Claude Agent
ecf5f2fa7a docs: de ce nu raspund ambele punti in chatul "Eu", si re-legarea fara QR
Corectie la ce am scris mai devreme in aceeasi zi: ambele punti chiar PRIMESC
mesajele din self-chat, dar Echo le arunca pe toate cu `fromMe && !isGroup`, iar
in self-chat tot ce scrii e fromMe. Deci raspunde doar Maria; Echo foloseste "Eu"
in celalalt sens, ca destinatie de notificari prin /send. Echilibrul e insa
accidental — scoaterea acelui `continue` aduce doua raspunsuri la fiecare mesaj.

Adaugat si cum se re-leaga o punte prin cod, nu prin QR (ambele au /pair), si de
ce nu se poate ocoli slotul de dispozitiv: doua procese cu acelasi auth/ folosesc
aceleasi chei si se dau afara reciproc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 21:28:15 +00:00
Claude Agent
7a1d2a2073 feat(maria): asociere prin cod de telefon si citirea capturilor cu erori
Doua lucruri cerute de utilizator, ambele pe acelasi drum: sa poti lega
puntea fara sa ai un ecran de scanat, si sa poti trimite Mariei o poza cu
eroarea in loc sa transcrii mesajul.

Asociere prin cod de 8 caractere (alternativa la QR):
- endpoint-ul /pair exista, dar nu era folosibil: fara `browser` explicit
  WhatsApp refuza codul, iar dupa introducerea lui corecta serverul cere un
  restart (515) care consuma din bugetul de reincercari si putea opri puntea.
  Acum descriptorul e Browsers.ubuntu('Chrome') si restartRequired reconecteaza
  imediat, fara sa numere.
- codul are TTL de 3 minute, iar /status il da doar cat timp e valabil —
  un cod expirat afisat in dashboard trimite omul sa tasteze degeaba.
- dashboard: camp pentru numar + buton, in acelasi card cu QR-ul.

Imagini cu erori (capturi de ecran):
- puntea descarca imaginile in ~/.maria-bridge/media/ (imageMessage sau
  document cu mimetype image/*, si prin ambalajele efemer/"vezi o data" —
  fara despachetare pareau mesaje fara continut si se aruncau tacut).
- rag/ocr.py: tesseract ron+eng. Modelul de raspuns e strict text, deci OCR
  nu e o optiune de calitate, e singura cale.
- cautarea in index merge DOAR pe liniile care arata a eroare; o fereastra
  intreaga de meniuri si totaluri dilueaza embedding-ul si scoate chunk-uri
  fara legatura. Modelul primeste captura intreaga, marcata ca text OCR.
- capturile se sterg imediat dupa citire (pot contine date de client).
- cand nu se citeste nimic si nu exista legenda, Maria cere textul erorii
  in loc sa raspunda in gol.

16 teste noi (42 in total). install.sh verifica tesseract; README documenteaza
ambele metode de asociere si drumul unei capturi.

Separat, in docs/chatboti-si-punti.md: chatul "Eu" e vazut de AMBELE punti de pe
numar. Puntea lui Echo (LXC 110) e asociata ca dispozitiv :11 al aceluiasi cont,
momentan nelegata dar pornita — daca se reasociaza, raspunde in "Eu" langa Maria.
Notat si ca serviciile lui Echo sunt unitati de UTILIZATOR: `systemctl is-active`
ca root raspunde "inactive" desi botul ruleaza.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 21:22:42 +00:00
Claude Agent
f5c8df7adf fix(docs): LXC 301 e template Proxmox, nu container oprit — riscul de IP nu exista
Documentatia de ieri descria LXC 301 ca un container oprit cu `onboot: 1` care
ar fura 10.0.20.37 de la VM 109 la reboot-ul lui pveelite. Configul are insa
`template: 1`: un template nu poate fi pornit, iar `onboot` e ignorat pentru el.
Riscul reapare doar daca e convertit inapoi in container.

Verificat si ca `basevol-301-disk-0@__base__` nu are niciun clon, ceea ce
infirma cealalta grija din pagina — se poate sterge in siguranta (~916 MB).

Recomandarea `pct set 301 -onboot 0` din handover e marcata infirmata, nu
stearsa, ca sa nu reapara intr-o sesiune viitoare.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SGMEagPnRavdLhWFqJHdja
2026-08-31 21:15:31 +00:00
Claude Agent
b4472511c9 docs: context handover pentru sesiunea 2026-08-31
Nu repeta infrastructura — e deja documentata in repo — ci doar ce s-a schimbat
azi si ce a ramas: starea serviciilor la finalul sesiunii, verificarea reindexarii
care era in curs, cele patru fire terminate (Gitea/repo-uri, memorie comuna +
flux plan-Opus/exec-Sonnet, atasamente Discord, mutarea Mariei pe LXC 171), si
ce a ramas de decis (onboot pe LXC 301, auditul sistematic, modelul Mariei).

Include si capcanele invatate pe pielea noastra azi, ca sa nu se repete: verifica
pe ce container esti inainte de a concluziona ceva, nu reporni prototipul de pe
104, rclone sync sterge la destinatie, si nu taia iesirea comenzilor de diagnostic
cu `head` — exact asta a produs concluzia gresita "nu asculta nimic pe 8091".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 21:08:34 +00:00
Claude Agent
824b215cf0 docs: LXC 301 — singurul guest nedocumentat, si conflictul de IP cu VM 109
LXC 301 (docker-portainer-template, pveelite, oprit) e predecesorul lui VM 109:
vechiul DR Oracle 19c, inlocuit de VM-ul Windows. Nu era documentat nicaieri.

Important: si-a pastrat IP-ul static 10.0.20.37 — acelasi pe care VM 109 l-a
mostenit de la el ("IP: 10.0.20.37 (same as current LXC)", din planul de
implementare DR) — si are onboot=1. La un reboot al lui pveelite porneste singur
si ocupa adresa. VM 109 are onboot=0, deci nimeni nu observa nimic pana la
urmatorul test DR, cand VM 109 gaseste adresa luata.

Recomandarea (nefacuta, e decizia utilizatorului): `pct set 301 -onboot 0`.
Stergerea completa cere verificat intai ca basevol-301-disk-0 nu e sablonul din
care s-au clonat alte containere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 21:06:57 +00:00
Claude Agent
3c8a29c2e9 fix(maria): recunoaste JID-urile LID, altfel toate mesajele erau aruncate tacut
Dupa asocierea de azi, puntea nu raspundea la niciun mesaj desi `connected: true`
si consumer-ul rula. WhatsApp livreaza self-chat-ul pe conturile noi cu un JID de
tip LID (`51947713372214@lid`), nu cu `<numar>@s.whatsapp.net`, iar filtrul
`TEST_MODE_SELF_CHAT_ONLY` compara doar cu al doilea — deci arunca tot.

- `ownLid` calculat din `sock.user.lid` (cu rezerva pe `creds.me.lid`), fara
  sufixul de dispozitiv: credentialele au "51947713372214:13@lid", dar
  remoteJid-ul mesajelor vine fara ":13".
- `isSelfChat` accepta acum ambele formate.
- Logul de conectare si `/status` arata si LID-ul.

Cauza pentru care a fost greu de gasit: mesajele respinse dispareau fara nicio
urma. Acum fiecare motiv de respingere se logheaza (nu e self-chat, fromMe in
afara self-chat, fara continut text), cu JID-urile comparate. Exact linia asta a
aratat problema in 30 de secunde:
  [whatsapp] ignorat (nu e self-chat): remoteJid=51947713372214@lid ownJid=40723197939@s.whatsapp.net

Verificat live: "token efactura expirat" -> preluat, cautat in RAG, raspuns trimis.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 21:02:24 +00:00
Claude Agent
d0224263f3 docs(maria): rolul a trecut pe LXC 171 — prototipul de pe 104 e oprit
LXC 171 a fost asociat la WhatsApp (+40723197939, dispozitiv nou al aceluiasi
cont), iar pe 104 `maria-whatsapp-bridge` si `maria-whatsapp-consumer` au fost
oprite si dezactivate ca sa nu raspunda amandoua la acelasi mesaj.
`llama-qwen35.service` ramane pornit acolo: el e LLM-ul de raspuns, folosit acum
de 171 prin retea.

docs/chatboti-si-punti.md a fost scris cu cateva ore inainte de mutare si spunea
ca instanta vie e cea de pe 104 — actualizat peste tot (tabel, sectiuni, porturi,
comenzi de verificare), plus un avertisment sa nu se reporneasca prototipul ca
"reparatie": ar raspunde in paralel, pe acelasi numar, cu un index de 24 de
chunk-uri.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 20:57:50 +00:00
Claude Agent
ae8acfbc8b docs: care chatbot e care — trei "Maria", doi boti de Discord, doua punti WhatsApp
Pe 2026-08-31 s-a pierdut aproape o ora de diagnostic fiindca Maria raspundea pe
WhatsApp, dar verificarile se faceau pe containerul gresit: pe LXC 171 totul parea
rupt (niciun LLM pe 8091, WhatsApp neasociat, log fara activitate) in timp ce
raspunsurile veneau de pe LXC 104. Nicio pagina nu spunea ca exista doua instalari.

docs/chatboti-si-punti.md, verificat live: tabel scurt "cine e cine", cate o
sectiune per instanta (Maria pe Flowise, Maria WhatsApp de pe 104 care e cea vie,
Maria din git de pe 171 care nu e asociata, Echo pe 110, puntea Discord), tabel de
porturi cu notarea ca 8099 si 11434 exista pe ambele containere cu continut
diferit, comenzi de verificat cu cine vorbesti, si capcanele (prototipul de pe 104
nu e in git, depozitul e oglinda a Drive-ului, reindexarea dureaza ~21 min,
granita Mariei fata de infrastructura).

Faptul care lega totul: WhatsApp-ul lui Echo (110) si al Mariei (104) sunt legate
la acelasi numar, +40723197939, ca dispozitive diferite ale aceluiasi cont.

Documentul e legat din CLAUDE.md si dintr-un citat pus in capul celor cinci
README-uri implicate, ca sa fie gasit de cine aterizeaza direct acolo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 19:30:06 +00:00
Claude Agent
16ac5a289f docs(maria): a doua instalare, pe LXC 104, si de ce diagnosticul a dus in eroare
Prototipul a ramas sa ruleze pe LXC 104 (/root/maria-whatsapp-bridge/), in afara
git-ului, si aceea era Maria conectata la WhatsApp — nu cea din git, de pe 171.
Indexul ei avea 24 de chunk-uri dintr-un singur fisier, deci inventa raspunsuri
la orice iesea din subiectul roaauto.

Pe 171 totul parea rupt (nimic pe 8091, WhatsApp neasociat, rag.log fara
activitate) desi utilizatorul primea raspunsuri pe telefon. Notat in README cum
se verifica intai care instanta e conectata, ca sa nu se mai piarda timp asa.

Notat si ca LLM-ul de raspuns (llama.cpp, Qwen3.5-2B-Q4, port 8091) ruleaza pe
LXC 104 si se foloseste prin retea; pe 171 Ollama are doar embeddings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 19:17:22 +00:00
Claude Agent
3b7069e954 fix(maria): lacat intre reindexari, scriere atomica a indexului, XML invalid raportat
Descoperit la prima sincronizare reala din Drive: `maria-sync.timer` a pornit
peste rularea manuala si doua procese faceau embeddings in paralel pe acelasi
Ollama, ambele urmand sa scrie acelasi rag_index.json. Embedding-ul a incetinit
de la ~7s la ~20s din concurenta, iar ultimul care termina ar fi suprascris
munca celuilalt.

- config.exclusive(): lacat `flock` intre procese, luat la intrarea in sync.py si
  indexer.py. Nu asteapta — a doua rulare iese curat cu "o reindexare e deja in
  curs", fiindca ar reface exact acelasi lucru. Verificat pe procese reale.
- indexer scrie indexul atomic (tmp + os.replace): consumer-ul reciteste fisierul
  la 30s si putea prinde un JSON pe jumatate scris.
- build() intoarce `warnings` pentru XML-urile care nu se pot parsa, iar rularea
  din linia de comanda le scrie in stderr. Pana acum, un XML invalid se indexa
  tacut ca text simplu, cu o singura linie pierduta in log.

Context de performanta, masurat pe LXC 171 fara alta incarcare: un embedding
`nomic-embed-text` ia ~7,3s, deci o reindexare completa a celor 173 de chunk-uri
dureaza ~21 de minute — mai mult decat intervalul timer-ului. Nu e o problema
practica (amprenta reindexeaza doar la schimbare, iar lacatul opreste
suprapunerea), dar explica de ce prima rulare pare blocata.

docs/rclone-google-drive-headless.md: procedura de conectare a unui container
headless la Drive prin `rclone authorize`, cu transcriptul rularii reale de pe
Windows, capcanele (sync e distructiv pe destinatie, connection string in loc de
cale pe nume, unde stau secretele) si de ce nu contul de serviciu. Indexata in
CLAUDE.md.

6 teste noi (lacat, eliberare la exceptie, scriere atomica, avertismente).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 18:42:01 +00:00
Claude Agent
f3f5eedb79 feat(maria): ops/setup-drive.sh — configurare Drive dintr-un token, intr-un pas
Pasii 2-4 din README (creare remote, verificare acces, scriere DRIVE_REMOTE,
prima sincronizare) erau de facut de mana, cu ID-ul dosarului copiat corect de
fiecare data. Scriptul ii face pe toti dintr-un token `rclone authorize`:
valideaza ca tokenul e JSON, refuza devreme daca rclone sau env-ul lipsesc,
face backup la env inainte de a-l rescrie, si e idempotent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 18:16:07 +00:00
Claude Agent
1c7899753a feat(maria): accepta .xml in depozit, preferat peste .md, cu chunking pe probleme
Dosarul document_store din Drive are 3 surse .xml pe care depozitul le ignora
complet, fiindca store.py accepta doar .txt/.md. La d406_saft_knowledge exista
ambele formate, iar .xml e cu trei luni mai nou (2026-01-28 vs 2025-10-15) si cu
50% mai mare (64 KB vs 41 KB) — deci indexam varianta mai saraca.

- store.py devine sursa unica pentru extensii (DOC_EXTENSIONS = .txt/.md/.xml).
  Cand acelasi nume de baza exista in mai multe formate, la indexare intra unul
  singur, cel mai bogat (.xml > .md > .txt); celalalt ramane pe disc, marcat
  `shadowed_by`. Fara asta, acelasi raspuns ar aparea de doua ori in rezultate.
  `list_documents()` arata tot (dashboard), `documents_for_index()` doar
  castigatorii (indexer).
- indexer.py taie XML-ul altfel: un chunk per element de nivel 1, adica o
  problema = un chunk, cu <mesaj_eroare> si <rezolvare> impreuna. Taierea pe
  linii goale le-ar separa si cautarea ar returna eroarea fara raspuns.
  Etichetele raman prefixe lizibile ("mesaj eroare: ..."), fara paranteze
  unghiulare care doar dilueaza embedding-ul. XML invalid nu opreste indexarea:
  cade pe taierea obisnuita, cu o linie in log. Elementele peste 4000 de
  caractere se taie mai departe pe granite de cuvant — `chunk_text` imparte doar
  pe linii goale, deci un element scris ca un paragraf lung ar fi ramas intreg
  (prins de test).
- sync.py: amprenta si `rclone --include` derivate din DOC_EXTENSIONS.
- dashboard: acelasi filtru si aceeasi preferinta (copie, fiindca nu poate
  importa `store` — coliziune de nume pe `config`), plus marcajul "umbrit de X"
  in tabelul de documente si numarul de documente chiar indexate.
- README: sectiunea Drive rescrisa pe `rclone authorize` (autorizezi pe o masina
  cu browser, muti tokenul) in loc de cont de serviciu — mai putini pasi, fara
  consola Google Cloud. Documentat si ca `sync` sterge local ce nu mai e in Drive.

tests/ nou (20 de teste, fara retea si fara Ollama): preferinta de format,
vizibilitatea in dashboard, taierea XML, entitati, comentarii, XML invalid,
elemente uriase. Suita puntii Discord: 426 pass, neafectata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 18:15:03 +00:00
Claude Agent
c42b95b7db feat(discord-bridge): imagini si fisiere text din Discord ajung la Claude
Puntea trimitea doar `content`-ul mesajului; orice atasament disparea tacut, iar
un mesaj fara text (doar poza) era respins ca "empty". Acum mesajul de utilizator
se construieste ca blocuri, in formatul pe care CLI-ul il accepta pe
`--input-format stream-json` (verificat pe CLI real: Claude descrie corect o
imagine trimisa asa).

- imagini png/jpeg/gif/webp -> blocuri `image` base64, max 4, max 3,5 MB brut
  (base64 umfla cu ~4/3, iar API-ul refuza peste ~5 MB codate)
- fisiere text (mime `text/*`, `application/json`, sau extensie cunoscuta) ->
  continutul intra in prompt, max 4, trunchiat la 100 KB
- restul (PDF, Office, arhive, svg, heic) -> doar numite, cu motivul

Detalii care conteaza:
- `image/jpg` si `image/png; charset=...` se normalizeaza; cand Discord nu
  trimite content_type cadem pe extensie
- marimea se verifica de doua ori: cea declarata (ca sa nu descarcam degeaba) si
  cea reala dupa descarcare
- nimic nu dispare tacut: ce n-a putut fi citit apare in prompt ca
  "Atasamente ignorate: ..."; o imagine stricata nu anuleaza restul mesajului
- merge si mid-tur: o poza trimisa in timpul unui tur intra pe stdin ca steering

31 de teste noi in tests/test_attachments.py (normalizare tipuri, limite,
trunchiere, erori de descarcare, integrare prin punte, steering). Suita: 426 pass.
README: sectiune "Atasamente" + limitarea veche corectata (ramane doar vocea).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 17:47:48 +00:00
Claude Agent
c5af5f2380 feat(maria): interzice explicit discutiile despre infrastructura interna
Maria raspunde clientilor ERP ROA. Depozitul ei de documente contine doar
material orientat spre client final, dar adaugam si o bariera in SYSTEM_PROMPT:
refuza intrebarile despre servere, Proxmox, containere, IP-uri, baze de date,
parole sau chei, chiar daca ceva de acest fel ajunge in context.

Regula completa (ce are voie sa intre in ~/.maria-bridge/documents/) e scrisa
in /workspace/claude-agent/CLAUDE.md, sectiunea "Maria (RAG) - GRANITA
OBLIGATORIE".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 17:38:51 +00:00
Claude Agent
4993148597 docs(maria): record local Ollama install and rclone setup progress
Ollama (+ nomic-embed-text) is now installed locally on LXC 171 for
embeddings — previously OLLAMA_URL was an unverified placeholder pointing
at nothing. rclone is installed too, but DRIVE_REMOTE still needs a Google
service account the user must create in GCP Console; noted the
document_store folder ID found via Drive search to save that step later.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 17:38:51 +00:00
Claude Agent
18977cd029 Consolidate Maria control into the shared Discord-bridge dashboard
Drop the standalone maria-dashboard.service — one common panel now
controls both bridges, at the operator's request. discord-bridge's
dashboard/api.py gains a Maria section (fixed-unit start/stop/restart
for maria-whatsapp/maria-rag, WhatsApp status+QR proxy, document CRUD,
reindex, Drive sync trigger, log tail) reached via subprocess/HTTP —
no cross-module imports, to avoid colliding with discord-bridge's own
`config` module name. index.html gets a matching "Maria — WhatsApp +
RAG" section. maria-whatsapp-bridge loses its dashboard/ folder and
DASHBOARD_* env keys; install.sh/README point at the shared panel.

Co-Authored-By: Claude Agent <noreply@anthropic.com>
2026-08-31 17:38:51 +00:00