improvements for agent onboarding

This commit is contained in:
Caren Thomas
2026-01-30 16:15:42 -08:00
parent 1d66f42dad
commit d9de732729
3 changed files with 181 additions and 57 deletions

View File

@@ -52,7 +52,7 @@ See the [documentation](https://docs.letta.com/guides/docker/) for more details
**Option 1: AI-Assisted Setup (Recommended)**
Paste this into Letta Code, Claude Code, Codex CLI, or any AI coding assistant:
Paste this into Letta Code, Claude Code, Cursor, or any AI coding assistant:
```
Clone https://github.com/letta-ai/lettabot, read the SKILL.md
@@ -63,7 +63,7 @@ You'll need:
- A Letta API key from [app.letta.com](https://app.letta.com) (or a [Letta Docker server](https://docs.letta.com/guides/docker/))
- A Telegram bot token from [@BotFather](https://t.me/BotFather)
The AI will handle the rest: cloning, installing dependencies, reading setup docs, and configuring your bot.
The AI will handle cloning, installing, and configuration autonomously.
**Option 2: Interactive Wizard**

141
SKILL.md
View File

@@ -19,14 +19,11 @@ npm install
npm run build
npm link
# 2. Configure via environment variables
# 2. Configure required variables
export LETTA_API_KEY="letta_..." # From app.letta.com
export LETTA_BASE_URL="https://api.letta.com" # Or self-hosted
export LETTA_AGENT_ID="agent-..." # Optional: use existing agent
# 3. Configure channel (example: Telegram)
export TELEGRAM_BOT_TOKEN="123456:ABC-DEF..." # From @BotFather
export TELEGRAM_DM_POLICY="pairing" # Optional: pairing | allowlist | open
# 4. Run non-interactive setup
lettabot onboard --non-interactive
@@ -35,6 +32,16 @@ lettabot onboard --non-interactive
lettabot server
```
**Safe defaults used if not set:**
- `LETTA_BASE_URL`: `https://api.letta.com`
- `LETTA_AGENT_NAME`: `"lettabot"`
- `LETTA_MODEL`: `"claude-sonnet-4"`
- `*_DM_POLICY`: `"pairing"` (requires approval before messaging)
- `WHATSAPP_SELF_CHAT_MODE`: `true` (only "Message Yourself" chat)
- `SIGNAL_SELF_CHAT_MODE`: `true` (only "Note to Self")
The setup will show which defaults are being used and validate safety-critical settings.
## Interactive Setup
For human-friendly setup with wizard:
@@ -50,72 +57,83 @@ The wizard will guide you through:
## Environment Variables
### Required
### Authentication
| Variable | Description |
|----------|-------------|
| `LETTA_API_KEY` | API key from app.letta.com (or skip for OAuth) |
| `LETTA_BASE_URL` | API endpoint (default: https://api.letta.com) |
| Variable | Description | Default |
|----------|-------------|---------|
| `LETTA_API_KEY` | API key from app.letta.com | Required (unless self-hosted) |
| `LETTA_BASE_URL` | API endpoint | `https://api.letta.com` |
### Agent Selection
| Variable | Description |
|----------|-------------|
| `LETTA_AGENT_ID` | Use existing agent (skip agent creation) |
| `LETTA_AGENT_NAME` | Name for new agent (default: "lettabot") |
| `LETTA_MODEL` | Model for new agent (default: "claude-sonnet-4") |
| Variable | Description | Default |
|----------|-------------|---------|
| `LETTA_AGENT_ID` | Use existing agent (skip agent creation) | Creates new agent |
| `LETTA_AGENT_NAME` | Name for new agent | `"lettabot"` |
| `LETTA_MODEL` | Model for new agent | `"claude-sonnet-4"` |
### Telegram
| Variable | Description | Required |
|----------|-------------|----------|
| `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | ✅ |
| `TELEGRAM_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ (default: pairing) |
| `TELEGRAM_ALLOWED_USERS` | Comma-separated user IDs (if dmPolicy=allowlist) | ❌ |
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | ✅ | - |
| `TELEGRAM_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ | `pairing` |
| `TELEGRAM_ALLOWED_USERS` | Comma-separated user IDs (if dmPolicy=allowlist) | ❌ | - |
### Slack (Socket Mode)
| Variable | Description | Required |
|----------|-------------|----------|
| `SLACK_BOT_TOKEN` | Bot User OAuth Token (xoxb-...) | ✅ |
| `SLACK_APP_TOKEN` | App-Level Token (xapp-...) for Socket Mode | ✅ |
| `SLACK_APP_NAME` | Custom app name (default: LETTA_AGENT_NAME or "LettaBot") | ❌ |
| `SLACK_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ (default: pairing) |
| `SLACK_ALLOWED_USERS` | Comma-separated Slack user IDs (if dmPolicy=allowlist) | ❌ |
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `SLACK_BOT_TOKEN` | Bot User OAuth Token (xoxb-...) | ✅ | - |
| `SLACK_APP_TOKEN` | App-Level Token (xapp-...) for Socket Mode | ✅ | - |
| `SLACK_APP_NAME` | Custom app name | ❌ | `LETTA_AGENT_NAME` or `"LettaBot"` |
| `SLACK_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ | `pairing` |
| `SLACK_ALLOWED_USERS` | Comma-separated Slack user IDs (if dmPolicy=allowlist) | ❌ | - |
**Setup Slack app:** See [Slack Setup Wizard](./src/setup/slack-wizard.ts) or run `lettabot onboard` for guided setup.
### Discord
| Variable | Description | Required |
|----------|-------------|----------|
| `DISCORD_BOT_TOKEN` | Bot token from discord.com/developers/applications | ✅ |
| `DISCORD_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ (default: pairing) |
| `DISCORD_ALLOWED_USERS` | Comma-separated Discord user IDs (if dmPolicy=allowlist) | ❌ |
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `DISCORD_BOT_TOKEN` | Bot token from discord.com/developers/applications | ✅ | - |
| `DISCORD_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ | `pairing` |
| `DISCORD_ALLOWED_USERS` | Comma-separated Discord user IDs (if dmPolicy=allowlist) | ❌ | - |
**Setup Discord bot:** See [docs/discord-setup.md](./docs/discord-setup.md)
### WhatsApp
| Variable | Description | Required |
|----------|-------------|----------|
| `WHATSAPP_ENABLED` | Enable WhatsApp: `true` \| `false` | ✅ Must be explicit |
| `WHATSAPP_SELF_CHAT` | Self-chat mode: `true` (personal number) \| `false` (dedicated bot number) | ✅ Must be explicit |
| `WHATSAPP_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ (default: pairing) |
| `WHATSAPP_ALLOWED_USERS` | Comma-separated phone numbers with + (if dmPolicy=allowlist) | ❌ |
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `WHATSAPP_ENABLED` | Enable WhatsApp: `true` \| `false` | ✅ | - |
| `WHATSAPP_SELF_CHAT_MODE` | Self-chat mode: `true` (personal) \| `false` (dedicated) | ✅ | `true` (safe) |
| `WHATSAPP_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ | `pairing` |
| `WHATSAPP_ALLOWED_USERS` | Comma-separated phone numbers with + (if dmPolicy=allowlist) | ❌ | - |
**Important:**
- `WHATSAPP_SELF_CHAT=false` (dedicated bot number): Responds to ALL incoming messages
- `WHATSAPP_SELF_CHAT=true` (personal number): Only responds to "Message Yourself" chat
- QR code appears on first run - scan with WhatsApp app
**CRITICAL - Read Before Enabling:**
- `WHATSAPP_SELF_CHAT_MODE=true` (personal number): Only responds to "Message Yourself" chat ✅ SAFE
- `WHATSAPP_SELF_CHAT_MODE=false` (dedicated bot number): Responds to ALL incoming messages ⚠️ USE WITH CAUTION
- Default is `true` for safety - bot will NOT message your contacts unless you explicitly set to `false`
**First-Time Setup - QR Code Warning:**
- QR code prints to console when `lettabot server` runs for the first time
- **DO NOT background the server** until after QR code is scanned
- **AI agents using Letta Code or similar**: Output may be truncated! If QR code is not visible:
1. Tell the agent to stop the server
2. Run `lettabot server` yourself in a terminal
3. Scan the QR code when it appears
4. After pairing, the agent can manage the server normally
- Alternative: Instruct agent "Run lettabot server in FOREGROUND and do NOT background it"
- After initial pairing completes, server can be backgrounded in future runs
### Signal
| Variable | Description | Required |
|----------|-------------|----------|
| `SIGNAL_PHONE_NUMBER` | Your phone number (with +) | ✅ |
| `SIGNAL_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ (default: pairing) |
| `SIGNAL_ALLOWED_USERS` | Comma-separated phone numbers with + (if dmPolicy=allowlist) | ❌ |
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `SIGNAL_PHONE_NUMBER` | Your phone number (with +) | ✅ | - |
| `SIGNAL_DM_POLICY` | Access control: `pairing` \| `allowlist` \| `open` | ❌ | `pairing` |
| `SIGNAL_ALLOWED_USERS` | Comma-separated phone numbers with + (if dmPolicy=allowlist) | ❌ | - |
**Setup:** Requires Signal CLI - see [signal-cli documentation](https://github.com/AsamK/signal-cli).
@@ -245,18 +263,41 @@ npm install && npm run build && npm link
# 4. Set environment variables
export LETTA_API_KEY="letta_..."
export TELEGRAM_BOT_TOKEN="123456:ABC-DEF..."
# Defaults will be used for LETTA_BASE_URL, agent name, model, and DM policy
# 5. Run non-interactive setup
lettabot onboard --non-interactive
# 6. Start server
lettabot server
# 6. Instruct user to start server
# DON'T start server automatically - user must see output
echo "Setup complete! Run 'lettabot server' to start the bot."
```
**For WhatsApp (QR code required on first run):**
```bash
export LETTA_API_KEY="letta_..."
export WHATSAPP_ENABLED="true"
export WHATSAPP_SELF_CHAT_MODE="true" # REQUIRED: true=personal number, false=dedicated bot
lettabot onboard --non-interactive
# CRITICAL: Do NOT auto-start the server!
# Tell user: "WhatsApp setup complete. Please run 'lettabot server' in your terminal."
# User must see QR code in console output to scan it.
# After first pairing, server can be managed normally.
```
**Agent Instructions:**
If an AI agent is helping with setup and WhatsApp is configured:
1. Complete `lettabot onboard --non-interactive`
2. **DO NOT** run `lettabot server` automatically
3. Tell user: "Setup complete! Please run `lettabot server` in a terminal to see the WhatsApp QR code"
4. Wait for user confirmation that QR was scanned
5. After confirmation, server can be managed normally
The agent can verify success by checking:
- `lettabot server` output shows "Connected to Telegram"
- `lettabot server` output shows "Connected to Telegram" (or other channel)
- Config file exists at `~/.config/lettabot/config.yaml`
- User can message bot on Telegram
- User can message bot on configured channel(s)
## Self-Hosted Letta

View File

@@ -45,7 +45,7 @@ function readConfigFromEnv(existingConfig: any): any {
whatsapp: {
enabled: process.env.WHATSAPP_ENABLED === 'true' || !!existingConfig.channels?.whatsapp?.enabled,
selfChat: process.env.WHATSAPP_SELF_CHAT === 'true' || !!existingConfig.channels?.whatsapp?.selfChat || false,
selfChat: process.env.WHATSAPP_SELF_CHAT_MODE !== 'false' && (existingConfig.channels?.whatsapp?.selfChat !== false),
dmPolicy: process.env.WHATSAPP_DM_POLICY || existingConfig.channels?.whatsapp?.dmPolicy || 'pairing',
allowedUsers: process.env.WHATSAPP_ALLOWED_USERS?.split(',').map(s => s.trim()) || existingConfig.channels?.whatsapp?.allowedUsers,
},
@@ -53,6 +53,7 @@ function readConfigFromEnv(existingConfig: any): any {
signal: {
enabled: !!process.env.SIGNAL_PHONE_NUMBER,
phoneNumber: process.env.SIGNAL_PHONE_NUMBER || existingConfig.channels?.signal?.phoneNumber,
selfChat: process.env.SIGNAL_SELF_CHAT_MODE !== 'false' && (existingConfig.channels?.signal?.selfChat !== false),
dmPolicy: process.env.SIGNAL_DM_POLICY || existingConfig.channels?.signal?.dmPolicy || 'pairing',
allowedUsers: process.env.SIGNAL_ALLOWED_USERS?.split(',').map(s => s.trim()) || existingConfig.channels?.signal?.allowedUsers,
},
@@ -1304,14 +1305,96 @@ export async function onboard(options?: { nonInteractive?: boolean }): Promise<v
const config = readConfigFromEnv(existingConfig);
// Show defaults being used
console.log('Configuration:');
console.log(` Server: ${config.baseUrl}`);
if (!process.env.LETTA_BASE_URL) {
console.log(' (using default - override with LETTA_BASE_URL)');
}
if (config.telegram.enabled) {
console.log(` Telegram: enabled`);
console.log(` DM Policy: ${config.telegram.dmPolicy}${!process.env.TELEGRAM_DM_POLICY ? ' (default)' : ''}`);
}
if (config.slack.enabled) {
console.log(` Slack: enabled`);
console.log(` DM Policy: ${config.slack.dmPolicy}${!process.env.SLACK_DM_POLICY ? ' (default)' : ''}`);
}
if (config.discord.enabled) {
console.log(` Discord: enabled`);
console.log(` DM Policy: ${config.discord.dmPolicy}${!process.env.DISCORD_DM_POLICY ? ' (default)' : ''}`);
}
if (config.whatsapp.enabled) {
console.log(` WhatsApp: enabled`);
console.log(` Self-chat: ${config.whatsapp.selfChat}`);
console.log(` DM Policy: ${config.whatsapp.dmPolicy}${!process.env.WHATSAPP_DM_POLICY ? ' (default)' : ''}`);
// Check if this is first-time WhatsApp setup (no auth data exists)
const { existsSync } = await import('node:fs');
const { resolve } = await import('node:path');
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
const authPath = resolve(homeDir, '.wwebjs_auth');
const isFirstTime = !existsSync(authPath);
if (isFirstTime) {
console.log('');
console.log('⚠️ CRITICAL: First-Time WhatsApp Setup');
console.log(' A QR code will print when you start the server.');
console.log(' You MUST see the QR code to scan it with your phone.');
console.log('');
console.log(' IF USING AN AI AGENT TO START THE SERVER:');
console.log(' - Tell the agent: "Run lettabot server in the FOREGROUND"');
console.log(' - OR: "Do NOT background the server process"');
console.log(' - The QR code output may be truncated - if you don\'t see it,');
console.log(' run "lettabot server" yourself in a terminal to see the full output');
console.log('');
console.log(' After first pairing, the server can be backgrounded normally.');
}
}
if (config.signal.enabled) {
console.log(` Signal: enabled`);
console.log(` DM Policy: ${config.signal.dmPolicy}${!process.env.SIGNAL_DM_POLICY ? ' (default)' : ''}`);
}
console.log('');
// Validate required fields
if (!config.baseUrl) {
console.error('❌ Error: LETTA_BASE_URL is required');
if (!config.apiKey && !config.baseUrl?.includes('localhost')) {
console.error('❌ Error: LETTA_API_KEY is required');
console.error(' Get your API key from: https://app.letta.com/settings');
console.error(' Then run: export LETTA_API_KEY="letta_..."');
console.error('');
console.error(' Or use self-hosted Letta:');
console.error(' export LETTA_BASE_URL="http://localhost:8283"');
process.exit(1);
}
if (!config.apiKey && !config.baseUrl?.includes('localhost')) {
console.error('❌ Error: LETTA_API_KEY is required (or use self-hosted with LETTA_BASE_URL)');
// Validate at least one channel is enabled
const hasChannel = config.telegram.enabled || config.slack.enabled || config.discord.enabled || config.whatsapp.enabled || config.signal.enabled;
if (!hasChannel) {
console.error('❌ Error: At least one channel must be configured');
console.error('');
console.error(' Telegram: export TELEGRAM_BOT_TOKEN="..." (from @BotFather)');
console.error(' Slack: export SLACK_BOT_TOKEN="..." and SLACK_APP_TOKEN="..."');
console.error(' Discord: export DISCORD_BOT_TOKEN="..."');
console.error(' WhatsApp: export WHATSAPP_ENABLED=true and WHATSAPP_SELF_CHAT_MODE=true');
console.error(' Signal: export SIGNAL_PHONE_NUMBER="+1234567890"');
process.exit(1);
}
// CRITICAL: Validate WhatsApp self-chat is explicitly set
if (config.whatsapp.enabled && process.env.WHATSAPP_SELF_CHAT_MODE === undefined) {
console.error('❌ Error: WhatsApp requires explicit WHATSAPP_SELF_CHAT_MODE for safety');
console.error('');
console.error(' For personal number (SAFE - only "Message Yourself" chat):');
console.error(' export WHATSAPP_SELF_CHAT_MODE=true');
console.error('');
console.error(' For dedicated bot number (UNSAFE - responds to ALL messages):');
console.error(' export WHATSAPP_SELF_CHAT_MODE=false');
process.exit(1);
}