improvements for agent onboarding
This commit is contained in:
@@ -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
141
SKILL.md
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user