Commit Graph

37 Commits

Author SHA1 Message Date
747afbaf9d feat(steering): mesaje mid-tur + /stop pe turul în zbor
Un al doilea mesaj trimis cât Claude încă lucra aștepta până se termina
turul 1 — corecția „stai, nu în master" ajungea după ce greșeala era gata.
Verificat în producție înainte de commit: mesajul 2 stătea 25s blocat în
lock, apoi pornea ca tur separat.

Acum canalele de chat pot ține un proces `claude` viu per canal, cu stdin
deschis, și al doilea mesaj intră în ACELAȘI tur.

- `src/claude_runner.py` — ClaudeProcess (steering, respawn cu --resume,
  drenare stderr, respawn la comutarea OpenRouter) + RunnerRegistry
  (max_live, reaper pe inactivitate, stop_all la shutdown)
- `src/stream_json.py` — parser stream-json partajat cu `_run_claude`;
  pur, nu aruncă niciodată pe is_error (PlanningSession retrimite pe
  error_max_turns și depinde de asta)
- `src/sentinels.py` — un singur loc pentru __AUDIO__/__STEERED__, în loc
  de 4 verificări copiate; repară și bug-ul preexistent prin care
  WhatsApp posta literal `__AUDIO__:/cale`
- dispecer în `send_message`: lock.acquire(blocking=False) — eșecul de a
  lua lock-ul ESTE „rulează un tur", ceea ce elimină flagul inflight din
  decizie și cursa TOCTOU odată cu el
- `/stop` oprește turul, nu sesiunea — active.json rămâne valid
- rate limit prin proces persistent vine ca result.is_error, nu ca exit
  code; convertit înapoi în același RuntimeError, altfel fallback-ul
  local nu s-ar mai declanșa niciodată, în tăcere

Steering-ul nu face niciodată cross-adapter (un mesaj text nu intră
într-un tur voice: împart același channel_id). Mesajele steered dintr-un
tur care pică sunt re-livrate, nu pierdute.

Testat live cu CLI-ul real: corecție la secunda 10 dintr-un tur de 24s,
un singur result, num_turns=2. Notă: mesajele steered sunt împachetate în
[EXTERNAL CONTENT], deci o corecție formulată ca override agresiv poate
fi refuzată ca prompt injection — pentru oprire folosește /stop.

Suită: 1199 passed, 12 failed (toate pre-existente pe HEAD curat).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SiJGsZVSEGjRHZEJiXaxCC
2026-09-02 11:05:58 +00:00
a1d39637c8 feat(fallback): unelte, internet, retea si conversatie pe modelul local
Fallback-ul local (Qwen3.5-2B, LXC 104) raspundea ca nu are unelte sau
internet. Acum conversează, are unelte read-only si istoric per canal.

Unelte (allowlist strict read-only, src/local_fallback_tools.py):
- cauta_web — DuckDuckGo Lite, fara API key
- masini — status Proxmox/LXC prin SSH paralel (src/net_status.py)
- vremea, sold, facturi, trezorerie, doctor, logs, email, kb,
  cauta_memorie, citeste_pagina

Protocol: function calling OpenAI nativ. Protocolul text anterior
("TOOL: nume arg") pierdea argumentul in 100% din cazuri.

Uneltele cu output deja formatat se intorc verbatim — modelul de 2B
transforma un `doctor` cu 5 linii OK in "Sistemul este in 5/5 state.".

Doua treceri: prima decide uneltele (cu tools, temp 0), a doua reface
turnul fara ele — prezenta definitiilor strica raspunsurile simple
("cat fac 128/4?" -> "Nu stiu ce inseamna 128/4"). Few-shot doar pe
cereri creative: pe turnuri factuale strica aritmetica (17*23 -> 471).

Ocoliri deterministe pentru ce modelul greseste reproductibil: vremea si
preturile live (raspundea din memorie), plus sarcini "instructiune: text"
(payload-ul dicta unealta — "scrie mai politicos: da-mi raportul acum"
chema `sold`). llama.cpp nu e determinist nici la temperatura 0 si
ignora tool_choice, deci promptul singur nu ajunge.

