camelagi 0.5.0 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +241 -104
  3. package/dist/agent/agent-sdk.js +136 -65
  4. package/dist/agent/agent-sdk.js.map +1 -1
  5. package/dist/agent.js +4 -1
  6. package/dist/agent.js.map +1 -1
  7. package/dist/bootstrap.js +148 -331
  8. package/dist/bootstrap.js.map +1 -1
  9. package/dist/channels/handler.js +109 -1
  10. package/dist/channels/handler.js.map +1 -1
  11. package/dist/cli/cmd-agents.js +11 -14
  12. package/dist/cli/cmd-agents.js.map +1 -1
  13. package/dist/cli/cmd-bootstrap.js +4 -4
  14. package/dist/cli/cmd-bootstrap.js.map +1 -1
  15. package/dist/cli/cmd-install.js +80 -0
  16. package/dist/cli/cmd-install.js.map +1 -0
  17. package/dist/cli/cmd-pairing.js +14 -15
  18. package/dist/cli/cmd-pairing.js.map +1 -1
  19. package/dist/cli/cmd-reset.js +10 -12
  20. package/dist/cli/cmd-reset.js.map +1 -1
  21. package/dist/cli/cmd-sessions.js +10 -13
  22. package/dist/cli/cmd-sessions.js.map +1 -1
  23. package/dist/cli/cmd-status.js +67 -0
  24. package/dist/cli/cmd-status.js.map +1 -0
  25. package/dist/cli/cmd-uninstall.js +79 -0
  26. package/dist/cli/cmd-uninstall.js.map +1 -0
  27. package/dist/cli/cmd-update.js +47 -0
  28. package/dist/cli/cmd-update.js.map +1 -0
  29. package/dist/cli.js +44 -26
  30. package/dist/cli.js.map +1 -1
  31. package/dist/core/config.js +53 -8
  32. package/dist/core/config.js.map +1 -1
  33. package/dist/core/log.js +33 -13
  34. package/dist/core/log.js.map +1 -1
  35. package/dist/core/update-check.js +11 -13
  36. package/dist/core/update-check.js.map +1 -1
  37. package/dist/core/version.js +4 -0
  38. package/dist/core/version.js.map +1 -0
  39. package/dist/runtime/orchestrate.js +25 -15
  40. package/dist/runtime/orchestrate.js.map +1 -1
  41. package/dist/serve.js +22 -7
  42. package/dist/serve.js.map +1 -1
  43. package/dist/setup.js +259 -231
  44. package/dist/setup.js.map +1 -1
  45. package/dist/telegram/admin-bot.js +325 -1
  46. package/dist/telegram/admin-bot.js.map +1 -1
  47. package/dist/telegram/agent-bot.js +143 -2
  48. package/dist/telegram/agent-bot.js.map +1 -1
  49. package/dist/telegram/resolve.js +11 -2
  50. package/dist/telegram/resolve.js.map +1 -1
  51. package/dist/telegram/wizards.js +361 -12
  52. package/dist/telegram/wizards.js.map +1 -1
  53. package/dist/telegram.js +2 -0
  54. package/dist/telegram.js.map +1 -1
  55. package/dist/usage.js +106 -0
  56. package/dist/usage.js.map +1 -1
  57. package/dist/workspace.js +20 -0
  58. package/dist/workspace.js.map +1 -1
  59. package/package.json +7 -4
  60. package/dist/agent/agent-openai.js +0 -206
  61. package/dist/agent/agent-openai.js.map +0 -1
  62. package/dist/approval-forward.js +0 -42
  63. package/dist/approval-forward.js.map +0 -1
  64. package/dist/approvals.js +0 -151
  65. package/dist/approvals.js.map +0 -1
  66. package/dist/camelagi-gateway.mjs +0 -93611
  67. package/dist/camelagi-gateway.mjs.map +0 -7
  68. package/dist/compact.js +0 -92
  69. package/dist/compact.js.map +0 -1
  70. package/dist/config.js +0 -153
  71. package/dist/config.js.map +0 -1
  72. package/dist/constants.js +0 -21
  73. package/dist/constants.js.map +0 -1
  74. package/dist/cron.js +0 -81
  75. package/dist/cron.js.map +0 -1
  76. package/dist/errors.js +0 -5
  77. package/dist/errors.js.map +0 -1
  78. package/dist/hooks.js +0 -72
  79. package/dist/hooks.js.map +0 -1
  80. package/dist/lanes.js +0 -62
  81. package/dist/lanes.js.map +0 -1
  82. package/dist/policy.js +0 -22
  83. package/dist/policy.js.map +0 -1
  84. package/dist/queue.js +0 -45
  85. package/dist/queue.js.map +0 -1
  86. package/dist/retry.js +0 -96
  87. package/dist/retry.js.map +0 -1
  88. package/dist/runs.js +0 -83
  89. package/dist/runs.js.map +0 -1
  90. package/dist/skills.js +0 -89
  91. package/dist/skills.js.map +0 -1
  92. package/dist/subagent.js +0 -71
  93. package/dist/subagent.js.map +0 -1
  94. package/dist/telegram-admin.js +0 -800
  95. package/dist/telegram-admin.js.map +0 -1
  96. package/dist/tools/edit.js +0 -29
  97. package/dist/tools/edit.js.map +0 -1
  98. package/dist/tools/exec.js +0 -38
  99. package/dist/tools/exec.js.map +0 -1
  100. package/dist/tools/fetch.js +0 -28
  101. package/dist/tools/fetch.js.map +0 -1
  102. package/dist/tools/index.js +0 -16
  103. package/dist/tools/index.js.map +0 -1
  104. package/dist/tools/read.js +0 -26
  105. package/dist/tools/read.js.map +0 -1
  106. package/dist/tools/search.js +0 -62
  107. package/dist/tools/search.js.map +0 -1
  108. package/dist/tools/subagent.js +0 -48
  109. package/dist/tools/subagent.js.map +0 -1
  110. package/dist/tools/write.js +0 -22
  111. package/dist/tools/write.js.map +0 -1
  112. package/dist/types.js +0 -3
  113. package/dist/types.js.map +0 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 CamelAGI Contributors
