Commit Graph

42 Commits

Author SHA1 Message Date
Cameron
e80c5b4dd6 fix: prevent non-foreground run events leaking into user response stream (#513) 2026-03-06 11:51:05 -08:00
Cameron
761de6d716 feat: add set-conversation command (CLI, API, channel, portal) (#512) 2026-03-06 11:30:06 -08:00
Cameron
bcbe6dff9b fix: prevent reasoning text from leaking as response on LLM API errors (#509) 2026-03-06 10:14:16 -08:00
dependabot[bot]
ba1ef7ab6e chore(deps): bump @letta-ai/letta-client from 1.7.10 to 1.7.11 (#490)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-06 09:52:24 -08:00
dependabot[bot]
a2e46f7420 chore(deps): bump @letta-ai/letta-client from 1.7.9 to 1.7.10 (#469)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-03 11:10:45 -08:00
dependabot[bot]
9cf929a716 chore(deps): bump @letta-ai/letta-client from 1.7.8 to 1.7.9 (#406)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-26 15:31:02 -08:00
Cameron
a3bb102381 fix: resolve stream queue contamination causing N-1 desync and silent mode leak (#411) 2026-02-26 14:00:18 -08:00
Cameron
b6394fb3a5 feat: client-side tool call accumulation in dedupedStream (#389) 2026-02-24 16:16:53 -08:00
dependabot[bot]
623532292c chore(deps): bump @letta-ai/letta-code-sdk from 0.1.2 to 0.1.6 (#380)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-24 11:22:32 -08:00
Cameron
1cad6e6508 feat(core): structured logging with pino (#368) 2026-02-23 16:35:23 -08:00
dependabot[bot]
92d8a0cf10 chore(deps): bump @letta-ai/letta-code-sdk from 0.1.1 to 0.1.2 (#352)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-23 11:22:07 -08:00
Charles Packer
3e38a35736 chore: bump @letta-ai/letta-code-sdk to 0.1.1 (#315) 2026-02-16 23:48:51 -08:00
Charles Packer
94bd4d2d12 chore(deps): bump @letta-ai/letta-code-sdk to 0.1.0 (#313) 2026-02-16 19:41:11 -08:00
Cameron
57f102dfd4 feat: add loom ASCII art startup banner (#270) 2026-02-10 17:57:19 -08:00
ghosttigerllc-bit
28adc22388 feat: add telegram-mtproto channel for user account messaging (#189)
Co-authored-by: Kai <noreply@gtb.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cameron <cameron@pfiffer.org>
2026-02-10 17:25:44 -08:00
Cameron
8cf9bd230e chore: bump @letta-ai/letta-code-sdk to 0.0.6 (#268) 2026-02-10 16:25:25 -08:00
cpacker
8a38896928 chore: bump letta code sdk to 0.0.6 and remove stale patches 2026-02-10 16:19:10 -08:00
Cameron
dad510150a fix: prevent duplicate Telegram messages on "not modified" edit (#254)
* fix: prevent duplicate Telegram messages on "not modified" edit

When streaming completes and the final editMessage call sends identical
content, Telegram returns "message is not modified". The catch block
treated this as a real failure and bubbled up to bot.ts, which fell back
to sending the response as a brand-new message -- causing duplicates.

Now the Telegram adapter silently returns on "message is not modified"
since the content is already correct.

Written by Cameron ◯ Letta Code

"The simplest fix is the one that doesn't fight the API."

* fix: update package-lock.json for remark/mdast dependencies

PR #234 (Slack mrkdwn formatter) added remark-gfm and related mdast
packages but the lockfile wasn't regenerated, causing npm ci to fail
in CI with "Missing from lock file" errors.

Written by Cameron ◯ Letta Code

"The lockfile remembers what package.json forgets."
2026-02-10 11:53:51 -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
Tom Fehring
d12633b792 Convert standard markdown to Slack mrkdwn for Slack messages (#234)
* Slack: convert Markdown to mrkdwn

* Slack: avoid literal dynamic import for optional dep

* Slack formatter: cache optional dependency load state

* fix: remove slackify-markdown from lockfile dependencies

The lockfile had slackify-markdown in both `dependencies` (pinned) and
`optionalDependencies`, but package.json only lists it in
optionalDependencies. This caused npm ci to treat it as required,
defeating the optional dependency pattern.

Regenerated lockfile with clean npm install to fix.

Written by Cameron ◯ Letta Code

"The lockfile giveth, and the lockfile taketh away." - npm, probably

---------

Co-authored-by: Cameron <cameron@pfiffer.org>
2026-02-09 16:59:46 -08:00
Cameron
b1d69965b5 feat: add <no-reply/> silent marker for agent opt-out (#196)
* feat: add {{NO_REPLY}} silent marker for agent opt-out

Allow the agent to respond with {{NO_REPLY}} to suppress message
delivery for messages that don't warrant a reply. The marker is
checked in three places: the streaming edit guard (prefix match to
prevent partial sends), finalizeMessage(), and the post-stream
response handler.

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

* refactor: use <no-reply/> XML marker instead of {{NO_REPLY}}

Switch to XML-style self-closing tag for consistency with the XML
envelope format used elsewhere, and because LLMs produce well-formed
XML tags more reliably than template syntax.

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

* feat: add no-reply hint to group chat envelopes

Agents created outside lettabot (via ADE, Letta Cloud) won't have the
system prompt telling them about <no-reply/>. Adding the hint to group
chat envelopes makes the opt-out mechanism self-documenting.

Written by Cameron ◯ Letta Code

"Silence is one of the great arts of conversation." -- Marcus Tullius Cicero

---------

Co-authored-by: Gabriele Sarti <gabriele.sarti996@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:08:56 -08:00
Cameron
5dea82acc5 fix: pin baileys to 6.7.21 and fix stale command count test (#193)
The ^6.7.21 caret range resolves to 6.17.16 on fresh npm install, which
ships incompatible TypeScript types (no call signatures on default export).
Pins exact version and adds explicit type annotation on getMessage key param.

Also updates commands.test.ts to expect 5 commands (reset was added but
test still expected 4).

Fixes #192

Written by Cameron ◯ Letta Code

"The caret giveth, and the caret taketh away."
2026-02-06 10:30:04 -08:00
dependabot[bot]
49db36f52f chore(deps): bump @letta-ai/letta-client from 1.7.7 to 1.7.8 (#186)
Bumps [@letta-ai/letta-client](https://github.com/letta-ai/letta-node) from 1.7.7 to 1.7.8.
- [Release notes](https://github.com/letta-ai/letta-node/releases)
- [Changelog](https://github.com/letta-ai/letta-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/letta-ai/letta-node/compare/v1.7.7...v1.7.8)

---
updated-dependencies:
- dependency-name: "@letta-ai/letta-client"
  dependency-version: 1.7.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-06 10:25:27 -08:00
Cameron
c85c4a3272 fix: telegram ESM compatibility and improved diagnostics (#161)
- 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
2026-02-05 10:31:53 -08:00
Cameron
b4058f17ce fix: add keyv as direct dependency (#154)
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
2026-02-04 18:43:10 -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
541bcf496e Revert "fix: auto-reset conversation when stream receives no data (#142)" (#143)
This reverts commit 4258e92c50.
2026-02-04 15:42:54 -08:00
Cameron
4258e92c50 fix: auto-reset conversation when stream receives no data (#142)
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
2026-02-04 15:27:25 -08:00
Cameron
df0343d0be feat: add reset-conversation command and better error detection (#122)
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
2026-02-03 21:59:08 -08:00
github-actions[bot]
41c6e88977 Upgrade Letta Code SDK to 0.0.5 (#117)
Breaking API changes in SDK 0.0.5 require:
- Use createAgent() for new agents with custom systemPrompt and memory
- Remove systemPrompt from createSession()/resumeSession() options

Fixes #116

Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com>
Co-authored-by: Cameron <cpfiffer@users.noreply.github.com>
2026-02-03 18:54:56 -08:00
Cameron
f3e619cd7b Add testing infrastructure with vitest (#67)
- 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
2026-02-01 22:38:25 -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
dependabot[bot]
f27227cfc3 chore(deps): bump @letta-ai/letta-client from 1.7.6 to 1.7.7 (#63)
Bumps [@letta-ai/letta-client](https://github.com/letta-ai/letta-node) from 1.7.6 to 1.7.7.
- [Release notes](https://github.com/letta-ai/letta-node/releases)
- [Changelog](https://github.com/letta-ai/letta-node/blob/main/CHANGELOG.md)
- [Commits](https://github.com/letta-ai/letta-node/compare/v1.7.6...v1.7.7)

---
updated-dependencies:
- dependency-name: "@letta-ai/letta-client"
  dependency-version: 1.7.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-01 20:19:16 -08:00
dependabot[bot]
4b681b0fa4 chore(deps): bump @letta-ai/letta-code-sdk from 0.0.3 to 0.0.4 (#62)
* chore(deps): bump @letta-ai/letta-code-sdk from 0.0.3 to 0.0.4

Bumps [@letta-ai/letta-code-sdk](https://github.com/letta-ai/letta-code-sdk) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/letta-ai/letta-code-sdk/releases)
- [Commits](https://github.com/letta-ai/letta-code-sdk/compare/v0.0.3...v0.0.4)

---
updated-dependencies:
- dependency-name: "@letta-ai/letta-code-sdk"
  dependency-version: 0.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix SDK 0.0.4 API changes

- createSession() signature changed: (agentId?, options?) instead of (options)
- Remove canUseTool callback - bypassPermissions mode auto-allows all tools

Written by Cameron ◯ Letta Code

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cameron Pfiffer <cameron@pfiffer.org>
2026-02-01 20:16:27 -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
Cameron
8017c4617e Add update-notifier to CLI (#53)
Shows a notification when a new version is available, helping users
stay up to date with security fixes.

Written by Cameron ◯ Letta Code
2026-01-30 17:16:49 -08:00
Sarah Wooders
e3f4d43dc8 Merge pull request #35 from letta-ai/fix/message-bubbles-and-tool-approval
Fix message bubbles and tool approval
2026-01-29 22:58:08 -08:00
Sarah Wooders
a41966657a Add Google Workspace setup wizard to onboarding
- 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>
2026-01-29 22:51:53 -08:00
Cameron
fe61c8869f Fix message bubbles and tool approval
- 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
2026-01-29 21:58:02 -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
Sarah Wooders
b72150c193 Add YAML config system (types and io) 2026-01-28 22:50:49 -08:00
Sarah Wooders
22770e6e88 Initial commit - LettaBot multi-channel AI assistant
Co-authored-by: Cameron Pfiffer <cameron@pfiffer.org>
Co-authored-by: Caren Thomas <carenthomas@gmail.com>
Co-authored-by: Charles Packer <packercharles@gmail.com>
Co-authored-by: Sarah Wooders <sarahwooders@gmail.com>
2026-01-28 18:02:51 -08:00