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
This commit is contained in:
Claude Agent
2026-09-01 06:16:21 +00:00
parent 5342875aa2
commit 4d6ff1940f
9 changed files with 475 additions and 82 deletions

View File

@@ -28,6 +28,12 @@ const MEDIA_DIR = process.env.MARIA_BRIDGE_DIR
: path.join(__dirname, 'media');
const MAX_RECONNECT_ATTEMPTS = 5;
const TEST_MODE_SELF_CHAT_ONLY = (process.env.TEST_MODE_SELF_CHAT_ONLY || 'true') !== 'false';
// Grupuri in care Maria are voie sa raspunda chiar si in modul self-chat. Testarea
// intr-un grup dedicat scoate zgomotul din chatul „Eu" — dar numai grupurile de
// aici trec, ca sa nu inceapa Maria sa vorbeasca in cele 197 de grupuri ale contului.
const ALLOWED_GROUP_JIDS = new Set(
(process.env.ALLOWED_GROUP_JIDS || '').split(/[,;]/).map((s) => s.trim()).filter(Boolean),
);
// WhatsApp invalideaza codul de asociere in cateva minute. Il tinem afisat doar
// atat: un cod expirat afisat in dashboard trimite omul sa tasteze degeaba.
const PAIRING_CODE_TTL_MS = 3 * 60 * 1000;
@@ -230,7 +236,8 @@ async function startConnection() {
|| (ownLid && msg.key.remoteJid === ownLid);
// Mesajele aruncate se logheaza. Fara asta, un JID care nu se potriveste cu
// ownJid (ex. formatul @lid) face bridge-ul sa taca fara nicio urma in log.
if (TEST_MODE_SELF_CHAT_ONLY && !isSelfChat) {
const isAllowedGroup = isGroup && ALLOWED_GROUP_JIDS.has(msg.key.remoteJid);
if (TEST_MODE_SELF_CHAT_ONLY && !isSelfChat && !isAllowedGroup) {
console.log(`[whatsapp] ignorat (nu e self-chat): remoteJid=${msg.key.remoteJid} ownJid=${ownJid} ownLid=${ownLid}`);
continue;
}
@@ -292,6 +299,7 @@ app.get('/status', (_req, res) => {
connected,
phone: phoneNumber,
selfChatOnly: TEST_MODE_SELF_CHAT_ONLY,
allowedGroups: [...ALLOWED_GROUP_JIDS],
lid: ownLid,
qr: connected ? null : currentQR,
pairing: connected || !p ? null : {