Thread-only mode previously relied on the global conversationMode to
determine whether threads got separate conversations. In shared or
per-channel mode, all threads shared one conversation causing crosstalk.
Add forcePerChat flag on InboundMessage that the Discord adapter sets
when thread-only mode is active. resolveConversationKey treats flagged
messages as per-chat regardless of the configured mode, giving each
thread its own isolated conversation while still sharing agent memory.
Written by Cameron ◯ Letta Code
"Time is an illusion. Lunchtime doubly so." -- Douglas Adams
Adds features.memfs config key that controls whether the Letta Code CLI
receives --memfs or --no-memfs when creating/resuming SDK sessions. This
enables lettabot users to opt into git-backed memory filesystem (context
repositories) for persistent local memory sync.
- Config types: memfs?: boolean on AgentConfig.features, LettaBotConfig.features, BotConfig
- Bot wiring: baseSessionOptions() and createAgent() pass memfs to SDK when defined
- Main wiring: YAML config takes precedence, LETTABOT_MEMFS env var as fallback
- Legacy fix: conversations passthrough in single-agent normalization
- Tests: 3 memfs wiring tests (true/false/undefined), 2 conversations passthrough tests
- Docs: configuration.md section with known limitations, example YAML
Fixes#335
Written by Cameron and Letta Code
"The best way to predict the future is to implement it." -- David Heinemeier Hansson
Adds documentation for per-channel conversation routing and updated group configuration options. Fills the gap left by the implementation PRs.
Closes#304
Written by Cameron and Letta Code
"Documentation is a love letter that you write to your future self." -- Damian Conway
Three bugs fixed:
1. server.api is now the canonical location for API server config (port,
host, CORS). Users naturally nest api under server -- this now works.
Top-level api still accepted with a deprecation warning.
2. Onboarding no longer silently drops api and attachments config when
saving. Both interactive and non-interactive paths now preserve
unmanaged top-level fields from the existing config.
3. When YAML parsing fails, the log no longer misleadingly says
"Loaded from lettabot.yaml". A didLoadFail() flag enables accurate
status reporting without changing 17+ loadConfig() call sites.
Written by Cameron ◯ Letta Code
"The map is not the territory, but a good map sure helps you find port 6702."
Consolidates listeningGroups and groups.requireMention into a single
groups config with explicit mode per group. Backward compatible --
legacy formats auto-normalize with deprecation warnings.
- Add shared group-mode.ts with isGroupAllowed/resolveGroupMode helpers
- Update all 5 channel adapters to use mode-based gating
- Default to mention-only for configured entries (safe), open when no config
- Listening mode now set at adapter level, bot.ts has legacy fallback
- Fix YAML large-ID parsing for groups map keys (Discord snowflakes)
- Add migration in normalizeAgents for listeningGroups + requireMention
- Add unit tests for group-mode helpers + update all gating tests
- Update docs, README, and example config
Closes#266
Written by Cameron and Letta Code
"Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away." -- Antoine de Saint-Exupery
Add optional displayName field to agent config. When set, outbound
agent responses are prefixed (e.g. "💜 Signo: Hello!").
Useful in multi-agent group chats where multiple bots share a channel
and users need to tell them apart.
Closes#252
Written by Cameron ◯ Letta Code
"The details are not the details. They make the design." -- Charles Eames
Add multi-account Gmail polling with per-account seen tracking, updated
onboarding flow, and config/env resolution.
Based on jasoncarreira's work in #214, rebased onto current main and
cleaned up:
- parseGmailAccounts() extracted to polling/service.ts with 10 unit tests
- Per-account seen email tracking (Map<string, Set<string>>) with legacy
migration from single-account format
- Onboarding supports multi-select for existing accounts + add new
- Config resolution: polling.gmail.accounts > integrations.google.accounts
(legacy) > GMAIL_ACCOUNT env (comma-separated)
- GoogleAccountConfig type for per-account service selection
- Updated docs/configuration.md
Closes#214.
Written by Cameron ◯ Letta Code
"Good artists copy, great artists steal." - Pablo Picasso
* feat: add POST /api/v1/chat endpoint for sending messages to agents
Adds an HTTP endpoint that accepts a JSON message, sends it to the
lettabot agent via sendToAgent(), and returns the agent's response.
This enables external systems (e.g. server-side tools in other agents)
to communicate with lettabot programmatically.
- Add ChatRequest/ChatResponse types
- Add AgentRouter interface extending MessageDeliverer with sendToAgent()
- Implement AgentRouter on LettaGateway with agent-name routing
- Add POST /api/v1/chat route with auth, validation, and JSON body parsing
Written by Cameron ◯ Letta Code
"The most profound technologies are those that disappear." -- Mark Weiser
* feat: add SSE streaming support to /api/v1/chat endpoint
When the client sends Accept: text/event-stream, the chat endpoint
streams SDK messages as SSE events instead of waiting for the full
response. Each event is a JSON StreamMsg (assistant, tool_call,
tool_result, reasoning, result). The result event signals end-of-stream.
- Export StreamMsg type from bot.ts
- Add streamToAgent() to AgentSession interface and LettaBot
- Wire streamToAgent() through LettaGateway with agent-name routing
- Add SSE path in chat route (Accept header content negotiation)
- Handle client disconnect mid-stream gracefully
Written by Cameron ◯ Letta Code
"Any sufficiently advanced technology is indistinguishable from magic." -- Arthur C. Clarke
* test+docs: add chat endpoint tests and API documentation
- 10 tests for POST /api/v1/chat: auth, validation, sync response,
agent routing, SSE streaming, stream error handling
- 6 tests for gateway sendToAgent/streamToAgent routing
- Fix timingSafeEqual crash on mismatched key lengths (return 401, not 500)
- Document chat endpoint in configuration.md with sync and SSE examples
- Add Chat API link to docs/README.md index
Written by Cameron ◯ Letta Code
"First, solve the problem. Then, write the code." -- John Johnson
* fix: switch group batching from fixed timer to 5-second debounce
The old 10-minute fixed timer caused groups to feel unresponsive after
inactivity. Now uses debounce: timer resets on every new message, flushes
after 5 seconds of quiet. @mentions still flush immediately.
New config: groupDebounceSec (default 5). Old groupPollIntervalMin still
works (converted to ms) for backward compatibility.
Fixes#229
Written by Cameron ◯ Letta Code
"The user is always right. If there is a problem with the use of the system, it's the system that's wrong, not the user." -- Don Norman
* docs: add group debounce configuration reference
Document groupDebounceSec, instantGroups, and debounce behavior
in the channel configuration section.
Written by Cameron ◯ Letta Code
"Good documentation is like a good joke: if you have to explain it, it's not that good." -- Kelsey Hightower
- Keep multi-agent normalizeAgents() flow from main
- Integrate deprecation warning for agent.model from PR
- Remove model from LettaBot constructor (server-side property)
- Remove Model: display from single-agent startup log
Written by Cameron ◯ Letta Code
"The best interface is no interface." -- Golden Krishna
* feat: custom heartbeat prompt via YAML config or file
Wire up the existing but unused HeartbeatConfig.prompt field so users
can customize what the agent sees during heartbeats. Adds three ways
to set it: inline YAML (prompt), file-based (promptFile, re-read each
tick for live editing), and env var (HEARTBEAT_PROMPT). Also documents
the <no-reply/> opt-out behavior.
Fixes#232
Written by Cameron ◯ Letta Code
"The only way to do great work is to love what you do." -- Steve Jobs
* test: add coverage for heartbeat prompt resolution
Tests buildCustomHeartbeatPrompt and HeartbeatService prompt resolution:
- default prompt fallback
- inline prompt usage
- promptFile loading
- inline > promptFile precedence
- live reload (file re-read each tick)
- graceful fallback on missing file
- empty file falls back to default
Written by Cameron ◯ Letta Code
"The only way to do great work is to love what you do." -- Steve Jobs
* docs: add multi-agent configuration reference
Document the agents[] YAML config, per-agent options, migration path
from single to multi-agent, and known limitations (#219, #220, #221).
Written by Cameron ◯ Letta Code
"Documentation is a love letter that you write to your future self." -- Damian Conway
* docs: fix channels required claim and soften isolation wording
- channels is not strictly required per-agent (validation is global)
- isolation has known exceptions, don't claim "fully isolated"
Written by Cameron ◯ Letta Code
"Clear is kind." -- Brene Brown
- Document api.port/host/corsOrigin in configuration.md (example,
reference table, and env var mapping)
- Add "Long Messages" section to telegram-setup.md noting the
automatic 4096-char splitting behavior
Written by Cameron ◯ Letta Code
"The best time to plant a tree was 20 years ago. The second best time is now." - Chinese Proverb
Add a top-level `polling` section to lettabot.yaml for configuring
background polling (Gmail, etc.) instead of relying solely on env vars.
- Add `PollingYamlConfig` type with `enabled`, `intervalMs`, and `gmail` subsection
- Update `configToEnv()` to map new polling config to env vars
- Update `main.ts` to read from YAML config with env var fallback
- Maintain backward compat with `integrations.google` legacy path
- Document polling config in docs/configuration.md
Fixes#201
Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com>
Co-authored-by: Cameron <cpfiffer@users.noreply.github.com>
The model field in lettabot.yaml was redundant and misleading -- the model
is configured on the Letta agent server-side, and lettabot shouldn't
override it. Users were confused seeing a model in their startup log that
didn't match the actual model being used.
Changes:
- Remove `model` from `LettaBotConfig.agent` (made optional for backward compat)
- Remove `model` from `BotConfig` interface and `bot.ts` createAgent() calls
- Remove `model` from `main.ts` config construction and startup log
- Stop saving `model` to lettabot.yaml during onboarding
- Stop mapping `agent.model` to `MODEL` env var in config/io.ts
- Add `getAgentModel()` and `updateAgentModel()` to letta-api.ts
- Add new `src/commands/model.ts` with three subcommands:
- `lettabot model` - interactive model selector
- `lettabot model show` - show current agent model
- `lettabot model set <handle>` - set model directly
- Wire up model command in cli.ts with help text
- Update docs/configuration.md, lettabot.example.yaml, SKILL.md
Model selection during `lettabot onboard` is preserved for new agent
creation -- the selected model is passed to createAgent() but is NOT
saved to the config file.
Fixes#169
Co-authored-by: Cameron <cpfiffer@users.noreply.github.com>
* docs: add TESTING.md guide
Comprehensive testing documentation covering:
- Unit test setup and patterns
- E2E test setup with Letta Cloud
- MockChannelAdapter usage
- CI/CD workflow
- Best practices
Written by Cameron ◯ Letta Code
* feat: add LETTABOT_CONFIG env var for config path
Addresses Discord feedback from fpl9000 who was confused about where
to put the config file after a global npm install.
Changes:
- Add LETTABOT_CONFIG env var that overrides the config search order
- Update error messages to show the env var option
- Document in docs/configuration.md
Now users doing global installs can either:
- Create ~/.lettabot/config.yaml, or
- Set LETTABOT_CONFIG=/path/to/config.yaml
Written by Cameron ◯ Letta Code
"Configuration should be explicit, not magic." - The Twelve-Factor App
When user selects "dedicated bot number" mode (selfChatMode=false),
skip the dmPolicy question and default to allowlist. Prompt for
allowed phone numbers immediately.
This is simpler and safer than pairing mode, which sends codes to
whoever messages the bot.
Users who want pairing or open mode can edit lettabot.yaml manually.
Also updates docs to reflect the new defaults.
Written by Cameron ◯ Letta Code
"Simplicity is the ultimate sophistication." - Leonardo da Vinci
Fixes and updates:
- README.md: Remove duplicate heartbeat troubleshooting section
- docs/getting-started.md: Fix Node version (18→20), commands, repo URL
- docs/commands.md: Rewrite with accurate command list (/start, /status, /heartbeat)
- docs/README.md: New multi-channel architecture diagram
- docs/whatsapp-setup.md: Add selfChatMode safety docs, media support section
- docs/slack-setup.md: Fix broken links
New documentation:
- docs/configuration.md: Complete YAML config reference
- docs/cron-setup.md: Scheduling guide (cron jobs + heartbeats)
Written by Cameron ◯ Letta Code
"Documentation is a love letter that you write to your future self." - Damian Conway