Masurat: 36/36 pe set combinat (17 alegere unealta + 19 comprehensiune),
de la 14/19 pe comprehensiune inainte de regulile negative din prompt.

Comenzi: /f (inlocuieste /testfallback, ramas alias), /f reset, /masini.
Prefixul "Claude e la limita" apare doar pe calea de rate limit.

roa2web: /otp <cod> pentru 2FA din chat — mesajul de eroare trimitea la
verify_2fa(code, email), imposibil de rulat din Discord.

Corectat memory/kb/tools/infrastructure.md: LXC 101 si 110 sunt pe pve1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B88u3KkzvUNnzU56y7GBn2
2026-08-23 07:46:46 +00:00
fe500a7227 chore: auto-commit from dashboard 2026-08-22 08:40:56 +00:00
c1414616ad feat: US-010 - Extinde /voice doctor cu health-check pocket-tts
- adaugă ping GET /health la pocket-tts (URL din config tts.pockettts_url)
- adaugă verificare prezență hf_token în keyring
- checks-urile existente (libopus, voice load error) rămân neschimbate

gates rulate: tests PASS (1043 passed, 22 preexistente neschimbate), /review (backend) manual PASS
2026-07-11 11:16:06 +00:00
504e12ac4e feat: US-009 - Autocomplete voci din catalog pentru /audio și /voice setvoice
- tools/tts.py: +list_voice_names(engine=None), sursă live din tts_voices.json
- src/fast_commands.py: _VOICES statice -> _supertonic_voice_names() din catalog
- discord_bot.py /audio și discord_voice.py /voice setvoice: choices statice -> autocomplete live
- gates rulate: tests PASS (1043 passed, 22 eșecuri preexistente neschimbate), ui/backend review PASS manual
2026-07-11 10:44:04 +00:00
3d5c2a4ace feat: US-008 - Comandă Discord /voice addvoice
- /voice addvoice <nume> <sample:Attachment>: validează wav+durată min 3s,
  oprește pocket-tts.service, exportă vocea prin pocket_tts_add_voice.py
  în venv separat, repornește serviciul, răspunde cu numele final + preview audio
- gates rulate: tests PASS (1043 passed, 22 preexistente neschimbate), review backend manual PASS
2026-07-11 10:38:15 +00:00
c7db236247 feat: US-007 - Comandă Discord /voice engine
- Comandă nouă /voice engine <pockettts|supertonic> setează tts.default_engine în config, persistă via Config().set+save()
- Helper _default_voice_for_engine() confirmă vocea implicită a engine-ului ales (alba pentru pockettts, voice.default_voice pentru supertonic)
- Gates: pytest PASS (22 eșecuri preexistente neschimbate), review manual PASS, wiring comandă verificat izolat (fără restart pe bot-ul live)
2026-07-11 10:31:38 +00:00
a8d024944d chore: auto-commit from dashboard 2026-06-09 09:13:35 +00:00
735b282179 automatic 2026-05-29 13:35:15 +00:00
0ce8a5a04d Update cron, dashboard, root +3 more (+1 ~11) 2026-05-28 20:21:28 +00:00
e79bed7afe feat(voice): unify Discord voice↔text session (squash of voice/text-unify)
Voice utterances and text messages on the same Discord channel now share
one Claude session, and Echo's voice replies are mirrored back into the
text channel. Replaces the old voice:<id> session-key split.

Changes:
- src/adapters/_text_chunks.py: new leaf module for split_message
  (used by both discord_bot and voice pipeline)
