Address review findings from self-review and codex:
- Commands (/reset, /cancel) now receive forcePerChat from the Discord
adapter and resolve the correct per-thread conversation key
- Group batcher propagates forcePerChat to synthetic batch messages so
debounced thread messages don't fall back to shared routing
- Reaction handler sets forcePerChat for thread-only reactions
- Session LRU eviction fires regardless of conversationMode so
forcePerChat sessions don't accumulate without bounds
- Docs now correctly state thread-only overrides shared/per-channel
modes (not disabled mode)
Written by Cameron ◯ Letta Code
"The best way to predict the future is to implement it." -- David Heinemeier Hansson
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 a new `<send-message>` directive that lets the agent proactively
send text messages to any connected channel:chat, and extends
`<send-file>` with optional `channel`/`chat` attributes for targeted
file delivery. Both work from any context including heartbeats and cron.
Written by Cameron ◯ Letta Code
"The question of whether a computer can think is no more interesting
than the question of whether a submarine can swim." -- Edsger Dijkstra
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
Users running npm install get a dirty lockfile that blocks git pull.
Add an update script that handles the reset+pull+install+build cycle,
and document npm ci as the recommended install method.
Written by Cameron ◯ Letta Code
"Simplicity is the ultimate sophistication." -- Leonardo da Vinci
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
* Fix action directives and reactions
* revert package-lock.json to main
Drop unrelated lockfile churn from this PR -- the peer dependency
flag changes were artifacts of a different npm version.
Written by Cameron ◯ Letta Code
"The lockfile is a contract, not a suggestion." -- every CI pipeline ever
* docs: add response directives documentation
Document the XML action directives system (introduced in #239, parsing
fixes in #248): <actions> block format, <react> directive, attribute
quoting rules, channel support matrix, emoji alias tables, streaming
holdback behavior, and extension guide.
Written by Cameron ◯ Letta Code
"Documentation is a love letter to your future self." -- Damian Conway
---------
Co-authored-by: Jason Carreira <jason@visotrust.com>
Co-authored-by: Cameron <cameron@pfiffer.org>
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