switchroom 0.12.13 → 0.12.15
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 +107 -371
- package/dist/cli/switchroom.js +627 -386
- package/dist/vault/approvals/kernel-server.js +88 -1
- package/dist/vault/broker/server.js +132 -2
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +289 -81
- package/telegram-plugin/gateway/approval-callback.test.ts +49 -1
- package/telegram-plugin/gateway/approval-callback.ts +85 -56
- package/telegram-plugin/gateway/gateway.ts +168 -19
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +39 -0
- package/telegram-plugin/gateway/pending-permission-decisions.ts +112 -0
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +117 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +71 -1
- package/telegram-plugin/tests/pending-permission-decisions.test.ts +73 -0
- package/telegram-plugin/tests/vault-save-inbound-builders.test.ts +96 -0
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="docs/assets/switchroom-hero-wide.png" alt="Switchroom
|
|
2
|
+
<img src="docs/assets/switchroom-hero-wide.png" alt="Switchroom: opinionated Telegram UX for Claude Code on your Pro or Max subscription" width="100%">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# Switchroom
|
|
@@ -10,176 +10,76 @@
|
|
|
10
10
|
[](https://github.com/switchroom/switchroom/actions/workflows/ci-evals.yml)
|
|
11
11
|
[](https://github.com/switchroom/switchroom/actions/workflows/ci-evals.yml)
|
|
12
12
|
|
|
13
|
-
**A switchboard for your Pro or Max.** Your Claude subscription, as a fleet of always-on specialist agents you talk to from Telegram. Opinionated UX, done properly.
|
|
14
|
-
|
|
15
|
-
> *I loved OpenClaw + Telegram. I wanted my Claude subscription. And the UX done properly. So I built this.*
|
|
13
|
+
**A switchboard for your Pro or Max.** Your Claude subscription, run as a fleet of always-on specialist agents you talk to from Telegram. Opinionated UX, done properly.
|
|
16
14
|
|
|
17
15
|
[Latest release notes →](CHANGELOG.md)
|
|
18
16
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
Every time an agent starts work, a **progress card** pins into its Telegram topic and updates in place as tools execute. Each Read, Bash, Edit, Grep is visible as it happens, with elapsed time so you can tell if something's stuck. Sub-agents surface in the same card. When the agent finishes, the card flips to Done and unpins.
|
|
22
|
-
|
|
23
|
-
No silent gaps. No ghosts. No squinting into a black box.
|
|
24
|
-
|
|
25
|
-
<p align="center"><img src="docs/diagrams/progress-card-anatomy.svg" width="700" alt="Annotated progress card: pin badge, user quote, last 5 steps, collapsed older, in-flight pulse, elapsed timer, sub-agent indent"></p>
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
⚙️ Working… · ⏱ 12s
|
|
29
|
-
💬 refactor the auth module to use JWT
|
|
30
|
-
─ ─ ─
|
|
31
|
-
… (+3 more earlier steps)
|
|
32
|
-
✅ Read src/auth/session.ts
|
|
33
|
-
✅ Grep "cookie" (in src/)
|
|
34
|
-
🤖 Edit src/auth/jwt.ts · 4s
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The card is the headline UX. The rest of the product is in service of it.
|
|
17
|
+
## Why this exists
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
- Last 5 steps stay visible. Older ones collapse into `(+N more earlier steps)`.
|
|
41
|
-
- Running steps show elapsed time so a stuck tool is obvious.
|
|
42
|
-
- Tool labels are deterministic, written by a `PreToolUse` hook, so the card never lies about what's running.
|
|
43
|
-
- Two agents working at once? Each gets its own card, labelled `(1/2)` and `(2/2)`.
|
|
44
|
-
|
|
45
|
-
### Sub-agent visibility
|
|
19
|
+
> *I loved OpenClaw + Telegram. I wanted my Claude subscription. And the UX done properly. So I built this.*
|
|
46
20
|
|
|
47
|
-
|
|
21
|
+
You had the obvious idea. Run Claude Code agents 24/7 on a cheap Linux box, talk to them from Telegram, use the Pro or Max subscription you already pay for.
|
|
48
22
|
|
|
49
|
-
|
|
23
|
+
Two ways to do that today. Both miss:
|
|
50
24
|
|
|
51
|
-
|
|
25
|
+
- **OpenClaw + Telegram.** Great UX. But it hits the Anthropic API on your own key, so a token bill ticks over in the background. You signed up to use your subscription, not to buy API credits on top of it.
|
|
26
|
+
- **Claude Code's built-in Telegram channel.** Uses the subscription correctly. But it's an MVP black box. Send a message, wait, eventually something comes back. What did the agent actually do? Which tools ran? Did it get stuck? No idea.
|
|
52
27
|
|
|
53
|
-
|
|
28
|
+
Switchroom is the third option. Subscription-honest, and the UX done properly. The headline: the agent is never a black box and never silent. You always see what it is doing, what it is waiting on, and when it is done.
|
|
54
29
|
|
|
55
|
-
|
|
30
|
+
## See it work
|
|
56
31
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
## What you get
|
|
60
|
-
|
|
61
|
-
| Feature | What it does |
|
|
62
|
-
|---|---|
|
|
63
|
-
| **Progress cards** | Pinned, in-place, every tool call visible. The headline UX. |
|
|
64
|
-
| **Claude Pro/Max auth** | OAuth, not API keys. No per-token billing. Fleet-wide active account + fallback order; broker-owned refresh and credential fanout. |
|
|
65
|
-
| **Approval kernel** | Inline allow/deny cards in Telegram for every gated tool. TTL'd grants, full audit trail. |
|
|
66
|
-
| **Sub-agents** | Opus plans, Sonnet implements. Sub-agent work surfaces in the parent card. |
|
|
67
|
-
| **Config cascade** | Defaults, then profiles, then per-agent YAML. Change one line, every agent updates. |
|
|
68
|
-
| **Scheduled tasks** | Cron-syntax tasks that fire across reboots. Headless secret access via the vault broker. |
|
|
69
|
-
| **Persistent memory** | Hindsight semantic memory with knowledge graphs and mental models. |
|
|
70
|
-
| **Session continuity** | Resume across restarts with freshness gating and a wake-audit. |
|
|
71
|
-
| **Encrypted vault** | AES-256-GCM for secrets. Optional auto-unlock keyed off `/etc/machine-id`. |
|
|
72
|
-
| **Drive MCP** | Read Google Docs, Sheets, and Drive files inline. Per-agent OAuth, no shared key. |
|
|
73
|
-
| **Card audit log** | Every progress-card edit appended to `card-events.jsonl` for retrospective debugging. |
|
|
74
|
-
| **15 Telegram MCP tools** | Reply, stream replies, edit, pin, react, native checklists, sticker aliases, voice-in transcription, attachments, history. |
|
|
75
|
-
|
|
76
|
-
## Architecture
|
|
77
|
-
|
|
78
|
-
One long-running service per agent. Each agent runs the stock `claude` CLI — not a fork, not the Agents SDK, not a wrapped harness — authenticated directly with Anthropic via official OAuth. Switchroom is scaffolding and lifecycle around the CLI you'd run by hand: a Telegram bot, an approval broker, a vault broker, and Docker Compose for supervision. See [`docs/architecture.md`](docs/architecture.md) for the process model and how each layer maps to the `claude` CLI.
|
|
32
|
+
Switchroom's UX is deterministic. Every agent topic carries a status line that is always one of three honest states, never a guess and never nothing:
|
|
79
33
|
|
|
80
34
|
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
@YourBot ──┬── switchroom-telegram MCP ──┬── agent supervisor ─── Claude Code CLI
|
|
85
|
-
│ (15 tools) │ (per-agent) │
|
|
86
|
-
│ │ ├─ .claude/agents/*.md (sub-agents)
|
|
87
|
-
├─ Progress cards ├─ Approval kernel ◄─────┤ settings.json (tools, hooks, MCP)
|
|
88
|
-
├─ Pin / unpin lifecycle │ (allow/deny broker) ├─ Hindsight plugin (memory)
|
|
89
|
-
├─ SQLite history ├─ Vault broker ◄────────┤ Drive MCP, Playwright MCP, …
|
|
90
|
-
├─ Card-events.jsonl audit ├─ Auth broker ◄─────────┤ in-agent scheduler sidecar
|
|
91
|
-
├─ Emoji reactions │ (OAuth refresh, └─ (cron, fires across reboots)
|
|
92
|
-
└─ Format conversion │ sole creds writer)
|
|
93
|
-
├─ hostd (host-control:
|
|
94
|
-
│ /restart, /update apply)
|
|
95
|
-
└─ Docker Compose restart (unless-stopped)
|
|
35
|
+
🟡 quiet · no turns yet
|
|
36
|
+
⚙️ working since 2m ago
|
|
37
|
+
🟢 idle · last reply 5m ago
|
|
96
38
|
```
|
|
97
39
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
## Approvals & safety
|
|
101
|
-
|
|
102
|
-
Tools that touch the world — Bash, Edit, Write, anything not on an agent's pre-approved allowlist — pause for explicit approval. Switchroom's **approval kernel** (shipped in v0.5.1) routes every gated tool call through an inline Telegram card with the actual diff or command shown. Tap Allow and the tool resumes. Tap Deny and the agent gets a clean refusal it can recover from.
|
|
103
|
-
|
|
104
|
-
<p align="center"><img src="docs/diagrams/approval-grant-flow.svg" width="700" alt="Approval grant flow: agent tool call pauses at the kernel, broker writes pending grant to sqlite, user taps Allow on the Telegram card, broker releases the gate, tool resumes"></p>
|
|
105
|
-
|
|
106
|
-
- **Inline cards.** Allow / Deny / Allow once / Allow for 1h. No leaving Telegram.
|
|
107
|
-
- **TTL'd grants.** "Allow Bash for 1h" expires automatically. No silent permanent escalation.
|
|
108
|
-
- **Audit trail.** Every grant, denial, and expiry written to a per-agent log you can replay.
|
|
109
|
-
- **Per-agent allowlist.** `switchroom agent grant <name> <tool>` for the boring ones you don't want to be asked about.
|
|
110
|
-
|
|
111
|
-
The kernel runs as an out-of-process broker over a unix socket. The agent process never decides its own permissions; it asks and waits.
|
|
112
|
-
|
|
113
|
-
### Compliance posture
|
|
114
|
-
|
|
115
|
-
Switchroom never intercepts auth, never proxies inference, never patches the CLI. The `claude` binary you run is the one Anthropic ships. See the [Compliance Attestation](docs/compliance-attestation.md) for the full analysis against Anthropic's April 2026 third-party policy.
|
|
116
|
-
|
|
117
|
-
## Survives real life
|
|
118
|
-
|
|
119
|
-
Each agent is a long-running service. They survive reboots, network drops, and your laptop closing. But "always on" isn't enough on its own. Things still die. The product has to handle that gracefully or the illusion breaks.
|
|
120
|
-
|
|
121
|
-
<p align="center"><img src="docs/diagrams/wake-audit-lifecycle.svg" width="700" alt="Wake-audit lifecycle: kill, crash-pane snapshot, auto-restart, agent boots with SWITCHROOM_PENDING_TURN, acks with three options"></p>
|
|
122
|
-
|
|
123
|
-
- **Auto-restart.** Agent containers come up with `restart: unless-stopped`, and each service has a healthcheck — a crashed or wedged agent is brought back automatically. No silent dropped work.
|
|
124
|
-
- **Resume protocol.** When an agent reboots mid-turn, `start.sh` exports `SWITCHROOM_PENDING_TURN=true` plus the original chat / message ids. The agent's first action on boot is to acknowledge the gap and ask the user how to proceed (start over, summarise and continue, or drop it).
|
|
125
|
-
- **Wake-audit.** On every fresh boot the agent checks for owed replies, orphan sub-agents, and stale in-progress todos. If everything's clean it stays quiet. If it owed you a reply, it tells you.
|
|
126
|
-
- **Token refresh.** The `switchroom-auth-broker` daemon owns the refresh loop and is the sole writer of every `credentials.json`. Per-account quota state fans out across the fleet in seconds; `auth.fallback_order` cycles when an account is exhausted.
|
|
40
|
+
While the agent works, the reply streams in place (about once a second, not one delayed dump at the end) and tool steps render as they run with `MM:SS` durations. Long-running work gets a Steer button so you can redirect mid-turn without killing it. When an agent delegates, a sub-agent block shows the live fleet, up to 5 members, each with its role and current tool:
|
|
127
41
|
|
|
128
|
-
|
|
42
|
+
```
|
|
43
|
+
⚙️ Working… · 00:12
|
|
44
|
+
refactor the auth module to use JWT
|
|
129
45
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
| Runtime | Claude Code CLI | Claude Code CLI | Custom runtime | Agents SDK |
|
|
134
|
-
| Auth | Pro/Max OAuth | Pro/Max OAuth | API key | API key |
|
|
135
|
-
| Sub-agent tracking | Yes, in card | No | No | No |
|
|
136
|
-
| Parallel task display | Labelled cards `(1/N)` | No | No | No |
|
|
137
|
-
| Approval UX | Inline Telegram cards | None | None | None |
|
|
138
|
-
| Config | YAML with cascade | None | JSON/TOML | Env vars |
|
|
139
|
-
| Setup | `switchroom setup` | Built-in (limited) | Docker compose | Docker compose |
|
|
46
|
+
✅ Read session.ts
|
|
47
|
+
✅ Grep "cookie"
|
|
48
|
+
🔵 Edit jwt.ts · 00:04
|
|
140
49
|
|
|
141
|
-
|
|
50
|
+
Sub-agents · 2 running
|
|
51
|
+
🔵 implement · Edit jwt.ts
|
|
52
|
+
🔵 test-runner · Bash npm test
|
|
53
|
+
[ Steer ]
|
|
54
|
+
```
|
|
142
55
|
|
|
143
|
-
|
|
56
|
+
<p align="center"><img src="docs/diagrams/deterministic-status-anatomy.svg" width="760" alt="Deterministic status anatomy: the never-silent state strip (quiet, working, idle) feeding a working-status message with streamed tool steps at MM:SS, a sub-agent fleet block, and a Steer button. Posted and edited in the topic, never pinned."></p>
|
|
144
57
|
|
|
145
|
-
|
|
58
|
+
Older sub-agents collapse to `+N more` once the live set passes 5. Tool arguments are sanitised before they render: file paths show the basename only, token-shaped strings get redacted, so a secret never lands in a status message. The card is posted and edited in the topic, not pinned, so it never leaves a stale `⚙️ Working…` stranded at the top of your chat. [Full UX behaviour →](docs/telegram-plugin.md)
|
|
146
59
|
|
|
147
|
-
|
|
60
|
+
## Quickstart
|
|
148
61
|
|
|
149
|
-
|
|
62
|
+
Runs on the box you already have. Supported production runtime is Linux + Docker. Canonical target: Ubuntu 24.04 LTS, 4 GiB RAM minimum, 8 GiB once you run more than one agent. macOS (Docker Desktop) works for development, not yet release-validated.
|
|
150
63
|
|
|
151
|
-
|
|
64
|
+
**Fresh Linux box, one script:**
|
|
152
65
|
|
|
153
66
|
```bash
|
|
154
67
|
curl -fsSL https://github.com/switchroom/switchroom/raw/main/scripts/install-deps.sh | sudo bash
|
|
155
68
|
```
|
|
156
69
|
|
|
157
|
-
Installs Docker Engine + Compose v2, Node.js 20.11+, Bun, and the `@anthropic-ai/claude-code` + `switchroom` CLIs. Idempotent.
|
|
158
|
-
|
|
159
|
-
Then log out and back in so the docker group takes effect, and:
|
|
70
|
+
Installs Docker Engine + Compose v2, Node.js 20.11+, Bun, and the `@anthropic-ai/claude-code` + `switchroom` CLIs. Idempotent. Log out and back in so the docker group takes effect, then:
|
|
160
71
|
|
|
161
72
|
```bash
|
|
162
73
|
switchroom setup # interactive: Telegram + vault + first agent
|
|
163
|
-
switchroom apply #
|
|
164
|
-
docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
|
|
165
|
-
switchroom auth add default --via-claude # OAuth your
|
|
74
|
+
switchroom apply # generate ~/.switchroom/compose/docker-compose.yml
|
|
75
|
+
docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
|
|
76
|
+
switchroom auth add default --via-claude # OAuth your Pro/Max account, AFTER the fleet is up
|
|
166
77
|
switchroom auth use default # make it the fleet-wide active account
|
|
167
78
|
```
|
|
168
79
|
|
|
169
|
-
Auth comes
|
|
80
|
+
Auth comes after the fleet is up on purpose. The `switchroom-auth-broker` is the sole writer of credentials and does not exist until the compose stack is running. After this you talk to the agent from Telegram and never touch the server again. To catch a running host up later use `switchroom update`, not a raw `docker compose up` (a bare compose-up on a live fleet skips the operator restart-marker, so boot cards render as crashes).
|
|
170
81
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
sudo npm install -g bun @anthropic-ai/claude-code switchroom
|
|
175
|
-
switchroom setup
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
`bun` is a hard runtime dep — the `switchroom` CLI's entrypoint is a Bun script. A Node-only CLI build is on the roadmap but not yet shipped.
|
|
179
|
-
|
|
180
|
-
### From inside Claude Code (the on-ramp)
|
|
181
|
-
|
|
182
|
-
If you already use Claude Code, this is the shortest path. Inside any session:
|
|
82
|
+
**Already in Claude Code?** Shortest path. Inside any session:
|
|
183
83
|
|
|
184
84
|
```
|
|
185
85
|
/plugin marketplace add switchroom/switchroom
|
|
@@ -187,237 +87,88 @@ If you already use Claude Code, this is the shortest path. Inside any session:
|
|
|
187
87
|
/switchroom:setup
|
|
188
88
|
```
|
|
189
89
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
### Static binary (planned, not yet shipped)
|
|
193
|
-
|
|
194
|
-
Pre-built single-binary releases (no Node or Bun required on the host) are scaffolded in [`install.sh`](install.sh) and referenced from the GitHub Releases page, but **the release workflow that publishes those binaries still does not exist as of v0.12.0**. Use the one-script or npm paths above. Tracking work: switching the release pipeline to actually upload `switchroom-linux-{amd64,arm64}` and `switchroom-macos-{amd64,arm64}` on every tag.
|
|
195
|
-
|
|
196
|
-
### One-shot happy path (no wizard)
|
|
197
|
-
|
|
198
|
-
If you already have Telegram credentials in `~/.switchroom/switchroom.yaml` and one Anthropic account already added, skip `switchroom setup`. `agent create --profile` writes a minimal entry; the new agent inherits the fleet-wide active account automatically — no per-agent OAuth flow:
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
switchroom agent create coach --profile health-coach
|
|
202
|
-
switchroom apply && docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Example configuration
|
|
206
|
-
|
|
207
|
-
```yaml
|
|
208
|
-
switchroom:
|
|
209
|
-
version: 1
|
|
210
|
-
|
|
211
|
-
telegram:
|
|
212
|
-
# Per-agent bot token (DM-only by default).
|
|
213
|
-
bot_token: "vault:telegram-bot-token"
|
|
214
|
-
|
|
215
|
-
memory:
|
|
216
|
-
backend: hindsight
|
|
217
|
-
|
|
218
|
-
defaults:
|
|
219
|
-
model: claude-opus-4-7 # or claude-sonnet-4-6, claude-haiku-4-5
|
|
220
|
-
tools: { allow: [all] }
|
|
221
|
-
subagents:
|
|
222
|
-
worker:
|
|
223
|
-
description: "Implementation tasks"
|
|
224
|
-
model: sonnet
|
|
225
|
-
background: true
|
|
226
|
-
isolation: worktree
|
|
227
|
-
schedule:
|
|
228
|
-
- cron: "0 8 * * 1-5"
|
|
229
|
-
prompt: "Morning briefing"
|
|
230
|
-
session:
|
|
231
|
-
max_idle: 2h
|
|
232
|
-
|
|
233
|
-
agents:
|
|
234
|
-
assistant:
|
|
235
|
-
topic_name: "General"
|
|
236
|
-
memory: { collection: general }
|
|
237
|
-
|
|
238
|
-
coach:
|
|
239
|
-
topic_name: "Coach"
|
|
240
|
-
extends: advisor
|
|
241
|
-
soul:
|
|
242
|
-
name: Coach
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
See [docs/configuration.md](docs/configuration.md) for the full reference.
|
|
246
|
-
|
|
247
|
-
## Vault broker (cron secrets)
|
|
248
|
-
|
|
249
|
-
Scheduled tasks run headless inside the agent container, so they can't prompt for the vault passphrase. The vault broker is a long-running container (`switchroom-vault-broker`) that holds the vault decrypted in memory after a one-time interactive unlock. Cron tasks fetch the specific keys they declare via a per-agent unix socket. The passphrase never sits on disk.
|
|
250
|
-
|
|
251
|
-
**Declare per-cron secrets in `switchroom.yaml`:**
|
|
90
|
+
Full new-user walkthrough, zero to first Telegram message in ~15 minutes, plus the npm-only path, the no-wizard one-shot, the BotFather steps, and static-binary status: **[docs/install.md](docs/install.md)**.
|
|
252
91
|
|
|
253
|
-
|
|
254
|
-
agents:
|
|
255
|
-
scout:
|
|
256
|
-
schedule:
|
|
257
|
-
- cron: "0 8 * * *"
|
|
258
|
-
prompt: "Morning brief."
|
|
259
|
-
secrets: [openai_api_key, polygon_api_key] # only these may be read
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
`secrets: []` (the default) means the cron has no vault access.
|
|
263
|
-
|
|
264
|
-
**Bootstrap once per host:**
|
|
265
|
-
|
|
266
|
-
```bash
|
|
267
|
-
switchroom apply # writes broker into docker-compose.yml
|
|
268
|
-
docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d switchroom-vault-broker
|
|
269
|
-
switchroom vault broker unlock # prompt for passphrase, primes broker
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
Or just run `switchroom vault get <key>` from a TTY. The broker offers to take the unlocked state with `[Y/n]` so you don't have to remember a separate unlock command.
|
|
273
|
-
|
|
274
|
-
**Identity model (v0.7+).** Path-as-identity. The broker binds one socket per agent at `/run/switchroom/broker/<agent>/sock` inside its own container, hosted via a per-agent named volume that's also mounted at `/run/switchroom/broker/` inside `agent-<agent>`. The agent name is parsed unspoofably from the bind path — see `src/vault/broker/peercred.ts:socketPathToAgent()`. A compromised agent cannot pose as another agent's cron because it only ever sees its own socket on its mount. ACL is bind-time, never wire-time.
|
|
275
|
-
|
|
276
|
-
The broker locks on `SIGTERM` (so a container restart zeros the in-memory state) and on demand via `switchroom vault broker lock`. Use `switchroom vault get <key> --no-broker` to bypass and prompt locally.
|
|
277
|
-
|
|
278
|
-
Vault file (post-v0.7.12) lives at `~/.switchroom/vault/vault.enc` — a directory, not a single file, so atomic rename can use the parent as the staging dir. See [docs/vault.md](docs/vault.md) for the layout rationale.
|
|
279
|
-
|
|
280
|
-
### Auto-unlock on boot (opt-in)
|
|
281
|
-
|
|
282
|
-
By default, the broker holds the unlocked state in memory only. Every restart (host reboot, service crash, reconcile that re-renders the unit) wipes it and requires `switchroom vault broker unlock` again. For unattended hosts where this is too painful, switchroom can encrypt the passphrase with a key derived from `/etc/machine-id` and have the broker unlock itself at boot:
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
switchroom vault broker enable-auto-unlock # one-time setup, prompts for passphrase
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
Done. The wizard prompts for your vault passphrase, encrypts it with AES-256-GCM keyed off `/etc/machine-id`, writes the result to `~/.switchroom/vault-auto-unlock` (mode 0600), flips `vault.broker.autoUnlock: true` in `switchroom.yaml`, restarts the broker, and verifies the vault came up unlocked. Every subsequent boot the broker reads + decrypts + unlocks itself.
|
|
289
|
-
|
|
290
|
-
Disable with `switchroom vault broker disable-auto-unlock`.
|
|
291
|
-
|
|
292
|
-
**Security tradeoff. Read this before enabling.** The encrypted blob lives at mode 0600 in your home directory; the encryption key is derived from `/etc/machine-id` plus a per-file random salt. Disk theft is safe (the blob doesn't decrypt on any other machine) and other UNIX users on the same box can't read it. But root on the host *can* read both the blob and the machine-id, so once root is on the machine the passphrase is recoverable. Same blast radius as the running broker process (anything with code-exec as you can already attach to the broker socket and exfiltrate secrets), but it shifts the convenience-vs-security knob: auto-unlock means a lost laptop is a lost vault even if the vault file itself is encrypted at rest. Use only on hosts you trust. See [docs/auto-unlock.md](docs/auto-unlock.md) for the full threat model and recovery instructions.
|
|
293
|
-
|
|
294
|
-
## CLI reference
|
|
295
|
-
|
|
296
|
-
```bash
|
|
297
|
-
switchroom setup # Interactive wizard
|
|
298
|
-
switchroom doctor # Health check
|
|
299
|
-
switchroom apply # Reconcile + regenerate docker-compose.yml (self-elevates via sudo for scaffolds). Does NOT run docker — prints the `up` command
|
|
300
|
-
switchroom update [--check|--status|--rebuild] # Operator catch-up: pull images + apply + recreate fleet + doctor
|
|
301
|
-
switchroom restart [agent] [--force] # Bounce agent(s); drains in-flight turn by default
|
|
302
|
-
switchroom version # Show versions + running agent health summary
|
|
303
|
-
|
|
304
|
-
switchroom agent list # Status of all agents
|
|
305
|
-
switchroom agent status <name> # Status of one agent
|
|
306
|
-
switchroom agent add [name] # Wizard: scaffold a new agent end-to-end (#543)
|
|
307
|
-
switchroom agent create <name> [--profile <p>] # Scaffold + install timers; --profile writes yaml entry
|
|
308
|
-
switchroom agent bootstrap <name> --profile <p> --bot-token <t> # One-shot scaffold + auth + start
|
|
309
|
-
switchroom agent reconcile <name|all> # Re-apply switchroom.yaml (without pulling/building)
|
|
310
|
-
switchroom agent start|stop|restart <name> # Lifecycle (with preflight)
|
|
311
|
-
switchroom agent interrupt <name> # Cancel in-flight turn without restarting
|
|
312
|
-
switchroom agent unquarantine <name> # Clear a crash-quarantine and resume supervision
|
|
313
|
-
switchroom agent rename <old> <new> # Rename an agent slug (#168)
|
|
314
|
-
switchroom agent destroy <name> # Remove from compose + scaffold dir
|
|
315
|
-
switchroom agent attach <name> # Interactive tmux session
|
|
316
|
-
switchroom agent send <name> <slash-cmd> # Inject a slash command into the agent's tmux pane
|
|
317
|
-
switchroom agent logs <name> [-f] # View logs
|
|
318
|
-
switchroom agent grant <name> <tool> # Grant a tool permission
|
|
319
|
-
switchroom agent permissions <name> # Show allow/deny list
|
|
320
|
-
switchroom agent dangerous <name> [off] # Toggle full tool access
|
|
321
|
-
|
|
322
|
-
switchroom soul path|show|reset <name> # Manage the agent's user-owned SOUL.md (persona)
|
|
323
|
-
switchroom hostd install|status|uninstall|audit # Host-control daemon (/restart, /update apply, …)
|
|
324
|
-
switchroom drive connect|disconnect <agent> # Per-agent Google Drive OAuth
|
|
325
|
-
```
|
|
92
|
+
## What you get
|
|
326
93
|
|
|
327
|
-
|
|
94
|
+
| Feature | What it does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| **Deterministic UX** | Every topic shows an honest state: quiet, working since, or idle. Never a black box, never silent. The headline. |
|
|
97
|
+
| **Live step streaming** | The reply streams in place while tools run, with durations and a Steer button to redirect mid-turn. Not pinned, no stale cards. |
|
|
98
|
+
| **Sub-agent fleet view** | Delegated workers surface as live rows (up to 5, then `+N more`), each with role and current tool. Args sanitised before render. |
|
|
99
|
+
| **Claude Pro/Max auth** | OAuth, not API keys. No per-token billing. Fleet-wide active account plus fallback order, broker-owned refresh and credential fanout. |
|
|
100
|
+
| **Vault + approval kernel** | AES-256-GCM secrets. Per-agent least-privilege ACL. Anything extra needs your tap on an inline Telegram Approve/Deny card. |
|
|
101
|
+
| **Sub-agents** | Opus plans, Sonnet implements. Sub-agent work surfaces in the fleet block. |
|
|
102
|
+
| **Config cascade** | Defaults, then profiles, then per-agent YAML. Change one line, every agent updates. |
|
|
103
|
+
| **Scheduled tasks** | Cron-syntax tasks that fire across reboots. Headless secret access through the vault broker. |
|
|
104
|
+
| **Persistent memory** | Hindsight semantic memory, including a per-user mental model that carries across conversations. |
|
|
105
|
+
| **Always-on** | Long-running service per agent. Survives reboots, network drops, your laptop closing. Resumes mid-turn with a wake-audit. |
|
|
106
|
+
| **17 Telegram MCP tools** | reply, stream, react, edit, pin, delete, forward, typing, history, checklists (+update), stickers, GIFs, attachment download, ask-user, vault request access/save. |
|
|
328
107
|
|
|
329
|
-
|
|
108
|
+
## Subscription-honest, and safe by default
|
|
330
109
|
|
|
331
|
-
|
|
110
|
+
**Stock CLI, real OAuth.** Each agent runs the unmodified `claude` binary, authenticated with Anthropic through the same OAuth flow you use on the desktop app. No API key. No harness. No patched CLI. No proxied inference. One bill, the one you already pay. See the [Compliance Attestation](docs/compliance-attestation.md) for the full analysis against Anthropic's April 2026 third-party policy.
|
|
332
111
|
|
|
333
|
-
|
|
334
|
-
2. Scaffolds the agent directory and registers the agent in `docker-compose.yml` on next `switchroom apply` (same as running `agent create` on an entry that already exists in yaml).
|
|
112
|
+
**Least privilege, and you hold the keys.** This is the core opinion. An agent never holds the vault passphrase and never sees a secret it was not given. Secrets live in an AES-256-GCM vault. Each agent reaches the vault broker over its own socket whose identity is the bind path, so a compromised agent cannot pose as another. An agent can read a key only if you listed it for that agent's task: that is the standing, least-privilege ACL. Anything beyond that is just-in-time. The agent asks, you get an inline Telegram Approve/Deny card showing exactly what and why, and only your tap mints a scoped, expiring grant. The agent cannot self-elevate.
|
|
335
113
|
|
|
336
|
-
|
|
114
|
+
<p align="center"><img src="docs/diagrams/approval-grant-flow.svg" width="700" alt="Approval grant flow: agent requests a key it does not have, broker stages a pending grant, you tap Allow on the Telegram card, broker mints a scoped TTL grant, the read proceeds"></p>
|
|
337
115
|
|
|
338
|
-
|
|
116
|
+
**The approval kernel gates risky actions too.** A separate kernel daemon handles action approvals on the same model: an agent that wants to write to a Google Doc requests it, ends its turn, and waits. You see the diff and tap Allow or Deny. Nothing destructive happens on the agent's say-so alone. TTL'd decisions expire, and every grant and denial is logged.
|
|
339
117
|
|
|
340
|
-
###
|
|
118
|
+
### How agents collaborate on files
|
|
341
119
|
|
|
342
|
-
The
|
|
120
|
+
Switchroom's position: agents should collaborate with you on real documents, not paste walls of text into chat. The supported path is Google Drive. An agent reads and proposes changes, and every write or suggestion is gated through the approval kernel exactly like a credential request, so an agent never silently edits your Drive. Today Drive is opt-in per agent: you connect a Google account and enable it for the agents that should have it (`switchroom drive connect <agent>`), and the broker enforces that allowlist at runtime. It is not on by default for a fresh agent.
|
|
343
121
|
|
|
344
|
-
|
|
345
|
-
switchroom auth add <label> --via-claude # New account, broader scope — recommended for first-time
|
|
346
|
-
switchroom auth add <label> --from-oauth # Narrow scope=user:inference (rejected by agents in server: mode)
|
|
347
|
-
switchroom auth add <label> --from-agent <name> # Seed from an existing agent's creds
|
|
348
|
-
switchroom auth add <label> --from-credentials <path> # Import a credentials.json
|
|
349
|
-
switchroom auth add <label> --via-claude --replace # Re-auth an existing label (drift recovery)
|
|
350
|
-
|
|
351
|
-
switchroom auth list # Accounts + health + which one is fleet-active
|
|
352
|
-
switchroom auth show [agent] # Full snapshot (fleet + agents + consumers), or one agent
|
|
353
|
-
switchroom auth use <label> # Fleet-wide active swap
|
|
354
|
-
switchroom auth rotate # Cycle to next non-exhausted in fallback_order
|
|
355
|
-
switchroom auth rm <label> # Remove an account (refused if it's the only one)
|
|
356
|
-
|
|
357
|
-
switchroom auth agent override <agent> <label> # Edge case: one agent on a different account
|
|
358
|
-
switchroom auth agent override <agent> --clear # Back to fleet active
|
|
359
|
-
|
|
360
|
-
switchroom auth refresh [label] # Diagnostic: force a refresh tick
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
The same surface is reachable from Telegram in any agent's chat: `/auth show` (read-only), `/auth use <label>`, `/auth rotate`. Mutating verbs are admin-gated against the per-agent `admin: true` flag (the same flag that gates `/agents`, `/restart`, `/update`, etc.). One knob to make an agent the fleet control panel.
|
|
122
|
+
## Survives real life
|
|
364
123
|
|
|
365
|
-
|
|
124
|
+
Always-on is not enough on its own. Things still die. The product has to handle that or the illusion breaks.
|
|
366
125
|
|
|
367
|
-
|
|
126
|
+
<p align="center"><img src="docs/diagrams/wake-audit-lifecycle.svg" width="700" alt="Wake-audit lifecycle: kill, crash-pane snapshot, auto-restart, agent boots with SWITCHROOM_PENDING_TURN, acks with three options"></p>
|
|
368
127
|
|
|
369
|
-
|
|
128
|
+
- **Auto-restart.** Agent containers run with `restart: unless-stopped` and only start once the auth-broker's healthcheck passes. The vault broker, approval kernel, and auth broker each have their own healthchecks, so a wedged dependency is caught instead of silently breaking agents.
|
|
129
|
+
- **Resume protocol.** When an agent reboots mid-turn it boots with `SWITCHROOM_PENDING_TURN` plus the original chat ids. Its first action is to acknowledge the gap and ask how to proceed: start over, summarise and continue, or drop it.
|
|
130
|
+
- **Wake-audit.** On every fresh boot the agent checks for owed replies, orphan sub-agents, and stale todos. Clean means it stays quiet. If it owed you a reply, it tells you.
|
|
131
|
+
- **Token refresh.** The `switchroom-auth-broker` owns the refresh loop and is the sole writer of every `credentials.json`. Per-account quota state fans out across the fleet in seconds, and `auth.fallback_order` cycles when an account is exhausted.
|
|
370
132
|
|
|
371
|
-
|
|
372
|
-
switchroom workspace path <agent> # Print the workspace dir
|
|
373
|
-
switchroom workspace show <agent> [file] # Print one workspace file (default AGENTS.md)
|
|
374
|
-
switchroom workspace edit <agent> [file] # Open in $EDITOR (default AGENTS.md)
|
|
375
|
-
switchroom workspace render <agent> --stable # Dump the stable bootstrap block (for start.sh)
|
|
376
|
-
switchroom workspace render <agent> --dynamic # Dump the dynamic block (for UserPromptSubmit)
|
|
377
|
-
switchroom workspace search <agent> <query...> # BM25-lite search over workspace markdown
|
|
378
|
-
switchroom workspace commit <agent> [-m <msg>] # Git checkpoint of workspace state
|
|
379
|
-
switchroom workspace status <agent> # git status on the workspace
|
|
380
|
-
```
|
|
133
|
+
## How it stacks up
|
|
381
134
|
|
|
382
|
-
|
|
135
|
+
| | Switchroom | Claude Code channels | OpenClaw | NanoClaw |
|
|
136
|
+
|---|---|---|---|---|
|
|
137
|
+
| Progress visibility | Deterministic status, never silent | Black box | None | None |
|
|
138
|
+
| Runtime | Claude Code CLI | Claude Code CLI | Custom runtime | Agents SDK |
|
|
139
|
+
| Auth | Pro/Max OAuth | Pro/Max OAuth | API key | API key |
|
|
140
|
+
| Sub-agent tracking | Yes, live fleet block | No | No | No |
|
|
141
|
+
| Parallel display | Shared fleet status, up to 5 rows | No | No | No |
|
|
142
|
+
| Approval UX | Inline Telegram cards | None | None | None |
|
|
143
|
+
| Config | YAML with cascade | None | JSON/TOML | Env vars |
|
|
144
|
+
| Setup | `switchroom setup` | Built-in (limited) | Docker compose | Docker compose |
|
|
383
145
|
|
|
384
|
-
|
|
385
|
-
switchroom debug turn <agent> # Dump the exact prompt layering from the last turn
|
|
386
|
-
switchroom memory setup|search|stats|reflect # Hindsight memory
|
|
387
|
-
```
|
|
146
|
+
The wedge against OpenClaw and NanoClaw is not the substrate. It is the stock `claude` CLI under your subscription, instead of a custom runtime under your API key. [vs OpenClaw](docs/vs-openclaw.md) and [vs NanoClaw](docs/vs-nanoclaw.md).
|
|
388
147
|
|
|
389
|
-
|
|
148
|
+
## Architecture
|
|
390
149
|
|
|
391
|
-
|
|
150
|
+
One long-running service per agent. Each agent runs the stock `claude` CLI, not a fork, not the Agents SDK, not a wrapped harness, authenticated directly with Anthropic over official OAuth. Switchroom is scaffolding and lifecycle around the CLI you would run by hand: a Telegram bot, an approval kernel, a vault broker, an auth broker, and Docker Compose for supervision.
|
|
392
151
|
|
|
393
|
-
```bash
|
|
394
|
-
switchroom topics sync|list|cleanup # Telegram forum topics
|
|
395
|
-
switchroom vault init|set|get|list|remove # Encrypted secrets
|
|
396
|
-
switchroom handoff <agent> # Cross-session handoff summarizer
|
|
397
|
-
switchroom web # Web dashboard
|
|
398
152
|
```
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
X_BEARER_TOKEN: x-api/bearer-token
|
|
416
|
-
directories:
|
|
417
|
-
garmin-tokens: garmin/tokens
|
|
153
|
+
You (Telegram)
|
|
154
|
+
│
|
|
155
|
+
▼
|
|
156
|
+
@YourBot ──┬── switchroom-telegram MCP ──┬── agent supervisor ─── Claude Code CLI
|
|
157
|
+
│ (17 tools) │ (per-agent) │
|
|
158
|
+
│ │ ├─ .claude/agents/*.md (sub-agents)
|
|
159
|
+
├─ Deterministic status ├─ Approval kernel ◄─────┤ settings.json (tools, hooks, MCP)
|
|
160
|
+
│ (quiet/working/idle) │ (action grants) ├─ Hindsight plugin (memory)
|
|
161
|
+
├─ Live step streaming ├─ Vault broker ◄────────┤ Drive MCP, Playwright MCP, …
|
|
162
|
+
├─ Sub-agent fleet block │ (per-agent ACL) ├─ in-agent scheduler sidecar
|
|
163
|
+
├─ SQLite history ├─ Auth broker ◄─────────┤ (cron, fires across reboots)
|
|
164
|
+
└─ Emoji reactions │ (OAuth refresh, │
|
|
165
|
+
│ sole creds writer) │
|
|
166
|
+
├─ hostd (host-control: │
|
|
167
|
+
│ /restart, /update) │
|
|
168
|
+
└─ Docker Compose restart (unless-stopped)
|
|
418
169
|
```
|
|
419
170
|
|
|
420
|
-
|
|
171
|
+
See [`docs/architecture.md`](docs/architecture.md) for the process model, IPC layout, supervisor choice, and how each layer maps to the `claude` CLI.
|
|
421
172
|
|
|
422
173
|
## Documentation
|
|
423
174
|
|
|
@@ -425,43 +176,27 @@ Overlay entries win on collision with built-in defaults. Unknown files that appe
|
|
|
425
176
|
|---|---|
|
|
426
177
|
| **[Install](docs/install.md)** | Zero-to-first-message new-user walkthrough |
|
|
427
178
|
| **[BotFather walkthrough](docs/botfather-walkthrough.md)** | Step-by-step bot creation in Telegram |
|
|
428
|
-
| **[
|
|
429
|
-
| **[
|
|
179
|
+
| **[Configuration](docs/configuration.md)** | Full field reference, cascade semantics, profiles, example config |
|
|
180
|
+
| **[CLI reference](docs/cli-reference.md)** | Every verb, grouped, with behaviour and usage |
|
|
430
181
|
| **[Vault](docs/vault.md)** | Architecture, per-cron secrets, ACL, audit log, threat model |
|
|
431
|
-
| **[Telegram Plugin](docs/telegram-plugin.md)** |
|
|
182
|
+
| **[Telegram Plugin](docs/telegram-plugin.md)** | Deterministic UX, 17 MCP tools, checklists, sticker aliases, voice-in |
|
|
432
183
|
| **[Sub-Agents](docs/sub-agents.md)** | Model routing, delegation patterns, frontmatter spec |
|
|
433
184
|
| **[Scheduling](docs/scheduling.md)** | Cron tasks (in-agent scheduler sidecar), model selection |
|
|
434
185
|
| **[Session Management](docs/session-optimization.md)** | Continuity, compaction, freshness policy |
|
|
435
|
-
| **[OpenClaw alternative](docs/vs-openclaw.md)** | Switchroom vs OpenClaw |
|
|
436
|
-
| **[NanoClaw alternative](docs/vs-nanoclaw.md)** | Switchroom vs NanoClaw |
|
|
437
186
|
| **[Compliance](docs/compliance-attestation.md)** | Anthropic compliance analysis |
|
|
187
|
+
| **[Changelog](CHANGELOG.md)** | Release notes, every version |
|
|
438
188
|
| **[Telemetry](docs/posthog.md)** | What Switchroom reports to PostHog and how to opt out |
|
|
439
189
|
|
|
440
|
-
## Telemetry
|
|
441
|
-
|
|
442
|
-
Switchroom reports anonymous usage events and errors to PostHog so I can spot regressions and understand which commands are used. **No personal data, code, or message content leaves your machine.** The anonymous ID lives at `~/.switchroom/analytics-id` and is a random UUID. Not tied to your username, email, IP, or machine identifier (we pass `disableGeoip: true` on every event).
|
|
443
|
-
|
|
444
|
-
To opt out, set this in your shell profile:
|
|
445
|
-
|
|
446
|
-
```bash
|
|
447
|
-
export SWITCHROOM_TELEMETRY_DISABLED=1
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
Full event catalogue, dashboard links, and the source module at [docs/posthog.md](docs/posthog.md).
|
|
451
|
-
|
|
452
190
|
## FAQ
|
|
453
191
|
|
|
454
192
|
**Can I use a Claude Pro or Max subscription instead of an API key?**
|
|
455
|
-
Yes. That
|
|
193
|
+
Yes. That is the whole point. Switchroom runs the unmodified `claude` CLI with the same OAuth flow you use on the desktop app. No API key. No per-token billing.
|
|
456
194
|
|
|
457
195
|
**How is this different from Claude Code's built-in Telegram channel?**
|
|
458
|
-
The built-in channel is message in, message out, with
|
|
196
|
+
The built-in channel is message in, message out, with no visibility into what the agent is doing in between. Switchroom shows a deterministic status on every topic (quiet, working, idle) and streams the steps in place as tools run. You always know the state, which is the bit the built-in channel gets wrong.
|
|
459
197
|
|
|
460
198
|
**Does it work with multiple agents at the same time?**
|
|
461
|
-
Yes. Each agent gets its own Telegram forum topic. When
|
|
462
|
-
|
|
463
|
-
**Can I see what sub-agents are doing?**
|
|
464
|
-
Yes. When an agent delegates to a sub-agent (a worker, a researcher), the sub-agent's activity shows up in its own indented section of the parent's progress card. You see the full hierarchy, not just the top-level agent.
|
|
199
|
+
Yes. Each agent gets its own Telegram forum topic with its own status. When an agent delegates, its sub-agents show up as live rows in a shared fleet block (up to 5, then `+N more`), each with role and current tool.
|
|
465
200
|
|
|
466
201
|
**What does it cost to run?**
|
|
467
202
|
A cheap Linux VPS (around $6/mo on Hetzner, DigitalOcean, wherever), plus your existing Claude Pro ($20/mo) or Max ($100/mo) subscription. Switchroom itself is MIT-licensed, free.
|
|
@@ -469,8 +204,9 @@ A cheap Linux VPS (around $6/mo on Hetzner, DigitalOcean, wherever), plus your e
|
|
|
469
204
|
**Is this against Anthropic's terms of service?**
|
|
470
205
|
No. Switchroom uses the official `claude` binary with the official OAuth flow. See [docs/compliance-attestation.md](docs/compliance-attestation.md) for the full analysis.
|
|
471
206
|
|
|
472
|
-
|
|
473
|
-
|
|
207
|
+
## Telemetry
|
|
208
|
+
|
|
209
|
+
Switchroom reports anonymous usage events and errors to PostHog so I can spot regressions and see which commands are used. **No personal data, code, or message content leaves your machine.** The anonymous ID at `~/.switchroom/analytics-id` is a random UUID, not tied to your username, email, IP, or machine identifier. Opt out with `export SWITCHROOM_TELEMETRY_DISABLED=1`. Full event catalogue at [docs/posthog.md](docs/posthog.md).
|
|
474
210
|
|
|
475
211
|
## License
|
|
476
212
|
|