switchroom 0.5.0 → 0.7.9
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 +142 -121
- package/bin/autoaccept.exp +29 -6
- package/dist/agent-scheduler/index.js +12261 -0
- package/dist/cli/autoaccept-poll.js +10 -0
- package/dist/cli/switchroom.js +27250 -25324
- package/dist/vault/approvals/kernel-server.js +12709 -0
- package/dist/vault/broker/server.js +15724 -0
- package/package.json +4 -3
- package/profiles/_base/start.sh.hbs +133 -0
- package/profiles/_shared/telegram-style.md.hbs +3 -3
- package/profiles/default/CLAUDE.md +3 -3
- package/profiles/default/CLAUDE.md.hbs +2 -2
- package/profiles/default/workspace/CLAUDE.md.hbs +9 -0
- package/skills/docx/VENDORED.md +1 -1
- package/skills/mcp-builder/VENDORED.md +1 -1
- package/skills/pdf/VENDORED.md +1 -1
- package/skills/pptx/VENDORED.md +1 -1
- package/skills/skill-creator/VENDORED.md +1 -1
- package/skills/switchroom-architecture/SKILL.md +8 -7
- package/skills/switchroom-cli/SKILL.md +23 -15
- package/skills/switchroom-health/SKILL.md +7 -7
- package/skills/switchroom-install/SKILL.md +36 -39
- package/skills/switchroom-manage/SKILL.md +4 -4
- package/skills/switchroom-status/SKILL.md +1 -1
- package/skills/webapp-testing/VENDORED.md +1 -1
- package/skills/xlsx/VENDORED.md +1 -1
- package/telegram-plugin/admin-commands/dispatch.test.ts +119 -1
- package/telegram-plugin/admin-commands/index.ts +71 -0
- package/telegram-plugin/ask-user.ts +1 -0
- package/telegram-plugin/card-event-log.ts +138 -0
- package/telegram-plugin/dist/bridge/bridge.js +178 -31
- package/telegram-plugin/dist/foreman/foreman.js +6875 -6526
- package/telegram-plugin/dist/gateway/gateway.js +13862 -11834
- package/telegram-plugin/dist/server.js +202 -40
- package/telegram-plugin/fleet-state.ts +25 -10
- package/telegram-plugin/foreman/foreman.ts +38 -3
- package/telegram-plugin/gateway/approval-callback.ts +126 -0
- package/telegram-plugin/gateway/approval-card.test.ts +90 -0
- package/telegram-plugin/gateway/approval-card.ts +127 -0
- package/telegram-plugin/gateway/approvals-commands.ts +126 -0
- package/telegram-plugin/gateway/boot-card.ts +31 -6
- package/telegram-plugin/gateway/boot-probes.ts +510 -72
- package/telegram-plugin/gateway/gateway.ts +822 -94
- package/telegram-plugin/gateway/ipc-protocol.ts +34 -1
- package/telegram-plugin/gateway/ipc-server.ts +35 -0
- package/telegram-plugin/gateway/startup-mutex.ts +110 -2
- package/telegram-plugin/hooks/hooks.json +19 -0
- package/telegram-plugin/hooks/tool-label-pretool.mjs +216 -0
- package/telegram-plugin/hooks/tool-label-stop.mjs +63 -0
- package/telegram-plugin/package.json +4 -1
- package/telegram-plugin/plugin-logger.ts +20 -1
- package/telegram-plugin/progress-card-driver.ts +202 -13
- package/telegram-plugin/progress-card.ts +2 -2
- package/telegram-plugin/quota-check.ts +1 -0
- package/telegram-plugin/registry/subagents-schema.ts +37 -0
- package/telegram-plugin/registry/subagents.test.ts +64 -0
- package/telegram-plugin/session-tail.ts +58 -5
- package/telegram-plugin/shared/bot-runtime.ts +48 -2
- package/telegram-plugin/subagent-watcher.ts +139 -7
- package/telegram-plugin/tests/_progress-card-harness.ts +4 -0
- package/telegram-plugin/tests/bg-agent-progress-card-757.test.ts +201 -0
- package/telegram-plugin/tests/boot-card-probe-target.test.ts +10 -34
- package/telegram-plugin/tests/boot-card-render.test.ts +6 -5
- package/telegram-plugin/tests/boot-probes.test.ts +564 -0
- package/telegram-plugin/tests/card-event-log.test.ts +145 -0
- package/telegram-plugin/tests/gateway-startup-mutex.test.ts +102 -0
- package/telegram-plugin/tests/ipc-server-validate-inject-inbound.test.ts +134 -0
- package/telegram-plugin/tests/progress-card-delay-842.test.ts +160 -0
- package/telegram-plugin/tests/quota-check.test.ts +37 -1
- package/telegram-plugin/tests/subagent-registry-bugs.test.ts +5 -0
- package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +104 -1
- package/telegram-plugin/tests/subagent-watcher.test.ts +5 -0
- package/telegram-plugin/tests/tool-label-sidecar.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +5 -3
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +10 -0
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +58 -14
- package/telegram-plugin/tests/welcome-text.test.ts +57 -0
- package/telegram-plugin/tool-label-sidecar.ts +140 -0
- package/telegram-plugin/tool-labels.ts +55 -0
- package/telegram-plugin/two-zone-card.ts +27 -7
- package/telegram-plugin/uat/SETUP.md +160 -0
- package/telegram-plugin/uat/assertions.ts +140 -0
- package/telegram-plugin/uat/driver.ts +174 -0
- package/telegram-plugin/uat/harness.ts +161 -0
- package/telegram-plugin/uat/login.ts +134 -0
- package/telegram-plugin/uat/port-allocator.ts +71 -0
- package/telegram-plugin/uat/scenarios/smoke-clerk-reply.test.ts +61 -0
- package/telegram-plugin/welcome-text.ts +44 -2
- package/bin/bridge-watchdog.sh +0 -967
package/README.md
CHANGED
|
@@ -9,29 +9,19 @@
|
|
|
9
9
|
[](https://buildkite.com/ken-thompson/switchroom)
|
|
10
10
|
[](https://buildkite.com/ken-thompson/switchroom)
|
|
11
11
|
|
|
12
|
-
**
|
|
12
|
+
**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.
|
|
13
13
|
|
|
14
14
|
> *I loved OpenClaw + Telegram. I wanted my Claude subscription. And the UX done properly. So I built this.*
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
[Latest release notes →](CHANGELOG.md)
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## See what your agent is doing
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Then you tried OpenClaw. Followed the docs, spun it up, got it running, only to realise halfway through that you're pinging the Anthropic API on your own key and your token bill is quietly ticking over in the background. Bit of a bait and switch, that one. You signed up for "use your subscription," not "buy API credits on top of your subscription."
|
|
23
|
-
|
|
24
|
-
So you gave Claude Code's built-in Telegram channel a crack instead. Sent a message. Waited. Something happened, maybe. Eventually a reply came back. What did the agent actually do? No idea. Which tools ran? No idea. Did it get stuck, crash, spawn a sub-agent, read half your repo? No idea. It's an MVP black box of death, and I got sick of squinting into it.
|
|
25
|
-
|
|
26
|
-
So I built this.
|
|
27
|
-
|
|
28
|
-
## What Switchroom is, and isn't
|
|
29
|
-
|
|
30
|
-
Switchroom is an opinionated implementation of a Telegram plugin and agent lifecycle layer, sitting on top of the official `claude` CLI. No fork. No custom runtime in Docker. No API key interception. Your Claude Pro or Max subscription does the work, the same way it does on your desktop, authenticated via the same OAuth flow, fully compliant with Anthropic's terms.
|
|
20
|
+
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.
|
|
31
21
|
|
|
32
|
-
|
|
22
|
+
No silent gaps. No ghosts. No squinting into a black box.
|
|
33
23
|
|
|
34
|
-
|
|
24
|
+
<p align="center"><img src="docs/diagrams/progress-card-anatomy.jpg" width="700" alt="Annotated progress card: pin badge, user quote, last 5 steps, collapsed older, in-flight pulse, elapsed timer, sub-agent indent"></p>
|
|
35
25
|
|
|
36
26
|
```
|
|
37
27
|
⚙️ Working… · ⏱ 12s
|
|
@@ -43,63 +33,114 @@ The whole thing is built around one idea. Every time an agent starts work, a **p
|
|
|
43
33
|
🤖 Edit src/auth/jwt.ts · 4s
|
|
44
34
|
```
|
|
45
35
|
|
|
46
|
-
|
|
36
|
+
The card is the headline UX. The rest of the product is in service of it.
|
|
37
|
+
|
|
38
|
+
- Cards update at most once every 5 seconds. Fast enough to follow, not so fast it floods.
|
|
39
|
+
- Last 5 steps stay visible. Older ones collapse into `(+N more earlier steps)`.
|
|
40
|
+
- Running steps show elapsed time so a stuck tool is obvious.
|
|
41
|
+
- Tool labels are deterministic, written by a `PreToolUse` hook, so the card never lies about what's running.
|
|
42
|
+
- Two agents working at once? Each gets its own card, labelled `(1/2)` and `(2/2)`.
|
|
43
|
+
|
|
44
|
+
### Sub-agent visibility
|
|
45
|
+
|
|
46
|
+
When an agent delegates to a sub-agent — Opus plans, Sonnet implements — the sub-agent's work shows up indented inside the parent's pinned card. One pinned surface per task, however many processes it spawns underneath. Nothing gets buried in a side-channel you have to go look for.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
### Right, so what's this about
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- No
|
|
50
|
+
So you had the bright idea. Run Claude Code agents 24/7 on a cheap Linux box, talk to them from Telegram, use the Claude Pro or Max subscription you're already paying for. Sensible. Obvious, even.
|
|
51
|
+
|
|
52
|
+
Then you tried OpenClaw. Followed the docs, spun it up, got it running, only to realise halfway through that you're pinging the Anthropic API on your own key and your token bill is quietly ticking over in the background. Bit of a bait and switch, that one. You signed up for "use your subscription," not "buy API credits on top of your subscription."
|
|
53
|
+
|
|
54
|
+
So you gave Claude Code's built-in Telegram channel a crack instead. Sent a message. Waited. Something happened, maybe. Eventually a reply came back. What did the agent actually do? No idea. Which tools ran? No idea. Did it get stuck, crash, spawn a sub-agent, read half your repo? No idea. It's an MVP black box of death, and I got sick of squinting into it.
|
|
55
|
+
|
|
56
|
+
So I built this.
|
|
57
|
+
|
|
58
|
+
## What you get
|
|
59
|
+
|
|
60
|
+
| Feature | What it does |
|
|
61
|
+
|---|---|
|
|
62
|
+
| **Progress cards** | Pinned, in-place, every tool call visible. The headline UX. |
|
|
63
|
+
| **Claude Pro/Max auth** | OAuth, not API keys. No per-token billing. Multi-account fallback pool per agent. |
|
|
64
|
+
| **Approval kernel** | Inline allow/deny cards in Telegram for every gated tool. TTL'd grants, full audit trail. |
|
|
65
|
+
| **Sub-agents** | Opus plans, Sonnet implements. Sub-agent work surfaces in the parent card. |
|
|
66
|
+
| **Config cascade** | Defaults, then profiles, then per-agent YAML. Change one line, every agent updates. |
|
|
67
|
+
| **Scheduled tasks** | Cron-syntax tasks that fire across reboots. Headless secret access via the vault broker. |
|
|
68
|
+
| **Persistent memory** | Hindsight semantic memory with knowledge graphs and mental models. |
|
|
69
|
+
| **Session continuity** | Resume across restarts with freshness gating and a wake-audit. |
|
|
70
|
+
| **Encrypted vault** | AES-256-GCM for secrets. Optional auto-unlock keyed off `/etc/machine-id`. |
|
|
71
|
+
| **Drive MCP** | Read Google Docs, Sheets, and Drive files inline. Per-agent OAuth, no shared key. |
|
|
72
|
+
| **Card audit log** | Every progress-card edit appended to `card-events.jsonl` for retrospective debugging. |
|
|
73
|
+
| **15 Telegram MCP tools** | Reply, stream replies, edit, pin, react, native checklists, sticker aliases, voice-in transcription, attachments, history. |
|
|
55
74
|
|
|
56
75
|
## Architecture
|
|
57
76
|
|
|
58
|
-
One
|
|
77
|
+
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.
|
|
59
78
|
|
|
60
79
|
```
|
|
61
80
|
You (Telegram)
|
|
62
81
|
│
|
|
63
82
|
▼
|
|
64
|
-
@YourBot
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
@YourBot ──┬── switchroom-telegram MCP ──┬── agent supervisor ─── Claude Code CLI
|
|
84
|
+
│ (15 tools) │ (per-agent) │
|
|
85
|
+
│ │ ├─ .claude/agents/*.md (sub-agents)
|
|
86
|
+
├─ Progress cards ├─ Approval kernel ◄─────┤ settings.json (tools, hooks, MCP)
|
|
87
|
+
├─ Pin / unpin lifecycle │ (allow/deny broker) ├─ Hindsight plugin (memory)
|
|
88
|
+
├─ SQLite history ├─ Vault broker ◄────────┤ Drive MCP, Playwright MCP, …
|
|
89
|
+
├─ Card-events.jsonl audit │ (cron secrets, IPC) └─ scheduled tasks across reboots
|
|
90
|
+
├─ Emoji reactions │
|
|
91
|
+
└─ Format conversion └─ Docker Compose restart (unless-stopped)
|
|
71
92
|
```
|
|
72
93
|
|
|
73
|
-
|
|
94
|
+
See [`docs/architecture.md`](docs/architecture.md) for the process model, IPC layout, supervisor choice, and how each layer maps to the `claude` CLI.
|
|
74
95
|
|
|
75
|
-
##
|
|
96
|
+
## Approvals & safety
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
<p align="center"><img src="docs/diagrams/approval-grant-flow.jpg" 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>
|
|
101
|
+
|
|
102
|
+
- **Inline cards.** Allow / Deny / Allow once / Allow for 1h. No leaving Telegram.
|
|
103
|
+
- **TTL'd grants.** "Allow Bash for 1h" expires automatically. No silent permanent escalation.
|
|
104
|
+
- **Audit trail.** Every grant, denial, and expiry written to a per-agent log you can replay.
|
|
105
|
+
- **Per-agent allowlist.** `switchroom agent grant <name> <tool>` for the boring ones you don't want to be asked about.
|
|
106
|
+
|
|
107
|
+
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.
|
|
108
|
+
|
|
109
|
+
### Compliance posture
|
|
87
110
|
|
|
88
|
-
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
## Survives real life
|
|
114
|
+
|
|
115
|
+
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.
|
|
116
|
+
|
|
117
|
+
<p align="center"><img src="docs/diagrams/wake-audit-lifecycle.jpg" width="700" alt="Wake-audit lifecycle: kill, crash-pane snapshot, auto-restart, agent boots with SWITCHROOM_PENDING_TURN, acks with three options"></p>
|
|
118
|
+
|
|
119
|
+
- **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.
|
|
120
|
+
- **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).
|
|
121
|
+
- **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.
|
|
122
|
+
- **Token refresh.** Runs unattended for weeks via a `refresh-tick` daemon. Multi-account fallback pool kicks in when the active slot hits its quota window.
|
|
123
|
+
|
|
124
|
+
## How it stacks up
|
|
89
125
|
|
|
90
126
|
| | Switchroom | Claude Code channels | OpenClaw | NanoClaw |
|
|
91
127
|
|---|---|---|---|---|
|
|
92
|
-
| Progress visibility | Live
|
|
128
|
+
| Progress visibility | Live cards, pinned | Black box | None | None |
|
|
93
129
|
| Runtime | Claude Code CLI | Claude Code CLI | Custom runtime | Agents SDK |
|
|
94
130
|
| Auth | Pro/Max OAuth | Pro/Max OAuth | API key | API key |
|
|
95
|
-
| Sub-agent tracking | Yes,
|
|
131
|
+
| Sub-agent tracking | Yes, in card | No | No | No |
|
|
96
132
|
| Parallel task display | Labelled cards `(1/N)` | No | No | No |
|
|
133
|
+
| Approval UX | Inline Telegram cards | None | None | None |
|
|
97
134
|
| Config | YAML with cascade | None | JSON/TOML | Env vars |
|
|
98
135
|
| Setup | `switchroom setup` | Built-in (limited) | Docker compose | Docker compose |
|
|
99
136
|
|
|
137
|
+
The wedge against OpenClaw and NanoClaw isn't the substrate — it's the stock `claude` CLI under your subscription, instead of a custom runtime under your API key.
|
|
138
|
+
|
|
100
139
|
## Install
|
|
101
140
|
|
|
102
|
-
Ubuntu 24.04 LTS
|
|
141
|
+
Runs on the box you already have. The supported production runtime is Linux + Docker (Ubuntu 24.04 LTS with 4GB RAM is the canonical target; other Linux distros work with minor tweaks). `switchroom apply` scaffolds every agent and writes a `docker-compose.yml` from your `switchroom.yaml`; you bring the fleet up yourself with `docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d`. Five published images on GHCR (`switchroom-base`, `switchroom-agent`, `switchroom-broker`, `switchroom-kernel`, `switchroom-scheduler`) — no `docker build` on the operator's host. macOS (Docker Desktop) works for development but is not yet release-validated.
|
|
142
|
+
|
|
143
|
+
> **Heads up on the package name.** The npm package was originally `switchroom-ai`. It's now just `switchroom`. The old name is deprecated and will stop receiving updates — `npm install -g switchroom` is the current path.
|
|
103
144
|
|
|
104
145
|
### From inside Claude Code (the on-ramp)
|
|
105
146
|
|
|
@@ -111,15 +152,32 @@ If you already use Claude Code, this is the shortest path. Inside any session:
|
|
|
111
152
|
/switchroom:setup
|
|
112
153
|
```
|
|
113
154
|
|
|
114
|
-
`/switchroom:setup` walks you through deps, `switchroom setup` (Telegram + vault + first agent), and `switchroom agent start`.
|
|
155
|
+
`/switchroom:setup` walks you through deps, `switchroom setup` (Telegram + vault + first agent), and `switchroom agent start`. Day-to-day: `/switchroom:start`, `/switchroom:stop`, `/switchroom:status`. See [`docs/publishing.md`](docs/publishing.md).
|
|
115
156
|
|
|
116
|
-
### One-liner (
|
|
157
|
+
### One-liner (static binary)
|
|
117
158
|
|
|
118
159
|
```bash
|
|
119
|
-
curl -fsSL https://
|
|
160
|
+
curl -fsSL https://github.com/switchroom/switchroom/raw/main/install.sh | sh
|
|
120
161
|
```
|
|
121
162
|
|
|
122
|
-
|
|
163
|
+
Auto-detects your platform (linux / macos) and arch (amd64 / arm64), downloads the matching pre-built binary from the latest [GitHub release](https://github.com/switchroom/switchroom/releases/latest), verifies its SHA256, and drops it in `/usr/local/bin` (or `~/.local/bin` if not writable). Source is [`install.sh`](install.sh).
|
|
164
|
+
|
|
165
|
+
The static binary still needs the `claude` CLI to run agents: `npm i -g @anthropic-ai/claude-code` (Node 20.11+).
|
|
166
|
+
|
|
167
|
+
**Manual install** if you'd rather not pipe to sh:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Pick the artifact for your platform/arch from the latest release page
|
|
171
|
+
curl -fsSL -o switchroom https://github.com/switchroom/switchroom/releases/latest/download/switchroom-linux-amd64
|
|
172
|
+
chmod +x switchroom
|
|
173
|
+
sudo mv switchroom /usr/local/bin/
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Replace `switchroom-linux-amd64` with `switchroom-linux-arm64`, `switchroom-macos-amd64`, or `switchroom-macos-arm64` as needed. Verify against `switchroom-checksums.txt` from the same release.
|
|
177
|
+
|
|
178
|
+
**macOS Gatekeeper note.** Releases are not yet Apple-code-signed. After installing on macOS you may need to clear the quarantine xattr so the binary will run: `xattr -d com.apple.quarantine /usr/local/bin/switchroom`. The `install.sh` one-liner handles this automatically.
|
|
179
|
+
|
|
180
|
+
**Mac (Sequoia+) one-time.** macOS 15 adds a second-stage notarization check that the `xattr` strip alone does not bypass — you may still see a Gatekeeper "cannot verify the developer" dialog the first time you run `switchroom`. `install.sh` attempts `sudo spctl --add /usr/local/bin/switchroom` automatically (best-effort, ignored if sudo isn't available). If the dialog still fires, run that `spctl --add` manually, or open System Settings → Privacy & Security → "Open Anyway" once.
|
|
123
181
|
|
|
124
182
|
Then:
|
|
125
183
|
|
|
@@ -127,7 +185,8 @@ Then:
|
|
|
127
185
|
switchroom setup # interactive Telegram wiring
|
|
128
186
|
switchroom agent create coach --profile health-coach # scaffold your first agent
|
|
129
187
|
switchroom auth login coach # link your Pro or Max session
|
|
130
|
-
switchroom
|
|
188
|
+
switchroom apply # write docker-compose.yml
|
|
189
|
+
docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
|
|
131
190
|
```
|
|
132
191
|
|
|
133
192
|
After the last command you talk to the agent from Telegram. You don't touch the server again.
|
|
@@ -139,7 +198,7 @@ npm install -g @anthropic-ai/claude-code switchroom
|
|
|
139
198
|
switchroom setup
|
|
140
199
|
```
|
|
141
200
|
|
|
142
|
-
Node 20.11+. `switchroom setup` is the interactive first-time wizard
|
|
201
|
+
Node 20.11+. `switchroom setup` is the interactive first-time wizard. Scaffolds config, handles Telegram wiring, sets up the vault.
|
|
143
202
|
|
|
144
203
|
### One-shot happy path (no wizard)
|
|
145
204
|
|
|
@@ -148,18 +207,18 @@ If you already have Telegram credentials in `~/.switchroom/switchroom.yaml`, ski
|
|
|
148
207
|
```bash
|
|
149
208
|
switchroom agent create coach --profile health-coach
|
|
150
209
|
switchroom auth login coach
|
|
151
|
-
switchroom
|
|
210
|
+
switchroom apply && docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d
|
|
152
211
|
```
|
|
153
212
|
|
|
154
|
-
## Example
|
|
213
|
+
## Example configuration
|
|
155
214
|
|
|
156
215
|
```yaml
|
|
157
216
|
switchroom:
|
|
158
217
|
version: 1
|
|
159
218
|
|
|
160
219
|
telegram:
|
|
220
|
+
# Per-agent bot token (DM-only by default).
|
|
161
221
|
bot_token: "vault:telegram-bot-token"
|
|
162
|
-
forum_chat_id: "-1001234567890"
|
|
163
222
|
|
|
164
223
|
memory:
|
|
165
224
|
backend: hindsight
|
|
@@ -195,11 +254,7 @@ See [docs/configuration.md](docs/configuration.md) for the full reference.
|
|
|
195
254
|
|
|
196
255
|
## Vault broker (cron secrets)
|
|
197
256
|
|
|
198
|
-
Scheduled tasks run headless
|
|
199
|
-
for the vault passphrase. The vault broker is a long-running user-level systemd
|
|
200
|
-
unit that holds the vault decrypted in memory after a one-time interactive
|
|
201
|
-
unlock. Cron tasks fetch the specific keys they declare via a unix socket; the
|
|
202
|
-
passphrase never sits on disk.
|
|
257
|
+
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-broker`) that holds the vault decrypted in memory after a one-time interactive unlock. Cron tasks fetch the specific keys they declare via a host-shared unix socket. The passphrase never sits on disk.
|
|
203
258
|
|
|
204
259
|
**Declare per-cron secrets in `switchroom.yaml`:**
|
|
205
260
|
|
|
@@ -217,69 +272,39 @@ agents:
|
|
|
217
272
|
**Bootstrap once per host:**
|
|
218
273
|
|
|
219
274
|
```bash
|
|
220
|
-
switchroom
|
|
275
|
+
switchroom apply # writes broker into docker-compose.yml
|
|
276
|
+
docker compose -p switchroom -f ~/.switchroom/compose/docker-compose.yml up -d switchroom-broker
|
|
221
277
|
switchroom vault broker unlock # prompt for passphrase, primes broker
|
|
222
278
|
```
|
|
223
279
|
|
|
224
|
-
Or just run `switchroom vault get <key>` from a TTY
|
|
225
|
-
take the unlocked state with `[Y/n]` so you don't have to remember a separate
|
|
226
|
-
unlock command.
|
|
280
|
+
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.
|
|
227
281
|
|
|
228
|
-
**Identity model.**
|
|
229
|
-
the connecting cron's systemd unit (`switchroom-<agent>-cron-<i>.service`).
|
|
230
|
-
Cgroup membership is set by systemd as root and is unspoofable from
|
|
231
|
-
userspace, so a compromised agent cannot pose as another agent's cron and
|
|
232
|
-
read its keys. macOS and other platforms degrade to UID-only via the socket
|
|
233
|
-
file mode 0600 — fine for desktop use, not recommended for production cron.
|
|
282
|
+
**Identity model.** The broker reads `/proc/<pid>/cgroup` on the host to find the connecting cron's container (`switchroom-<agent>-scheduler` or `switchroom-<agent>`), which Docker sets unspoofably from userspace, so a compromised agent cannot pose as another agent's cron and read its keys. macOS (Docker Desktop) degrades to UID-only via the socket file mode 0600. Fine for desktop use, not recommended for production cron.
|
|
234
283
|
|
|
235
|
-
The broker locks on `SIGTERM` (so a
|
|
236
|
-
and on demand via `switchroom vault broker lock`. Use
|
|
237
|
-
`switchroom vault get <key> --no-broker` to bypass and prompt locally.
|
|
284
|
+
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.
|
|
238
285
|
|
|
239
|
-
|
|
286
|
+
Broker socket lives at `~/.switchroom/vault-broker.sock` (host-mounted into every agent container).
|
|
240
287
|
|
|
241
288
|
### Auto-unlock on boot (opt-in)
|
|
242
289
|
|
|
243
|
-
By default, the broker holds the unlocked state in memory only
|
|
244
|
-
restart (host reboot, service crash, reconcile that re-renders the unit)
|
|
245
|
-
wipes it and requires `switchroom vault broker unlock` again. For
|
|
246
|
-
unattended hosts where this is too painful, switchroom can encrypt the
|
|
247
|
-
passphrase with a key derived from `/etc/machine-id` and have the broker
|
|
248
|
-
unlock itself at boot:
|
|
290
|
+
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:
|
|
249
291
|
|
|
250
292
|
```bash
|
|
251
293
|
switchroom vault broker enable-auto-unlock # one-time setup, prompts for passphrase
|
|
252
294
|
```
|
|
253
295
|
|
|
254
|
-
Done. The wizard prompts for your vault passphrase, encrypts it with
|
|
255
|
-
AES-256-GCM keyed off `/etc/machine-id`, writes the result to
|
|
256
|
-
`~/.config/switchroom/auto-unlock.bin` (mode 0600), flips
|
|
257
|
-
`vault.broker.autoUnlock: true` in `switchroom.yaml`, restarts the
|
|
258
|
-
broker, and verifies the vault came up unlocked. Every subsequent boot
|
|
259
|
-
the broker reads + decrypts + unlocks itself.
|
|
296
|
+
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.
|
|
260
297
|
|
|
261
298
|
Disable with `switchroom vault broker disable-auto-unlock`.
|
|
262
299
|
|
|
263
|
-
**Security tradeoff
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
disk theft is safe (the blob doesn't decrypt on any other machine) and
|
|
267
|
-
other UNIX users on the same box can't read it — but root on the host
|
|
268
|
-
*can* read both the blob and the machine-id, so once root is on the
|
|
269
|
-
machine the passphrase is recoverable. Same blast radius as the
|
|
270
|
-
running broker process (anything with code-exec as you can already
|
|
271
|
-
attach to the broker socket and exfiltrate secrets), but it shifts the
|
|
272
|
-
convenience-vs-security knob: auto-unlock means a lost laptop is a lost
|
|
273
|
-
vault even if the vault file itself is encrypted at rest. Use only on
|
|
274
|
-
hosts you trust. See [docs/auto-unlock.md](docs/auto-unlock.md) for the
|
|
275
|
-
full threat model and recovery instructions.
|
|
276
|
-
|
|
277
|
-
## CLI Reference
|
|
300
|
+
**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.
|
|
301
|
+
|
|
302
|
+
## CLI reference
|
|
278
303
|
|
|
279
304
|
```bash
|
|
280
305
|
switchroom setup # Interactive wizard
|
|
281
306
|
switchroom doctor # Health check
|
|
282
|
-
switchroom
|
|
307
|
+
switchroom apply # Reconcile + (re)write docker-compose.yml; bring fleet up via `docker compose ... up -d`
|
|
283
308
|
switchroom restart [agent] [--force] # Bounce agent(s); drains in-flight turn by default
|
|
284
309
|
switchroom version # Show versions + running agent health summary
|
|
285
310
|
|
|
@@ -292,7 +317,7 @@ switchroom agent reconcile <name|all> # Re-apply switchroom.yaml (withou
|
|
|
292
317
|
switchroom agent start|stop|restart <name> # Lifecycle (with preflight)
|
|
293
318
|
switchroom agent interrupt <name> # Cancel in-flight turn without restarting
|
|
294
319
|
switchroom agent rename <old> <new> # Rename an agent slug (#168)
|
|
295
|
-
switchroom agent destroy <name> #
|
|
320
|
+
switchroom agent destroy <name> # Remove from compose + scaffold dir
|
|
296
321
|
switchroom agent attach <name> # Interactive tmux session
|
|
297
322
|
switchroom agent logs <name> [-f] # View logs
|
|
298
323
|
switchroom agent grant <name> <tool> # Grant a tool permission
|
|
@@ -305,7 +330,7 @@ Profiles live in `profiles/` at the repo root. Bundled ones for `--profile`: `co
|
|
|
305
330
|
`switchroom agent create <name> --profile <profile>` does two things in one step:
|
|
306
331
|
|
|
307
332
|
1. Adds an entry to `switchroom.yaml` under `agents:` with `extends: <profile>` and a derived `topic_name` (capitalized agent name). Edit the yaml afterwards to change the topic name, emoji, tools, etc.
|
|
308
|
-
2. Scaffolds the agent directory and
|
|
333
|
+
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).
|
|
309
334
|
|
|
310
335
|
If the agent is already in yaml, `--profile` must match the existing `extends:` value or it errors. If the yaml entry has no `extends:` and you pass `--profile`, the flag is written in additively with a warning. Running `agent create` with no `--profile` on a missing entry keeps the old "Agent not defined in switchroom.yaml" error, now with a hint to use `--profile`.
|
|
311
336
|
|
|
@@ -313,10 +338,7 @@ Model aliases: the bare names `opus`, `sonnet`, `haiku` are accepted alongside t
|
|
|
313
338
|
|
|
314
339
|
### Authentication (multi-account slot pool)
|
|
315
340
|
|
|
316
|
-
Each agent has a pool of Claude OAuth slots. The **active** slot is what
|
|
317
|
-
the agent uses; other slots are automatic fallbacks when the active slot
|
|
318
|
-
hits its quota window. Every `<slot>` option defaults to the active slot
|
|
319
|
-
if omitted.
|
|
341
|
+
Each agent has a pool of Claude OAuth slots. The **active** slot is what the agent uses; other slots are automatic fallbacks when the active slot hits its quota window. Every `<slot>` option defaults to the active slot if omitted.
|
|
320
342
|
|
|
321
343
|
```bash
|
|
322
344
|
switchroom auth status # All agents, one table
|
|
@@ -333,15 +355,11 @@ switchroom auth list <agent> [--json] # Show slots: health, quota st
|
|
|
333
355
|
switchroom auth rm <agent> <slot> [--force] # Remove a slot (refuses active/last slot)
|
|
334
356
|
```
|
|
335
357
|
|
|
336
|
-
The fallback pool also works from Telegram. The switchroom MCP plugin
|
|
337
|
-
exposes the same verbs as `/auth add|use|list|rm` inside the chat.
|
|
358
|
+
The fallback pool also works from Telegram. The switchroom MCP plugin exposes the same verbs as `/auth add|use|list|rm` inside the chat.
|
|
338
359
|
|
|
339
360
|
### Workspace (agent bootstrap layer)
|
|
340
361
|
|
|
341
|
-
Each agent has a workspace directory (`~/.switchroom/agents/<name>/workspace/`)
|
|
342
|
-
with editable stable files (`AGENTS.md`, `SOUL.md`, `USER.md`, `IDENTITY.md`,
|
|
343
|
-
`TOOLS.md`) and dynamic files (`MEMORY.md`, `memory/YYYY-MM-DD.md`,
|
|
344
|
-
`HEARTBEAT.md`) that are injected into the model's context at turn time.
|
|
362
|
+
Each agent has a workspace directory (`~/.switchroom/agents/<name>/workspace/`) with editable stable files (`AGENTS.md`, `SOUL.md`, `USER.md`, `IDENTITY.md`, `TOOLS.md`) and dynamic files (`MEMORY.md`, `memory/YYYY-MM-DD.md`, `HEARTBEAT.md`) that are injected into the model's context at turn time.
|
|
345
363
|
|
|
346
364
|
```bash
|
|
347
365
|
switchroom workspace path <agent> # Print the workspace dir
|
|
@@ -361,6 +379,8 @@ switchroom debug turn <agent> # Dump the exact prompt layeri
|
|
|
361
379
|
switchroom memory setup|search|stats|reflect # Hindsight memory
|
|
362
380
|
```
|
|
363
381
|
|
|
382
|
+
The progress card driver also writes a per-agent `card-events.jsonl` audit log: every edit, pin, unpin, and tool-label transition the user sees in Telegram, captured locally so a debug session doesn't depend on Telegram's history. Tail it like any other journal.
|
|
383
|
+
|
|
364
384
|
### Other
|
|
365
385
|
|
|
366
386
|
```bash
|
|
@@ -374,7 +394,7 @@ switchroom web # Web dashboard
|
|
|
374
394
|
|
|
375
395
|
`scripts/import-openclaw-credentials.ts` is a one-shot migration script that lifts `/data/openclaw-config/credentials/` into the Switchroom vault. It ships with a small set of default mappings for filenames OpenClaw documents out of the box.
|
|
376
396
|
|
|
377
|
-
User-specific credential filenames (your custom bot tokens, SSH keys, and so on) belong in a local overlay file
|
|
397
|
+
User-specific credential filenames (your custom bot tokens, SSH keys, and so on) belong in a local overlay file, not the source repository. Create `~/.switchroom/import-openclaw.yaml`:
|
|
378
398
|
|
|
379
399
|
```yaml
|
|
380
400
|
# ~/.switchroom/import-openclaw.yaml
|
|
@@ -395,12 +415,13 @@ Overlay entries win on collision with built-in defaults. Unknown files that appe
|
|
|
395
415
|
## Documentation
|
|
396
416
|
|
|
397
417
|
| Guide | Description |
|
|
398
|
-
|
|
418
|
+
|---|---|
|
|
419
|
+
| **[Changelog](CHANGELOG.md)** | Release notes, every version |
|
|
399
420
|
| **[Configuration](docs/configuration.md)** | Full field reference, cascade semantics, profiles |
|
|
400
421
|
| **[Vault](docs/vault.md)** | Architecture, per-cron secrets, ACL, audit log, threat model |
|
|
401
|
-
| **[Telegram Plugin](docs/telegram-plugin.md)** | Progress cards,
|
|
422
|
+
| **[Telegram Plugin](docs/telegram-plugin.md)** | Progress cards, 15 MCP tools, native checklists, sticker aliases, voice-in |
|
|
402
423
|
| **[Sub-Agents](docs/sub-agents.md)** | Model routing, delegation patterns, frontmatter spec |
|
|
403
|
-
| **[Scheduling](docs/scheduling.md)** | Cron tasks
|
|
424
|
+
| **[Scheduling](docs/scheduling.md)** | Cron tasks (per-agent scheduler container), model selection |
|
|
404
425
|
| **[Session Management](docs/session-optimization.md)** | Continuity, compaction, freshness policy |
|
|
405
426
|
| **[OpenClaw alternative](docs/vs-openclaw.md)** | Switchroom vs OpenClaw |
|
|
406
427
|
| **[NanoClaw alternative](docs/vs-nanoclaw.md)** | Switchroom vs NanoClaw |
|
|
@@ -409,7 +430,7 @@ Overlay entries win on collision with built-in defaults. Unknown files that appe
|
|
|
409
430
|
|
|
410
431
|
## Telemetry
|
|
411
432
|
|
|
412
|
-
Switchroom reports anonymous usage events and errors to PostHog so
|
|
433
|
+
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).
|
|
413
434
|
|
|
414
435
|
To opt out, set this in your shell profile:
|
|
415
436
|
|
|
@@ -431,7 +452,7 @@ The built-in channel is message in, message out, with zero visibility into what
|
|
|
431
452
|
Yes. Each agent gets its own Telegram forum topic. When multiple agents are working simultaneously, each has its own pinned progress card labelled `(1/N)`, `(2/N)` and so on.
|
|
432
453
|
|
|
433
454
|
**Can I see what sub-agents are doing?**
|
|
434
|
-
Yes. When an agent delegates to a sub-agent (a worker, a researcher), the sub-agent's activity shows up in its own section of the progress card. You see the full hierarchy, not just the top-level agent.
|
|
455
|
+
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.
|
|
435
456
|
|
|
436
457
|
**What does it cost to run?**
|
|
437
458
|
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.
|
package/bin/autoaccept.exp
CHANGED
|
@@ -34,13 +34,36 @@ expect {
|
|
|
34
34
|
# spawned shell. Without this the loop hangs forever and expect
|
|
35
35
|
# owns stdin, dropping any injected keystrokes.
|
|
36
36
|
}
|
|
37
|
+
-re {Loading.{1,30}development.{1,30}channels} {
|
|
38
|
+
# NEW dev-channels prompt wording (Claude Code mid-2026+):
|
|
39
|
+
# WARNING: Loading development channels
|
|
40
|
+
# ❯ 1. I am using this for local development
|
|
41
|
+
# 2. Exit
|
|
42
|
+
# Option 1 ("local development") is already highlighted, so just
|
|
43
|
+
# press Enter — no Down keystroke needed. Tightly scoped to the
|
|
44
|
+
# literal "Loading … development channels" warning header so we
|
|
45
|
+
# don't over-match anywhere else.
|
|
46
|
+
sleep 0.5
|
|
47
|
+
send "\r"
|
|
48
|
+
exp_continue
|
|
49
|
+
}
|
|
50
|
+
-re {using this for local development} {
|
|
51
|
+
# Belt-and-suspenders for the new prompt: match on the option-row
|
|
52
|
+
# text in case the WARNING header has scrolled out of the capture
|
|
53
|
+
# window. Same scoping discipline — "local development" is unique
|
|
54
|
+
# to this prompt and won't appear in per-tool confirmations.
|
|
55
|
+
sleep 0.5
|
|
56
|
+
send "\r"
|
|
57
|
+
exp_continue
|
|
58
|
+
}
|
|
37
59
|
-re {I.{0,5}accept.{0,80}development.{0,10}channels} {
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
60
|
+
# LEGACY dev-channels acknowledgement — old wording before the
|
|
61
|
+
# 2026 TUI rename ("Yes, I accept the use of development channels").
|
|
62
|
+
# Kept as a fallback for older Claude Code releases. Tightly scoped
|
|
63
|
+
# to the literal "development channels" phrase to avoid over-matching
|
|
64
|
+
# per-tool confirmations that start with "Yes, I accept" e.g.
|
|
65
|
+
# "Yes, I accept this file edit." Those must fall through to the
|
|
66
|
+
# plugin's permission_request flow.
|
|
44
67
|
sleep 0.5
|
|
45
68
|
send "\033\[B\r"
|
|
46
69
|
exp_continue
|