- src/router.py: drop voice: prefix from session_key; add [voice] marker;
  strip leading [speaker:/[voice] tokens from user input (anti-jailbreak);
  remove dead double-clear of voice: key
- src/claude_session.py: include personality/VOICE_MODE.md unconditionally
  (rules become per-turn-aware via [speaker:] prefix instead of session flag)
- src/voice/pipeline.py: VoiceSession splits text_channel_id +
  voice_channel_id; resolve text channel per-send (no stale refs); mirror
  Echo's reply text into the text channel after route_message returns
- src/adapters/discord_voice.py: /voice join passes both channel ids
- src/adapters/discord_bot.py: import split_message from leaf module
- personality/VOICE_MODE.md: rewrite as per-turn dynamic rules;
  add synthesis instructions for text turns after voice turns

Tests:
- tests/test_router.py: 4 new cases (plain channel_id, anti-jailbreak,
  text-adapter regression, no-double-clear)
- tests/test_pipeline_mirror.py: new — Echo reply mirror chunking,
  empty guard, mirror_enabled=False, send-raises resilience
- tests/test_voice_session_channel_ids.py: new — split-attr contract
  + metrics payload schema
- tests/test_voice_session_cleanup.py: update for new kwargs

Plan: /home/moltbot/.claude/plans/vreau-ca-tot-textul-greedy-rivest.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 14:24:15 +00:00
4be70440e8 feat(voice): DAVE E2E + full voice UX (squash of voice/dave-recv)
Squashed branch: voice/dave-recv → master. Closes Pas 12 (DAVE E2E) and lands
voice-mode UX polish + verbal voice control on top of the Pas 1-10 scaffolding
already on master.

## DAVE E2E receive-side decrypt (e4f3177)

Vendored fork: discord-ext-voice-recv 0.5.3a+echo.dave1. Patches the receive
pipeline to handle Discord's mandatory DAVE encryption on voice gateway v=8.
- `_maybe_dave_decrypt`: uses davey.can_passthrough(user_id) as primary gate,
  falls through to dave.decrypt for DAVE-epoch peers, drops on decrypt failure
  without killing the reader thread.
- VAD fix: silero-vad v5+ requires exactly 512 samples; our 100ms window
  (1600 samples) was silently raising ValueError → STT never fired. Now slice
  into 512-sample chunks.
- Whisper: bumped beam_size 1→5 and added RO initial_prompt.
- Tests: 11 DAVE unit tests + 2 callback integration tests + contract test
  with fork-version guard.

## Voice UX polish (d1bc77e)

- Killed the 3s "mă gândesc" filler (always collided with Claude p50 4-7s).
- Barge-in via `ttsq.clear()` at top of `on_segment_done`.
- DTX silence-flush poller (200ms tick) — Discord stops sending RTP packets
  when silent, so the inline silence-check in sink.write() never fired for
  trailing audio; background thread handles it.
- `EchoStreamingAudioSource.read()` non-blocking — old `get_frame(timeout=0.1)`
  wrecked Discord's 20ms cadence and the client interpreted bursts as
  stuttering (Marius heard "4 de minute" instead of full sentence).
- RO time expansion: 23:09 → "douăzeci și trei și nouă minute".
- Supertonic Unicode sanitize centralized in tools/tts.py.
- Whisper local_files_only=True — no HF metadata GET on each startup.
- Diagnostic logging through sink → VAD → Claude stream → TTS chain.

## Voice mode iteration (e589e48)

- `personality/VOICE_MODE.md` — voice-tailored system prompt (short, no
  markdown, no abbreviations, time without seconds, distances in
  "mii"/"milioane"); plumbed via build_system_prompt(voice_mode=True).
- Isolated voice session key `voice:<channel_id>` — voice doesn't share
  context with text adapter on the same channel; auto-applied without
  /clear ceremony. /clear drops both keys.
- Metric units + Romanian thousands (normalize.py): "384.000 km" →
  "trei sute optzeci și patru de mii de kilometri" with feminine-correct
  pluralization and "de" particle for ≥20.
- `/voice setvoice <M1-F5>` slash command with native autocomplete; swaps
  live + persists voice.default_voice to config.json.
- Verbal voice change (src/voice/voice_commands.py + 29 tests) — "schimbă
  vocea pe M5", "voce em cinci", with permissive substring fallback for
  Whisper-mangled forms like "Mâcinci"=M5 and "unul cinci"=M5. Whisper
  initial_prompt now lists voice vocabulary to bias STT toward clean
  outputs.
- Fast barge-in: VAD ≥2 consecutive windows (~200ms) on Marius's user
  while Echo has pending TTS frames → cut him off mid-sentence so user
  doesn't wait the full silence + STT cycle. Acoustic echo bleed-through
  still requires headphones (no AEC).

## Test suite

130 voice + router tests pass (test_voice_recv_dave, test_voice_session_cleanup,
test_voice_adapter_contract, test_voice_normalize, test_voice_commands,
test_router).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 21:00:27 +00:00
13931db953 feat(voice): Pas 7 — discord_voice.py slash group + discord_bot wiring (CONVERGENCE)
src/adapters/discord_voice.py (NEW, ~280 linii):
- /voice slash group cu subcommands: join, leave, doctor, mirror on|off,
  record on|off
- warmup_models() async — eager faster-whisper + silero-vad load la
  on_ready pe background task
- _voice_load_error guard — /voice join responds ephemeral graceful
  dacă models load fail
- _voice_sessions: dict[int, VoiceSession] keyed pe guild_id
- _get_whitelist() re-reads config la fiecare apel — runtime edits la
  voice.allowed_user_ids fără bot restart
- Double-join guard, try/except graceful pe connect/listen/play/presence
- /voice doctor surfaces _voice_load_error + libopus state ephemeral
- await interaction.response.defer(ephemeral=True) în orice voice
  command (Discord 3s timeout pattern din CLAUDE.md)

src/adapters/discord_bot.py — 3 surgical edits:
- Linia 115: intents.voice_states = True (după intents.message_content)
- Liniile 963-966: import + register_voice(tree, client) +
  tree.add_command(voice_group), după /audio body
- Liniile 1126-1130: discord_voice._models_warmup_future =
  asyncio.create_task(discord_voice.warmup_models()) la end of on_ready

Adapted la pipeline.py API actual (channel_id int nu str, kw-only args
după *, EchoVoiceSink(session, bot_user_id) signature, loop kwarg
mandatory pentru cross-thread bot.change_presence).

Smoke import OK. test_discord.py 61 pass / 4 fail (pre-existing pe
master, verificat via git stash). test_voice_session_cleanup 5/5 +
test_voice_adapter_contract 22/22.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:55:57 +00:00
44cf0001bb chore: auto-commit from dashboard 2026-05-27 06:12:13 +00:00
574f9be5ea feat(discord): add /audio slash command with voce + text_sau_url params
Adds missing /audio slash command on Discord with:
- voce: optional choices M1-M5 / F1-F5 with descriptions
- text_sau_url: optional text or URL input
- handles __AUDIO__: response by sending WAV as file attachment

Telegram already had /audio fully implemented.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 06:00:54 +00:00
3dd2ddbd6a chore: auto-commit from dashboard 2026-05-27 05:40:22 +00:00
dedeedf024 fix(ralph): "Planifică" deschide modal/ForceReply când descrierea lipsește
Înainte: click pe 🧠 Planifică (Discord/Telegram) sau /plan <slug> fără descriere
pe un proiect din workspace fără entry în approved-tasks.json → mesaj eroare
"Adaugă mai întâi cu /p <slug> <descriere>" și user-ul trebuia să facă două
operații.

Acum:
- Discord button "Planifică" cu descriere goală → deschide RalphPlanModal cu
  TextInput pentru descriere; on_submit pornește direct start_planning_session
- Discord /plan <slug> fără description param și fără entry → același modal
  (response.send_modal ÎNAINTE de defer — Discord constraint)
- Telegram callback "Planifică" cu descriere goală → set state
  STEP_INPUT_DESCRIPTION_THEN_PLAN + ForceReply; handle_message detectează
  step și pornește planning cu textul user-ului
- ralph_flow.py: nou STEP_INPUT_DESCRIPTION_THEN_PLAN (alături de cel existent
  pentru propose-only)

start_planning_session deja auto-creează entry în approved-tasks.json dacă
proiectul lipsește, deci flow-ul e end-to-end: workspace → click → descriere
→ planning agent activ.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:51:09 +00:00
51e56af557 feat(ralph): conversational planning agent (W2)
Echo Core devine planning agent: poartă o conversație multi-fază cu Marius
folosind skill-urile gstack (/office-hours → /plan-ceo-review →
/plan-eng-review → /plan-design-review opt) și produce final-plan.md în
~/workspace/<slug>/scripts/ralph/, gata să fie consumat de Ralph PRD
generator (W3) noaptea.

Decizii arhitecturale (din eng review + spike findings):
- PlanningSession ca clasă SEPARATĂ de chat-ul main (NU mode=string param)
  — separation explicit. claude_session.py rămâne strict pentru chat;
  planning trăiește în src/planning_session.py + src/planning_orchestrator.py.
  Inheritance literală nu se aplică (claude_session.py expune funcții
  module-level, nu o clasă) — separation e satisfacută prin module distinct.
- Fresh subprocess PER skill phase, NU single resumed session — phase-urile
  coordinează via disk artifacts (gstack convention în
  ~/.gstack/projects/<slug>/). Avoids context window growth.
- --max-turns 20 default + retry pe error_max_turns la --max-turns 30.
  Spike a arătat că prompt-uri complexe pot exploda turn budget-ul.
- approved-tasks.json schema extins cu planning_session_id + final_plan_path
  (Status flow: pending → planning → approved → running → complete).
- State separat în sessions/planning.json (NU active.json), keyed pe
  (adapter, channel_id) pentru re-resume la restart echo-core.

Trigger-e:
- Discord: slash command /plan <slug> [descriere] cu autocomplete pe pending,
  buton "🧠 Planifică" în RalphProjectView, și /cancel slash command.
- Telegram: /plan + /cancel commands, plus buton "🧠 Planifică" în
  ralph project keyboard.
- Router: state-aware routing — dacă chat-ul e în planning, mesajele plain
  trec la PlanningOrchestrator.respond() prin --resume; /cancel revine la
  status pending; /advance / "Continuă faza" advance fază nouă (fresh
  subprocess); /finalize sau "Dau drumul" promote la status approved.

Discord defer pattern: toate butoanele noi (PlanningActiveView,
PlanningFinalView, "🧠 Planifică") apelează await
interaction.response.defer(ephemeral=True) ÎNAINTE de orice IO — evită
"Interaction failed" pe IO >3s.

UX strings warm + colaborativ (per design review): "🧠 Pornesc planning
pentru ...", "Răspunde aici", "Continuă faza", "Dau drumul tonight",
"Anulează" — niciun "Submit/Approve/Cancel" generic.

Tests: 23 noi (test_planning_session, test_planning_orchestrator,
test_router_planning) — toate pass. Mock pe _run_claude pentru a evita
subprocess Claude real în CI.

Files new:
  prompts/planning_agent.md
  src/planning_session.py
  src/planning_orchestrator.py
  tests/test_planning_session.py
  tests/test_planning_orchestrator.py
  tests/test_router_planning.py

Files modified:
  src/claude_session.py        — _run_claude(cwd=...) optional + surface subtype/is_error
  src/router.py                — state-aware routing, start_planning_session, planning_advance/approve/cancel, _ralph_propose schema cu planning_session_id + final_plan_path
  src/adapters/discord_bot.py  — /plan + /cancel slash commands; planning views imported
  src/adapters/discord_views.py — PlanningActiveView, PlanningFinalView, "Planifică" button în RalphProjectView, _split_chunks helper
  src/adapters/telegram_bot.py — /plan + /cancel handlers, callback_ralph extins cu plan/planadvance/plancancel/planapprove, planning keyboards

Status testelor pe modulele atinse: 75 passed, 0 failed
(test_claude_session security_section preexistent — neatins).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:38:51 +00:00
86384b38e3 feat(ralph): interactive UX layer pe Discord și Telegram (W1)
Adaugă straturile interactive peste slash commands flat:

**Discord (`src/adapters/discord_views.py`):**
- `RalphRootView` — listă proiecte workspace cu emoji status + Refresh + Close
- `RalphProjectView` — Propose / Vezi PRD / Aprobă tonight / Status / Stop / Înapoi
- `RalphProposeModal` — TextInput pentru descriere feature
- Critical pattern: `await interaction.response.defer(ephemeral=True)` în orice button
  callback cu I/O (eng review concern #2 — "Discord 3s timeout")
- `/p slug` autocomplete din `~/workspace/`
- `/l` afișează `RalphRootView` ephemeral

**Telegram (`src/adapters/telegram_bot.py`):**
- `cmd_ralph_l` (fără arg) trimite `InlineKeyboardMarkup` cu workspace + active
- `callback_ralph` cu pattern `^ralph:` rutează: project, menu, refresh, close,
  propose, prd, status, approve, stop
- Pentru "Propose feature" → set ralph_flow state cu step=input_description
  + `ForceReply()`; `handle_message` detectează state și rutează la `_ralph_propose`
- Pasează `adapter_name="telegram"` la `route_message`

**State management (`src/ralph_flow.py`):**
- Atomic JSON peste `sessions/ralph_flow.json` (pattern reusat din claude_session)
- Schema per (adapter, chat, user): `{step, project?, expires_at, ...}`
- TTL 10 min default; `cleanup_expired()` și auto-drop la `get_state` pe expirate

**Router (`src/router.py`):**
- `route_message` primește `adapter_name` keyword arg
- `_maybe_whatsapp_redirect` adaugă "💡 Pentru meniu interactiv folosește
  Discord sau Telegram" la mesajele de usage când adapter_name="whatsapp"
- WhatsApp `_handle_chat` pasează `adapter_name="whatsapp"`

**Tests:**
- `test_ralph_flow.py` — 10 teste (round-trip, isolation, expiry, atomic write)
- `test_router.py::TestRalphDispatch` — 3 teste (whatsapp redirect, discord
  no-redirect, usage message)

Foundation pentru W2 (planning agent — STEP_IN_PLANNING reservat).

Spike Step 0 PASS: skill subprocess + AskUserQuestion→text serialization
confirmat empiric (vezi tasks/spike-planning-findings.md).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 18:14:24 +00:00
094c6be5a9 feat(ralph): unified slash commands /p /a /l /k cu legacy aliases
Restructurează comenzile Ralph într-un dispatcher unificat (_try_ralph_dispatch)
care suportă atât comenzile noi scurte (/p /a /l /k) cât și aliasurile legacy
(!propose !approve !status !stop). Pe Discord adaugă slash commands native cu
autocomplete dinamic pentru pending (/a) și running (/k). Pe Telegram apar în
meniul /. WhatsApp le parsează ca text plain.

Activează cron jobs morning-report (08:30) și evening-report (21:00) și adaugă
night-execute (23:00) pentru execuția autonomă a proiectelor aprobate.

Foundation pentru W1 din planul "Echo Core conversational planning agent".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 17:46:52 +00:00
9314d63aa0 feat(email): add digest and forward commands to WhatsApp
Digest summarizes unread emails via Claude CLI; forward sends raw
content (split to 4096 chars). Wired as /email digest and
/email forward slash commands, plus instant per-guild sync on ready.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 05:45:02 +00:00
5fafc29dc1 chore: auto-commit from dashboard 2026-04-02 19:43:01 +00:00
MoltBot Service
fd9d962ad2 chore: auto-commit from dashboard 2026-02-15 23:23:29 +00:00
MoltBot Service
5928077646 cleanup: remove clawd/openclaw references, fix permissions, add architecture docs
- Replace all ~/clawd and ~/.clawdbot paths with ~/echo-core equivalents
  in tools (git_commit, ralph_prd_generator, backup_config, lead-gen)
- Update personality files: TOOLS.md repo/paths, AGENTS.md security audit cmd
- Migrate HANDOFF.md architectural decisions to docs/architecture.md
- Tighten credentials/ dir to 700, add to .gitignore
- Add .claude/ and *.pid to .gitignore
- Various adapter, router, and session improvements from prior work

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 21:44:13 +00:00
MoltBot Service
624eb095f1 fix WhatsApp group chat support and self-message handling
Bridge: allow fromMe messages in groups, include participant field in
message queue, bind to 0.0.0.0 for network access, QR served as HTML.

Adapter: process registered group messages (route to Claude), extract
participant for user identification, fix unbound 'phone' variable.

Tested end-to-end: WhatsApp group chat with Claude working. 442 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:31:22 +00:00
MoltBot Service
80502b7931 stage-13: WhatsApp bridge with Baileys + Python adapter
Node.js bridge (bridge/whatsapp/): Baileys client with Express HTTP API
on localhost:8098 — QR code linking, message queue, reconnection logic.

Python adapter (src/adapters/whatsapp.py): polls bridge every 2s, routes
through router.py, separate whatsapp.owner/admins auth, security logging.

Integrated in main.py alongside Discord + Telegram via asyncio.gather.
CLI: echo whatsapp status/qr. 442 tests pass (32 new, zero failures).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:41:16 +00:00
MoltBot Service
2d8e56d44c stage-12: Telegram bot adapter
- New src/adapters/telegram_bot.py: full Telegram adapter with python-telegram-bot v22
  - Commands: /start, /help, /clear, /status, /model, /register
  - Inline keyboards for model selection
  - Message routing through existing router.py
  - Private chat: admin-only access
  - Group chat: responds to @mentions and replies to bot
  - Security logging for unauthorized access attempts
  - Message splitting for 4096 char limit
- Updated main.py: runs Discord + Telegram bots concurrently
  - Telegram is optional (gracefully skipped if no telegram_token)
- Updated requirements.txt: added python-telegram-bot>=21.0
- Updated config.json: added telegram_channels section
- Updated cli.py doctor: telegram token check (optional)
- 37 new tests (410 total, zero failures)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 20:55:04 +00:00
MoltBot Service
d1bb67abc1 stage-11: security hardening
- Prompt injection protection: external messages wrapped in [EXTERNAL CONTENT]
  markers, system prompt instructs Claude to never follow external instructions
- Invocation logging: all Claude CLI calls logged with channel, model, duration,
  token counts to echo-core.invoke logger
- Security logging: separate echo-core.security logger for unauthorized access
  attempts (DMs from non-admins, unauthorized admin/owner commands)
- Security log routed to logs/security.log in addition to main log
- Extended echo doctor: Claude CLI functional check, config.json secret scan,
  .gitignore completeness, file permissions, Ollama reachability, bot process
- Subprocess env stripping logged at debug level

373 tests pass (10 new security tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:01:31 +00:00
MoltBot Service
85c72e4b3d rename secrets.py to credential_store.py, enhance /status, add usage tracking
- Rename src/secrets.py → src/credential_store.py (avoid stdlib conflict)
- Enhanced /status command: uptime, tokens, cost, context window usage
- Session metadata now tracks input/output tokens, cost, duration
- _safe_env() changed from allowlist to blocklist approach
- Better Claude CLI error logging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 17:54:59 +00:00
MoltBot Service
0ecfa630eb stage-10: memory search with Ollama embeddings + SQLite
Semantic search over memory/*.md files using all-minilm embeddings.
Adds /search Discord command and `echo memory search/reindex` CLI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:49:57 +00:00
MoltBot Service
0bc4b8cb3e stage-9: heartbeat system with periodic checks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:40:39 +00:00
MoltBot Service
24a4d87f8c stage-8: cron scheduler with APScheduler
Scheduler class, cron/jobs.json, Discord /cron commands, CLI cron subcommand, job lifecycle management. 88 new tests (281 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 16:12:56 +00:00
MoltBot Service
09d3de003a stage-7: CLI tool with full subcommands
echo status/doctor/restart/logs/sessions/channel/send commands, symlink at ~/.local/bin/echo. QA fix: discord chat handler tuple unpacking bug. 32 new tests (193 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:29:39 +00:00
MoltBot Service
5bdceff732 stage-6: model selection and advanced commands
/model (show/change), /restart (owner), /logs, set_session_model API, model reset on /clear. 20 new tests (161 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:13:38 +00:00
MoltBot Service
a1a6ca9a3f stage-5: full discord-claude chat integration
Message router, typing indicator, emoji reactions, auto start/resume sessions, message splitting >2000 chars. 34 new tests (141 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:54:19 +00:00
MoltBot Service
6cd155b71e stage-4: discord bot minimal with channel/admin management
Discord.py bot with slash commands (/ping, /help, /setup, /channel, /admin), PID file, graceful shutdown. 30 new tests (119 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:42:28 +00:00
MoltBot Service
f2973aa76f stage-1: project bootstrap
Structure, config loader, personality/tools/memory from clawd, venv, 22 tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 10:20:55 +00:00