CamelAGI — Developer Guide
Personal AI assistant with TUI, gateway server, Telegram bots, admin control plane, and cron jobs. Built on the Claude Agent SDK + OpenAI-compatible fallback for multi-provider support.
Quick Start
Install dependencies:
npm install
Setup and run:
| Command | Description |
|---|---|
npx tsx src/cli.ts setup |
Interactive CLI setup |
npx tsx src/cli.ts chat |
Start chatting (TUI) |
npx tsx src/cli.ts serve |
Start the gateway server |
npx tsx src/cli.ts "message" |
One-shot mode |
Setup (Telegram-first)
- Create a bot in Telegram via @BotFather
- Run
camel setupand paste the token - Send a message to your bot — it pairs with your account
- Use
/newagentin Telegram to create AI agents
CLI Commands
| Command | Description |
|---|---|
camelagi bootstrap |
First-time setup via Telegram admin bot |
camelagi bootstrap <token> |
Non-interactive bootstrap |
camelagi chat |
Interactive TUI session |
camelagi chat --session <name> |
Resume a named session |
camelagi "message" |
One-shot mode (prints response and exits) |
camelagi serve |
Start gateway server (HTTP + WebSocket + Telegram) |
camelagi serve --port 3000 |
Custom port |
camelagi sessions |
List saved sessions |
camelagi sessions rm <name> |
Delete a session |
camelagi agents |
List configured agents |
camelagi agents rm <id> |
Remove an agent |
camelagi soul <id> |
Edit agent’s SOUL.md in $EDITOR |
camelagi cron |
List all cron jobs (config + runtime) |
camelagi cron add |
Add a runtime cron job |
camelagi cron rm <id> |
Remove a runtime cron job |
camelagi cron run <id> |
Trigger a cron job immediately |
camelagi setup |
Interactive setup wizard |
camelagi doctor |
Health checks |
camelagi daemon install |
Install as launchd daemon (auto-start on boot) |
camelagi daemon uninstall |
Remove launchd daemon |
camelagi daemon status |
Check daemon status |
camelagi logs |
Tail server request log |
camelagi logs -n 100 |
Show last N log lines |
camelagi config list |
Show all config |
camelagi config get <key> |
Get a config value |
camelagi config set <key> <val> |
Set a config value |
Architecture
┌──────────────────────────────────────────────────────────┐
│ CLI (cli.ts) │
│ Parses args → routes to: bootstrap, chat, serve, setup, │
│ doctor, sessions, agents, soul, config, or one-shot │
└───────┬────────────┬────────────────┬────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌────────────┐ ┌──────────────┐
│ TUI │ │ One-Shot │ │ Standalone │
│ (tui.ts) │ │ (cli.ts) │ │ Gateway │
│ ▲ │ │ │ │ (serve.ts) │
│ │ WS │ │ HTTP POST │ │ ▲ ▲ │
│ ▼ │ │ │ │ │ │ │
│ Embedded │ │ Embedded │ │ │ │ │
│ Gateway │ │ Gateway │ │ │ │ │
└────┬─────┘ └──────┬──────┘ └───┼─────┼────┘
│ │ │ │
└───────┬───────┘ │ │
▼ │ │
┌─────────────────────────┐ │ │
│ Gateway Server │◄───────┘ │
│ (serve.ts) │ │
│ │ ┌──────────┘
│ Express HTTP: │ │
│ POST /chat │ │ WebSocket:
│ GET /sessions │ │ { type: "chat" }
│ GET /health │ │ { type: "abort" }
│ CRUD /agents │ │ { type: "switch_session" }
│ /approvals/:id/decide │ │
│ │ │
│ Channels: │
│ Telegram (grammY) │
│ Discord (discord.js) │
│ + pluggable adapters │
└────────┬────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Agent (agent.ts) │
│ │
│ ┌─────────────────────┐ ┌───────────────────────┐ │
│ │ Claude Agent SDK │ │ OpenAI-Compatible │ │
│ │ (Claude models) │ │ (All other models) │ │
│ │ │ │ │ │
│ │ • Built-in tools │ │ • Streaming chat │ │
│ │ • MCP server │ │ • OpenAI SDK │ │
│ │ • Thinking │ │ • Any provider │ │
│ │ • Subagents │ │ │ │
│ │ • Session resume │ │ │ │
│ │ • Hooks │ │ │ │
│ └─────────────────────┘ └───────────────────────┘ │
└──────────┬──────────────────────────────────────────┘
│
┌─────┼──────────┬───────────┬──────────┐
▼ ▼ ▼ ▼ ▼
┌────────┐┌────────┐┌──────────┐┌─────────┐┌────────┐
│Sessions││Compact ││ Memory ││ Hooks ││ Skills │
│ .jsonl││ LLM ││ search ││ shell ││ .md │
│ files ││summary ││ keyword ││ scripts ││prompts │
└────────┘└────────┘└──────────┘└─────────┘└────────┘
Dual-Path Agent
The agent routes execution based on the model:
- Claude models (
claude-*) → Claude Agent SDK with full tool support, thinking, subagents, session resume, and MCP server - All other models (GPT, DeepSeek, Ollama, etc.) → OpenAI-compatible streaming chat via the
openaiSDK
Detection: model.startsWith("claude-") || model.includes("/claude-")
Data Flow (TUI Chat)
User types message
│
▼
TUI sends WS: { type: "chat", message, session, sdkSessionId }
│
▼
Gateway receives → loads history → compacts if needed → retries
│
▼
Agent runs (SDK or OpenAI path)
│
▼
Events stream back: tool_call, tool_result, stream_text,
thinking, subagent_start, usage, chunk
│
▼
Gateway sends WS events → TUI renders in real-time
│
▼
Final result saved to session (JSONL)
Project Structure
src/
├── cli.ts # Entry point — command routing
├── agent.ts # Dual-path agent (SDK + OpenAI)
├── serve.ts # Express + WebSocket gateway
├── daemon.ts # launchd daemon install/uninstall/status
├── config.ts # YAML config + Zod schema
├── session.ts # JSONL session persistence
├── workspace.ts # Bootstrap files + system prompt
├── compact.ts # Context compaction + memory flush
├── model.ts # Anthropic SDK client (direct API)
├── retry.ts # Error classification + retry logic
├── runs.ts # Active run tracking (by runId)
├── queue.ts # Message queue for concurrent requests
├── lanes.ts # Concurrency lanes (main/cron/subagent)
├── hooks.ts # Lifecycle hooks (~/.camelagi/hooks/)
├── skills.ts # Skill loader (~/.camelagi/skills/)
├── usage.ts # Token usage tracking
├── constants.ts # Shared constants (no magic numbers)
├── errors.ts # Error helpers
├── chunker.ts # Block-aware text chunker
├── telegram.ts # Telegram bots via grammY (multi-agent)
├── channels/
│ ├── types.ts # Channel interface
│ ├── adapter.ts # ChannelAdapter interface + RuntimeState
│ ├── handler.ts # Shared command handling + streaming + chunking
│ ├── registry.ts # Channel registry (start/stop/reconcile)
│ ├── index.ts # Lazy channel loader
│ ├── telegram.ts # TelegramChannel wrapper
│ └── discord.ts # Discord channel (discord.js)
├── bootstrap.ts # First-time setup (token → admin bot)
├── approvals.ts # Tool approval engine
├── approval-forward.ts # Headless approval forwarding
├── boot.ts # BOOT.md startup script
├── cron.ts # Scheduled agent runs
├── doctor.ts # Health checks
├── setup.ts # Interactive setup wizard
├── policy.ts # Tool allow/deny filtering
├── types.ts # Shared types
├── gateway/
│ ├── routes.ts # REST API endpoints
│ ├── state.ts # Shared gateway state + auth
│ ├── ws-handler.ts # WebSocket message dispatch
│ ├── logger.ts # JSON-line request logger
│ ├── rate-limit.ts # Per-IP rate limiting middleware
│ └── csrf.ts # CSRF protection middleware
├── tools/
│ ├── memory.ts # memory_search + memory_get (MCP)
│ ├── patch.ts # apply_patch tool (MCP)
│ └── cron.ts # cron management tool (MCP)
└── tui/
├── tui.ts # Full TUI (~800+ LOC)
├── theme.ts # Colors and styling
└── components/
├── chat-log.ts # Scrollable message list
├── assistant-message.ts # Streaming markdown render
├── user-message.ts # User message display
├── tool-execution.ts # Tool call/result display
├── welcome.ts # Claude Code-style welcome screen
├── hint-bar.ts # Bottom hints bar
└── custom-editor.ts # Multi-line input editor
Configuration
Config file: ~/.camelagi/config.yaml
# Provider and model
provider: anthropic # anthropic | openai
model: claude-sonnet-4-20250514
apiKey: sk-ant-... # or set ANTHROPIC_API_KEY env var
baseUrl: # custom API endpoint (OpenRouter, Ollama, etc.)
# Agent behavior
thinking: off # off | low | medium | high
effort: high # low | medium | high | max (SDK only)
maxBudgetUsd: # cost cap per run (SDK only)
maxTurns: 25 # max agent turns per message
timeoutSeconds: 300 # per-run timeout
# Gateway server
serve:
port: 18789
host: 127.0.0.1
token: # auth token for HTTP/WS
rateLimit:
windowMs: 60000 # 1 minute window
max: 60 # max requests per window per IP
# Named agents (multi-bot)
agents:
admin:
name: Admin
admin: true # admin bot gets control plane commands
telegram:
botToken: "..."
allowedUsers: [123456789]
personal:
name: Personal
model: deepseek/deepseek-chat
telegram:
botToken: "..."
allowedUsers: [123456789]
discord: # optional Discord channel
botToken: "MTk..."
allowedChannels: []
allowedRoles: []
mentionOnly: true
# Voice transcription
voice:
enabled: false
provider: groq # groq | openai | deepgram
# apiKey: gsk_...
# model: whisper-large-v3-turbo
# language: en
# Legacy single-bot Telegram (deprecated — use agents instead)
telegram:
botToken:
allowedUsers: []
groups:
mentionOnly: true # only respond when @mentioned in groups
# Approvals
approvals:
mode: off # off | smart | always
allowlist: [] # e.g. ["Bash:git *", "Read"]
timeoutSeconds: 120
fallback: deny # deny | allow
forwardTo: # Telegram chat ID for headless approvals
# Context management
compaction:
enabled: true
maxTokens: 80000 # trigger compaction at 80% of this
keepTurns: 6 # keep last N turns, summarize rest
# Tool policy
tools:
allow: [] # whitelist (empty = allow all)
deny: [] # blacklist
# Skills
skills:
enabled: true
deny: [] # skills to disable
# Lifecycle hooks
hooks:
enabled: false
# Retry
retry:
maxRetries: 3
backoffMs: 1000
# Concurrency lanes
lanes:
main: 3
cron: 1
subagent: 5
# Boot script
boot: true
# Cron jobs (config-defined — runtime jobs are in ~/.camelagi/cron/jobs.json)
cron:
- id: daily-summary
schedule: "24h" # or cron: "*/30 * * * *"
prompt: "Summarize today's activity"
session: daily
enabled: false
Environment Variables
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Anthropic API key |
OPENAI_API_KEY |
OpenAI API key (fallback if no Anthropic key) |
CAMELAGI_MODEL |
Model override |
CAMELAGI_PROVIDER |
Provider override |
CAMELAGI_BASE_URL |
Custom base URL |
CAMELAGI_TOKEN |
Gateway auth token |
TELEGRAM_BOT_TOKEN |
Telegram bot token (legacy) |
Telegram Admin Bot
The admin bot is a BotFather-style control plane. It uses conversational wizards for multi-step flows and inline keyboards for selection.
Admin Commands
| Command | Description |
|---|---|
/start |
Welcome + auto-lock to first user |
/help |
List all commands |
/setup |
Wizard: configure API provider, key, model |
/newagent |
Wizard: create a new agent (name → description → model → token) |
/agents |
List all agents with status |
/deleteagent |
Pick & delete an agent (inline buttons) |
/soul |
View/edit agent SOUL.md (inline buttons) |
/config |
View config or /config <key> <value> to update |
/sessions |
List sessions with cleanup buttons |
/pairing |
List pending access requests with Approve/Deny buttons |
/voice |
Configure voice transcription (Groq/OpenAI/Deepgram) |
/status |
System health (bots, API, sessions) |
/restart |
Restart all bots or /restart <id> for one |
/cancel |
Cancel active wizard |
Agent Bot Commands
Each non-admin agent bot has:
| Command | Description |
|---|---|
/start |
Welcome message |
/help |
List commands |
/clear |
Clear chat history |
/status |
Model, message count, usage |
/model <name> |
Switch model (runtime) |
/think |
Set thinking level (inline buttons) |
/effort |
Set effort level (inline buttons) |
/usage |
Token usage breakdown for this session |
/compact |
Force history compaction |
/voice |
Voice transcription info (redirects to admin if not configured) |
How It Works
- Bootstrap:
camelagi bootstrap→ enter bot token → starts admin bot - Auto-lock: First
/startlocks the bot to your Telegram account - Wizards: Multi-step flows with inline keyboards, 10-min timeout,
/cancelto abort - Hot-start: New agents created via
/newagentstart polling immediately - ID generation: Agent names auto-generate unique IDs (e.g. “Personal Finance” →
personalfinance)
Workspace
All workspace files live in ~/.camelagi/workspace/:
| File | Purpose |
|---|---|
AGENTS.md |
Agent instructions and guidelines |
SOUL.md |
Personality and tone |
IDENTITY.md |
Name and emoji |
USER.md |
User profile (name, timezone, preferences) |
TOOLS.md |
Environment-specific tool notes |
MEMORY.md |
Curated long-term memory |
memory/*.md |
Daily auto-flushed memory notes |
BOOT.md |
Startup script (runs on gateway launch) |
Per-agent workspaces live in ~/.camelagi/agents/{id}/ with their own SOUL.md, TOOLS.md, MEMORY.md, and memory/ directory.
These files are injected into the system prompt (truncated if needed: 20K per file, 150K total).
Channels
TUI
The TUI connects to an embedded gateway server via WebSocket. Features:
- Claude Code-style welcome screen with ASCII art
- Real-time streaming with markdown rendering
- Tool call visualization with timing
- Thinking/reasoning display
- Autocomplete for slash commands
- Session management (switch, create, clear)
- Status spinner (thinking, responding, tool use, subagent)
- SDK session resume across messages
Slash commands: /help, /model, /config, /sessions, /session, /new, /clear, /tools, /skills, /think, /effort, /context, /status, /compact, /setup, /agents, /soul, /exit
Keyboard shortcuts: Ctrl+L (model), Ctrl+P (session), Ctrl+O (tools), Escape (abort)
Channel System
CamelAGI uses a pluggable channel architecture. Channels implement a common Channel interface for lifecycle management (start/stop/reconcile/hot-reload) and use a shared ChannelAdapter for command handling, streaming, and chunking. Adding a new channel means implementing one adapter file.
Shared across all channels:
- Command handling (
/help,/clear,/model,/think,/effort,/status,/usage,/compact) - Message flow (orchestrate → stream → chunk)
- Runtime overrides (per-conversation model/thinking/effort)
- Session persistence
Channel-specific:
- Platform SDK (grammY, discord.js, etc.)
- Auth/access control
- Message format, reactions, inline UI
- Rate limits and message size limits
Telegram
The Telegram adapter uses grammY with multi-bot support:
- Admin bot: Wizards + control commands (no AI chat)
- Agent bots: AI chat with streaming, tools, approvals
- Message reactions for status (👀 seen, 🤔 thinking, 💭 reasoning, 🔧 tool use)
- Real-time streaming (edits throttled to 1.2s)
- Block-aware chunking for long responses (4096 char Telegram limit)
- Mention gating in groups (
@botnameto trigger) - User allowlist per agent
- Pairing flow for new user approval (see below)
- Session per agent+chat (
{agentId}-{chatId}) - Inline keyboards for approval requests
- Compaction and retry
- Auto-restart polling with exponential backoff
Voice Transcription
Agent bots can receive and transcribe voice messages. Supported providers:
| Provider | Config Key | Notes |
|---|---|---|
| Groq | groq |
Free tier, fast, recommended |
| OpenAI | openai |
Whisper API |
| Deepgram | deepgram |
Real-time transcription |
Setup: Use /voice in the admin bot to configure. The wizard walks through provider selection, API key, model, and language.
How it works: When a voice message is received, the agent bot downloads the audio, sends it to the transcription provider, and passes the text to the LLM as [Voice] transcribed text.
Discord
The Discord adapter uses discord.js with the following features:
- Guild messages + DMs
- Mention-only mode in servers (responds to @mention or replies to bot)
- Channel restriction via
allowedChannels - Role restriction via
allowedRoles - Streaming — sends a message then edits it as text arrives (600ms throttle)
- All shared commands (
/help,/clear,/model,/think,/effort,/status,/usage,/compact) - Auto-chunking for responses >2000 chars (Discord limit)
- Hot-reload — start/stop/reconcile on config change
Config:
agents:
myagent:
name: "My Agent"
discord:
botToken: "MTk..."
allowedChannels: [] # empty = all channels
allowedRoles: [] # empty = all users
mentionOnly: true # require @mention in servers
User Pairing (Access Approval)
When a new user messages an agent bot and they’re not in allowedUsers:
- User gets: “Access requested. Waiting for admin approval.” + a 6-character pairing code
- Admin bot gets: Notification with user info (username, ID) + Approve / Deny inline buttons
- Admin taps Approve → user is added to
allowedUsersin config.yaml, user gets “Access approved” - Admin taps Deny → user gets “Access denied”, request is removed
Details:
- Pairing codes use a human-friendly alphabet (no O/0/I/1 confusion)
- Requests expire after 1 hour
- Max 10 pending requests at a time
- Duplicate requests return the same code (no spam)
- Admin can also use
/pairingto list all pending requests - Pairing data stored at
~/.camelagi/pairing.json
If allowedUsers is empty (no access control), all users are allowed without pairing.
HTTP API
The gateway exposes REST endpoints:
| Endpoint | Description |
|---|---|
POST /chat |
Send a message, get a response |
GET /sessions |
List all sessions |
GET /sessions/:id/messages |
Get session messages |
DELETE /sessions/:id |
Delete a session |
GET /agents |
List all agents |
POST /agents |
Create an agent |
DELETE /agents/:id |
Delete an agent |
GET /agents/:id/soul |
Get agent SOUL.md |
PUT /agents/:id/soul |
Update agent SOUL.md |
GET /config |
Get config (API key masked) |
PATCH /config |
Update config |
POST /approvals/:id/decide |
Decide on a pending approval |
GET /health |
Health check |
Approvals
Tool approval system with three modes:
- off: All tools auto-approved
- smart: Read-only tools auto-approved, write tools need approval
- always: Every tool call needs approval
Approval UI:
- TUI: SelectList overlay (Allow once / Always allow / Deny)
- Telegram: Inline keyboard buttons
- Headless: Forward to configured Telegram chat via
forwardTo
Allowlist patterns: "Bash:git *", "Write:/path/*", "ToolName" (glob matching)
Agent Events
The agent emits event types for real-time UI updates:
| Event | Description |
|---|---|
init |
SDK session initialized (contains sessionId) |
stream_text |
Incremental text token |
chunk |
Final complete response |
tool_call |
Tool invoked (with id, name, args) |
tool_result |
Tool completed (with id, name, preview) |
thinking |
Thinking started/ended |
thinking_delta |
Incremental thinking text |
subagent_start |
Subagent spawned |
subagent_progress |
Subagent progress update |
subagent_done |
Subagent completed |
usage |
Token usage report |
approval_request |
Tool needs approval (with id, toolName, preview) |
approval_resolved |
Approval decided |
Context Compaction
When conversation history exceeds 80% of maxTokens:
- Split history into old messages and recent N turns
- Memory flush: Extract durable facts from old messages → append to
memory/YYYY-MM-DD.md - Summarize: Compress old messages into a summary via LLM
- Replace old messages with summary + keep recent turns
Error Handling & Retry
Errors are classified into kinds:
| Kind | Retryable | Examples |
|---|---|---|
auth |
No | 401, 403, invalid API key |
billing |
No | 402, payment required |
format |
No | 400, invalid request |
rate_limit |
Yes | 429, 5xx server errors |
timeout |
Yes | Timeouts, aborted |
overflow |
Once | Context too large (triggers compaction) |
unknown |
Once | Anything else |
Retry uses exponential backoff: backoffMs * 2^attempt.
Concurrency
Lanes limit parallel agent runs:
main(3): TUI/HTTP/Telegram messagescron(1): Scheduled jobssubagent(5): SDK subagents
Run tracking uses runId as primary key (not session ID) to prevent race conditions when multiple connections share a session.
Cron Jobs
Scheduled agent runs with two sources:
- Config-defined:
config.yamlcron:array — managed by editing the file - Runtime-defined:
~/.camelagi/cron/jobs.json— managed via agent tool or CLI
Schedule Formats
| Format | Type | Example |
|---|---|---|
5m, 1h, 30s, 1d |
Repeating interval | Every 5 minutes |
*/5 * * * * |
Cron expression | Every 5 minutes (minute field) |
+20m, +2h |
One-shot relative | Runs once in 20 min, auto-deletes |
2026-03-14T09:00:00Z |
One-shot absolute | Runs once at exact time, auto-deletes |
Agent Tool (Claude models only)
The agent has a cron tool with 4 actions. Users can say things like:
- “remind me in 20 minutes to check the deploy”
- “schedule a daily summary every morning”
- “show me my cron jobs”
- “delete the daily-check job”
The tool supports: list, add, remove, run.
Relative schedules (+20m) are converted to absolute ISO timestamps on creation, so they survive server restarts correctly.
CLI
# List all jobs (config + runtime)
camelagi cron
# Add a repeating job
camelagi cron add --name "Daily Check" --schedule "1d" --prompt "check server status"
# Add a one-shot reminder
camelagi cron add --schedule "+20m" --prompt "remind me about the meeting"
# Remove a runtime job
camelagi cron rm job-m1abc
# Trigger a job immediately
camelagi cron run daily-summary
Error Backoff
On consecutive failures, retry delay escalates: 30s → 1m → 5m → 15m → 60m. Resets to normal schedule after a successful run.
Execution
- Jobs run using
setTimeoutchains (notsetInterval) to support dynamic backoff - Repeating jobs run immediately on startup, then on schedule
- One-shot jobs run after the delay, then auto-delete from the store
- Each job gets its own session:
cron-{jobId}(or customsessionfield) - Runs through the standard
runAgent()with full tool access - Cron lane limits parallel execution (default: 1 concurrent cron job)
Hooks
Place shell scripts in ~/.camelagi/hooks/:
~/.camelagi/hooks/
before_prompt.log.sh # Runs before each user message
after_response.notify.sh # Runs after each response
before_tool.audit.sh # Runs before each tool call
after_tool.metrics.sh # Runs after each tool call
Context is passed via environment variables: CAMELAGI_HOOK_POINT, CAMELAGI_HOOK_SESSION, CAMELAGI_HOOK_MESSAGE, CAMELAGI_HOOK_TOOL, etc.
Skills
Place skill directories in ~/.camelagi/skills/:
~/.camelagi/skills/
my-skill/
SKILL.md # Skill definition with optional YAML frontmatter
Frontmatter format:
---
name: my-skill
description: Does something useful
---
# Skill instructions here...
Skills are injected into the system prompt (budget: 30K chars total).
Security
Timing-Safe Token Comparison
The gateway auth token is compared using crypto.timingSafeEqual with SHA-256 hashing on both sides. This prevents timing attacks where an attacker could deduce the token character-by-character by measuring response times.
CSRF Protection
The gateway blocks cross-origin mutation requests (POST, PUT, PATCH, DELETE) from browsers:
- Rejects requests with
Sec-Fetch-Site: cross-site - Rejects requests with non-loopback
Originheader - Rejects requests with non-loopback
Refererheader (when noOriginpresent) - Non-browser clients (curl, Node.js) that don’t send these headers pass through
Security Audit (camelagi doctor)
The doctor command includes security checks:
- Config file permissions — warns if
config.yamlis readable by other users (should be600) - State directory permissions — warns if
~/.camelagi/is accessible by others (should be700) - Auth token — warns if no token is set or if token is shorter than 24 characters
- Bind address — warns if the server binds to a non-loopback address (exposed to network)
Dependencies
| Package | Purpose |
|---|---|
@anthropic-ai/claude-agent-sdk |
Claude Agent SDK (tools, thinking, subagents) |
@anthropic-ai/sdk |
Anthropic API client (compaction, doctor) |
openai |
OpenAI-compatible API client (multi-provider fallback) |
@mariozechner/pi-tui |
Terminal UI framework |
grammy |
Telegram bot framework |
discord.js |
Discord bot framework |
express |
HTTP server |
ws |
WebSocket server |
zod |
Schema validation |
yaml |
YAML config parsing |
chalk |
Terminal colors |
dotenv |
Environment variable loading |
Daemon (macOS)
CamelAGI can run as a background daemon that auto-starts on boot and auto-restarts on crash. This uses macOS launchd.
# Install — generates plist and starts immediately
camelagi daemon install
# Check if it's running
camelagi daemon status
# Remove — stops and removes the plist
camelagi daemon uninstall
The plist is written to ~/Library/LaunchAgents/com.camelagi.server.plist and runs camelagi serve. Daemon stdout/stderr logs go to ~/.camelagi/logs/daemon.{stdout,stderr}.log.
This is opt-in — by default you still start the server manually with camelagi serve.
Request Logging
The gateway logs every HTTP request as a JSON line to ~/.camelagi/logs/server.log. Each entry contains:
{ "ts": "2026-03-13T12:00:00.000Z", "method": "POST", "path": "/chat", "status": 200, "ms": 1523, "sessionId": "my-session" }
- Logs auto-rotate daily (old log renamed to
server-YYYY-MM-DD.log) - Rotated logs older than 7 days are cleaned up on startup
- Only active when the server runs in non-silent mode (i.e.
camelagi serve, not embedded mode)
# View recent logs
camelagi logs
# Show last 100 lines
camelagi logs -n 100
Rate Limiting
The gateway includes per-IP rate limiting to protect against runaway clients burning API credits.
- Default: 60 requests per minute per IP
- Returns
429 Too Many RequestswithX-RateLimit-*headers when exceeded - Configurable in
config.yaml:
serve:
rateLimit:
windowMs: 60000 # 1 minute
max: 60 # max requests per window
This works alongside concurrency lanes (which limit parallel agent runs) — rate limiting caps total request volume, lanes cap concurrent execution.
Claude Agent SDK Features Used
Core
query()— Main agent execution with streaming message iterationtool()— Define custom MCP tools (memory_search, memory_get, apply_patch, cron)createSdkMcpServer()— In-process MCP server for custom tools
Built-in Tools
Read,Write,Edit,Bash— File and shell operationsGlob,Grep— File searchWebSearch,WebFetch— Web accessAgent— Subagent spawning
Configuration
systemPrompt— Custom system prompt with bootstrap filesallowedTools— Tool whitelistdisallowedTools— Tool blacklistmaxTurns— Turn limit per runmaxBudgetUsd— Cost cap per runpermissionMode: "bypassPermissions"— Headless executioncwd— Working directoryenv— Environment variables (ANTHROPIC_API_KEY)thinking: { type: "adaptive" }— Adaptive thinking for Claudeeffort— Thinking effort levelsettingSources: ["project"]— Load project-level settingsincludePartialMessages— Enable stream events
Hooks (SDK-level)
PreToolUse— Before each tool call (logging, audit, approval, custom hooks)PostToolUse— After each tool call (logging, metrics)HookCallback— Type for hook functions
Session Management
session_id— Captured frominitsystem messageresume— Resume a previous SDK session by ID
Streaming Events
result— Final result with usage statssystem— Init, task lifecycle (started, progress, notification)stream_event— SSE events (text_delta, thinking_delta, content_block_start/stop)
Subagent Tracking
task_started,task_progress,task_notification— Subagent lifecycle events withagent_id,tool_use_id,tool_count,duration_ms