Skip to content

Configuration reference

Bestie stores non-secret settings in .bestie/config.json during local development. Secrets live in .bestie/.env and must never be printed or logged.

Installed builds may use user-level paths such as ~/.config/bestie-agent/config.json, ~/.local/share/bestie-agent/, and ~/.local/state/bestie-agent/logs/.

{
"version": 1,
"agent": {
"name": "Bestie",
"ownerName": "Owner",
"language": "vi",
"toneIntensity": 7,
"emojiLevel": "light"
},
"llm": {
"provider": "openai-compatible",
"baseUrl": "https://api.openai.com/v1",
"model": "provider-model-name",
"apiKeyEnv": "BESTIE_LLM_API_KEY",
"timeoutMs": 60000,
"maxRetries": 1,
"retryDelayMs": 500
},
"memory": {
"provider": "sqlite",
"writePolicy": "ask"
}
}

.bestie/.env contains secrets:

Terminal window
BESTIE_LLM_API_KEY=
BESTIE_TELEGRAM_BOT_TOKEN=
BESTIE_TRANSCRIPTION_API_KEY=
COMPOSIO_CONSUMER_API_KEY=
ZEP_API_KEY=

Only add secrets for features you enable. Basic setup only requires the LLM API key.

llm config selects the OpenAI-compatible provider:

{
"llm": {
"provider": "openai-compatible",
"baseUrl": "https://api.openai.com/v1",
"model": "provider-model-name",
"apiKeyEnv": "BESTIE_LLM_API_KEY",
"timeoutMs": 60000,
"maxRetries": 1,
"retryDelayMs": 500
}
}

apiKeyEnv is the environment variable name, not the secret value.

{
"memory": {
"provider": "sqlite",
"writePolicy": "ask",
"sqlitePath": "~/.local/share/bestie-agent/memory.sqlite",
"zepEnabled": false,
"zepApiKeyEnv": "ZEP_API_KEY"
}
}

memory.writePolicy controls model-requested writes through internal.remember_memory:

Policy Behavior
allow Store allowed non-secret memories directly.
ask Queue allowed non-secret memories for approval. This is the default.
deny Reject memory writes.

Memory commands include status, pause, resume, list, search, add, inspect, edit, forget, export, clear, pending inspection, approve, reject, and reject-all flows.

{
"workspace": {
"defaultPath": ".bestie/workspace",
"externalPaths": []
}
}

workspace.defaultPath controls where relative write, edit, and exec paths land. workspace.externalPaths is an explicit allowlist for absolute paths outside the project root and agent workspace.

{
"internalTools": {
"policies": {
"internal.write_file": "ask",
"internal.edit_file": "ask",
"internal.apply_patch": "ask",
"internal.exec": "ask",
"internal.list_processes": "allow",
"internal.read_url": "ask"
},
"exec": {
"timeoutMs": 120000
}
}
}

Policies use allow, ask, or deny.

Telegram config stays non-secret in .bestie/config.json:

{
"channels": {
"telegram": {
"enabled": true,
"botTokenEnv": "BESTIE_TELEGRAM_BOT_TOKEN",
"ownerUserId": "123456789",
"attachments": {
"downloadPolicy": "allow",
"maxBytes": 20971520,
"previewMaxBytes": 16384,
"parseMaxBytes": 5242880,
"visionPolicy": "deny",
"visionMaxBytes": 4194304,
"transcriptionPolicy": "deny",
"transcriptionMaxBytes": 10485760,
"deleteAfterProcessingKinds": [],
"allowedMimeTypes": ["text/*", "application/json"]
}
}
}
}

Run setup with:

Terminal window
bestie telegram setup

Run the bot locally:

Terminal window
bestie telegram
bestie telegram --once

Supported Telegram commands include /start, /help, /status, /doctor, /memory, /memory pending, /memory pause, /memory resume, /approvals, /approve <id>, and /deny <id>.

OpenAI-compatible transcription:

{
"transcription": {
"provider": "openai-compatible",
"baseUrl": "https://api.openai.com/v1",
"model": "whisper-1",
"apiKeyEnv": "BESTIE_TRANSCRIPTION_API_KEY",
"timeoutMs": 60000
}
}

Local whisper transcription:

{
"transcription": {
"provider": "local-whisper",
"command": ".bestie/tools/local-whisper-transcribe.sh",
"args": ["{modelPath}", "{audioPath}", "-l", "vi"],
"modelPath": ".bestie/models/ggml-small.bin",
"timeoutMs": 120000
}
}

The local command runs without a shell. {audioPath} and {modelPath} are replaced by Bestie.

Voice replies are opt-in for Telegram voice/audio inputs:

{
"speech": {
"provider": "elevenlabs",
"apiKeyEnv": "ELEVENLABS_API_KEY",
"voiceId": "NOpBlnGInO9m6vDvFkFC",
"modelId": "eleven_v3",
"languageCode": "en",
"outputFormat": "mp3_44100_128",
"timeoutMs": 60000
},
"channels": {
"telegram": {
"voiceReplyPolicy": "voice-input-only"
}
}
}

Bestie sends the text reply first, then converts generated speech to Telegram Ogg/Opus voice-note format.

{
"mcp": {
"servers": [
{
"name": "local-files",
"enabled": false,
"transport": "stdio",
"command": "node",
"args": ["server.js"],
"tools": [
{ "name": "read_file", "category": "read" }
]
}
]
}
}

A top-level mcpServers object is also accepted and normalized to mcp.servers for compatibility with common MCP snippets.