Commit Graph

27 Commits

Author SHA1 Message Date
Cameron
bb0ccd65e1 docs: consolidate voice documentation into docs/voice.md (#485) 2026-03-04 16:18:37 -08:00
Cameron
025fd38d5f feat: add per-group daily message limits (dailyLimit + dailyUserLimit) (#484) 2026-03-04 12:55:49 -08:00
Cameron
c37638d7ce fix: add message chunking to Discord adapter and log delivery failures (#459) 2026-03-02 14:53:08 -08:00
Cameron
8ab5add972 feat: channel-aware per-message directives via adapter hints (#419) 2026-02-27 16:58:11 -08:00
Cameron
6a2493da5c feat: make streaming edits toggleable per-channel, disabled by default (#436) 2026-02-27 16:08:51 -08:00
Cameron
6fa8c98924 feat: /cancel slash command to abort active agent runs (#422) 2026-02-26 17:14:07 -08:00
Cameron
7658538b73 feat: add /model slash command to show and switch agent model (#420) 2026-02-26 16:47:06 -08:00
Cameron
955bdacab7 fix: merge env var credentials into YAML channel blocks and warn on silent skip (#408) 2026-02-26 10:48:14 -08:00
Cameron
1cad6e6508 feat(core): structured logging with pino (#368) 2026-02-23 16:35:23 -08:00
Jason Carreira
1fbd6d5a2e Add send-file directive and Discord/CLI file support (#319)
Co-authored-by: Jason Carreira <jason@visotrust.com>
Co-authored-by: Cameron <cameron@pfiffer.org>
Co-authored-by: Charles Packer <packercharles@gmail.com>
Co-authored-by: Sarah Wooders <sarahwooders@gmail.com>
Co-authored-by: Letta <noreply@letta.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:44:34 -08:00
jamesdanielwhitford
cae5b104b3 feat: add Mistral Voxtral transcription support (#228) 2026-02-23 13:37:12 -08:00
Cameron
560380d721 feat: per-channel conversation routing (#299) 2026-02-13 17:21:38 -08:00
Cameron
09ce3b810f fix: listen mode streaming leak + receiveBotMessages for Discord (#295) 2026-02-12 18:57:13 -08:00
Cameron
c405c96c9d feat: add per-group allowedUsers filtering for all channels (#283) 2026-02-11 15:20:01 -08:00
Cameron
c410decd18 feat: unified group modes (open/listen/mention-only) (#267)
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
2026-02-10 16:01:21 -08:00
Cameron
f5371a9ba7 feat: add group gating to Telegram, Discord, and Slack (#258) (#265)
WhatsApp and Signal already had groups config with requireMention and
group allowlists. This brings the same pattern to the remaining three
channels, giving operators consistent control over which groups the bot
participates in and whether mentions are required.

- New `groups` config for Telegram, Discord, Slack (per-group allowlist
  with requireMention, wildcard support)
- Telegram: standalone gating module with entity, text, command, and
  regex mention detection; applied to text, voice, and attachment handlers
- Discord: inline gating using native message.mentions API
- Slack: gating in message handler (drops non-mentions) and app_mention
  handler (allowlist check); helper methods on adapter
- Signal: pass wasMentioned through to InboundMessage (was detected but
  never forwarded)
- Config wiring: groups/mentionPatterns forwarded from main.ts to all
  adapter constructors
- 17 new tests for Telegram gating

Written by Cameron ◯ Letta Code

"Even in the group chat of life, sometimes you gotta be @mentioned
to know the universe is talking to you." — Ancient Internet Proverb
2026-02-10 14:47:19 -08:00
Jason Carreira
a4934a77d4 Fix action directives parsing and reactions (#248)
* 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>
2026-02-10 11:40:23 -08:00
Gabriele Sarti
66e8c462bf feat: group message batching + Telegram group gating + instantGroups (#187)
* feat: add group message batching, Telegram group gating, and instantGroups

Group Message Batcher:
- New GroupBatcher buffers group chat messages and flushes on timer or @mention
- Channel-agnostic: works with any ChannelAdapter
- Configurable per-channel via groupPollIntervalMin (default: 10min, 0 = immediate)
- formatGroupBatchEnvelope formats batched messages as chat logs for the agent
- Single-message batches unwrapped to use DM-style formatMessageEnvelope

Telegram Group Gating:
- my_chat_member handler: bot leaves groups when added by unpaired users
- Groups added by paired users are auto-approved via group-store
- Group messages bypass DM pairing (middleware skips group/supergroup chats)
- Mention detection for @bot in group messages

Channel Group Support:
- All adapters: getDmPolicy() interface method
- Discord: serverId (guildId), wasMentioned, pairing bypass for guilds
- Signal: group messages bypass pairing
- Slack: wasMentioned field on messages

instantGroups Config:
- Per-channel instantGroups config to bypass batching for specific groups
- For Discord, checked against both serverId and chatId
- YAML config → env vars → parsed in main.ts → Set passed to bot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: preserve large numeric IDs in instantGroups YAML config

Discord snowflake IDs exceed Number.MAX_SAFE_INTEGER, so YAML parses
unquoted IDs as lossy JavaScript numbers. Use the document AST to
extract the original string representation and avoid precision loss.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Slack dmPolicy, Telegram group gating check

- Add dmPolicy to SlackConfig and wire through config/env/adapter
  (was hardcoded to 'open', now reads from config like other adapters)
- Check isGroupApproved() in Telegram middleware before processing
  group messages (approveGroup was called but never checked)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 14:47:22 -08:00
Cameron
0bed2cc166 fix: replace deprecated discord.js 'ready' event with 'clientReady' (#178)
The 'ready' event is deprecated in discord.js v14 and will break in v15.
Also switch from on() to once() since this event fires only once per login.

Reported-by: Aeo

Written by Cameron ◯ Letta Code

"The quietest fix is the one that prevents the loudest crash." -- a wise linter
2026-02-05 16:19:47 -08:00
Cameron
d6113cab66 fix: graceful transcription fallback when ffmpeg unavailable (#155)
* fix: graceful transcription fallback when ffmpeg unavailable

When voice transcription fails (e.g., ffmpeg not installed), the agent
now receives informative error messages instead of silent failures.

Changes:
- transcribeAudio() returns TranscriptionResult with success/error/audioPath
- Tiered fallback: try format rename first, then ffmpeg, then fail gracefully
- Check ffmpeg availability once and cache result
- All channel adapters updated to show transcription errors to agent
- Agent can explain to user why transcription failed

Before:
  Agent sees: "[Voice message received]"
  Agent: "I received your voice message but there's no content..."

After:
  Agent sees: "[Voice message - transcription failed: Cannot transcribe .aac format. Install ffmpeg for audio conversion, or send in a supported format (mp3, ogg, wav, m4a). Audio saved to: /path/to/file.aac]"
  Agent: "I couldn't transcribe your voice message because ffmpeg isn't installed. You could type your message instead."

Fixes voice transcription on systems without ffmpeg.

Written by Cameron ◯ Letta Code

"Fail gracefully, inform clearly." - Error handling wisdom

* fix: handle undefined transcription errors better

* fix: correct API param for tool approval + workaround letta-client type bug
2026-02-04 19:31:50 -08:00
Cameron
21957f5c6e feat: add slash command support to all channels (#145)
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)
2026-02-04 16:56:44 -08:00
Cameron
550ea6cf2e feat: add inbound reaction handling (#134)
* Add inbound reaction handling

* Add inbound reaction types

* Add reaction envelope test

---------

Co-authored-by: Jason Carreira <jason@visotrust.com>
2026-02-04 12:08:49 -08:00
Cameron
67f0550bd3 Add inbound attachment support with download, metadata, and pruning (#64)
* 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>
2026-02-01 22:14:30 -08:00
Cameron
053763bf89 Add voice message transcription support (#54)
* 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>
2026-02-01 20:07:57 -08:00
Sarah Wooders
c68ead7a40 Fix heartbeat skip logic to only check user messages (#43)
- 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>
2026-01-30 10:41:52 -08:00
Jason Carreira
d420e5d3b5 Add reactions CLI for Telegram/Slack/Discord (#20)
* Add lettabot-react CLI and message IDs

* Expose message IDs for reactions

---------

Co-authored-by: Jason Carreira <jason@visotrust.com>
2026-01-29 15:40:50 -08:00
Jason Carreira
0f68b9b52f Add Discord channel support (#16)
* Add Discord channel support and pairing (#15)

* Address Discord adapter review feedback

* Fix stream counter after merge

* Remove stream count logging

---------

Co-authored-by: Jason Carreira <jason@visotrust.com>
2026-01-29 14:11:50 -08:00