claude-telegram-bot 0.3.36 → 0.4.0

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/README.md CHANGED
@@ -6,19 +6,26 @@
6
6
  [![npm downloads](https://img.shields.io/npm/dm/claude-telegram-bot.svg)](https://www.npmjs.com/package/claude-telegram-bot)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
9
- Use Claude Code from Telegram — run it anywhere, from any device.
9
+ Use Claude Code or Codex from Telegram — run coding agents anywhere, from any device.
10
10
 
11
- **A zero-dependency, single-file, daemonized Claude Code bot — no Bun, no Python, no open session.**
11
+ **A zero-dependency, daemonized Telegram bridge for Claude Code and Codex — no Bun, no Python, no open session.**
12
12
 
13
- A tiny bridge that takes your Telegram messages, runs `claude -p` (Claude Code headless mode)
14
- in a project folder, and sends the result back to the chat. One `.mjs` file on Node 18+ built-ins
15
- nothing to `npm install`, nothing to audit but under 1 000 readable lines.
13
+ A small bridge that takes your Telegram messages, runs the selected coding provider in a project
14
+ folder, and sends the result back to the chat. It uses only Node 18+ built-ins, with no runtime
15
+ dependencies to install.
16
16
 
17
- ```
18
- [you] → Telegram → bot.mjs → claude -p (config.projectDir) → result → Telegram
17
+ ```text
18
+ [you] → Telegram → bot.mjs
19
+ ├─ Claude provider → state.sessionId
20
+ ├─ Codex provider → state.codexSessionId
21
+ └─ Ollama mode/fallback
22
+
23
+ result → Telegram
24
+
25
+ Claude limit → Codex fallback → codex-handoff.md → next Claude call
19
26
  ```
20
27
 
21
- Drive Claude Code from your phone: run tests, edit files, commit, push — all from a chat.
28
+ Drive a coding agent from your phone: run tests, edit files, commit, push — all from a chat.
22
29
  It runs as a **background daemon** (launchd), so there's no interactive session to keep open.
23
30
 
24
31
  > ### ⚠️ This is a remote code-execution tool by design. Read the [Security](#security) section before running it.
@@ -32,7 +39,9 @@ It runs as a **background daemon** (launchd), so there's no interactive session
32
39
  - **Multi-persona** — split the *same* project into role-based bots (e.g. Developer + Planner)
33
40
  with per-bot system prompts and **differentiated permission levels**.
34
41
  - **Session continuity** — conversations resume across restarts (`--resume`); `/new` to reset.
35
- - **Attachments** — send photos/docs/voice/video; they're saved locally and handed to Claude.
42
+ - **Provider switching** — use Claude or Codex as the main agent and switch with `/provider`.
43
+ - **Fallback handoff** — Codex can take over when Claude hits a limit and leave handoff notes.
44
+ - **Attachments** — send photos/docs/voice/video; they're saved locally and handed to the active provider.
36
45
  - **Always-on** — ships with a launchd template for macOS (auto-start, auto-restart).
37
46
 
38
47
  ## How it compares
@@ -43,12 +52,12 @@ can pick the right tool:
43
52
  | | This bot | [Official Claude Code Channels](https://code.claude.com/docs/en/channels) | [claude-code-telegram](https://github.com/RichardAtCT/claude-code-telegram) |
44
53
  |---|---|---|---|
45
54
  | Runtime | **Node built-ins only** | Bun + MCP plugin | Python 3.11+ + libs |
46
- | Execution model | headless `claude -p` per message | events pushed into an **open** `claude --channels` session | Claude SDK / CLI |
55
+ | Execution model | headless Claude or Codex process per message | events pushed into an **open** `claude --channels` session | Claude SDK / CLI |
47
56
  | Stays running as | **background daemon** (no session open) | a live interactive session you keep running | service / daemon |
48
57
  | Multi-persona, permission-scoped bots on one repo | **yes** (dev=`bypass`, planner=`plan`) | no | no |
49
58
  | Per-action permission approval (inline buttons) | no (set `permissionMode`) | **yes** | partial |
50
59
  | Feature breadth (webhooks, cron, voice, export) | minimal | medium | **large** |
51
- | Lines of code to read/fork | **~400, one file** | larger | large |
60
+ | Runtime dependencies | **none (Node built-ins)** | plugin runtime | Python packages |
52
61
 
53
62
  **Use the official Channels** if you want per-action approvals and don't mind keeping a session
54
63
  open. **Use claude-code-telegram** if you want maximum features. **Use this** if you want a
@@ -82,12 +91,16 @@ to execute commands; that power is the point, and also the risk. Read this befor
82
91
  - **Guard the token like a credential.** `config.json` and `state.json` are in `.gitignore` so you
83
92
  don't commit it — keep it that way. Never paste the token into issues, logs, or screenshots. The
84
93
  startup log redacts it (`token: <redacted>`); don't add it back.
85
- - **There is no sandbox.** The bot runs `claude` as *your* user, with your filesystem, your SSH/git
86
- credentials, and your Claude OAuth/keychain session. It can do anything you can.
94
+ - **The bot itself is not a security boundary.** Provider controls reduce risk but do not isolate
95
+ Telegram, custom commands, or the daemon process. Claude runs with `permissionMode`; Codex uses
96
+ `codexSandbox`. Both processes still inherit the daemon user's environment and credentials.
87
97
 
88
98
  ### Choose the least permission you can live with
89
99
 
90
- `permissionMode` is the main safety dial:
100
+ Claude and Codex use different safety controls. Switching provider can therefore change the
101
+ effective permission boundary.
102
+
103
+ For Claude, `permissionMode` is the main safety dial:
91
104
 
92
105
  | Mode | What it allows | Use when |
93
106
  |---|---|---|
@@ -101,6 +114,10 @@ Practical hardening:
101
114
  - Point `projectDir` at a **specific project**, not your home directory — limit the blast radius.
102
115
  - For multi-persona setups, give only **one** bot `bypassPermissions`; keep the rest on `plan`.
103
116
  - Consider running on a dedicated user account or VM if you'll leave it always-on.
117
+ - For Codex, keep `codexSandbox: "workspace-write"` unless you understand the implications of a
118
+ broader sandbox setting. `/plan`, `/compact`, and automatic compact are currently Claude-only.
119
+ - Custom `/commands` run directly as the daemon user; neither `permissionMode` nor `codexSandbox`
120
+ restricts them.
104
121
 
105
122
  ### Reporting a vulnerability
106
123
 
@@ -115,7 +132,9 @@ This is a standalone CLI/daemon, **not a library** — you don't `import` it. In
115
132
  run via `npx`), point a config file at any project, and run it. `projectDir` in the config decides
116
133
  which folder Claude works in, independent of where the bot is installed.
117
134
 
118
- Prerequisites: **Node 18+** and the **`claude` CLI installed and authenticated** on the host.
135
+ Prerequisites: **Node 18+**, a Telegram bot token, and at least one configured provider. Install and
136
+ authenticate the **Claude CLI** for Claude, the **Codex CLI** for Codex or Codex fallback, and
137
+ **Ollama plus a local model** only if you enable Ollama mode/fallback.
119
138
 
120
139
  **Option A — npx (no install)**
121
140
 
@@ -162,7 +181,22 @@ auth layer.)
162
181
  - `run the solver tests and commit + push if they pass`
163
182
  - `add an edge case to solve-2nd-floor-edges.ts`
164
183
 
165
- Commands: `/new` (reset context / new session) · `/compact` (compress context, keep session) · `/plan <request>` (plan only, then approve/cancel) · `/stop` (stop current task; `--reset` to also roll back the session) · `/ollama` (toggle Ollama chat mode) · `/cron` (list / add / remove scheduled tasks) · `/reserve` (show queue status at usage-limit · `/reserve rm` to cancel) · `/restart` (syntax-check & restart the bot) · `/status` (bot status & version) · `/model` (view / switch the model) · `/id` (show chat ID) · `/help`.
184
+ Core commands:
185
+
186
+ | Command | Purpose |
187
+ |---|---|
188
+ | `/provider [claude\|codex\|default]` | View or switch the Telegram bot's provider override |
189
+ | `/model [name\|default]` | View or switch the active provider's model |
190
+ | `/new` | Reset the active provider's conversation session |
191
+ | `/plan <request>` | Produce a plan and wait for approval (Claude only) |
192
+ | `/compact` | Compact the current context (Claude only) |
193
+ | `/stop [--reset]` | Stop the task; optionally restore its previous session ID |
194
+ | `/ollama` | Toggle local Ollama chat mode |
195
+ | `/testfallback` | Test the configured Codex or Ollama fallback |
196
+ | `/status` | Show version, provider, CLI versions, model, fallback, and session state |
197
+ | `/remember <text>` · `/memory` | Save or inspect persistent memory |
198
+ | `/cron` · `/reserve` | Manage scheduled jobs and usage-limit retries |
199
+ | `/autocompact` · `/restart` · `/id` · `/help` | Maintenance and help commands |
166
200
 
167
201
  > **`/plan <request>`** runs the request in read-only plan mode (no edits, no shell) regardless of
168
202
  > the bot's configured `permissionMode`, and replies with the plan plus **✅ Proceed / ❌ Cancel**
@@ -171,7 +205,7 @@ Commands: `/new` (reset context / new session) · `/compact` (compress context,
171
205
  > you a review step before a `bypassPermissions` bot touches anything. The pending approval expires
172
206
  > if you start a new session with `/new` first.
173
207
 
174
- > **`/stop`** kills the running Claude process immediately and clears any queued messages.
208
+ > **`/stop`** kills the running provider process immediately and clears any queued messages.
175
209
  > Add `--reset` to also restore the session to the state it was in *before* the task started,
176
210
  > so the conversation history doesn't include the interrupted work.
177
211
 
@@ -188,6 +222,22 @@ Commands: `/new` (reset context / new session) · `/compact` (compress context,
188
222
 
189
223
  ---
190
224
 
225
+ ## Compatibility
226
+
227
+ The bot depends on CLI flags and machine-readable output that may change between releases. These are
228
+ the development-environment versions recorded as the compatibility baseline on 2026-07-10; they are
229
+ reference versions, not strict pins or a claim that every path passes. Use `/status` to see the
230
+ versions actually installed on the bot host.
231
+
232
+ | CLI | Recorded version | Relevant integration |
233
+ |---|---:|---|
234
+ | Claude Code | `2.1.206` | JSON output, session resume, permission mode |
235
+ | Codex CLI | `0.144.1` | `exec`, `exec resume`, JSONL events, workspace sandbox |
236
+ | Ollama | `0.31.1` | `ollama launch claude`, model selection, session handoff |
237
+
238
+ When upgrading one of these CLIs, run `/testfallback` and a normal Claude message before relying on
239
+ the bot unattended. Update this table after recording the new environment and checking those paths.
240
+
191
241
  ## Configuration
192
242
 
193
243
  ```sh
@@ -200,20 +250,44 @@ Edit `mybot.json`:
200
250
  |---|---|
201
251
  | `token` | Bot token from BotFather |
202
252
  | `allowedChatId` | **Leave empty at first** → the bot tells you (step 3). Required before it runs anything. |
203
- | `projectDir` | Absolute path to the working folder Claude runs in |
253
+ | `projectDir` | Absolute path to the working folder the selected provider runs in |
254
+ | `provider` | (optional) Main provider for Telegram messages and scheduled jobs: `"claude"` (default) or `"codex"` |
204
255
  | `claudeBin` | Output of `which claude` (absolute path recommended) |
205
- | `permissionMode` | `plan` / `acceptEdits` / `bypassPermissions` — see [Security](#security) |
206
- | `model` | Empty = default. Or `opus` / `sonnet`, etc. Override at runtime with `/model` (persists in state). |
256
+ | `permissionMode` | Claude-only: `plan` / `acceptEdits` / `bypassPermissions` — see [Security](#security) |
257
+ | `model` | Claude model. Empty = CLI default. Override with `/model` while Claude is active. |
207
258
  | `lang` | (optional) UI language. Empty = auto-detect per user (English default, Korean for Korean Telegram clients). Force with `"en"` / `"ko"`. |
208
259
  | `name` | (optional) Bot name shown in `/help` — handy for telling multiple bots apart |
209
260
  | `persona` | (optional) Role system prompt — defines a persona (developer/planner/…). See below |
210
261
  | `appendSystemPrompt` | (optional) Override the default "be concise for Telegram" instruction |
211
- | `env` | (optional) Extra environment variables passed to the `claude` process |
262
+ | `env` | (optional) Extra environment variables passed to provider processes |
212
263
  | `schedule` | (optional) Cron jobs that run a prompt on a timer — see [Scheduled tasks](#scheduled-tasks-cron) |
213
264
  | `commands` | (optional) Custom `/commands` that run shell scripts — see [Custom commands](#custom-commands) |
214
- | `ollamaFallback` | (optional) `true` to enable Ollama as a fallback when Claude is rate-limited or out of credits |
215
- | `ollamaModel` | (optional) Ollama model to use for fallback (default: `"phi3:mini"`) |
216
- | `autoCompactThreshold` | (optional) Auto-compact context when cached input tokens exceed this value (default: `100000`). Set to `0` to disable. |
265
+ | `codexFallback` | (optional) `true` to enable Codex as the preferred fallback when Claude is rate-limited or out of credits |
266
+ | `codexBin` | (optional) Path to the `codex` binary. Defaults to `"codex"` on `PATH`; use an absolute path for launchd |
267
+ | `codexModel` | (optional) Codex model override passed with `--model` |
268
+ | `codexSandbox` | (optional) Codex sandbox for a new `codex exec` session (default: `"workspace-write"`) |
269
+ | `codexTimeout` | (optional) Milliseconds to wait for Codex before falling back to reserve/Ollama (default: `600000`) |
270
+ | `ollamaFallback` | (optional) `true` to enable Ollama as a secondary fallback when Claude is rate-limited or out of credits |
271
+ | `ollamaModel` | (optional) Ollama model to use for fallback (default: `"qwen3.5:4b"`). The context a session carries over is capped by this model's runtime context window (`num_ctx`), which Ollama defaults to ~4K regardless of the model's architectural max — too small for a long Claude session. Bake a larger window into a variant with a `Modelfile` (`FROM qwen3.5:4b` + `PARAMETER num_ctx 6144`, `ollama create qwen3.5:4b-ctx6k -f Modelfile`) and point this at it. Size it to your RAM — on an 8 GB machine ~6K is the safe ceiling; 32K swaps the machine to death. |
272
+ | `ollamaBin` | (optional) Path to the `ollama` binary. Auto-detected at `/opt/homebrew/bin/ollama`, `/usr/local/bin/ollama`, `/usr/bin/ollama`, falling back to `"ollama"` on `PATH` — set this explicitly if your install lives elsewhere, since a launchd-run bot doesn't inherit your shell's `PATH` |
273
+ | `ollamaTimeout` | (optional) Milliseconds to wait for an Ollama reply before giving up (default: `360000` — local models can be slow to cold-start) |
274
+ | `autoCompactThreshold` | (optional) Auto-compact context when cached input tokens exceed this value (default: `100000`). Set to `0` to disable. Override at runtime with `/autocompact` (persists in state). |
275
+
276
+ The same config also drives local interactive sessions. `ctb mybot.json` uses `config.provider`,
277
+ while an explicit flag overrides it for that invocation:
278
+
279
+ ```sh
280
+ ctb mybot.json --provider claude
281
+ ctb mybot.json --provider codex
282
+ ```
283
+
284
+ Claude resumes `state.sessionId`; Codex resumes `state.codexSessionId` with
285
+ `codex resume <session-id>`. The sessions remain separate. The `/plan` approval workflow currently
286
+ requires `provider: "claude"`; normal messages, attachments, and scheduled jobs support both.
287
+
288
+ `/provider` stores a Telegram-only override in state, survives bot restarts, and does not rewrite
289
+ the config. The local `ctb` command intentionally continues to use `config.provider` or its explicit
290
+ `--provider` flag.
217
291
 
218
292
  State and downloaded attachments live in a hidden **`.claude-bot/`** folder next to the config
219
293
  file, so projects stay isolated. Upgrading from an older version **auto-moves** an existing
@@ -231,14 +305,16 @@ your launchd plist points them.
231
305
  - **Formatting**: the reply's Markdown (bold/code/headings/tables) is converted to Telegram-safe
232
306
  HTML. If conversion ever produces invalid HTML, the message is automatically resent as plain text.
233
307
  - **Attachments**: send a photo/document/voice/video and it's downloaded into `attachments/`; the
234
- absolute path is handed to Claude (caption included as the message). Images can be opened with Read.
235
- - **Sessions**: conversations resume automatically (`--resume`); the last session id is saved in
236
- `state.json`, so context survives restarts. Use `/new` to start fresh.
308
+ absolute path is handed to the active provider (caption included as the message).
309
+ - **Sessions**: Claude and Codex keep separate session IDs, so switching providers preserves both
310
+ conversations. `/new` resets only the active provider's session.
237
311
  - **Message queue**: if you send a message while a task is running, it is queued (not dropped). When the task finishes, all queued messages are merged into a single prompt so Claude can resolve corrections and follow-ups in one pass (e.g. "do X" then "never mind, do Y" → handled together). Use `/stop` to cancel the running task and discard the queue.
238
312
  - **Model hint**: the bot tells Claude which model it is running as. If Claude judges a question to be beyond its current tier, it appends a one-line suggestion at the end of the reply (e.g. 💡 `/model sonnet`). Switch with `/model <name>` — `haiku`, `sonnet`, `opus`, `fable`, or a full model id. The choice persists in `state.json` across restarts.
239
- - **Usage-limit queue**: when a Claude Max / API rate-limit error includes a reset time, the triggering message is automatically queued and retried at that time — just like messages queued while Claude is busy. Any additional messages you send during the limit window are also added to the queue. Use `/reserve` to check queue status and reset time, `/reserve rm` to cancel and clear the queue.
240
- - **Ollama fallback**: set `"ollamaFallback": true` and point `"ollamaModel"` at a locally-running [Ollama](https://ollama.ai) model (default: `"phi3:mini"`). When Claude is rate-limited or out of credits, the bot automatically falls back to Ollama and prepends a notice to the reply. Use `/ollama` to toggle Ollama as your primary chat partner at any time, even when Claude is working fine useful for lightweight queries.
241
- - **Auto-compact**: the bot tracks how many cached tokens are in the session context. When `cache_read_input_tokens` exceeds `autoCompactThreshold` (default 100 000), it automatically runs `/compact` and notifies you. Tune the threshold in config or set it to `0` to disable. You can also run `/compact` manually at any time.
313
+ - **Usage-limit queue**: when a Claude Max / API rate-limit error includes a reset time, the bot first tries enabled fallbacks. If no fallback is enabled or every fallback fails, the triggering message is queued and retried at that time — just like messages queued while Claude is busy. Any additional messages you send during the limit window are also added to the queue. Use `/reserve` to check queue status and reset time, `/reserve rm` to cancel and clear the queue.
314
+ - **Codex fallback**: set `"codexFallback": true` to run `codex exec` when Claude is rate-limited or out of credits. Codex keeps its own session in `state.codexSessionId` using `codex exec resume <id>`, but Claude and Codex sessions are not interoperable. Each successful Codex fallback appends a summary to `.claude-bot/codex-handoff.md`, and future Claude calls receive the recent handoff notes as context.
315
+ - **Runtime provider switching**: use `/provider` to view the active provider, `/provider claude` or `/provider codex` to store a bot-state override, and `/provider default` to return to the config value. Each provider's session is preserved separately.
316
+ - **Ollama fallback**: set `"ollamaFallback": true` and point `"ollamaModel"` at a locally-installed [Ollama](https://ollama.ai) model (default: `"qwen3.5:4b"`). Ollama is now a secondary automatic fallback when Codex is disabled or fails, and `/ollama` still toggles local chat mode manually. It runs Claude Code through the local model via `ollama launch claude … --resume <session>`, but this remains best-effort because local model context windows are much smaller than Claude's.
317
+ - **Auto-compact**: the bot tracks how many cached tokens are in the session context. When `cache_read_input_tokens` exceeds `autoCompactThreshold` (default 100 000), it automatically runs `/compact` and notifies you. Tune the threshold in config, or at runtime with `/autocompact <number>` (`/autocompact off` to disable, `/autocompact default` to reset) — the override persists in `state.json` across restarts. You can also run `/compact` manually at any time.
242
318
 
243
319
  ### Custom commands
244
320
 
@@ -431,8 +507,9 @@ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.pl
431
507
  ## Requirements
432
508
 
433
509
  - Node.js 18+ (for built-in `fetch`)
434
- - The `claude` CLI installed and authenticated on the host
435
510
  - A Telegram bot token from `@BotFather`
511
+ - At least one provider: authenticated Claude CLI or authenticated Codex CLI
512
+ - Optional: Codex CLI for Codex fallback; Ollama and a downloaded model for Ollama mode/fallback
436
513
 
437
514
  ## License
438
515