- Replace telegram-markdown-v2 with telegramify-markdown (ESM compatible)
- Add raw text fallback when Telegram formatting fails, with error notice
- Improve empty response diagnostics: log agent ID, show conversation ID
- Add reset-conversation command hint to user messages
- Add telegram-format.test.ts with 7 tests
Fixes Railway deployment ERR_REQUIRE_ESM error with remark package.
Written by Cameron and Letta Code
"The best error message is the one that never shows up." - Thomas Fuchs
keyv is a transitive dependency of Baileys via @cacheable/utils,
but since Baileys is in optionalDependencies, some npm versions
don't properly install its transitive dependencies on fresh installs.
Adding keyv as a direct dependency ensures it's always installed.
Fixes ERR_MODULE_NOT_FOUND: Cannot find package 'keyv' on fresh installs.
Written by Cameron ◯ Letta Code
"The best time to fix a dependency bug is before someone reports it.
The second best time is immediately after." - Ancient npm proverb
Adds /status, /heartbeat, /help, and /start commands to:
- Signal (was missing all commands)
- Slack (was missing all commands)
- WhatsApp (was missing all commands)
- Discord (was missing /help and /start)
Telegram already had full support.
Changes:
- Create src/core/commands.ts with shared HELP_TEXT and parseCommand()
- Add onCommand property to Signal, Slack, WhatsApp adapters
- Add command detection before onMessage in each adapter
- /help and /start are handled locally, /status and /heartbeat
delegate to onCommand callback
Fixes#91
Written by Cameron ◯ Letta Code
"Consistency is the last refuge of the unimaginative." - Oscar Wilde
(But sometimes it's just good UX)
When a conversation has a stuck tool approval from a previous session,
the stream receives NO data at all (not even init). This leaves users
stuck with "(No response from agent)" and no clear path to recovery.
Changes:
- Track if stream received ANY data (not just assistant messages)
- If stream times out with zero data, assume stuck approval state
- Auto-reset conversation and notify user to try again
- Add message type counts to "no response" logs for debugging
- Fix 'system' -> 'init' type comparison (was causing TS error)
This addresses the issue reported by Signo on Discord where responses
were going to ADE instead of the channel due to stuck approvals.
Related issues: #125, #127, #132
Written by Cameron ◯ Letta Code
"When the stream runs dry, dig a new well." - Infrastructure proverb
When conversations become corrupted on Letta Cloud, users see empty
responses with no useful error message. This adds:
1. Warning message when empty result detected:
- Logs: "Agent returned empty result with no response"
- Suggests running `lettabot reset-conversation`
2. New CLI command `lettabot reset-conversation`:
- Clears the conversationId from lettabot-agent.json
- Preserves agent and memory
- Next message creates fresh conversation
Symptoms of corrupted conversation:
- stop_reason: "error" with empty result
- Messages not appearing in agent history
- duration_api_ms: 0 (no API call made)
Written by Cameron ◯ Letta Code
"When in doubt, start fresh." - Ancient debugging wisdom
- 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>
* 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>
- 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