3
+ Copyright (c) 2026 Nawaf Almutairi & Brickell Lab, Kuwait
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -16,27 +16,186 @@
16
16
 
17
17
  <br>
18
18
 
19
- CamelAGI is a self-hosted AI assistant that runs on your server and puts you in full control from your phone. One command to set up, then manage everything from Telegram no terminal needed.
19
+ CamelAGI is a self-hosted AI assistant that runs on your server and puts you in full control from your phone. One command to install, then manage everything — create agents, switch models, approve tools, monitor usage — all from Telegram, Discord, or your terminal.
20
20
 
21
21
  <p align="center">
22
- <a href="https://camelagi.net"><strong>🌐 CamelAGI.net</strong></a>
22
+ <img src="assets/Claude_Logo.png" alt="Claude Logo" width="200" />
23
+ </p>
24
+ <p align="center">
25
+ Powered by <a href="https://platform.claude.com/docs/en/agent-sdk/overview">Claude Agent SDK</a> — the same runtime behind Claude Code.
26
+ </p>
27
+
28
+ <p align="center">
29
+ <a href="https://camelagi.net"><strong>camelagi.net</strong></a>
23
30
  </p>
24
31
 
25
32
  ## Contents
26
33
 
27
- - [Built on Claude Agent SDK](#built-on-claude-agent-sdk)
28
- - [Admin Bot — BotFather for Your AI Server](#admin-bot--botfather-for-your-ai-server)
29
- - [Claude Agent SDK vs pi-agent](#claude-agent-sdk-vs-pi-agent)
30
34
  - [Quick Start](#quick-start)
31
35
  - [Features](#features)
32
- - [Terminal UI — `camel chat`](#terminal-ui--camel-chat)
33
- - [Developer Experience](#developer-experience)
34
- - [Roadmap](#roadmap)
36
+ - [Channels](#channels)
37
+ - [Admin Bot Commands](#admin-bot-commands)
38
+ - [Agent Bot Commands](#agent-bot-commands)
39
+ - [CLI Commands](#cli-commands)
40
+ - [Built on Claude Agent SDK](#built-on-claude-agent-sdk)
41
+ - [Claude Agent SDK vs pi-agent](#claude-agent-sdk-vs-pi-agent)
42
+ - [Configuration](#configuration)
43
+ - [Architecture](#architecture)
35
44
  - [Documentation](#documentation)
45
+ - [Roadmap](#roadmap)
36
46
  - [License](#license)
37
47
 
38
48
  <br>
39
49
 
50
+ ## Quick Start
51
+
52
+ **Install** (no Node.js required)
53
+ ```bash
54
+ curl -fsSL https://raw.githubusercontent.com/inawafalm/CamelAGI/main/install.sh | bash
55
+ ```
56
+
57
+ **Setup & Run**
58
+ ```bash
59
+ camel bootstrap # Full setup: admin bot + pairing + API config
60
+ camel serve # Start the gateway server
61
+ camel chat # Terminal UI
62
+ ```
63
+
64
+ `bootstrap` walks you through everything: create a Telegram admin bot, pair your account with OTP, pick your AI provider and model. After that, use `/newagent` in Telegram to create your first AI agent.
65
+
66
+ <br>
67
+
68
+ ## Features
69
+
70
+ > 3 channels, 18 CLI commands, 10 built-in tools — terminal, Telegram, or Discord.
71
+
72
+ | | Feature | Description |
73
+ |---|---|---|
74
+ | 🤖 | **Telegram — Admin Bot** | BotFather for AI agents. Create, configure, clone, and manage agents entirely from Telegram — instant commands, zero tokens |
75
+ | 💬 | **Telegram — Agent Bots** | Each agent gets its own Telegram bot. Message it like any chat — it runs tools, reads files, remembers context |
76
+ | 🎮 | **Discord Bots** | Per-agent Discord bots with mention-only mode, role filtering, and channel restrictions |
77
+ | ⌨️ | **Terminal UI** | Full TUI with streaming, slash commands, model switching, session management, and markdown rendering |
78
+ | 🧠 | **Agent Memory** | Isolated two-tier memory per agent — curated MEMORY.md + daily auto-journaling with recency-boosted search |
79
+ | 🎙️ | **Voice Transcription** | Send voice messages to agent bots — transcribed via Groq, OpenAI, or Deepgram and processed as text |
80
+ | 🔌 | **MCP Servers** | Connect external tool servers (stdio, HTTP, SSE). Global or per-agent. Add/remove from Telegram with `/mcp` |
81
+ | 💭 | **Extended Thinking** | Claude reasons step by step before answering. Configure depth: off, low, medium, high |
82
+ | ⏰ | **Cron Jobs** | Schedule AI tasks — daily summaries, monitoring, automations. Intervals, cron expressions, or one-shot timers |
83
+ | 📋 | **Brief Mode** | Toggle short text-message-style replies per chat or per agent — ideal for Telegram |
84
+ | 📊 | **Usage Tracking** | Per-agent token usage and cost breakdown — input, output, cache reads, API calls |
85
+ | 🧬 | **Agent Cloning** | Clone an existing agent with all its config, personality, memory, and MCP servers |
86
+ | 🔐 | **Secure Pairing** | OTP-based user verification. No hardcoded IDs — pairing code + 5-digit OTP from Telegram |
87
+ | 🔁 | **Multi-Provider** | Anthropic, OpenAI, OpenRouter, Ollama — any OpenAI-compatible endpoint. Zero vendor lock-in |
88
+ | 🛡️ | **Tool Approvals** | Human-in-the-loop safety. Approve dangerous operations from Telegram with inline buttons |
89
+ | 🪝 | **Skills & Hooks** | Teach agents skills via Markdown. Run shell/JS hooks before and after tool calls |
90
+ | 🔄 | **Auto Compaction** | Summarizes old turns at 80% capacity. Flushes facts to memory first — nothing is lost |
91
+ | ⚙️ | **Same Engine** | All channels run the same agent loop, same tools, same memory. Switch freely between them |
92
+
93
+ <br>
94
+
95
+ ## Channels
96
+
97
+ CamelAGI runs across three channels — all sharing the same agent runtime, tools, and memory.
98
+
99
+ ### Telegram
100
+
101
+ Two bot types work together:
102
+
103
+ - **Admin Bot** — A non-AI command bot (zero tokens burned). Create agents, manage config, approve users, monitor sessions, configure MCP servers, set up voice — all from Telegram. Think [@BotFather](https://t.me/BotFather), but for your entire AI infrastructure.
104
+ - **Agent Bots** — Each agent gets its own Telegram bot. Message it like any contact — it runs tools, reads files, remembers context, and supports voice messages.
105
+
106
+ ### Discord
107
+
108
+ Per-agent Discord bots with:
109
+ - **Mention-only mode** — responds only to @mentions in guild channels, all messages in DMs
110
+ - **Role filtering** — optional allowlist of Discord roles
111
+ - **Channel restrictions** — optional allowlist of channels
112
+
113
+ ### Terminal
114
+
115
+ `camel chat` gives you a full TUI with streaming, slash commands, keyboard shortcuts, model switching, session management, and one-shot mode (`camel "your question"`).
116
+
117
+ <br>
118
+
119
+ ## Admin Bot Commands
120
+
121
+ The Admin Bot is a non-AI Telegram bot for managing your entire CamelAGI server.
122
+
123
+ | Category | Command | Description |
124
+ |----------|---------|-------------|
125
+ | **Setup** | `/setup` | Configure API provider, key, model |
126
+ | | `/config` | View configuration |
127
+ | | `/config <key> <value>` | Update config |
128
+ | **Agents** | `/newagent` | Create agent wizard |
129
+ | | `/agents` | List all agents |
130
+ | | `/agent` | View/edit agent config |
131
+ | | `/deleteagent` | Delete an agent |
132
+ | | `/soul` | View/edit agent personality |
133
+ | **Tools** | `/mcp` | Manage MCP servers (add/list/remove) |
134
+ | | `/voice` | Configure voice transcription provider |
135
+ | **Monitor** | `/status` | System health & stats |
136
+ | | `/sessions` | List & manage sessions |
137
+ | | `/usage` | Per-agent usage & cost summary |
138
+ | | `/restart` | Restart agent bots |
139
+ | **Security** | `/pairing` | Manage access requests |
140
+ | **Utility** | `/help` | List all commands |
141
+ | | `/cancel` | Cancel active wizard |
142
+
143
+ <br>
144
+
145
+ ## Agent Bot Commands
146
+
147
+ Each agent bot supports these commands in Telegram:
148
+
149
+ | Category | Command | Description |
150
+ |----------|---------|-------------|
151
+ | **Chat** | `/clear` | Clear this chat's history |
152
+ | | `/compact` | Force compaction of chat history |
153
+ | | `/brief` | Toggle brief response mode |
154
+ | | `/export` | Export session as markdown file |
155
+ | **Model** | `/model` | Switch model for this chat |
156
+ | | `/think` | Set thinking level (off/low/medium/high) |
157
+ | | `/effort` | Set effort level (low/medium/high/max) |
158
+ | **Session** | `/session` | Show or switch session |
159
+ | | `/status` | Show model, message count, token usage |
160
+ | | `/usage` | Token usage for this session |
161
+ | **Tools** | `/skills` | List active skills |
162
+ | | `/mcp` | Manage MCP tool servers |
163
+ | | `/voice` | Voice transcription info |
164
+ | **Utility** | `/help` | List commands and current config |
165
+
166
+ <br>
167
+
168
+ ## CLI Commands
169
+
170
+ ```
171
+ camel <command> [options]
172
+ ```
173
+
174
+ | Category | Command | Description |
175
+ |----------|---------|-------------|
176
+ | **Getting Started** | `bootstrap` | First-time setup wizard |
177
+ | | `setup` | Interactive setup (re-run anytime) |
178
+ | | `chat` | Interactive terminal UI |
179
+ | **Server** | `serve` | Start the gateway server |
180
+ | | `daemon` | Manage launchd daemon (install/uninstall/status) |
181
+ | | `logs` | Tail server request log |
182
+ | | `status` | System health overview |
183
+ | **Agents & Sessions** | `agents` | List configured agents |
184
+ | | `soul` | View/edit agent SOUL.md in $EDITOR |
185
+ | | `sessions` | List saved sessions |
186
+ | | `pairing` | List and approve/deny pending requests |
187
+ | **Configuration** | `config` | View/edit config (get/set/list) |
188
+ | | `cron` | Manage cron jobs (list/add/rm/run) |
189
+ | **Maintenance** | `doctor` | Run health checks |
190
+ | | `reset` | Delete all config, sessions, agents |
191
+ | | `install` | Install to ~/.camelagi/versions/ and add to PATH |
192
+ | | `uninstall` | Remove CamelAGI completely |
193
+ | | `update` | Update to the latest version |
194
+
195
+ One-shot mode: `camel "your question"` — spins up an ephemeral gateway and answers inline.
196
+
197
+ <br>
198
+
40
199
  ## Built on Claude Agent SDK
41
200
 
42
201
  <p align="center">
@@ -99,123 +258,101 @@ CamelAGI uses **Claude Agent SDK**. OpenClaw uses **pi-agent-core**. Here's why
99
258
 
100
259
  <br>
101
260
 
102
- ## Quick Start
103
-
104
- **One-Liner**
105
- ```bash
106
- curl -fsSL https://camelAGI.ai/install.sh | bash
261
+ ## Configuration
262
+
263
+ CamelAGI uses a single YAML config file at `~/.camelagi/config.yaml`, validated with Zod.
264
+
265
+ ```yaml
266
+ # Provider & Model
267
+ provider: anthropic # anthropic | openai | openrouter | ollama
268
+ model: claude-sonnet-4-20250514
269
+ anthropicApiKey: sk-ant-...
270
+
271
+ # Telegram
272
+ telegramBotToken: "123456:ABC..."
273
+ allowedTelegramUsers: [123456789]
274
+
275
+ # Agents
276
+ agents:
277
+ coder:
278
+ model: claude-sonnet-4-20250514
279
+ thinkingLevel: medium
280
+ briefMode: true
281
+ telegramBotToken: "654321:XYZ..."
282
+ mcp:
283
+ servers:
284
+ github:
285
+ type: stdio
286
+ command: npx
287
+ args: ["-y", "@modelcontextprotocol/server-github"]
288
+
289
+ # MCP Servers (global)
290
+ mcp:
291
+ servers:
292
+ supabase:
293
+ type: http
294
+ url: https://mcp.supabase.com/...
107
295
  ```
108
296
 
109
- **npm**
110
- ```bash
111
- npm install -g camelagi
112
- ```
297
+ Environment variables override file values: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `CAMELAGI_MODEL`, `CAMELAGI_PROVIDER`, `CAMELAGI_TOKEN`, `TELEGRAM_BOT_TOKEN`.
113
298
 
114
- **Usage**
115
- ```bash
116
- camel bootstrap # Full setup: admin bot + pairing + API config
117
- camel serve # Start the gateway (after bootstrap)
118
- camel chat # Terminal UI
119
- ```
299
+ See [featuresDocs/configuration.md](featuresDocs/configuration.md) for the full configuration reference.
120
300
 
121
301
  <br>
122
302
 
123
- ## Admin Bot — BotFather for Your AI Server
124
-
125
- Create agents, manage config, approve users, monitor sessions, restart bots — all from Telegram. The Admin Bot is a **non-AI Telegram bot** — no LLM calls, no tokens burned, just instant commands. Think [@BotFather](https://t.me/BotFather), but for your entire AI infrastructure.
126
-
127
- ### Getting Started
128
-
129
- 1. Create a bot with [@BotFather](https://t.me/BotFather) on Telegram
130
- 2. Run `camel bootstrap` and paste the bot token
131
- 3. Send a message to your bot on Telegram — the CLI detects you
132
- 4. Approve yourself in the CLI, then enter the OTP in Telegram
133
- 5. Pick your AI provider and model (or skip and do it later via `/setup` in Telegram)
134
- 6. Done — use `/newagent` in Telegram to create your first AI agent
135
-
136
- ### Commands
137
-
138
- | Category | Command | Description |
139
- |----------|---------|-------------|
140
- | **Agents** | `/newagent` | Create agent wizard |
141
- | | `/agents` | List all agents |
142
- | | `/deleteagent` | Delete an agent |
143
- | | `/soul` | View/edit agent personality |
144
- | **Config** | `/config` | View configuration |
145
- | | `/config <key> <value>` | Update config |
146
- | | `/setup` | API provider wizard |
147
- | **Monitor** | `/status` | System health & stats |
148
- | | `/sessions` | List & manage sessions |
149
- | | `/restart` | Restart agent bots |
150
- | **Security** | `/pairing` | Manage access requests |
303
+ ## Architecture
151
304
 
152
- <br>
153
-
154
- ## Features
305
+ ```
306
+ ~/.camelagi/
307
+ ├── config.yaml ← Single config file
308
+ ├── workspace/ ← Default agent workspace
309
+ │ ├── SOUL.md ← Default personality
310
+ │ ├── MEMORY.md ← Default curated memory
311
+ │ └── memory/ ← Daily notes
312
+ ├── agents/ ← Per-agent isolated directories
313
+ │ └── <agent-id>/
314
+ │ ├── SOUL.md
315
+ │ ├── TOOLS.md
316
+ │ ├── MEMORY.md
317
+ │ └── memory/
318
+ ├── sessions/ ← JSONL conversation history
319
+ ├── skills/ ← Markdown skill files
320
+ └── usage/ ← Token usage data
321
+ ```
155
322
 
156
- > Terminal or Telegram — same agent, same tools, same memory.
323
+ **Request flow:**
157
324
 
158
- | | Feature | Description |
159
- |---|---|---|
160
- | ⌨️ | **camel chat — Terminal UI** | Full TUI with streaming, slash commands, model switching, session management, tool output, and markdown rendering |
161
- | 🤖 | **Telegram Admin Bot** | @BotFather for AI agents. Create, configure, and manage agents entirely from Telegram — instant commands, zero tokens burned |
162
- | 💬 | **Telegram Agent Bots** | Each agent gets its own Telegram bot. Message it like any chat — it runs tools, reads files, remembers context |
163
- | ⚙️ | **Same Engine** | Both interfaces run the same agent loop, same 10 tools, same two-tier memory. Switch between terminal and Telegram anytime |
164
- | 🧠 | **Agent Memory** | Each agent gets isolated two-tier memory — curated MEMORY.md + daily auto-journaling with recency-boosted search |
165
- | | **Cron Jobs** | Schedule AI tasks — daily summaries, monitoring, automations. Manage from Telegram, CLI, or the agent itself |
166
- | 🛡️ | **Tool Approvals** | Human-in-the-loop safety. Approve dangerous operations from Telegram with inline buttons — even headless |
167
- | 💭 | **Extended Thinking** | Claude reasons step by step before answering. Configure depth: off, low, medium, high |
168
- | 🔌 | **Multi-Provider** | Anthropic, OpenAI, OpenRouter, Ollama — any OpenAI-compatible endpoint. Zero vendor lock-in |
169
- | 🔐 | **Secure Pairing** | OTP-based user verification. No hardcoded IDs — pairing code + 5-digit OTP from Telegram |
170
- | 🪝 | **Skills & Hooks** | Teach agents skills via Markdown. Run shell/JS hooks before and after tool calls |
171
- | 🔄 | **Auto Compaction** | Summarizes old turns at 80% capacity. Flushes facts to memory first — nothing is lost |
325
+ ```
326
+ Inbound message (TUI / REST / WS / Telegram / Discord)
327
+ Queue check (per-session, prevents concurrent runs)
328
+ Lane acquisition (concurrency limits: main/cron/subagent)
329
+ History loading + compaction (summarize at 80% of maxTokens)
330
+ Agent execution with retry (classify error backoff or compact retry)
331
+ Message persistence (JSONL sessions)
332
+ Cleanup (release lane, drain queued messages)
333
+ ```
172
334
 
173
335
  <br>
174
336
 
175
- ## Terminal UI — `camel chat`
176
-
177
- Don't want to use Telegram? `camel chat` gives you a full terminal interface with the same agent capabilities.
178
-
179
- - Streaming responses with markdown rendering
180
- - Slash commands (`/model`, `/sessions`, `/tools`, `/compact`, `/status`, `/context`)
181
- - Model selector overlay (`Ctrl+L`), session switcher (`Ctrl+P`)
182
- - Tool output toggle (`Ctrl+O`), abort with `Escape`
183
- - Shell execution with `!command`
184
- - Agent creation wizard, SOUL.md editing
185
- - Thinking indicators, subagent progress, approval overlay
186
- - One-shot mode: `camel "your question"` for quick answers
187
-
188
- <br>
337
+ ## Documentation
189
338
 
190
- ## Developer Experience
339
+ | Document | Description |
340
+ |----------|-------------|
341
+ | [DOCS.md](DOCS.md) | Full reference documentation |
342
+ | [GUIDE.md](GUIDE.md) | User guide with examples |
343
+ | [featuresDocs/](featuresDocs/) | Deep-dive feature docs |
191
344
 
192
- | | **CamelAGI** | **OpenClaw** |
193
- |---|---|---|
194
- | **Codebase size** | ~10K LOC | ~700K+ LOC |
195
- | **AI Agent runtime** | [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) — fully documented by Anthropic, easy to extend | pi-agent & custom abstractions |
345
+ Feature docs cover: [agent system](featuresDocs/agent-system.md), [memory](featuresDocs/memory-system.md), [Telegram bots](featuresDocs/telegram-bots.md), [gateway server](featuresDocs/gateway-server.md), [runtime](featuresDocs/runtime.md), [tools](featuresDocs/tools.md), [extensions](featuresDocs/extensions.md), [configuration](featuresDocs/configuration.md), [CLI](featuresDocs/cli-commands.md), [TUI](featuresDocs/tui.md), [pairing](featuresDocs/pairing-otp.md).
196
346
 
197
347
  <br>
198
348
 
199
349
  ## Roadmap
200
350
 
201
- We're building CamelAGI to be the most capable open-source AI platform — for individuals and businesses alike.
202
-
203
351
  | | Feature | Description |
204
352
  |---|---|---|
205
- | 📡 | **More Channels** | WhatsApp, Discord, Slack — connect your AI agents to every platform your team already uses |
206
- | 🧩 | **ClawHub Skills** | Browse and install community skills from [clawhub.io](https://clawhub.io) — one command to add new capabilities to any agent |
207
- | 🖥️ | **Native Desktop Apps** | Standalone macOS and Windows apps to run CamelAGI natively no terminal, no companion app required |
208
- | 🏢 | **Business Ready** | Deploy CamelAGI for your business — finance, accounting, operations, customer support. AI agents that understand your workflows |
209
-
210
- <br>
211
-
212
- ## Documentation
213
-
214
- | Document | Description |
215
- |----------|-------------|
216
- | [DOCS.md](DOCS.md) | Full reference documentation |
217
- | [GUIDE.md](GUIDE.md) | User guide with examples |
218
- | [featuresDocs/](featuresDocs/) | Deep-dive feature docs |
353
+ | 📡 | **More Channels** | WhatsApp, Slack — connect your AI agents to every platform your team already uses |
354
+ | 🧩 | **ClawHub Skills** | Browse and install community skills from [clawhub.io](https://clawhub.io) — one command to add new capabilities |
355
+ | 🏢 | **Business Ready** | Deploy for your business finance, operations, customer support. AI agents that understand your workflows |
219
356
 
220
357
  <br>
221
358
 
@@ -32,6 +32,16 @@ const BUILTIN_TOOLS = [
32
32
  "WebSearch", "WebFetch",
33
33
  "Agent",
34
34
  ];
35
+ /** Extract text from an SDK "assistant" message's content blocks */
36
+ function extractAssistantText(msg) {
37
+ if (msg.type !== "assistant" || !msg.message?.content)
38
+ return "";
39
+ const parts = Array.isArray(msg.message.content) ? msg.message.content : [];
40
+ return parts
41
+ .filter((b) => b.type === "text" && b.text)
42
+ .map((b) => b.text)
43
+ .join("");
44
+ }
35
45
  export async function runAgentSdk(apiKey, model, systemPrompt, history, userMessage, opts) {
36
46
  // Build effective prompt: if resuming, just use user message.
37
47
  // Otherwise, prepend history as structured context in the prompt itself
@@ -130,80 +140,141 @@ export async function runAgentSdk(apiKey, model, systemPrompt, history, userMess
130
140
  }
131
141
  let result = "";
132
142
  let sdkSessionId;
133
- const q = query({
134
- prompt: effectivePrompt,
135
- options: {
136
- model,
137
- systemPrompt,
138
- allowedTools: [...BUILTIN_TOOLS],
139
- ...(disallowedTools && { disallowedTools }),
140
- mcpServers: { camelagi: mcpServer, ...(opts?.mcpServers ?? {}) },
141
- maxTurns: opts?.maxTurns ?? DEFAULT_MAX_TURNS,
142
- permissionMode: "bypassPermissions",
143
- allowDangerouslySkipPermissions: true,
144
- cwd: process.cwd(),
145
- env: { ANTHROPIC_API_KEY: apiKey },
146
- thinking,
147
- ...(opts?.effort && { effort: opts.effort }),
148
- ...(opts?.maxBudgetUsd && { maxBudgetUsd: opts.maxBudgetUsd }),
149
- ...(opts?.resumeSessionId && { resume: opts.resumeSessionId }),
150
- ...(abortController && { abortController }),
151
- includePartialMessages: !!emit,
152
- settingSources: ["project"],
153
- hooks: {
154
- PreToolUse: [{ matcher: ".*", hooks: [preToolHook] }],
155
- PostToolUse: [{ matcher: ".*", hooks: [postToolHook] }],
156
- },
143
+ // Build options object step-by-step to isolate any spread errors
144
+ const mcpServers = { camelagi: mcpServer, ...(opts?.mcpServers ?? {}) };
145
+ const allowedTools = [
146
+ ...BUILTIN_TOOLS,
147
+ "mcp__camelagi__*",
148
+ ...Object.keys(opts?.mcpServers ?? {}).map(name => `mcp__${name}__*`),
149
+ ];
150
+ const envVars = { ...(process.env ?? {}), ...buildSdkEnv(apiKey, opts?.provider, opts?.baseUrl) };
151
+ const queryOptions = {
152
+ model,
153
+ systemPrompt,
154
+ allowedTools,
155
+ mcpServers,
156
+ maxTurns: opts?.maxTurns ?? DEFAULT_MAX_TURNS,
157
+ permissionMode: "bypassPermissions",
158
+ allowDangerouslySkipPermissions: true,
159
+ cwd: process.cwd(),
160
+ env: envVars,
161
+ thinking,
162
+ includePartialMessages: !!emit,
163
+ settingSources: ["project"],
164
+ hooks: {
165
+ PreToolUse: [{ matcher: ".*", hooks: [preToolHook] }],
166
+ PostToolUse: [{ matcher: ".*", hooks: [postToolHook] }],
157
167
  },
158
- });
159
- for await (const message of q) {
160
- if (message.type === "result") {
161
- const resultMsg = message;
162
- result = resultMsg.result;
163
- sdkSessionId = resultMsg.session_id;
164
- if (resultMsg.usage && emit) {
165
- emit({ type: "usage", inputTokens: resultMsg.usage.input_tokens ?? 0, outputTokens: resultMsg.usage.output_tokens ?? 0, cacheReadTokens: resultMsg.usage.cache_read_input_tokens, cacheWriteTokens: resultMsg.usage.cache_creation_input_tokens });
166
- }
167
- if (resultMsg.usage && opts?.sessionId) {
168
- recordUsage(opts.sessionId, { inputTokens: resultMsg.usage.input_tokens ?? 0, outputTokens: resultMsg.usage.output_tokens ?? 0, cacheReadTokens: resultMsg.usage.cache_read_input_tokens ?? 0, cacheWriteTokens: resultMsg.usage.cache_creation_input_tokens ?? 0 });
169
- }
170
- emit?.({ type: "chunk", text: result });
171
- }
172
- else if (message.type === "system" && emit) {
173
- const sysMsg = message;
174
- if (sysMsg.subtype === "init" && sysMsg.session_id) {
175
- sdkSessionId = sysMsg.session_id;
176
- emit({ type: "init", sessionId: sysMsg.session_id });
177
- }
178
- else if (sysMsg.subtype === "task_started") {
179
- emit({ type: "subagent_start", agentId: sysMsg.agent_id ?? "subagent", taskId: sysMsg.task_id });
180
- }
181
- else if (sysMsg.subtype === "task_progress") {
182
- emit({ type: "subagent_progress", agentId: sysMsg.agent_id ?? "subagent", toolCount: sysMsg.tool_count, duration: sysMsg.duration_ms ? Math.round(sysMsg.duration_ms / 1000) : undefined });
183
- }
184
- else if (sysMsg.subtype === "task_notification") {
185
- emit({ type: "subagent_done", agentId: sysMsg.agent_id ?? "subagent", toolUseId: sysMsg.tool_use_id });
168
+ };
169
+ if (disallowedTools)
170
+ queryOptions.disallowedTools = disallowedTools;
171
+ if (opts?.effort)
172
+ queryOptions.effort = opts.effort;
173
+ if (opts?.maxBudgetUsd)
174
+ queryOptions.maxBudgetUsd = opts.maxBudgetUsd;
175
+ if (opts?.resumeSessionId)
176
+ queryOptions.resume = opts.resumeSessionId;
177
+ if (abortController)
178
+ queryOptions.abortController = abortController;
179
+ const q = query({ prompt: effectivePrompt, options: queryOptions });
180
+ // The for-await loop may throw if the SDK subprocess exits unexpectedly
181
+ // (e.g. non-Claude models via OpenRouter). If we already captured a result
182
+ // from "assistant" messages, we still want to return it.
183
+ try {
184
+ for await (const message of q) {
185
+ const msg = message;
186
+ // "assistant" messages: non-Claude models (via OpenRouter) emit these
187
+ // with the response text instead of a final "result" message.
188
+ const assistantText = extractAssistantText(msg);
189
+ if (assistantText) {
190
+ result = assistantText;
191
+ if (msg.session_id)
192
+ sdkSessionId = msg.session_id;
186
193
  }
187
- }
188
- else if (message.type === "stream_event" && emit) {
189
- const streamMsg = message;
190
- const event = streamMsg.event;
191
- if (event.type === "content_block_start" && event.content_block?.type === "thinking") {
192
- emit({ type: "thinking", state: "start" });
194
+ if (message.type === "result") {
195
+ const resultMsg = message;
196
+ // Only overwrite if non-empty (non-Claude models return text via
197
+ // "assistant" messages; the trailing "result" may be empty).
198
+ if (resultMsg.result) {
199
+ result = resultMsg.result;
200
+ }
201
+ sdkSessionId = resultMsg.session_id ?? sdkSessionId;
202
+ if (resultMsg.usage && emit) {
203
+ emit({ type: "usage", inputTokens: resultMsg.usage.input_tokens ?? 0, outputTokens: resultMsg.usage.output_tokens ?? 0, cacheReadTokens: resultMsg.usage.cache_read_input_tokens, cacheWriteTokens: resultMsg.usage.cache_creation_input_tokens });
204
+ }
205
+ if (resultMsg.usage && opts?.sessionId) {
206
+ recordUsage(opts.sessionId, { inputTokens: resultMsg.usage.input_tokens ?? 0, outputTokens: resultMsg.usage.output_tokens ?? 0, cacheReadTokens: resultMsg.usage.cache_read_input_tokens ?? 0, cacheWriteTokens: resultMsg.usage.cache_creation_input_tokens ?? 0 });
207
+ }
208
+ emit?.({ type: "chunk", text: result });
193
209
  }
194
- else if (event.type === "content_block_delta") {
195
- if (event.delta?.type === "text_delta" && event.delta.text)
196
- emit({ type: "stream_text", text: event.delta.text });
197
- else if (event.delta?.type === "thinking_delta" && event.delta.thinking)
198
- emit({ type: "thinking_delta", text: event.delta.thinking });
210
+ else if (message.type === "system" && emit) {
211
+ const sysMsg = message;
212
+ if (sysMsg.subtype === "init" && sysMsg.session_id) {
213
+ sdkSessionId = sysMsg.session_id;
214
+ emit({ type: "init", sessionId: sysMsg.session_id });
215
+ }
216
+ else if (sysMsg.subtype === "task_started") {
217
+ emit({ type: "subagent_start", agentId: sysMsg.agent_id ?? "subagent", taskId: sysMsg.task_id });
218
+ }
219
+ else if (sysMsg.subtype === "task_progress") {
220
+ emit({ type: "subagent_progress", agentId: sysMsg.agent_id ?? "subagent", toolCount: sysMsg.tool_count, duration: sysMsg.duration_ms ? Math.round(sysMsg.duration_ms / 1000) : undefined });
221
+ }
222
+ else if (sysMsg.subtype === "task_notification") {
223
+ emit({ type: "subagent_done", agentId: sysMsg.agent_id ?? "subagent", toolUseId: sysMsg.tool_use_id });
224
+ }
199
225
  }
200
- else if (event.type === "content_block_stop") {
201
- emit({ type: "thinking", state: "end" });
226
+ else if (message.type === "stream_event" && emit) {
227
+ const streamMsg = message;
228
+ const event = streamMsg.event;
229
+ if (event.type === "content_block_start" && event.content_block?.type === "thinking") {
230
+ emit({ type: "thinking", state: "start" });
231
+ }
232
+ else if (event.type === "content_block_delta") {
233
+ if (event.delta?.type === "text_delta" && event.delta.text)
234
+ emit({ type: "stream_text", text: event.delta.text });
235
+ else if (event.delta?.type === "thinking_delta" && event.delta.thinking)
236
+ emit({ type: "thinking_delta", text: event.delta.thinking });
237
+ }
238
+ else if (event.type === "content_block_stop") {
239
+ emit({ type: "thinking", state: "end" });
240
+ }
202
241
  }
203
242
  }
204
243
  }
244
+ catch (err) {
245
+ // If the subprocess exited but we already have a response, return it.
246
+ // Otherwise, re-throw so the retry/error handling picks it up.
247
+ if (!result)
248
+ throw err;
249
+ }
205
250
  const userMsg = { role: "user", content: userMessage };
206
251
  const aiMsg = { role: "assistant", content: result };
207
252
  return { response: result, newMessages: [userMsg, aiMsg], usage: null, sessionId: sdkSessionId };
208
253
  }
254
+ /** Build environment variables for the SDK subprocess based on provider */
255
+ function buildSdkEnv(apiKey, provider, baseUrl) {
256
+ // OpenRouter: use ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN
257
+ if (baseUrl?.includes("openrouter")) {
258
+ // SDK expects /api not /api/v1
259
+ const sdkBaseUrl = baseUrl.replace(/\/v1\/?$/, "");
260
+ return {
261
+ ANTHROPIC_BASE_URL: sdkBaseUrl,
262
+ ANTHROPIC_AUTH_TOKEN: apiKey,
263
+ ANTHROPIC_API_KEY: "",
264
+ };
265
+ }
266
+ // Direct Anthropic
267
+ if (provider === "anthropic" || !provider) {
268
+ return { ANTHROPIC_API_KEY: apiKey };
269
+ }
270
+ // Custom base URL (non-OpenRouter)
271
+ if (baseUrl) {
272
+ return {
273
+ ANTHROPIC_API_KEY: apiKey,
274
+ ANTHROPIC_BASE_URL: baseUrl,
275
+ };
276
+ }
277
+ // Default
278
+ return { ANTHROPIC_API_KEY: apiKey };
279
+ }
209
280
  //# sourceMappingURL=agent-sdk.js.map