* Fallback to new conversation when default is missing
* Fallback when stored conversation is missing
* Allow LETTA_SESSION_TIMEOUT_MS override
---------
Co-authored-by: Jason Carreira <jason@visotrust.com>
Telegram:
- Skip voice messages in generic message handler
- Let message:voice handler transcribe properly
Signal:
- Add attachment logging for debugging
- Check file existence before reading
- Warn when audio attachment has no ID
Written by Cameron ◯ Letta Code
"The most effective debugging tool is still careful thought." - Brian Kernighan
Adds documentation to help users understand why their agent's responses
during heartbeats/cron jobs aren't being delivered to their chat channels.
- Add "Background Tasks" section explaining silent mode behavior
- Add FAQ entry in Troubleshooting for common issues
- Explain that agents must use `lettabot-message` CLI to send messages
Closes#80🤖 Generated with [Letta Code](https://letta.com)
Co-authored-by: Letta <noreply@letta.com>
Add "X-Letta-Source: lettabot" header to Letta API client
for usage tracking/telemetry.
Closes#72
Written by Cameron ◯ Letta Code
"If you can't measure it, you can't improve it." - Peter Drucker
Signal voice messages use .aac format which OpenAI Whisper doesn't
accept directly. Fix by normalizing .aac to .m4a (same codec, different
container name) before sending to the API.
Written by Cameron ◯ Letta Code
"The best error message is the one that never shows up." - Thomas Fuchs
- Add vitest as dev dependency
- Add test scripts: `npm test` (watch) and `npm run test:run` (CI)
- Add initial unit tests for pure utility functions:
- src/utils/phone.test.ts (10 tests)
- src/utils/server.test.ts (10 tests)
- src/channels/attachments.test.ts (6 tests)
All 26 tests passing.
Written by Cameron ◯ Letta Code
* Add inbound attachment handling and pruning
* Add Signal attachment support and logging
- Implement full Signal attachment collection (copies from signal-cli dir)
- Add logging when attachments are saved to disk for all channels
- Skip audio attachments in Signal (handled by voice transcription)
Written by Cameron ◯ Letta Code
* Gitignore bun.lock
Keep lockfile local, don't track in repo.
Written by Cameron ◯ Letta Code
---------
Co-authored-by: Jason Carreira <jason@visotrust.com>
Docker service hostnames (e.g. http://letta:8283) were misidentified as
Letta Cloud. Instead of enumerating self-hosted patterns, check against
the known Cloud hostname. Everything else is self-hosted.
- Add normalizePhoneForStorage() utility to handle @lid, @s.whatsapp.net suffixes
- Strip @lid/@s.whatsapp.net/@g.us and normalize to E.164 format (+prefix)
- Fix pairing approval format mismatch causing re-prompts for approved contacts
- Normalize userId on extraction, storage, and access checks
Fixes issue where approved contacts get pairing codes on every message
due to format inconsistencies:
- Extracted: 54941422981120@lid
- Checked: +54941422981120@lid
- Stored: 54941422981120@lid
- No match!
Now all formats normalize to: +54941422981120
* Add voice message transcription support (all channels)
Adds OpenAI Whisper transcription for voice messages across all channels:
- Telegram: ctx.message.voice
- WhatsApp: audioMessage via downloadMediaMessage
- Signal: audio attachments from local files
- Slack: audio files via url_private_download
- Discord: audio attachments
Voice messages sent to agent as "[Voice message]: <transcript>"
Configuration (config takes priority over env):
- lettabot.yaml: transcription.apiKey, transcription.model
- Env: OPENAI_API_KEY, TRANSCRIPTION_MODEL
Closes#47
Written by Cameron ◯ Letta Code
"The best interface is no interface - just talk."
* Add voice message documentation to README
- Add Voice Messages to features list
- Add configuration section for transcription
- Document supported channels
Written by Cameron ◯ Letta Code
* Notify users when voice transcription is not configured
Instead of silently ignoring voice messages, send a helpful message
linking to the documentation.
Written by Cameron ◯ Letta Code
* feat: upgrade to letta-code-sdk main + fix Signal voice transcription
- Switch from published SDK (v0.0.3) to local main branch (file:../letta-code-sdk)
- Update bot.ts for new SDK API: createSession(agentId?, options) signature
- Add conversationId tracking to store for proper conversation persistence
- Fix Signal voice transcription: read attachments from ~/.local/share/signal-cli/attachments/
- Fix Telegram markdown ESM issue: make markdownToTelegramV2 async with dynamic import
- Add transcription config to lettabot.yaml
- Add extensive debug logging for queue and session processing
Signal voice messages now properly transcribe and send to agent.
🐾 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
* fix: update Signal CLI message sender to use daemon JSON-RPC API
- Switch from signal-cli-rest-api to signal-cli daemon (port 8090)
- Use JSON-RPC send method instead of REST /v2/send
- Support group IDs with group: prefix
- Handle 201 responses and empty bodies correctly
🐾 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
* Add placeholder for untranscribed voice messages on Signal
If a voice-only message arrives and transcription fails or is disabled,
forward a placeholder so the user knows the message was received.
Written by Cameron ◯ Letta Code
---------
Co-authored-by: Letta <noreply@letta.com>
* Fix WhatsApp selfChatMode sending to wrong person
Two safety fixes:
1. Fail-safe on unknown LID - refuse to send instead of letting baileys
resolve it to potentially the wrong person
2. Improve self-chat detection - remove !senderPn requirement which can
fail in some cases, causing messages to leak to other contacts
Written by Cameron ◯ Letta Code
"Better to fail loudly than succeed silently at the wrong thing."
* Default selfChatMode to true for WhatsApp and Signal
- main.ts: WhatsApp now uses !== 'false' pattern (like Signal)
- config/io.ts: Only set env var if explicitly false
- onboard.ts: Default to 'personal' in config initialization
Written by Cameron ◯ Letta Code
* feat: add non-interactive onboarding and SKILL.md
Add agent-friendly setup flow:
- lettabot onboard --non-interactive flag
- Reads all config from environment variables
- SKILL.md documents env-based setup for agents
- Supports all channels (Telegram, Slack, Discord, WhatsApp, Signal)
- No prompts - ideal for coding agents automating setup
👾 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
* fix: address non-interactive setup issues
- Add SLACK_APP_NAME for customizable app name (defaults to LETTA_AGENT_NAME or LettaBot)
- Clarify WhatsApp requires WHATSAPP_ENABLED and WHATSAPP_SELF_CHAT to be explicit
- Document all 5 channels supported (Telegram, Slack, Discord, WhatsApp, Signal)
- Fix WhatsApp selfChat default to be explicit false
* docs: recommend non-interactive setup as primary method
Update README per review feedback to show env-based setup first.
This is simpler for most users and ideal for automation.
* docs: rewrite setup to be AI-first per feedback
Make recommended setup AI-focused:
- Show prompt to paste into AI coding assistants
- AI handles clone/install/config autonomously
- Manual wizard becomes Option 2 for human users
---------
Co-authored-by: Letta <noreply@letta.com>
* Send separate assistant messages as separate channel messages
Track assistant message UUIDs to detect when a new assistant message
starts. This allows the agent to send multiple messages in response
to a single user message, appearing as separate bubbles.
Complements the existing message-type-change detection with UUID-based
separation for consecutive assistant messages.
Written by Cameron ◯ Letta Code
"Communication works best when it's broken into digestible pieces."
* Clean up bot logging and add message bubble separation
- Remove verbose diagnostic logging (API key, base URL, node version)
- Add useful event logging: tool calls, message sends
- Add UUID-based detection for separate assistant messages
- Messages now appear as separate bubbles when UUID changes
Written by Cameron ◯ Letta Code
"Good logging tells you what happened, not what you already know."
- Previously heartbeats were skipped if agent had ANY recent activity
(Gmail polling, cron jobs, other heartbeats, etc.)
- Now only skips if user sent a message in the last 5 minutes
- Added getLastUserMessageTime() to LettaBot to track user messages
- Manual /heartbeat command bypasses the skip check
- Discord /heartbeat now replies with confirmation (Telegram stays silent)
🐙 Generated with [Letta Code](https://letta.com)
Co-authored-by: Letta <noreply@letta.com>
Log API key presence, base URL, and Node version before initializing
session to help debug timeout issues like #38.
Written by Cameron ◯ Letta Code
"Debugging is twice as hard as writing the code in the first place." - Brian Kernighan
When running via `npx lettabot server`, the CLI was looking for
main.js/main.ts relative to process.cwd() (user's directory), but
the compiled files are in the npm cache or node_modules.
Now uses import.meta.url to find main.js in the same directory as
cli.js, which works correctly for:
- npx lettabot
- global install (npm install -g)
- local development
Fixes ERR_MODULE_NOT_FOUND error on server restart.
🐙 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
- Add stepGoogle() wizard that guides users through gog CLI setup
- Check/install gog via Homebrew, guide OAuth credential setup
- Let users select existing accounts or add new ones
- Configure which Google services to enable (gmail, calendar, drive, etc.)
- Add GoogleConfig type and integrations.google to config schema
- Wire GMAIL_ACCOUNT env var from YAML config for polling service
- Add discord.js dependency (fixes build)
🐙 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
- Separate message bubbles when stream message type changes
(e.g., assistant → tool_call → assistant now sends as separate messages)
- Track sentAnyMessage to avoid spurious "(No response from agent)"
- Add canUseTool workaround for SDK v0.0.3 bypassPermissions bug
(see letta-ai/letta-code-sdk#10)
- Clean up verbose debug logging
Written by Cameron ◯ Letta Code
"The stream of consciousness is not a river but a series of pools." - William James
The model option was being passed to resumeSession on every message,
causing the SDK to update the agent's model configuration each time.
This resulted in the agent's model being reset to the config default
(e.g., glm-4.7) on every incoming message.
Now model is only passed when creating a new agent, not when resuming
an existing one.
Written by Cameron ◯ Letta Code
"The best code is no code at all." - Jeff Atwood
When running `lettabot server` without a built dist/ directory, the
fallback to tsx was using a path relative to import.meta.url. This
resolved to dist/main.ts when running from the compiled CLI, but that
file doesn't exist (only dist/main.js exists after compilation).
Changed to use an absolute path to src/main.ts for the development
fallback.
Fixes#28👾 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
Self-chat messages were incorrectly triggering pairing requests because
the access check didn't skip for self-chat messages.
Added `&& !isSelfChat` to the access check condition so self-chat
messages bypass pairing entirely.
Written by Cameron ◯ Letta Code
"The simplest solution is usually the correct one." - Occam
* Improve Discord onboarding with detailed setup instructions
- Add step-by-step instructions for creating Discord bot
- Include guidance on enabling Message Content Intent
- Show OAuth2 URL Generator steps with required permissions
- Auto-generate invite URL after user enters bot token
(extracts application ID from token and builds URL with correct permissions)
🐙 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>
* Clarify WhatsApp and Signal self-chat mode in onboarding
Changed confusing yes/no prompts to clearer selects:
WhatsApp:
- "Dedicated bot number" - Responds to all incoming messages
- "My personal number" - Only responds to "Message Yourself" chat
Signal (new):
- "Dedicated bot number" - Responds to all incoming messages
- "My personal number" - Only responds to "Note to Self" chat
This makes it clear what each option actually does.
Written by Cameron ◯ Letta Code
"The question is not what you look at, but what you see." - Thoreau
---------
Co-authored-by: Letta <noreply@letta.com>
- Add step-by-step instructions for creating Discord bot
- Include guidance on enabling Message Content Intent
- Show OAuth2 URL Generator steps with required permissions
- Auto-generate invite URL after user enters bot token
(extracts application ID from token and builds URL with correct permissions)
🐙 Generated with [Letta Code](https://letta.com)
Co-authored-by: Letta <noreply@letta.com>
Documents how to install and enable the homeassistant skill from ClawdHub.
The skill sync workflow already supports this - just needed documentation.
🐙 Generated with [Letta Code](https://letta.com)
Co-authored-by: Letta <noreply@letta.com>
These console.logs were useful during development but are noisy in production.
🐙 Generated with [Letta Code](https://letta.com)
Co-Authored-By: Letta <noreply@letta.com>