Element X sends E2EE keys via encrypted to-device messages targeting
the device_id from the call.member state event. Bot was advertising
device_id='AIBOT' but its actual Matrix session is on device
'PEYRKFEXFP'. Keys were sent to a non-existent device.
Now uses the real device_id from nio credentials so Element X's
encryptAndSendToDevice reaches the correct device.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
git pull + docker compose restart bot now works without --build.
Patched FFI binary and proto bindings stay in the image; only
Python source files are mounted read-only.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Element X (26.03.3+) sends io.element.call.encryption_keys as
to-device messages, not room timeline events. Added
UnknownToDeviceEvent callback to catch these and deliver keys
to active voice sessions.
Also added m.room.encrypted decryption attempt in timeline scan
as fallback for older Element versions that send encrypted timeline
events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Element X embeds E2EE keys inside memberships[].encryption_keys,
not at the top level of the call.member state event content.
Bot was only checking content.encryption_keys, so it never found
the caller's key — causing 'Warten auf Medien' (waiting for media)
because encrypted audio couldn't be decrypted.
- Added _extract_enc_keys_from_content() helper handling both formats
- Updated on_unknown handler, VoiceSession creation, and key fetch
- Bot now publishes keys in both formats for compatibility
- Updated voice.py state fetch to check memberships[] fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Element Call v0.17+ embeds encryption_keys in call.member state events
instead of separate timeline events. In E2EE rooms, timeline events are
encrypted and the bot HTTP fetch cannot decrypt them, causing DEC_FAILED.
- Extract caller keys from call.member state event on join
- Embed bot key in call.member state event
- Check call.member state in key fetch (before timeline fallback)
- Handle key updates in call.member during active calls
- Update voice.py key poller to check call.member state first
- Add debug logging for UnknownEvent types in call rooms
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same pattern as files: download and cache in _recent_images without
responding. When user next @mentions the bot, the cached image is
available as context. Applied to both plain and encrypted image handlers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files uploaded to group rooms are now downloaded, parsed, and stored
in _room_document_context even without @mention. When the user later
mentions the bot, the document context is automatically included.
Previously files were silently dropped if the caption didn't contain
a mention, so the bot would say it can't access uploaded PDFs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bot user is @ai:agiliton.eu but display name is 'Claude'.
Element renders mentions using display name, so the old check
for 'ai' in message body never matched '@Claude: ...' messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add pitrader_script executor for running PITrader scripts (pi-scan,
playbook, execute_trades) as pipeline steps with vault credential
injection and JSON output capture.
Extend claude_prompt step with vision support (image_b64 in trigger
context). Add image pipeline trigger to on_image_message handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Route HTTP-fetched keys through on_encryption_key() for proper rotation detection
- Replace boolean refetch gate with 500ms timestamp throttle for faster recovery
- Reduce DEC_FAILED cooldown from 2s to 0.5s
- Extend proactive key poll from 3s to 10s window
- Add continuous background key poller (3s interval) during active calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Renders extracted items as markdown links with details instead of
raw JSON. Handles common patterns: list of dicts with title/link.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Track running job IDs to avoid creating duplicate Skyvern tasks
when the pending check runs faster than the task completes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cloud API uses 'prompt', self-hosted uses 'navigation_goal' and
'data_extraction_goal'. Pass them separately.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Skyvern container (public.ecr.aws) with dedicated PostgreSQL,
connected to LiteLLM proxy for LLM calls. Replace browser_executor
stub with full Skyvern API integration (create task, poll, extract).
Implement skyvern pipeline step for workflow chaining.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a video track is subscribed (screen share starts), Element Call
rotates the E2EE key. Instead of waiting for DEC_FAILED, proactively
poll the timeline for the new key (6x @ 500ms = 3s window).
Also reduce DEC_FAILED threshold from 3→1 and cooldown from 5s→2s
for faster recovery when the proactive poll misses the rotation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Element appends U+FE0F to emoji reactions (👍️ vs 👍). Strip before
matching against approval map.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>