Minimal Telegram bot that forwards messages to a local CLI agent (Codex by default). Each message is executed locally and the output is sent back to the chat.
- Runs your configured CLI agent for every message
- Queues requests per chat to avoid overlapping runs
- Keeps agent session state per agent when JSON output is detected
- Handles text, audio (via Parakeet), images, and documents
- Supports
/thinking,/agent, and/cronfor runtime tweaks
- Node.js 24+
- Agent CLI on PATH (default:
codex, orclaude/gemini/opencodewhen configured) - Audio (optional):
parakeet-mlx+ffmpeg
git clone https://github.com/antoniolg/aipal.git
cd aipal
npm install
cp .env.example .env- Create a Telegram bot with BotFather and get the token.
- Set
TELEGRAM_BOT_TOKENin.env. - Start the bot:
npm startOpen Telegram, send /start, then any message.
- Text: send a message and get the agent response
- Audio: send a voice note or audio file (transcribed with Parakeet)
- Images: send a photo or image file (caption becomes the prompt)
- Documents: send a file (caption becomes the prompt)
/reset: clear the current agent session (drops the stored session id for this agent) and trigger memory curation/thinking <level>: set reasoning effort (mapped tomodel_reasoning_effort) for this session/agent <name>: set the CLI agent- In root: sets global agent (persisted in
config.json) - In a topic: sets an override for this topic (persisted in
agent-overrides.json)
- In root: sets global agent (persisted in
/agent default: clear agent override for the current topic and return to global agent/reset: clear the current agent session for this topic (drops the stored session id for this agent)/model [model_id|reset]: view/set/reset the model for the current agent (persisted inconfig.json)/memory [status|tail [n]|search <query>|curate]: inspect, search, and curate automatic memory/cron [list|reload|chatid|assign|unassign|run <jobId>]: manage cron jobs (see below)/help: list available commands and scripts/document_scripts confirm: generate short descriptions for scripts (writesscripts.json; requiresALLOWED_USERS)/<script> [args]: run an executable script from~/.config/aipal/scripts
Scripts can define metadata in scripts.json (stored inside AIPAL_SCRIPTS_DIR) to add descriptions or LLM post-processing.
Example:
{
"scripts": {
"xbrief": {
"description": "Filter briefing to AI/LLMs",
"llm": {
"prompt": "Filter the briefing to keep only AI and LLM items.\nRemove everything that is not AI without inventing or omitting anything relevant.\nMerge duplicates (same link or same content).\nKeep all sections and preserve links in [link](...) format.\nIf a section ends up empty, mark it as \"(No results)\".\nRespond in Spanish, direct and without filler."
}
}
}
}If llm.prompt is present, the script output is passed to the agent as context and the bot replies with the LLM response (not the raw output).
Aipal supports Telegram Topics. Sessions and agent overrides are kept per-topic.
- Messages in the main chat ("root") have their own sessions.
- Messages in any topic thread have their own independent sessions.
- You can set a different agent for each topic using
/agent <name>.
Cron jobs are loaded from ~/.config/aipal/cron.json (or $XDG_CONFIG_HOME/aipal/cron.json) and are sent to a single Telegram chat (the cronChatId configured in config.json).
/cron chatid: prints your chat ID (use this value ascronChatId)./cron list: lists configured jobs./cron reload: reloadscron.jsonwithout restarting the bot./cron run <jobId>: triggers one job immediately using its configured target chat/topic.
If the agent generates an image, save it under the image folder (default: OS temp under aipal/images) and reply with:
[[image:/absolute/path]]
The bot will send the image back to Telegram.
If the agent generates a document (or needs to send a file), save it under the documents folder (default: OS temp under aipal/documents) and reply with:
[[document:/absolute/path]]
The bot will send the document back to Telegram.
The only required environment variable is TELEGRAM_BOT_TOKEN in .env.
Optional:
AIPAL_SCRIPTS_DIR: directory for slash scripts (default:~/.config/aipal/scripts)AIPAL_SCRIPT_TIMEOUT_MS: timeout for slash scripts (default: 120000)AIPAL_MEMORY_CURATE_EVERY: auto-curate memory after N captured events (default: 20)AIPAL_MEMORY_RETRIEVAL_LIMIT: max retrieved memory lines injected per request (default: 8)ALLOWED_USERS: comma-separated list of Telegram user IDs allowed to interact with the bot (if unset/empty, bot is open to everyone)
The bot stores /agent in a JSON file at:
~/.config/aipal/config.json (or $XDG_CONFIG_HOME/aipal/config.json).
Example:
{
"agent": "codex",
"cronChatId": 123456789
}See docs/configuration.md for details.
If soul.md, tools.md, and/or memory.md exist next to config.json, their contents are injected into the first prompt of a new conversation in this order:
soul.mdtools.mdmemory.md
Location:
~/.config/aipal/soul.md, ~/.config/aipal/tools.md, and ~/.config/aipal/memory.md (or under $XDG_CONFIG_HOME/aipal/).
- Every interaction is captured automatically in per-thread files under
~/.config/aipal/memory/threads/*.jsonl(or$XDG_CONFIG_HOME/aipal/memory/threads/*.jsonl). - Memory is isolated by
chatId:topicId:agentIdto avoid collisions across agents and topics. memory.mdremains the global curated memory. The bot can curate it automatically and via/memory curate.- Retrieval (iteration 1): lexical + recency retrieval over captured thread events is injected into prompts automatically, mixing local and global memory scope.
- Captured events are indexed in SQLite (
memory/index.sqlite) for faster and broader retrieval across topics. /memory statusshows memory health,/memory tailshows recent events,/memory searchlets you inspect retrieval hits.
This bot executes local commands on your machine. Run it only on trusted hardware, keep the bot private, and avoid sharing the token.
To restrict access, set ALLOWED_USERS in .env to a comma-separated list of Telegram user IDs. Unauthorized users are ignored (no reply).
- Builds a shell command with a base64-encoded prompt to avoid quoting issues
- Executes the command locally via
bash -lc - If the agent outputs Codex-style JSON, stores
thread_idand usesexec resume - Audio is downloaded, transcribed, then forwarded as text
- Images are downloaded into the image folder and included in the prompt
ENOENT parakeet-mlx: installparakeet-mlxand ensure it is on PATH.Error processing response.: check thatcodexis installed and accessible on PATH.- Telegram
ECONNRESET: usually transient network, retry.
MIT. See LICENSE.
