- Add RoomEncryptedImage callback with decrypt_attachment for E2E rooms
- Cache recent images per room (60s TTL) so follow-up text messages
like "was ist das" get the image context instead of hallucinating
- Treat filenames (containing dots) as no-caption, default to
"What's in this image?"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upload with encrypt=True and filesize param. Handle UploadError
gracefully. Use m.file encrypted format when encryption keys returned.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Register RoomMessageFile callback, filter for application/pdf
- Extract text from PDFs using pymupdf (fitz)
- Send extracted text as context to LLM for summarization/Q&A
- Truncate at 50k chars to avoid token limits
- Add pymupdf to requirements.txt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Register RoomMessageImage callback to handle incoming images
- Download and base64-encode images, send as multimodal content to LLM
- Add LLM tool calling with generate_image tool for natural image generation
- Upload generated images back to Matrix via m.image events
- Update system prompt to inform LLM about vision and image gen capabilities
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
!ai connect (no args) now starts a browser-based device authorization
flow instead of requiring a raw API key. Direct key input preserved
as fallback. Bot polls WildFiles for approval with 5s interval.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- !ai connect <key>: validates key against WildFiles, stores per-user mapping, redacts message
- !ai disconnect: removes stored key
- RAG searches use per-user API key when available, fall back to WILDFILES_ORG
- Keys stored in /data/user_keys.json (Docker volume)
Implements WF-90
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
_md_to_html now converts [text](url) to <a> tags and auto-links bare URLs.
Also instructs LLM to use markdown links instead of raw URLs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
format_context() was only passing metadata (title, date, link) to the LLM,
so the bot could not answer content questions. Now passes full OCR text.
Also removes auto-rename for DMs (Element reuses single DM room per user pair).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Matrix reuses a single DM room per user pair, so 'new' DMs jump
back to the old thread. Now the bot re-renames the room if >5min
has passed since the last rename, reflecting the new topic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Embedding generation on CPU (bge-m3) takes ~3s, plus network latency
can exceed 5s causing silent failures and empty results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DM rooms (1:1 with bot) now auto-rename after first response
without needing !ai auto-rename on
- Group rooms still require explicit opt-in
- Skip rename if room already has a meaningful name
- Improved prompt: emoji prefix, 3-5 words, same language as chat
(inspired by Open WebUI title generation)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a user asks "Wer ist Mieter in diesem Haus?" after discussing
a specific house, the raw message lacks context for RAG search.
Now uses a quick LLM call to resolve pronouns/references before
searching WildFiles (e.g. "diesem Haus" -> "Mieter Haus Coburg").
Also moved history fetch before RAG search so context is available.
Refs: WF-90
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
source_url is now a top-level field on DocumentChunk, not nested
in metadata. Fall back to metadata for backwards compatibility.
Refs: WF-90
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add !ai auto-rename on/off command to auto-name rooms based on conversation topic
- Persist auto-rename setting via room state event (ai.agiliton.auto_rename)
- Generate short title via LLM after first AI response, set as m.room.name
- Load persisted model and auto-rename settings lazily from room state
- Strengthen system prompt: prohibit asking about document storage, file locations
- Fix bot suggesting !ai commands and admin contact to users
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace room.timeline (non-existent in nio) with client.room_messages() API
- Add markdown-to-HTML conversion for formatted Matrix messages
- Route in-room verification events from both UnknownEvent and RoomMessageUnknown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Element withholds megolm keys from unverified devices. Implements
the full in-room m.key.verification.* protocol so Element Verify works.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extends bot.py with text message handling:
- RoomMessageText callback with @mention detection
- LLM responses via LiteLLM (OpenAI-compatible)
- WildFiles document search (DocumentRAG class)
- Per-room model selection via room state events
- Commands: !ai help/models/set-model/search
- Typing indicators during AI response generation
- 30s staleness check to avoid replaying history
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AgentServer in livekit-agents 1.4.x does not support --agent-name CLI
flag. The agent_name must be set on @server.rtc_session() decorator.
Also reverts docker-compose.yml command back to plain python agent.py start.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes for voice agent not responding to speech:
1. Agent name: add --agent-name matrix-ai to CLI (was empty, dispatch couldnt match)
2. Move dispatch from on_invite to on_unknown call handler (dispatch when call starts, not on room join)
3. Use LiveKit room name from foci_preferred instead of raw Matrix room ID
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Custom rtc.Room skipped ctx.connect(), leaving framework audio
input pipeline uninitialized. STT/VAD never received audio frames.
Switch to standard approach: ctx.connect() + ctx.room.
Added debug event logging for speech pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Agent disconnects custom room when all real participants leave
(prevents zombie participants blocking auto-dispatch)
- Bot sends m.call.member state event on call detection
(Element Call shows bot as joined)
- Use RoomInputOptions(participant_identity=...) to target real user
audio input (framework agent-AJ_xxx participant was confusing RoomIO)
- Removed incorrect bot dispatch (Matrix room ID != LiveKit room name)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Save user_id/device_id/access_token to crypto store on first login
- restore_login() on subsequent starts (no new device each restart)
- Enables proper Olm session persistence across restarts
CF-1147
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bot now trusts all room member devices on each sync, enabling
Megolm key exchange. Logs undecryptable events for debugging.
CF-1147
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- matrix-nio[e2e] with libolm for Megolm encryption
- Persistent crypto store volume for key persistence
- Auto-accept key verification (SAS)
- Upload device keys on first login
CF-1147
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- STT: elevenlabs.STT() (Scribe) instead of Whisper via LiteLLM
- TTS: elevenlabs.TTS() (already direct)
- LLM: still routed through LiteLLM/OpenRouter
- No extra API accounts needed — only ElevenLabs + OpenRouter
CF-1147
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- livekit-plugins-silero 1.4.x (not 0.25)
- livekit/livekit-api 1.x (not 0.x)
- Use livekit-plugins-elevenlabs directly instead of routing through LiteLLM
CF-1147
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>