A simple multi-agent system for Slack (designed to extend to more platforms later).
- Node.js + TypeScript + OpenAI Responses API β each agent gets
web_search(optional) and a sandboxed code container (code interpreter) for skill execution - SQLite for agents / cron jobs / conversations, JSONL for per-agent long-term memory
- TUI (codex-style slash-command REPL) for creating, configuring, and test-chatting with agents
- One Slack app per agent, connected via Socket Mode (no public URL needed β Docker friendly)
- Cron jobs per agent, with "run now" for testing
data/
app.db # SQLite: agents, cron_jobs, conversations, message_log
agents/
<slug>/
AGENT.md # the agent's persona / system prompt
memory.jsonl # long-term memory (agent appends via save_memory tool)
skills/*.md # skill playbooks (scripts execute in the OpenAI code container)
Each agent is fully isolated: its own Slack app + tokens, its own channel, its own AGENT.md / memory / skills, its own OpenAI conversation threads.
cp .env.example .env # fill in OPENAI_API_KEY and SLACK_WORKSPACE
npm install # also compiles TypeScript β dist/
npm start # launches the TUI βββββββββββββββββββββββββββββββββββββββββββββββ
β β² multi-agent β Slack AI agents β
β OpenAI Responses Β· SQLite Β· JSONL memory β
βββββββββββββββββββββββββββββββββββββββββββββββ
βΊ /create
Agent name: Release Bot
Step 1 β Create a Slack app for this agent β¦
βΊ /talk release-bot
you βΊ summarize open PRs every morning?
release-bot βΊ Sure β add me as a cron job: /cron release-bot β¦
In the TUI:
| Command | What it does |
|---|---|
/create |
Guided agent creation (see flow below) |
/agents |
List agents and their status |
/talk <agent> |
Playground chat in the terminal |
/edit <agent> |
Change tokens, channel, model, web_search, AGENT.md, skills |
/cron <agent> |
Add / list / run now / toggle / delete cron jobs |
/memory <agent> |
Show the agent's saved memories |
/start / /stop |
Connect / disconnect Slack listeners |
/logs |
Recent activity |
/create walks you through:
- Slack app β you're pointed to https://api.slack.com/apps β Create New App β
From a manifest; the TUI prints a ready-to-paste manifest (Socket Mode enabled,
correct scopes and events). Then you paste:
- the app-level token (
xapp-β¦, scopeconnections:write) from Basic Information - the bot token (
xoxb-β¦) from Install App
- the app-level token (
- Channel β invite the bot (
/invite @AgentName) and paste the channel ID (one channel per agent for now). DMs to the bot also work. - AGENT.md β persona / instructions (opens
$EDITOR, or edit the file later). - Skills β markdown playbooks; when a skill needs computation or scripting the agent writes & runs code in its OpenAI-hosted container.
- web_search on/off and model choice.
You can skip the Slack steps and use the agent playground-only, then add tokens later
with /edit.
/cron <agent> β Add job. A job = cron schedule (0 9 * * 1-5) + a prompt the agent
runs. Results post to the agent's Slack channel (optional). Every job can be run
immediately from the TUI to test it. Timezone comes from TZ in .env.
Agents get a save_memory tool; durable facts append to memory.jsonl and the most
recent 50 entries are loaded into every prompt. Inspect with /memory <agent> or just
read the file.
cp .env.example .env # fill in keys
docker compose up -d # headless: Slack listeners + cron schedulerManage agents with the TUI inside the container (same volume, same DB):
docker compose run --rm agent node dist/index.js tui
docker compose restart agent # pick up new agents in the headless serviceSocket Mode uses an outbound websocket, so no ports or public URLs are required.
| Var | Required | Description |
|---|---|---|
OPENAI_API_KEY |
β | Powers agents, web_search, code container |
OPENAI_MODEL |
Default model for new agents (default gpt-5) |
|
OPENAI_BASE_URL |
Optional API gateway/proxy | |
SLACK_WORKSPACE |
Workspace subdomain, used in setup guidance | |
DATA_DIR |
Storage root (default ./data, /data in Docker) |
|
DB_PATH |
Override SQLite path (default $DATA_DIR/app.db) |
|
TZ |
Cron timezone (default UTC) |
|
LOG_LEVEL |
debug/info/warn/error |
Per-agent Slack tokens are not env vars β they're entered during /create and
stored in SQLite, since each agent has its own Slack app.
src/slack/manager.ts is the only Slack-specific surface: it maps platform messages to
runAgent(agent, text, { convKey, source }) and posts replies back. A Discord/Telegram/
Lark adapter just needs to do the same mapping and store its tokens on the agent row.
- Node.js β₯ 22.5 (SQLite comes from the built-in
node:sqliteβ no native builds) - An OpenAI API key
- A Slack workspace where you can create apps
- Bot doesn't reply in the channel β make sure the bot was invited
(
/invite @AgentName) and the channel ID on the agent matches (/edit <agent>). invalid_authon connect β the bot token (xoxb-) and app token (xapp-) must come from the same Slack app; regenerate and update via/edit.- Cron fired but nothing in Slack β the job posts to Slack only when the agent
has a bot token and a channel ID; check
/cron <agent>β Show last result.