LettaBot
LettaBot is a powerful CLI tool for building and managing conversational AI agents with persistent memory capabilities. It enables developers to create, configure, and deploy AI chatbots that can maintain context across conversations, remember user preferences, and learn from interactions over time. The tool features flexible agent configuration, memory management systems, and integration capabilities for various platforms and APIs. LettaBot streamlines the development workflow with commands for agent training, testing, and deployment, making it ideal for building customer service bots, personal assistants, and interactive AI applications. Its memory-first architecture ensures consistent, personalized user experiences while providing developers with granular control over agent behavior and knowledge retention.
Latest Releases
All releases- Extracted a
DisplayPipelineasync generator fromprocessMessage, separating stream semantics (run ID filtering, reasoning accumulation, result classification) from channel delivery logic. Net reduction of ~224 lines frombot.ts. - Fixed an infinite recovery loop when conversations get stuck with invalid tool call IDs -- the session now auto-clears and retries cleanly.
- Discord thread messages in non-thread-only mode now get their own conversations (
forcePerChat), fixing lost responses when multiple threads competed on a shared conversation. - Reasoning text no longer leaks into cron/heartbeat delivery responses.
Bug Fixes
- Session recovery: Auto-clear stuck conversations on invalid tool call ID mismatch instead of looping (#555).
- Reasoning leak: Skip reasoning messages in
sendToAgentresponse accumulation, preventing internal thinking from reaching users via cron/heartbeat (#557). - Reasoning formatting: Separate consecutive reasoning blocks with a newline when the new block starts with a markdown indicator (
**,##,-,1.), fixing concatenated sections from OpenAI models (#569). - Memfs diagnostics: Surface memfs errors in heartbeat with actionable diagnostics -- pre-flight
git statuscheck and improved error catch with fix commands (#559). - Discord thread routing: Set
threadIdandforcePerChatfor all thread messages (not just thread-only mode), propagated through message, reaction, and command handler paths (#564). - Discord command gating: Gate managed commands (
/reset,/status, etc.) behindonCommandavailability and harden attachment fetches against 403/404 (#549). - WhatsApp streaming: Warn when streaming is set in WhatsApp config since WA doesn't support progressive edits (#558).
Refactoring
- Extract
DisplayPipelinefromprocessMessagestream loop -- encapsulates run ID filtering, reasoning accumulation, stream_event skipping, result text selection, and stale/cancelled classification (#550). - Extract memfs directory resolution into a shared utility with server-mode awareness (#560).
Improvements
- Default group chat settings and voice transcription to 'no' in onboarding flow (#563).
- Telegram: simplify command responses and enrich
/statusoutput (#565). - Telegram: centralize
/helpoutput with shared command text (#566). - Bluesky: clarify notification filtering and reply behavior in docs (#551).
Tests
- Cover invalid tool-call mismatch recovery paths in session contract tests (#562).
- Add display pipeline unit tests for reasoning block separation (#569).
Full Changelog
https://github.com/letta-ai/lettabot/compare/v0.2.0-alpha.12...v0.2.0-alpha.13
- Added Bluesky channel support with runtime tooling and notification handling.
- Added an interactive config TUI editor.
- Added config-driven sleeptime with memfs-aware behavior.
- Improved Discord thread-only behavior and conversation isolation.
- Added robust default-conversation approval deadlock recovery without conversation reset.
wantedDidscontrols who we ingest.groupsmode controls auto-reply policy (open,listen,mention-only,disabled).handle+appPasswordcontrols whether posting is possible.
groups["*"]is only the default mode for DIDs without explicit overrides.- If
groups["*"]is omitted, default mode islisten. openmeans assistant responses are posted as Bluesky replies automatically.listenmeans read-only unless you post manually via CLI.
yaml
channels:
bluesky:
enabled: true
wantedDids: ["did:plc:..."]
handle: you.bsky.social
appPassword: xxxx-xxxx-xxxx-xxxx
groups:
"*": { mode: open }
`
Manual posting via CLI:
`bash
lettabot-bluesky post --text "Hello" --agent <name>
lettabot-bluesky post --reply-to at://did:plc:.../app.bsky.feed.post/... --text "Reply" --agent <name>
`
Optional notifications:
`yaml
channels:
bluesky:
notifications:
enabled: true
intervalSec: 60
reasons: ["mention", "reply", "quote"]
`
How To: Config TUI
Run:
`bash
lettabot config tui
`
Use it to configure server auth, channels, agent identity, and feature toggles interactively.
How To: Sleeptime
Use YAML:
`yaml
features:
memfs: true
sleeptime:
trigger: step-count
behavior: reminder
stepCount: 10
`
Or env vars:
`bash
SLEEPTIME_TRIGGER=step-count
SLEEPTIME_BEHAVIOR=reminder
SLEEPTIMESTEPCOUNT=10
`
Note: sleeptime requires memfs: true.
How To: Discord Thread-Only Mode
`yaml
channels:
discord:
token: "your-bot-token"
groups:
"CHANNEL_ID":
mode: open
threadMode: thread-only
autoCreateThreadOnMention: true
`
Behavior:
- Thread messages are handled in their own thread-scoped conversation.
- Top-level mentions can auto-create a thread and continue there.
How To: send-message and send-file Directives
<send-message> example:
`xml
<send-message channel="telegram" chat="123456">Job completed.</send-message>
`
<send-file> examples:
`xml
<send-file path="/tmp/report.pdf" caption="Report attached" />
<send-file path="/tmp/result.txt" channel="whatsapp" chat="5511999999999" caption="Here's your file" />
`
How To: Approval Recovery and Stuck Runs
Default-conversation approval conflicts now attempt in-place recovery and retry once automatically.
For manual inspection on self-hosted setups:
`bash
curl http://localhost:8283/v1/agents/YOURAGENTID/tools | jq '.[].requires_approval'
`
If needed, disable approval on a tool:
`bash
curl -X PATCH http://localhost:8283/v1/tools/TOOL_ID \
-H "Content-Type: application/json" \
-d '{"requires_approval": null}'
`
Also Included
- Telegram Markdown safety fixes for blockquote/italic edge cases.
- Stream/run-id reliability fixes and reduced log noise.
- Voice memo diagnostics hardening.
@letta-ai/letta-client bump to 1.7.12`.
Full Changelog
https://github.com/letta-ai/lettabot/compare/v0.2.0-alpha.11...v0.2.0-alpha.12
The biggest addition this release: full conversation management from anywhere.
/setconv command across all channels (#512): Switch the active conversation from Telegram, Discord, Slack, Signal, or WhatsApp with /setconv <conversation-id>. Scoped to the caller's conversation key -- the session is invalidated immediately and the next message picks up the new conversation. Previously, switching conversations on hosted deployments (Railway, etc.) required SSH access to hand-edit lettabot-agent.json.CLI and API support (#512):
lettabot set-conversation <id> from the CLI (with multi-agent picker), plus POST /api/v1/conversation and GET /api/v1/status API endpoints for remote management. The /portal admin UI also gets a new "Status" tab with agent cards and a set-conversation form.Per-channel conversation creation (#503): Fixed a bug where per-channel conversation mode incorrectly resumed the agent's default conversation when no conversation was stored for a channel key. Now correctly creates a new conversation per channel.
Stream Reliability
Two significant fixes for cases where agents produce incorrect or leaked output.
Non-foreground run event filtering (#513): When the Letta API produces events from multiple runs in a single stream (background runs, retries), events from non-foreground runs were leaking into the user-visible response.
processMessage now tracks the foreground run ID, defers ambiguous pre-foreground events, and drops events from non-foreground runs entirely. Adds runId to StreamMsg for per-event run identity.Reasoning text leak on LLM errors (#509): When an LLM API error occurred, the
result field could contain raw reasoning text (e.g. "Evaluating response protocol...") which was being delivered as the visible response. Result-text fallback paths now only fire on successful results. Also bumps @letta-ai/letta-code-sdk to ^0.1.9 which adds generation counter + run_id tagging in the stream pump, closing the N-1 desync race.Three fixes for WhatsApp-specific issues.
LID-to-phone resolution for inbound DMs (#510): WhatsApp Web clients use
@lid format JIDs instead of @s.whatsapp.net, giving the same user different userIds depending on device. This broke debouncing, daily limits, and conversation routing. LIDs are now resolved to phone numbers via Baileys' senderPn field or signal repository mapping, with fallback to LID-stripped numbers.Link preview generation (#511): URLs in agent responses were arriving as plain text. Outbound messages now detect HTTPS URLs and attach a minimal
linkPreview object so WhatsApp renders them as tappable links with auto-fetched previews.Group message sender identification (#491):
userId in group chats was derived from the group JID instead of the individual sender's phone number, making per-user identification impossible. Now uses the sender's E.164 phone number.New Features
ChatGPT subscription connect flow (#487):
lettabot connect chatgpt delegates to Letta Code's OAuth flow. ChatGPT/Codex appears as an onboarding provider option, with subscription models grouped separately from API key providers in the model picker.Per-group daily message limits (#484): New
dailyLimit (group-wide total) and dailyUserLimit (per-user) config options for GroupModeConfig. In-memory counters that auto-reset daily, integrated into all 5 channel adapters.Async chat endpoint (#329):
POST /api/v1/chat/async returns 202 immediately and processes the message in the background. Same auth and validation as /api/v1/chat, built for fire-and-forget use cases like escalation processing.Custom Gmail polling prompts (#471): Configure custom prompts for email polling -- global defaults for all Gmail accounts, per-account overrides, or prompt files that are re-read each poll for live editing.
Unified
agents.yml initialization (#393): LettaBot reads lettactl's agents.yml natively. Define your agent and runtime config in one file, run lettactl apply, then lettabot server. Backwards compatible -- existing lettabot.yaml configs take priority.Bug Fixes
Telegram emoji reactions (#499): The
heart alias resolved to U+2764 + U+FE0F but Telegram expects U+2764 only. Variation selectors are now stripped, so emoji reactions work regardless of source encoding.Serialized sendToAgent calls (#501): Removed a lock bypass that allowed concurrent
session.send() calls on the same Session. Also replaced a shared currentCanUseTool field with a per-key map so concurrent sessions on different conversation keys don't clobber each other's tool approval callbacks. Fixes #488.Stuck 409 PENDINGAPPROVAL recovery (#478):
getLatestRunError() now detects stopreason=requires_approval as a fallback when error messages don't contain expected strings. Shows an approval-specific recovery message.EADDRINUSE on restart (#477): The API server was never closed during graceful shutdown. Now calls
apiServer.close() in the shutdown handler.Silent mode diagnostic logging (#476): When an agent produces text in silent mode (heartbeat, polling, cron) but never calls
lettabot-message send, a warning now explains the response was discarded.Email prompt builder consolidation (#474): Consolidated
buildEmailPrompt and buildCustomEmailPrompt into a single function. Added 8 tests covering the full priority chain.Refactoring
Shared channel utilities (#492): Extracted access control, emoji resolution, and message splitting from 5 channel adapters into
src/channels/shared/. Eliminates copy-pasted code across Discord, Telegram, Telegram-MTProto, Signal, and Slack.Housekeeping
- Bump
@letta-ai/letta-clientfrom 1.7.9 to 1.7.11 (#469, #490) - Consolidate voice documentation into
docs/voice.md(#485) - Note that skills CLI commands are directory-scoped (#473)
- Add code formatting to
/modelcommand output (#472) - Update README to use local preview image (#481)
Several things in the skills system were broken or missing. This release fixes all of them.
PATH timing bug (#462): Skills installed to
WORKING_DIR/.skills/ weren't available when the agent session started because the subprocess was spawning before the directory was added to PATH. Fixed — skills are now reliably on PATH from the first message.Bundled skills missing from sync (#461): The
skills/ directory bundled with lettabot wasn't showing up in lettabot skills status or the sync wizard. Fixed source priority ordering so bundled skills are correctly discovered.Global skill library now visible (#468):
~/.letta/skills/ (the full Letta SDK skill library — linear, slack, imessage, google-workspace, and more) was invisible to the sync wizard because it uses a nested category structure (tools/, meta/, etc.) that the flat directory scan missed. The wizard and lettabot skills enable now walk the category subdirs correctly.lettabot skills disable <name> (#465): The enable command existed but there was no non-interactive way to disable a skill. Now there is.Sync wizard pre-checks installed skills (#465): The sync wizard used to open with everything unchecked, forcing you to re-select your full skill set on every run. It now opens with your currently-installed skills already checked.
Heartbeat
Default interval raised to 4 hours (#467): The 30-minute default was unsustainable for real users. At ~$0.60/invocation that's ~$29/day — a real user (Graham, running on Signal) hit this immediately after deploying. New default is 4 hours (~6 invocations/day). The onboard wizard now shows a warning with invocation counts before prompting for the interval, so the cost impact is visible before you commit to a value.
Heartbeat routing decoupled from conversation mode (#463): Changing conversation mode was breaking dedicated heartbeat delivery targets. Heartbeat routing is now evaluated independently.
New:
disabled conversation mode (#453)A new conversation mode that keeps the bot connected to all channels but ignores all incoming messages. Useful for maintenance windows or temporarily silencing a bot without stopping the process.
Session reliability
Stale duplicate run results (#458): Fixed a race condition where duplicate background session reuse could produce stale results being delivered to the wrong chat.
Stream result desync (#444): Guarded against an off-by-one in streamed tool result handling that could cause the last result in a sequence to be dropped.
default conversationId (#452): The literal string "default" as a conversationId wasn't being handled correctly. It now maps to resumeSession behavior as expected.Discord
Message chunking (#459): Messages over Discord's 2000-character limit were being silently dropped. They're now split into chunks and delivery failures are logged.
Cron
Default delivery target (#454): Cron jobs with no explicit delivery target now default to the last active message target rather than failing silently.
Internals
SessionManager extracted from bot.ts into its own class (#457). bot.ts was doing too much — session lifecycle now lives in one place.Skills architecture and authoring guide added to
docs/ (#455) for anyone writing custom skills.
LettaBot can now run in Docker on any cloud platform. Pass your full config as an environment variable (
LETTABOTCONFIGYAML), use the included Dockerfile, and manage pairing approvals through the new /portal web UI.LETTABOTCONFIGYAMLenv var accepts base64 or raw YAML -- no config files neededlettabot config encode/lettabot config decodeCLI commands for easy config management- Multi-stage Dockerfile with
node:22-slimruntime - Web portal at
/portalfor pairing approvals (dark theme, auto-refresh) - Cloud deployment guide at
docs/cloud-deploy.md
Streaming Edits: Now Opt-In (#433, #436)
Streaming edits (progressively updating messages as tokens arrive) are now disabled by default for Telegram, Discord, and Slack. The previous always-on behavior caused jittery UX and Telegram 429 rate limits on longer responses. Enable per-channel if you want it:
``
yamlchannels: telegram: streaming: true # opt-in`
When enabled, edits are throttled to 1500ms intervals with automatic 429 backoff.
Channel-Aware Directives (#419)
Each channel adapter now declares its own capabilities (reactions, file sending, formatting). Directive documentation is injected per-message based on what the channel actually supports, rather than a one-size-fits-all block. This shrinks the system prompt by ~45 lines and gives agents accurate guidance regardless of which channel a message arrives on.
Refactoring (#435)
bot.ts has been decomposed: error classification moved to errors.ts (~110 lines) and tool/reasoning display logic moved to display.ts (~275 lines). bot.ts drops from ~2570 to ~2190 lines. Part of the ongoing #372 effort.
Fixes
- Scoped cron jobs (#432): In multi-agent mode, each agent now gets its own cron store instead of sharing a single file. Existing jobs migrate automatically on upgrade.
- Ghost agent recovery (#431): When a stored agent ID no longer exists on the server, the bot now detects the failure, clears the stale ID, and re-creates the agent instead of looping forever.
- Docker git (#437): The SDK CLI needs git for memory sync;
node:22-slim doesn't include it. Fixed in the Dockerfile.
Docs
- Consolidated conversation routing documentation with full per-chat mode reference (#426)
list-groups CLI subcommand extracted into shared module with --agent support (#418)
Full Alpha Series (v0.2.0-alpha.5 through alpha.9)
For those upgrading from v0.1.x, the v0.2.0 alpha series includes 80+ PRs covering:
- Multi-agent mode -- multiple agents in one config, per-agent channels/tools/features
- Per-chat conversations -- isolated message history per DM/group with LRU eviction
- Cloud deployment -- Dockerfile, inline config, admin portal
- Streaming controls -- toggleable per-channel, rate limit handling
- OpenAI-compatible API --
/v1/chat/completions endpointVoice -- TTS responses via ElevenLabs/OpenAI, Voxtral transcription File sending -- <send-file> directive across Discord, Signal, CLISecurity -- outbound secret/PII redaction, scoped /reset, agent store hardeningObservability -- structured logging (pino), /cancel command, /model commandChannel improvements -- Signal reactions, channel-aware directives, per-channel conversation overrides, group listing CLI
Install
`bashnpx lettabot onboardornpm install -g lettabot``Full Changelog: https://github.com/letta-ai/lettabot/compare/v0.2.0-alpha.8...v0.2.0-alpha.9