@mininglamp-oss/cc-channel-octo 1.0.1
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.
- package/CHANGELOG.md +349 -0
- package/LICENSE +191 -0
- package/README.md +577 -0
- package/config.bot.example.json +15 -0
- package/config.example.json +33 -0
- package/dist/agent-bridge.d.ts +79 -0
- package/dist/agent-bridge.js +392 -0
- package/dist/agent-bridge.js.map +1 -0
- package/dist/commands.d.ts +57 -0
- package/dist/commands.js +121 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +278 -0
- package/dist/config.js +330 -0
- package/dist/config.js.map +1 -0
- package/dist/cron-evaluator.d.ts +53 -0
- package/dist/cron-evaluator.js +191 -0
- package/dist/cron-evaluator.js.map +1 -0
- package/dist/cron-fire-marker.d.ts +24 -0
- package/dist/cron-fire-marker.js +25 -0
- package/dist/cron-fire-marker.js.map +1 -0
- package/dist/cron-scheduler.d.ts +46 -0
- package/dist/cron-scheduler.js +114 -0
- package/dist/cron-scheduler.js.map +1 -0
- package/dist/cron-store.d.ts +62 -0
- package/dist/cron-store.js +63 -0
- package/dist/cron-store.js.map +1 -0
- package/dist/cron-tool.d.ts +44 -0
- package/dist/cron-tool.js +151 -0
- package/dist/cron-tool.js.map +1 -0
- package/dist/cwd-resolver.d.ts +72 -0
- package/dist/cwd-resolver.js +166 -0
- package/dist/cwd-resolver.js.map +1 -0
- package/dist/db-adapter.d.ts +21 -0
- package/dist/db-adapter.js +64 -0
- package/dist/db-adapter.js.map +1 -0
- package/dist/file-inline-wrap.d.ts +94 -0
- package/dist/file-inline-wrap.js +243 -0
- package/dist/file-inline-wrap.js.map +1 -0
- package/dist/gateway.d.ts +100 -0
- package/dist/gateway.js +420 -0
- package/dist/gateway.js.map +1 -0
- package/dist/group-config.d.ts +41 -0
- package/dist/group-config.js +104 -0
- package/dist/group-config.js.map +1 -0
- package/dist/group-context.d.ts +64 -0
- package/dist/group-context.js +396 -0
- package/dist/group-context.js.map +1 -0
- package/dist/inbound.d.ts +136 -0
- package/dist/inbound.js +667 -0
- package/dist/inbound.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +922 -0
- package/dist/index.js.map +1 -0
- package/dist/media-inbound.d.ts +38 -0
- package/dist/media-inbound.js +131 -0
- package/dist/media-inbound.js.map +1 -0
- package/dist/mention-utils.d.ts +99 -0
- package/dist/mention-utils.js +185 -0
- package/dist/mention-utils.js.map +1 -0
- package/dist/octo/api.d.ts +148 -0
- package/dist/octo/api.js +320 -0
- package/dist/octo/api.js.map +1 -0
- package/dist/octo/socket.d.ts +102 -0
- package/dist/octo/socket.js +793 -0
- package/dist/octo/socket.js.map +1 -0
- package/dist/octo/types.d.ts +126 -0
- package/dist/octo/types.js +35 -0
- package/dist/octo/types.js.map +1 -0
- package/dist/prompt-safety.d.ts +78 -0
- package/dist/prompt-safety.js +148 -0
- package/dist/prompt-safety.js.map +1 -0
- package/dist/session-router.d.ts +127 -0
- package/dist/session-router.js +432 -0
- package/dist/session-router.js.map +1 -0
- package/dist/session-store.d.ts +89 -0
- package/dist/session-store.js +297 -0
- package/dist/session-store.js.map +1 -0
- package/dist/skill-linker.d.ts +31 -0
- package/dist/skill-linker.js +160 -0
- package/dist/skill-linker.js.map +1 -0
- package/dist/stream-relay.d.ts +42 -0
- package/dist/stream-relay.js +243 -0
- package/dist/stream-relay.js.map +1 -0
- package/dist/url-policy.d.ts +103 -0
- package/dist/url-policy.js +290 -0
- package/dist/url-policy.js.map +1 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
<h1 align="center">cc-channel-octo</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
Bridge <a href="https://docs.anthropic.com/en/docs/claude-code">Claude Code</a> to <a href="https://github.com/nicco-io/octo">Octo</a> IM — an independent Node.js gateway powered by the <a href="https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk">Claude Agent SDK</a>.
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/Mininglamp-OSS/cc-channel-octo/actions"><img src="https://github.com/Mininglamp-OSS/cc-channel-octo/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/cc-channel-octo"><img src="https://img.shields.io/npm/v/cc-channel-octo" alt="npm version"></a>
|
|
10
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a>
|
|
11
|
+
<img src="https://img.shields.io/node/v/cc-channel-octo" alt="Node.js version">
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Users talk to a bot in Octo (DM or group @mention). The bot sends messages to Claude Code via the Agent SDK, streams the response back in real time, and persists conversation history in SQLite — all as a single self-contained process.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Streaming output** — Real-time response delivery via Octo's stream API with 800 ms throttled flushes, typing indicators, and automatic fallback to plain messages.
|
|
21
|
+
- **Tool progress** *(opt-in)* — With `sdk.toolProgress` enabled, the bot posts brief `🔧 Running <tool>(<params>)…` notices as the agent invokes tools (params are a truncated one-liner), so users see activity during long tool-heavy turns (deduped + capped per turn).
|
|
22
|
+
- **Group chat awareness** — Responds only to @mentions. Injects recent group conversation as context so Claude understands the discussion.
|
|
23
|
+
- **Session persistence** — SQLite-backed conversation history (40-message sliding window) with automatic 7-day expiry.
|
|
24
|
+
- **In-chat commands** — `/reset` clears your own session's stored history (not the shared recent-group-context cache), `/config` shows the active settings, `/help` lists commands. Scoped per-user (even in groups); subject to the same per-session rate limit as normal messages.
|
|
25
|
+
- **Rate limiting** — Per-session token bucket (default 5 req/min) with debounced rejection notices.
|
|
26
|
+
- **Security by configuration** — `allowedTools` whitelist + per-session workspace isolation. No runtime permission prompts (headless mode).
|
|
27
|
+
- **Multi-bot** — Run several independent bots in one process via a `bots[]` config array; each gets its own token, data directory, and sandbox root (no shared history).
|
|
28
|
+
- **Zero infrastructure** — Single process, single SQLite file, `npm start` and go.
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### Prerequisites
|
|
33
|
+
|
|
34
|
+
- Node.js ≥ 22
|
|
35
|
+
- An Octo bot token (`bf_*`)
|
|
36
|
+
- Claude Code CLI installed (`npm i -g @anthropic-ai/claude-code`)
|
|
37
|
+
- `ANTHROPIC_API_KEY` set in your environment
|
|
38
|
+
|
|
39
|
+
### Install & Run
|
|
40
|
+
|
|
41
|
+
**Option A — install from npm (recommended):**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g @mininglamp-oss/cc-channel-octo
|
|
45
|
+
# or run without installing:
|
|
46
|
+
npx @mininglamp-oss/cc-channel-octo
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This installs the `cc-channel-octo` command (prebuilt — no compile step). Skip
|
|
50
|
+
straight to creating the config files below, then run `cc-channel-octo`.
|
|
51
|
+
|
|
52
|
+
To use it as a library instead, `npm install @mininglamp-oss/cc-channel-octo`
|
|
53
|
+
and import from it; the Claude Agent SDK is a peer dependency, so install
|
|
54
|
+
`@anthropic-ai/claude-agent-sdk` alongside it.
|
|
55
|
+
|
|
56
|
+
**Option B — build from source:**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/Mininglamp-OSS/cc-channel-octo.git
|
|
60
|
+
cd cc-channel-octo
|
|
61
|
+
npm install
|
|
62
|
+
npm run build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
cc-channel-octo uses a fixed, bot-first directory layout under **`~/.cc-channel-octo/`**:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
~/.cc-channel-octo/
|
|
69
|
+
├── config.json ← GLOBAL: shared defaults + the list of bots (no token)
|
|
70
|
+
├── skills/ ← optional: skills shared by ALL bots (see Agent skills)
|
|
71
|
+
└── <botId>/ ← one self-contained subtree per bot ("default" for a single bot)
|
|
72
|
+
├── config.json ← THIS bot: botToken (required) + per-bot overrides
|
|
73
|
+
├── SOUL.md ← optional personality (overrides sdk.systemPrompt)
|
|
74
|
+
├── skills/ ← optional: skills for THIS bot (override same-named global)
|
|
75
|
+
├── data/ ← SQLite history
|
|
76
|
+
├── workspace/ ← per-session cwd sandboxes (auto-created)
|
|
77
|
+
└── memory/ ← long-term auto-memory (auto-created)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The directory holding the global `config.json` is the **baseDir**; every bot's
|
|
81
|
+
`data`/`workspace`/`memory` are **derived** from `<baseDir>/<id>/…` and are not
|
|
82
|
+
configurable, so a bot can never escape its own subtree.
|
|
83
|
+
|
|
84
|
+
Create the two config files:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
mkdir -p ~/.cc-channel-octo/default
|
|
88
|
+
cp config.example.json ~/.cc-channel-octo/config.json # global (shared + bots list)
|
|
89
|
+
cp config.bot.example.json ~/.cc-channel-octo/default/config.json # the bot (token + overrides)
|
|
90
|
+
chmod 600 ~/.cc-channel-octo/config.json ~/.cc-channel-octo/default/config.json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Global `~/.cc-channel-octo/config.json` (shared; **no token**):
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
{
|
|
97
|
+
"apiUrl": "https://your-octo-instance.com",
|
|
98
|
+
"bots": [{ "id": "default" }]
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Per-bot `~/.cc-channel-octo/default/config.json`:
|
|
103
|
+
|
|
104
|
+
```jsonc
|
|
105
|
+
{
|
|
106
|
+
"botToken": "bf_YOUR_BOT_TOKEN",
|
|
107
|
+
"sdk": { "model": "vertexai/claude-opus-4-8" }
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Start the gateway:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm start # from source (Option B)
|
|
115
|
+
# or, if installed from npm (Option A):
|
|
116
|
+
cc-channel-octo
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The bot is now online. Send it a DM or @mention it in a group.
|
|
120
|
+
|
|
121
|
+
### Install via Claude Code CLI (copy-paste prompts)
|
|
122
|
+
|
|
123
|
+
Prefer to let Claude Code do the setup? Run `claude` in an empty working
|
|
124
|
+
directory and paste **one prompt per step**. The two steps are deliberately
|
|
125
|
+
separate: **Install** clones + builds the code (no secrets), then **Configure**
|
|
126
|
+
writes your token(s) and starts the gateway. Both steps support single- and
|
|
127
|
+
multi-bot deployments.
|
|
128
|
+
|
|
129
|
+
> **Heads up — these are agentic prompts, not scripts.** Claude Code will run
|
|
130
|
+
> `git`, `npm`, `mkdir`, and write files under `~/.cc-channel-octo/`. Read what
|
|
131
|
+
> it proposes before approving. Your bot token(s) go only into
|
|
132
|
+
> `~/.cc-channel-octo/<id>/config.json` (chmod `600`) — never into the repo,
|
|
133
|
+
> shell history, or this prompt's output.
|
|
134
|
+
|
|
135
|
+
#### Step 1 — Install (clone + build, no secrets)
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
Install the cc-channel-octo gateway from source.
|
|
139
|
+
|
|
140
|
+
1. Verify Node.js >= 22 is on PATH (`node -v`); stop and tell me if it is older.
|
|
141
|
+
2. Clone https://github.com/Mininglamp-OSS/cc-channel-octo.git into the current
|
|
142
|
+
directory (skip the clone if a cc-channel-octo/ checkout is already here — just
|
|
143
|
+
`git pull` it instead) and cd into it.
|
|
144
|
+
3. Run `npm install` then `npm run build`.
|
|
145
|
+
4. Run `npm test` and confirm it passes.
|
|
146
|
+
5. Print the absolute path of the repo and tell me to run the Configure prompt
|
|
147
|
+
next. Do NOT create any config files or ask me for a token in this step.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Step 2 — Configure & run (writes token, starts gateway)
|
|
151
|
+
|
|
152
|
+
Edit the bracketed values first, then paste. For **multi-bot**, list more than
|
|
153
|
+
one entry under "Bots".
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
Configure and start the cc-channel-octo gateway you just installed.
|
|
157
|
+
|
|
158
|
+
Octo API URL: https://your-octo-instance.com
|
|
159
|
+
Bots (one line each — "id = token"; ids are slugs [a-z0-9._-]):
|
|
160
|
+
default = bf_YOUR_BOT_TOKEN
|
|
161
|
+
# add more lines for multi-bot, e.g.:
|
|
162
|
+
# support = bf_TOKEN_A
|
|
163
|
+
# ops = bf_TOKEN_B
|
|
164
|
+
Claude model: vertexai/claude-opus-4-8 # or leave blank for the SDK default
|
|
165
|
+
|
|
166
|
+
Do this:
|
|
167
|
+
1. Create the fixed layout under ~/.cc-channel-octo/ : a GLOBAL config.json
|
|
168
|
+
holding { "apiUrl": "<the URL>", "bots": [ { "id": "<id>" }, ... ] } with one
|
|
169
|
+
entry per bot above and NO tokens in it.
|
|
170
|
+
2. For EACH bot, create ~/.cc-channel-octo/<id>/config.json containing that bot's
|
|
171
|
+
{ "botToken": "<its token>", "sdk": { "model": "<model, if given>" } }.
|
|
172
|
+
3. `chmod 600` the global config and every per-bot config.json. Tokens must
|
|
173
|
+
appear ONLY in the per-bot files — never echo a token back to me or put one in
|
|
174
|
+
the global file.
|
|
175
|
+
4. Validate: the global `bots[]` ids must exactly match the per-bot directory
|
|
176
|
+
names, and each per-bot file must have a non-empty botToken. Fix mismatches.
|
|
177
|
+
5. From the repo dir, start the gateway with `npm start` and watch the logs until
|
|
178
|
+
you see "Ready — listening for messages" (multi-bot also logs "Multi-bot mode:
|
|
179
|
+
starting N bots" and one "Bot connected" per bot). Report success, or surface
|
|
180
|
+
the first error if a bot fails to register/connect.
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
After "Ready", DM the bot or @mention it in a group. To change config later,
|
|
184
|
+
edit the JSON files and restart (`npm start`); see [Configuration](#configuration)
|
|
185
|
+
for every available field.
|
|
186
|
+
|
|
187
|
+
## Configuration
|
|
188
|
+
|
|
189
|
+
All configuration comes from JSON files — there are **no environment-variable
|
|
190
|
+
overrides**. Two layers: a **global** `~/.cc-channel-octo/config.json` (shared
|
|
191
|
+
defaults + the `bots` list) and one **per-bot** `~/.cc-channel-octo/<id>/config.json`
|
|
192
|
+
(its token + overrides). Per-bot fields win over the global layer; per-bot
|
|
193
|
+
directories are derived from `baseDir` (see the tree above) and are not
|
|
194
|
+
configurable.
|
|
195
|
+
|
|
196
|
+
| Field | Default | Description |
|
|
197
|
+
|-------|---------|-------------|
|
|
198
|
+
| `botToken` | *(required, per-bot)* | Octo bot token (`bf_*`). Lives in `<baseDir>/<id>/config.json`, not the global file. |
|
|
199
|
+
| `apiUrl` | *(required)* | Octo API base URL (shared; a bot may override). |
|
|
200
|
+
| `bots` | `[{id:"default"}]` | Which bots to run; each `id` selects its subtree + per-bot config. |
|
|
201
|
+
| *(dirs)* | *(derived)* | `data`/`workspace`/`memory`/`skills` are always `<baseDir>/<id>/…` — not configurable. |
|
|
202
|
+
| `sdk.model` | *(SDK default)* | Claude model override |
|
|
203
|
+
| `sdk.allowedTools` | `"*"` | Either `"*"` (allow every tool the SDK exposes) or an explicit string array whitelist. |
|
|
204
|
+
| `sdk.permissionMode` | `bypassPermissions` | SDK permission mode |
|
|
205
|
+
| `sdk.maxTurns` | *(SDK default)* | Max agentic turns per query |
|
|
206
|
+
| `sdk.systemPrompt` | *(built-in)* | Custom system prompt (a `<baseDir>/<id>/SOUL.md` overrides this). |
|
|
207
|
+
| `sdk.toolProgress` | `false` | When true, post `🔧 Running <tool>(<params>)…` notices as the agent invokes tools (params truncated; deduped, capped per turn) |
|
|
208
|
+
| `sdk.settingSources` | `['project']` | Filesystem settings sources the SDK loads. Default `['project']` so it discovers skills symlinked into the session sandbox's `.claude/skills/` (see [Agent skills](#agent-skills)). Memory stays isolated regardless (inline auto-memory dir pin). Add `'user'` only to deliberately load the operator's real `~/.claude`. |
|
|
209
|
+
| `groupConfigDir` | *(unset)* | Directory of per-group instruction files (`<groupId>.md`). A match is injected into the system prompt as trusted custom instructions for that group. See [Per-group instructions](#per-group-instructions). |
|
|
210
|
+
| `sdk.anthropicBaseUrl` | *(unset)* | Override the upstream Claude API endpoint. See [Self-hosted gateway](#self-hosted-gateway) below. |
|
|
211
|
+
| `sdk.env` | *(unset)* | Extra environment variables (`{ "KEY": "value" }`) injected verbatim into the agent's tool subprocess. Per-bot. Use to give a bot's skills what their CLIs need — e.g. `{ "OCTO_BOT_ID": "<robotId>" }` so a multi-bot deploy's `octo-cli` selects the right stored profile. See [Agent skills](#agent-skills). |
|
|
212
|
+
| `sdk.skills` | *(SDK default)* | Which skills this bot enables: `'all'` or a `string[]` of skill names. Per-bot **selection** over the centrally-maintained skill library (maintain once, each bot picks its subset). Omit to use the SDK default. See [Agent skills](#agent-skills). |
|
|
213
|
+
| `sdk.cron` | `false` | When true, give the agent a `cron` tool set to register per-bot scheduled tasks (persisted to `<baseDir>/<id>/cron.json`, fired through the normal pipeline). Creation is **owner-gated**. See [Scheduled tasks](#scheduled-tasks). |
|
|
214
|
+
| `rateLimit.maxPerMinute` | `5` | Max requests per minute per session |
|
|
215
|
+
| `context.maxContextChars` | `6000` | Max characters of group context injected into prompts |
|
|
216
|
+
| `context.historyLimit` | `40` | Max messages in session history window |
|
|
217
|
+
| `botBlocklist` | `[]` | Bot UIDs to ignore in DMs (prevents bot loops) |
|
|
218
|
+
| `mentionFreeGroups` | `[]` | Group channel IDs where the bot responds to every text message without requiring an `@bot` mention (G12). |
|
|
219
|
+
|
|
220
|
+
### Self-hosted gateway
|
|
221
|
+
|
|
222
|
+
If you proxy the Claude API through your own gateway (corporate egress, regional
|
|
223
|
+
endpoint, model router, etc.), set `sdk.anthropicBaseUrl` to the gateway origin.
|
|
224
|
+
The value is forwarded to the Claude Agent SDK subprocess as the standard
|
|
225
|
+
`ANTHROPIC_BASE_URL` environment variable (scoped to the subprocess — it does
|
|
226
|
+
not mutate the gateway's own environment), so any deployment that already speaks
|
|
227
|
+
the Anthropic protocol will Just Work — no code changes required.
|
|
228
|
+
|
|
229
|
+
Because this endpoint receives the Anthropic API key and all prompt/response
|
|
230
|
+
content, it is SSRF-validated at boot exactly like `apiUrl`: it must be `https://`
|
|
231
|
+
(or `http://localhost` / `http://127.0.0.1` for local development), and may not
|
|
232
|
+
resolve to a private/link-local address. An unsafe value fails fast at startup.
|
|
233
|
+
|
|
234
|
+
```jsonc
|
|
235
|
+
{
|
|
236
|
+
"sdk": {
|
|
237
|
+
"anthropicBaseUrl": "https://claude-gw.internal.example.com"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Leave the field unset to talk to Anthropic's public endpoint directly.
|
|
243
|
+
|
|
244
|
+
### Per-group instructions
|
|
245
|
+
|
|
246
|
+
Give a specific group its own persona or rules without code changes: set
|
|
247
|
+
`groupConfigDir` to a directory you control, and drop a `<groupId>.md` file in
|
|
248
|
+
it (the group's channel id, e.g. `s12_345.md`). Its contents are injected into
|
|
249
|
+
that group's system prompt as a trusted, **unsanitized** `[Group instructions]`
|
|
250
|
+
block. Only groups use this; DMs key on the peer uid. The key is the channel id,
|
|
251
|
+
so all topics under one `CommunityTopic` channel id share the same file.
|
|
252
|
+
|
|
253
|
+
```jsonc
|
|
254
|
+
// ~/.cc-channel-octo/config.json
|
|
255
|
+
{ "groupConfigDir": "/home/deploy/cc-octo-groups" }
|
|
256
|
+
// /home/deploy/cc-octo-groups/s12_345.md:
|
|
257
|
+
// Always answer in formal English and cite sources.
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
> ⚠️ **Security — this is a trusted, unsanitized prompt-injection sink.** Its
|
|
261
|
+
> safety depends entirely on the files being writable **only** by the operator.
|
|
262
|
+
> Putting `groupConfigDir` outside every bot's `workspace/` is required (the
|
|
263
|
+
> gateway refuses otherwise) but **not sufficient**: under the shipped defaults
|
|
264
|
+
> (`allowedTools: "*"` + `bypassPermissions`) the agent has `Bash`/`Write` and
|
|
265
|
+
> can write **absolute** paths outside its sandbox — the workspace is a starting
|
|
266
|
+
> dir, not a chroot (see [Security Model](#security-model)). A malicious user
|
|
267
|
+
> could then have the agent write `<groupConfigDir>/<otherGroup>.md` and inject
|
|
268
|
+
> persistent, trusted instructions into another group. So you **must**:
|
|
269
|
+
> - make `groupConfigDir` and its files **non-writable by the gateway process
|
|
270
|
+
> user** (e.g. root-owned, mode `0755`/`0644`), and/or
|
|
271
|
+
> - harden the deployment (drop `Bash` from `allowedTools`, run unprivileged,
|
|
272
|
+
> sandbox the filesystem).
|
|
273
|
+
>
|
|
274
|
+
> As defense-in-depth the gateway refuses to inject a group/world-writable file,
|
|
275
|
+
> but that is a backstop, not the guarantee. Files larger than 16 KiB are
|
|
276
|
+
> truncated; an unsafe group id (path separators, `..`) is ignored.
|
|
277
|
+
|
|
278
|
+
### Agent skills
|
|
279
|
+
|
|
280
|
+
cc supports external tooling generically through **Claude skills**. Drop a skill
|
|
281
|
+
into a `skills/` directory and the agent can use it — there is **no per-tool code
|
|
282
|
+
in cc**. A skill is a standard directory with a `SKILL.md` (plus optional
|
|
283
|
+
`references/` and `scripts/`); it teaches the agent how to drive some external
|
|
284
|
+
CLI (`octo-cli`, `gh`, anything on `PATH`).
|
|
285
|
+
|
|
286
|
+
**Two layers** (mirroring the config model):
|
|
287
|
+
|
|
288
|
+
| Location | Scope |
|
|
289
|
+
|----------|-------|
|
|
290
|
+
| `~/.cc-channel-octo/skills/<name>/` | shared by **all** bots |
|
|
291
|
+
| `~/.cc-channel-octo/<id>/skills/<name>/` | **one** bot (overrides a same-named global skill) |
|
|
292
|
+
|
|
293
|
+
cc symlinks both layers into each session sandbox's `.claude/skills/` on every
|
|
294
|
+
turn, and the SDK discovers them because `sdk.settingSources` defaults to
|
|
295
|
+
`['project']`. (Memory isolation is unaffected — the auto-memory directory is
|
|
296
|
+
pinned via inline settings, which the SDK ranks above any project-level value.)
|
|
297
|
+
|
|
298
|
+
**Installing a skill.** Copy or symlink any `SKILL.md` directory into a `skills/`
|
|
299
|
+
folder. For octo operations, octo-cli ships ready-made skills:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
mkdir -p ~/.cc-channel-octo/skills
|
|
303
|
+
octo-cli skills --install ~/.cc-channel-octo/skills # octo-shared, octo-messaging, …
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Per-bot skill selection.** The library is maintained once; each bot picks its
|
|
307
|
+
subset via `sdk.skills` in its per-bot config.json — `'all'`, or a list of skill
|
|
308
|
+
names:
|
|
309
|
+
|
|
310
|
+
```jsonc
|
|
311
|
+
// ~/.cc-channel-octo/issue-triage/config.json
|
|
312
|
+
{ "sdk": { "skills": ["octo-messaging", "github-issue-triage"] } }
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Omit it for the SDK default. So a `triage` bot can enable the triage + messaging
|
|
316
|
+
skills while another bot enables only messaging — from the same shared library.
|
|
317
|
+
|
|
318
|
+
**Per-bot identity.** Each bot's persona/rules are independent:
|
|
319
|
+
|
|
320
|
+
- `<id>/SOUL.md` — persona (overrides `sdk.systemPrompt`).
|
|
321
|
+
- `<id>/CLAUDE.md` — behavior rules. Discovered because the `project` source
|
|
322
|
+
walks up from the session sandbox to the bot subtree.
|
|
323
|
+
- `~/.cc-channel-octo/CLAUDE.md` (optional) — an all-bots baseline (the same
|
|
324
|
+
upward walk reaches it).
|
|
325
|
+
|
|
326
|
+
> ⚠️ **CLAUDE.md upward-walk has no project boundary.** The walk continues past
|
|
327
|
+
> the bot subtree all the way up the filesystem — so a `CLAUDE.md` in the host
|
|
328
|
+
> HOME or any ancestor directory **leaks into every bot's context**. Keep the
|
|
329
|
+
> deploy machine's `$HOME` (and ancestors) free of `CLAUDE.md`; put bot rules in
|
|
330
|
+
> `<id>/CLAUDE.md` and shared rules in `~/.cc-channel-octo/CLAUDE.md`. (This is
|
|
331
|
+
> also why `settingSources` stays `['project']`, not `['user']` — `user` would
|
|
332
|
+
> additionally pull in the host's personal `~/.claude` config/skills.)
|
|
333
|
+
|
|
334
|
+
**Prerequisites are the operator's responsibility, out-of-band:** install the
|
|
335
|
+
CLI a skill needs (`npm i -g @mininglamp-oss/octo-cli`, etc.) and authenticate it
|
|
336
|
+
(`octo-cli auth login`, `gh auth login`). **cc never handles credentials** — the
|
|
337
|
+
agent only runs the already-authenticated CLI. Skills are operator-owned and
|
|
338
|
+
trusted (like `SOUL.md`/`GROUP.md`); since their contents are visible to the
|
|
339
|
+
model, **never put secrets in a skill file**.
|
|
340
|
+
|
|
341
|
+
**Multi-bot tool identity.** When several bots share one CLI whose credential
|
|
342
|
+
store keys by identity (e.g. `octo-cli`, which needs `--bot-id`/`OCTO_BOT_ID` to
|
|
343
|
+
pick among ≥2 stored profiles), give each bot its selector via `sdk.env` in its
|
|
344
|
+
per-bot config.json — e.g. `{ "sdk": { "env": { "OCTO_BOT_ID": "<robotId>" } } }`.
|
|
345
|
+
cc injects it into that bot's tool subprocess so the CLI acts as the right bot.
|
|
346
|
+
|
|
347
|
+
### Scheduled tasks
|
|
348
|
+
|
|
349
|
+
Enable `sdk.cron: true` to give the agent a `cron` tool set so it can schedule
|
|
350
|
+
work — the missing "non-IM trigger" that makes a bot more than purely reactive.
|
|
351
|
+
|
|
352
|
+
```jsonc
|
|
353
|
+
// ~/.cc-channel-octo/<id>/config.json
|
|
354
|
+
{ "sdk": { "cron": true } }
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The agent calls:
|
|
358
|
+
- `cron_create(schedule, prompt, recurring?)` — `schedule` is a 5-field cron
|
|
359
|
+
expression (`"0 9 * * 1-5"` = weekdays 9am) or a one-shot ISO datetime
|
|
360
|
+
(`"2026-06-09T09:00:00Z"`). Cron fields use the **gateway's local timezone**
|
|
361
|
+
(set `TZ` on the process); ISO datetimes are absolute instants.
|
|
362
|
+
- `cron_list` / `cron_delete(id)`.
|
|
363
|
+
|
|
364
|
+
Tasks persist to `<baseDir>/<id>/cron.json` and survive restarts. When a task is
|
|
365
|
+
due, the gateway scheduler re-runs its `prompt` **through the normal message
|
|
366
|
+
pipeline, bound to the chat that created it** — so the result posts back in that
|
|
367
|
+
same channel, exactly as if the prompt had arrived as a message.
|
|
368
|
+
|
|
369
|
+
> **Security.** The `cron_create`/`cron_delete` **owner check** (`registerBot.owner_uid`)
|
|
370
|
+
> stops the agent from *casually* registering a task for a non-owner — but it is
|
|
371
|
+
> **not a hard boundary**: under the default `bypassPermissions` + `allowedTools: "*"`
|
|
372
|
+
> the agent can `Write` `cron.json` directly. That's inherent to a full-tool bot
|
|
373
|
+
> (it can already run any command), so **only enable `sdk.cron` for bots you'd
|
|
374
|
+
> already trust with full tools** (your own DM, trusted-team rooms). For an
|
|
375
|
+
> untrusted-input bot, restrict `allowedTools` instead — a cron-specific lock
|
|
376
|
+
> would be false assurance. A fired task bypasses the group @mention gate
|
|
377
|
+
> (authenticated by a per-process nonce so a real inbound payload can't forge it)
|
|
378
|
+
> and is still rate-limited; it is itself offered the cron tools, so it can
|
|
379
|
+
> self-schedule.
|
|
380
|
+
|
|
381
|
+
### Multi-bot
|
|
382
|
+
|
|
383
|
+
To run several bots from one process, list them in the global config's `bots`
|
|
384
|
+
array. Each `id` is a slug (letters, digits, `.`, `_`, `-`) that names the bot's
|
|
385
|
+
subtree under `baseDir`; each bot's **token + overrides live in its own
|
|
386
|
+
`<baseDir>/<id>/config.json`** (the highest-priority layer):
|
|
387
|
+
|
|
388
|
+
Global `~/.cc-channel-octo/config.json`:
|
|
389
|
+
|
|
390
|
+
```jsonc
|
|
391
|
+
{
|
|
392
|
+
"apiUrl": "https://your-octo-instance.com",
|
|
393
|
+
"bots": [
|
|
394
|
+
{ "id": "support" },
|
|
395
|
+
{ "id": "ops", "model": "vertexai/claude-opus-4-8" }
|
|
396
|
+
]
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Per-bot `~/.cc-channel-octo/support/config.json` and `~/.cc-channel-octo/ops/config.json`:
|
|
401
|
+
|
|
402
|
+
```jsonc
|
|
403
|
+
{ "botToken": "bf_AAA" }
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Each bot runs a fully independent stack (gateway, router, store). Its
|
|
407
|
+
`data`/`workspace`/`memory` are derived as `<baseDir>/<id>/…`, so **bots never
|
|
408
|
+
share conversation history, working directories, or memory** — the isolation is
|
|
409
|
+
structural and not overridable. A bot may override shared fields (`apiUrl`,
|
|
410
|
+
`model`, `systemPrompt`, `botBlocklist`, mention lists)
|
|
411
|
+
in its inline `bots[]` entry or, with higher priority, its per-bot config.json.
|
|
412
|
+
A single bot is just one entry (conventionally `id: "default"`).
|
|
413
|
+
|
|
414
|
+
## Security Model
|
|
415
|
+
|
|
416
|
+
cc-channel-octo runs Claude Code in **headless automation mode**. There is no terminal for interactive permission prompts, so `bypassPermissions` is the default. Security relies on two mechanisms:
|
|
417
|
+
|
|
418
|
+
### 1. `allowedTools` Whitelist
|
|
419
|
+
|
|
420
|
+
The `allowedTools` field accepts either the wildcard `"*"` (allow every tool
|
|
421
|
+
the SDK exposes — the default) or an explicit string array whitelist. Reduce
|
|
422
|
+
the list to reduce risk:
|
|
423
|
+
|
|
424
|
+
| Profile | `allowedTools` | Risk Level |
|
|
425
|
+
|---------|---------------|------------|
|
|
426
|
+
| **Full** (default) | `"*"` | High — every SDK tool available |
|
|
427
|
+
| **Explicit full** | `["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch", "WebSearch"]` | High — same surface, pinned list |
|
|
428
|
+
| **No network** | `["Read", "Write", "Edit", "Bash", "Glob", "Grep"]` | Medium — no SSRF risk |
|
|
429
|
+
| **No shell** | `["Read", "Write", "Edit", "Glob", "Grep"]` | Medium — no arbitrary commands |
|
|
430
|
+
| **Read-only** | `["Read", "Glob", "Grep"]` | Low — code reading only |
|
|
431
|
+
|
|
432
|
+
### 2. Workspace Isolation (derived `workspace/`)
|
|
433
|
+
|
|
434
|
+
**Each bot's `workspace/` (`<baseDir>/<botId>/workspace`) is the parent under
|
|
435
|
+
which each session gets its own hashed sandbox.** A 16-hex SHA-256 subdirectory
|
|
436
|
+
is derived from the same per-session key used for conversation history, so
|
|
437
|
+
isolation matches the session granularity: **per DM peer**, and **per group
|
|
438
|
+
channel** — a whole group shares one sandbox (all members work in the same tree,
|
|
439
|
+
by design; a group is a shared workspace). Different DM peers, and different
|
|
440
|
+
groups, cannot read or mutate each other's working trees, and different **bots**
|
|
441
|
+
are fully isolated by their separate subtrees. Subdirectories idle for more than
|
|
442
|
+
7 days (from the last inbound message) are cleaned up automatically every 6 hours.
|
|
443
|
+
|
|
444
|
+
**Limitation — cwd is a starting directory, not a chroot.** With `Bash`/`Read`
|
|
445
|
+
in the tool set and `bypassPermissions`, the agent can still be instructed to
|
|
446
|
+
read absolute paths outside the sandbox (e.g. `/etc/passwd`). Per-session
|
|
447
|
+
sandboxing partitions sessions from *each other*; it does not confine a single
|
|
448
|
+
session to its directory. For a hard boundary, run the gateway as an
|
|
449
|
+
unprivileged user in a container/VM and tighten `allowedTools` (drop `Bash`).
|
|
450
|
+
|
|
451
|
+
Because the layout is fixed under `~/.cc-channel-octo/`, the bot's own
|
|
452
|
+
`config.json` (with the token) lives in the bot subtree root, a **sibling** of
|
|
453
|
+
`workspace/` — never inside it. Keep other secrets out of the bot subtree too;
|
|
454
|
+
treat `workspace/` as untrusted ground that any user who can message the bot can
|
|
455
|
+
read within their own session sandbox.
|
|
456
|
+
|
|
457
|
+
### Built-in System Prompt
|
|
458
|
+
|
|
459
|
+
A default system prompt instructs Claude to treat input as untrusted and decline requests for sensitive file reads or credential exfiltration. This is a **soft constraint** (model-level guidance), not a security boundary. The `allowedTools` whitelist and per-session workspace isolation are the real security controls.
|
|
460
|
+
|
|
461
|
+
### Bot Loop Prevention
|
|
462
|
+
|
|
463
|
+
- The bot ignores its own messages (by `robot_id`).
|
|
464
|
+
- Configure `botBlocklist` with UIDs of other bots to prevent DM ping-pong loops.
|
|
465
|
+
- In group chats, bot messages are cached as context but do not trigger AI processing (unless explicitly @mentioned).
|
|
466
|
+
|
|
467
|
+
## Architecture
|
|
468
|
+
|
|
469
|
+
```
|
|
470
|
+
Octo User
|
|
471
|
+
↓ WuKongIM WebSocket (binary, AES-CBC encrypted)
|
|
472
|
+
Gateway ─── bot registration, token refresh, heartbeat, process lock
|
|
473
|
+
↓
|
|
474
|
+
SessionRouter ─── session key routing, mention gating, rate limiting
|
|
475
|
+
↓
|
|
476
|
+
AgentBridge ─── prompt construction → Claude Agent SDK query()
|
|
477
|
+
↓ AsyncIterable<string>
|
|
478
|
+
StreamRelay ─── 800ms throttled flush, typing heartbeat, message splitting
|
|
479
|
+
↓
|
|
480
|
+
Octo REST API
|
|
481
|
+
↓
|
|
482
|
+
Octo User
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
The gateway connects to Octo via the WuKongIM binary protocol (DH key exchange + AES-CBC encryption). Inbound messages are routed through the session router, which enforces @mention gating for groups and per-session rate limiting. The agent bridge constructs prompts from conversation history and group context, then calls the Claude Agent SDK. Responses stream back through the stream relay, which throttles output and handles Octo's stream API lifecycle.
|
|
486
|
+
|
|
487
|
+
See [ARCHITECTURE.md](./ARCHITECTURE.md) for the full design document, and
|
|
488
|
+
[RUNTIME.md](./RUNTIME.md) for how cc provides an openclaw-style agent runtime on
|
|
489
|
+
top of the Claude Agent SDK (identity, memory, sessions, skills — and the gaps).
|
|
490
|
+
|
|
491
|
+
## Development
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
# Install dependencies (sets up husky pre-commit hook via `prepare` script)
|
|
495
|
+
npm install
|
|
496
|
+
|
|
497
|
+
# Type-check
|
|
498
|
+
npm run type-check
|
|
499
|
+
|
|
500
|
+
# Lint (zero warnings enforced)
|
|
501
|
+
npm run lint
|
|
502
|
+
|
|
503
|
+
# Run tests
|
|
504
|
+
npm test
|
|
505
|
+
|
|
506
|
+
# Watch mode
|
|
507
|
+
npm run test:watch
|
|
508
|
+
|
|
509
|
+
# Coverage report (HTML + lcov in coverage/)
|
|
510
|
+
npm run test:coverage
|
|
511
|
+
|
|
512
|
+
# Build
|
|
513
|
+
npm run build
|
|
514
|
+
|
|
515
|
+
# Start (after build)
|
|
516
|
+
npm start
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Quality gates
|
|
520
|
+
|
|
521
|
+
This repo enforces three layers of automated checks:
|
|
522
|
+
|
|
523
|
+
1. **Pre-commit (`.husky/pre-commit`)** — runs `lint-staged` (ESLint on staged files with `--max-warnings 0`) and `tsc --noEmit`. Set up automatically by `npm install`.
|
|
524
|
+
2. **CI (`.github/workflows/ci.yml`)** — every PR runs `type-check`, `lint`, `test`, and `test:coverage` as separate jobs. PRs cannot merge if any job fails.
|
|
525
|
+
3. **Strict TypeScript** — `noUnusedLocals`, `noUnusedParameters`, and full `strict` mode are on. Dead code fails the build.
|
|
526
|
+
|
|
527
|
+
Coverage artifacts are uploaded per CI run (retained 14 days). No hard threshold yet — baselines are being established.
|
|
528
|
+
|
|
529
|
+
Reviewers must follow [`docs/REVIEW_CHECKLIST.md`](./docs/REVIEW_CHECKLIST.md)
|
|
530
|
+
on security-adjacent PRs. The 9-item checklist is distilled from Stage 6
|
|
531
|
+
review-process failures and codifies hard-won rules like "reproduction test
|
|
532
|
+
before APPROVED", "refresh reviews state before clicking APPROVED",
|
|
533
|
+
"enumerate canonical-equivalent forms for attacker-input validation", etc.
|
|
534
|
+
|
|
535
|
+
### Project Structure
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
src/
|
|
539
|
+
├── index.ts # Entry point — orchestrates all modules
|
|
540
|
+
├── config.ts # Three-level config loading (env > file > defaults)
|
|
541
|
+
├── gateway.ts # WKSocket lifecycle, bot registration, token refresh
|
|
542
|
+
├── session-router.ts # Session routing, mention gating, rate limiting
|
|
543
|
+
├── agent-bridge.ts # Claude Agent SDK integration
|
|
544
|
+
├── session-store.ts # SQLite session + message persistence
|
|
545
|
+
├── group-context.ts # Group message cache, member mapping, mention resolution
|
|
546
|
+
├── stream-relay.ts # Throttled streaming output + fallback
|
|
547
|
+
├── db-adapter.ts # SQLite adapter interface (better-sqlite3)
|
|
548
|
+
└── octo/
|
|
549
|
+
├── socket.ts # WuKongIM binary protocol (forked from openclaw-channel-octo)
|
|
550
|
+
├── api.ts # Octo Bot REST API client
|
|
551
|
+
└── types.ts # Protocol type definitions
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Known Limitations (v1.0)
|
|
555
|
+
|
|
556
|
+
- **Per-session workspace isolation** — Each session gets its own SHA-256 hex sandbox under the bot's `workspace/` (`<baseDir>/<botId>/workspace`), partitioned by the same key as conversation history — **per DM peer** and **per group channel** (a whole group shares one sandbox by design). Idle sandboxes (>7d) are auto-cleaned every 6h. Note: it separates sessions from each other but does not confine a session to its directory (absolute-path reads via Bash/Read remain possible) — see the Security Model section.
|
|
557
|
+
- **Groups are a shared workspace** — All members of a group share one history, one sandbox, and one auto-memory store (the session key is the channel id). There is **no member-to-member isolation within a group**; DM sessions remain private per peer.
|
|
558
|
+
- **Auto-memory is not TTL-reclaimed** — Long-term memory lives at `<baseDir>/<botId>/memory` (a sibling of `workspace/`) and is never swept by the cwd janitor, so it persists across `/reset` and grows unbounded on long-lived deploys.
|
|
559
|
+
- **SDK session owns conversation history** — every turn resumes the stored SDK session (the source of truth for history + workspace state); the system prompt is frozen (no per-turn history/context, so the prompt cache hits). A session's first turn — or a migration from existing SQLite history — injects prior history once into the user message; later turns rely on resume. A stale/expired session id is recovered automatically (cleared + retried with history re-injected). SQLite keeps a durable record for migration/recovery, not live prompt history.
|
|
560
|
+
|
|
561
|
+
## Roadmap
|
|
562
|
+
|
|
563
|
+
| Version | Scope |
|
|
564
|
+
|---------|-------|
|
|
565
|
+
| **v0.1** | Text messaging, streaming, session persistence, rate limiting, security model |
|
|
566
|
+
| **v0.2** | Media reception & sending (image/file/RichText), @mention, group context, per-session `cwdBase` isolation, self-hosted gateway, SSRF/prompt-injection hardening |
|
|
567
|
+
| **v1.0** *(current)* | Slash commands, tool progress, multi-bot, SDK-session-owned history, GROUP.md per-group instructions, scheduled tasks (cron), skill-as-data external tooling, JSON-only config |
|
|
568
|
+
|
|
569
|
+
## Contributing
|
|
570
|
+
|
|
571
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, coding standards, and the PR process.
|
|
572
|
+
|
|
573
|
+
## License
|
|
574
|
+
|
|
575
|
+
[Apache-2.0](./LICENSE)
|
|
576
|
+
|
|
577
|
+
Copyright (c) 2026 Mininglamp-OSS
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "PER-BOT config — install at ~/.cc-channel-octo/<id>/config.json (the <id> must match an entry in the global config's `bots` list; for a single bot use id 'default'). This file is the highest-priority layer: it overrides both the inline bots[] entry and the global shared fields. It holds the bot's REQUIRED botToken. The bot's directories are derived automatically as siblings of this file: ./data, ./workspace, ./memory. Drop a ./SOUL.md next to this file to give the bot a personality (it overrides sdk.systemPrompt).",
|
|
3
|
+
"botToken": "bf_YOUR_BOT_TOKEN",
|
|
4
|
+
|
|
5
|
+
"_comment_overrides": "Any of these optional fields override the global config for THIS bot only:",
|
|
6
|
+
"_comment_apiUrl": "apiUrl — override the Octo API base for this bot.",
|
|
7
|
+
|
|
8
|
+
"sdk": {
|
|
9
|
+
"_comment_model": "Override the model for this bot, e.g. 'vertexai/claude-opus-4-8'.",
|
|
10
|
+
"_comment_systemPrompt": "Inline personality string. A ./SOUL.md file next to this config overrides it.",
|
|
11
|
+
"_comment_skills": "Per-bot skill selection: 'all' or a string[] of skill names from the shared library (~/.cc-channel-octo/skills + this bot's ./skills).",
|
|
12
|
+
"_comment_env": "Extra env injected into the agent's tool subprocess, e.g. {\"OCTO_BOT_ID\":\"<robotId>\"} so a shared CLI acts as this bot.",
|
|
13
|
+
"_comment_cron": "Set cron=true to let the agent schedule tasks (cron_create/list/delete → ./cron.json, owner-gated)."
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "GLOBAL config — install at ~/.cc-channel-octo/config.json. Holds SHARED defaults + the list of bots to run. It does NOT hold a botToken: each bot's token lives in ~/.cc-channel-octo/<id>/config.json (see config.bot.example.json). The directory containing THIS file is the baseDir; every bot is a self-contained subtree at <baseDir>/<id>/{config.json, SOUL.md, data/, workspace/, memory/}, all auto-created. Per-bot dirs are NOT configurable — they are always derived from baseDir + id, so a bot can never escape its subtree.",
|
|
3
|
+
"apiUrl": "https://your-octo-instance.com",
|
|
4
|
+
|
|
5
|
+
"_comment_bots": "Which bots to run. Each entry's `id` selects its subtree <baseDir>/<id>/ and its per-bot config.json. A single bot is just one entry, conventionally id 'default'. Per-bot config.json overrides anything here (and these shared fields). You MAY put shared inline overrides on an entry (model, etc.), but the token belongs in the per-bot file.",
|
|
6
|
+
"bots": [
|
|
7
|
+
{ "id": "default" }
|
|
8
|
+
],
|
|
9
|
+
|
|
10
|
+
"_comment_group_config_dir": "v1.0: optional directory of per-group instruction files (<groupId>.md), injected as trusted custom instructions. Operator-controlled — keep it OUTSIDE every bot's workspace (the agent can write there) AND non-writable by the gateway user. Omit to disable.",
|
|
11
|
+
|
|
12
|
+
"sdk": {
|
|
13
|
+
"_comment_anthropic_base_url": "Q1: Optional self-hosted Claude API gateway base URL. Forwarded to the SDK subprocess (scoped). Must be https:// (or http://localhost for dev) — SSRF-validated at boot. Omit to use Anthropic's public endpoint.",
|
|
14
|
+
"_comment_allowed_tools": "Q2: either '*' (allow every tool the SDK exposes) or an explicit string[] whitelist.",
|
|
15
|
+
"allowedTools": "*",
|
|
16
|
+
"permissionMode": "bypassPermissions",
|
|
17
|
+
"_comment_tool_progress": "v0.3: set toolProgress=true to post '🔧 Running <tool>(<params>)…' notices (params truncated). Off by default.",
|
|
18
|
+
"_comment_setting_sources": "#100: which host filesystem settings the SDK loads (user=~/.claude, project=.claude, local=.claude/*.local). Default [\"project\"] so the SDK discovers skills symlinked into each session sandbox's .claude/skills/. Memory stays isolated regardless (the auto-memory dir is pinned via inline settings, ranked above projectSettings). Add \"user\" only to deliberately load the operator's real ~/.claude.",
|
|
19
|
+
"settingSources": ["project"]
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
"rateLimit": {
|
|
23
|
+
"maxPerMinute": 5
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
"context": {
|
|
27
|
+
"maxContextChars": 6000,
|
|
28
|
+
"historyLimit": 40
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
"_comment_mention_free": "G12: group_ids listed here process every text message without requiring an @bot mention.",
|
|
32
|
+
"mentionFreeGroups": []
|
|
33
|
+
}
|