@inceptionstack/roundhouse 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +321 -9
- package/architecture.md +77 -8
- package/package.json +9 -6
- package/src/agents/pi.ts +433 -26
- package/src/agents/registry.ts +8 -0
- package/src/cli/cli.ts +384 -189
- package/src/cli/cron.ts +296 -0
- package/src/cli/doctor/checks/agent.ts +68 -0
- package/src/cli/doctor/checks/config.ts +88 -0
- package/src/cli/doctor/checks/credentials.ts +62 -0
- package/src/cli/doctor/checks/disk.ts +69 -0
- package/src/cli/doctor/checks/stt.ts +76 -0
- package/src/cli/doctor/checks/system.ts +86 -0
- package/src/cli/doctor/checks/systemd.ts +76 -0
- package/src/cli/doctor/output.ts +58 -0
- package/src/cli/doctor/runner.ts +142 -0
- package/src/cli/doctor/shell.ts +33 -0
- package/src/cli/doctor/types.ts +44 -0
- package/src/cli/doctor.ts +48 -0
- package/src/cli/setup-telegram.ts +148 -0
- package/src/cli/setup.ts +936 -0
- package/src/commands.ts +23 -0
- package/src/config.ts +188 -0
- package/src/cron/constants.ts +54 -0
- package/src/cron/durations.ts +33 -0
- package/src/cron/format.ts +139 -0
- package/src/cron/helpers.ts +30 -0
- package/src/cron/runner.ts +148 -0
- package/src/cron/schedule.ts +101 -0
- package/src/cron/scheduler.ts +295 -0
- package/src/cron/store.ts +125 -0
- package/src/cron/template.ts +89 -0
- package/src/cron/types.ts +76 -0
- package/src/gateway.ts +927 -18
- package/src/index.ts +1 -58
- package/src/memory/bootstrap.ts +98 -0
- package/src/memory/files.ts +100 -0
- package/src/memory/inject.ts +41 -0
- package/src/memory/lifecycle.ts +245 -0
- package/src/memory/policy.ts +122 -0
- package/src/memory/prompts.ts +42 -0
- package/src/memory/state.ts +43 -0
- package/src/memory/types.ts +90 -0
- package/src/notify/telegram.ts +48 -0
- package/src/types.ts +68 -1
- package/src/util.ts +28 -2
- package/src/voice/providers/whisper.ts +339 -0
- package/src/voice/stt-service.ts +284 -0
- package/src/voice/types.ts +63 -0
package/README.md
CHANGED
|
@@ -5,6 +5,12 @@ A multi-platform chat gateway that routes messages through a single configured A
|
|
|
5
5
|
One gateway instance = one agent target (pi, Kiro, etc.), configured at install time.
|
|
6
6
|
Multiple chat inputs (Telegram, Slack, Discord via [Vercel Chat SDK](https://chat-sdk.dev)) all feed into that same agent.
|
|
7
7
|
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @inceptionstack/roundhouse
|
|
12
|
+
```
|
|
13
|
+
|
|
8
14
|
## Architecture
|
|
9
15
|
|
|
10
16
|
```
|
|
@@ -44,27 +50,80 @@ Multiple chat inputs (Telegram, Slack, Discord via [Vercel Chat SDK](https://cha
|
|
|
44
50
|
└────────────────────┘
|
|
45
51
|
```
|
|
46
52
|
|
|
53
|
+
See [architecture.md](architecture.md) for full system diagrams, data flow, config model, and module dependency graph.
|
|
54
|
+
|
|
47
55
|
### Design decisions
|
|
48
56
|
|
|
49
57
|
- **One gateway = one agent target.** The `agent` block in config picks the type and its settings. All chat inputs route to this single agent instance.
|
|
50
58
|
- **Multiple chat inputs into the same agent.** Telegram and Slack messages go to the same agent, each on their own session thread (`telegram:<id>`, `slack:<id>`).
|
|
51
59
|
- **AgentRouter is a seam.** Today it's `SingleAgentRouter` (hardcoded pass-through). The interface exists so we can later swap in per-thread routing, multi-agent, or load-balanced strategies without changing the gateway or adapters.
|
|
52
|
-
- **AgentAdapter is the only abstraction we own.** The chat side is Vercel Chat SDK — we don't wrap it. The agent side is our `AgentAdapter` interface: `prompt(threadId,
|
|
60
|
+
- **AgentAdapter is the only abstraction we own.** The chat side is Vercel Chat SDK — we don't wrap it. The agent side is our `AgentAdapter` interface: `prompt(threadId, message) → AgentResponse`.
|
|
53
61
|
- **Config-driven.** `gateway.config.json` (or `--config` flag, or env vars) determines everything at startup. No runtime reconfiguration.
|
|
54
62
|
- **Persistent sessions.** Each thread gets its own session file on disk. Gateway restarts resume from the same file. Pi CLI can join the same session.
|
|
55
63
|
|
|
56
64
|
## Quick start
|
|
57
65
|
|
|
66
|
+
### 1. Create a Telegram bot
|
|
67
|
+
|
|
68
|
+
1. Message [@BotFather](https://t.me/BotFather) on Telegram
|
|
69
|
+
2. `/newbot` → pick a name and username
|
|
70
|
+
3. Copy the **bot token**
|
|
71
|
+
|
|
72
|
+
### 2. Run (dev mode)
|
|
73
|
+
|
|
58
74
|
```bash
|
|
75
|
+
git clone https://github.com/inceptionstack/roundhouse.git
|
|
76
|
+
cd roundhouse
|
|
59
77
|
npm install
|
|
60
78
|
export TELEGRAM_BOT_TOKEN="your-token"
|
|
61
79
|
export ALLOWED_USERS="your_telegram_username"
|
|
62
80
|
npm start
|
|
63
81
|
```
|
|
64
82
|
|
|
83
|
+
### 3. Or install globally and run as a daemon
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install -g @inceptionstack/roundhouse
|
|
87
|
+
export TELEGRAM_BOT_TOKEN="your-token"
|
|
88
|
+
export ALLOWED_USERS="your_username"
|
|
89
|
+
roundhouse install # installs as systemd service, starts automatically
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## CLI
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
roundhouse <command>
|
|
96
|
+
|
|
97
|
+
Commands:
|
|
98
|
+
start Start the gateway (foreground)
|
|
99
|
+
tui [thread] Open agent TUI on a gateway session
|
|
100
|
+
install Install as a systemd daemon (requires sudo)
|
|
101
|
+
uninstall Remove the systemd daemon
|
|
102
|
+
update Update from npm + restart daemon
|
|
103
|
+
status Show daemon status (rich detail view)
|
|
104
|
+
logs Tail daemon logs
|
|
105
|
+
stop Stop the daemon
|
|
106
|
+
restart Restart the daemon
|
|
107
|
+
config Show config path and contents
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `roundhouse status`
|
|
111
|
+
|
|
112
|
+
Shows detailed daemon info including version, state, PID, uptime, memory, agent type and version, platforms, allowed users, notify chats, debug flags, and config paths.
|
|
113
|
+
|
|
114
|
+
### `roundhouse tui`
|
|
115
|
+
|
|
116
|
+
Opens the configured agent's interactive TUI, resumed to a gateway chat session. This lets you continue the same conversation from Telegram in your terminal.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
roundhouse tui # pick from all threads
|
|
120
|
+
roundhouse tui telegram # filter to telegram threads
|
|
121
|
+
roundhouse tui telegram_c12345 # exact thread match
|
|
122
|
+
```
|
|
123
|
+
|
|
65
124
|
## Config
|
|
66
125
|
|
|
67
|
-
Place `gateway.config.json` in the project root, or use `--config path`:
|
|
126
|
+
Place `gateway.config.json` in `~/.roundhouse/` (created by `roundhouse install`), or in the project root, or use `--config path`:
|
|
68
127
|
|
|
69
128
|
```json
|
|
70
129
|
{
|
|
@@ -93,7 +152,12 @@ Without a config file, defaults are used with env vars (`TELEGRAM_BOT_TOKEN`, `B
|
|
|
93
152
|
| `agent.sessionDir` | Override session storage path |
|
|
94
153
|
| `chat.botUsername` | Bot display name for Chat SDK |
|
|
95
154
|
| `chat.allowedUsers` | Telegram usernames / user IDs allowed (empty = allow all) |
|
|
155
|
+
| `chat.notifyChatIds` | Telegram chat IDs to notify on startup (env: `NOTIFY_CHAT_IDS`) |
|
|
96
156
|
| `chat.adapters.telegram` | `{ "mode": "polling" \| "webhook" \| "auto" }` |
|
|
157
|
+
| `voice.stt.enabled` | Enable automatic voice transcription (default: off unless configured) |
|
|
158
|
+
| `voice.stt.autoInstall` | Auto-install whisper via pip3 if missing (default: false) |
|
|
159
|
+
| `voice.stt.chain` | STT provider chain, e.g. `["whisper"]` |
|
|
160
|
+
| `voice.stt.providers.whisper` | `{ "model": "small", "timeoutMs": 30000 }` |
|
|
97
161
|
|
|
98
162
|
Secrets stay in env vars: `TELEGRAM_BOT_TOKEN`, `ANTHROPIC_API_KEY`, etc.
|
|
99
163
|
|
|
@@ -102,11 +166,237 @@ Secrets stay in env vars: `TELEGRAM_BOT_TOKEN`, `ANTHROPIC_API_KEY`, etc.
|
|
|
102
166
|
Sessions are stored at `~/.pi/agent/gateway-sessions/<thread>/`. Resume from CLI:
|
|
103
167
|
|
|
104
168
|
```bash
|
|
169
|
+
# Via roundhouse (recommended — auto-discovers sessions)
|
|
170
|
+
roundhouse tui
|
|
171
|
+
|
|
172
|
+
# Or directly via pi
|
|
105
173
|
pi --resume ~/.pi/agent/gateway-sessions/<thread_dir>/<session>.jsonl
|
|
106
174
|
```
|
|
107
175
|
|
|
108
176
|
Messages from Telegram/Slack and from the CLI share the same context.
|
|
109
177
|
|
|
178
|
+
## Telegram bot commands
|
|
179
|
+
|
|
180
|
+
Roundhouse automatically registers these commands with Telegram on startup:
|
|
181
|
+
|
|
182
|
+
| Command | Description |
|
|
183
|
+
|---------|-------------|
|
|
184
|
+
| `/new` | Start a fresh conversation (resets the agent session for this chat) |
|
|
185
|
+
| `/compact` | Compact session context to free up tokens |
|
|
186
|
+
| `/verbose` | Toggle tool status messages on/off for this chat |
|
|
187
|
+
| `/status` | Show gateway status: version, agent, model, context usage, uptime, etc. |
|
|
188
|
+
| `/stop` | Stop the current agent run (abort tools, LLM calls, compaction) |
|
|
189
|
+
| `/restart` | Restart the gateway service (requires `allowedUsers` to be configured) |
|
|
190
|
+
| `/doctor` | Run health checks and show system status |
|
|
191
|
+
| `/crons` | Manage scheduled jobs (list, trigger, pause, resume) |
|
|
192
|
+
| `/jobs` | List scheduled jobs (alias for /crons) |
|
|
193
|
+
|
|
194
|
+
These appear in Telegram's `/` command menu automatically.
|
|
195
|
+
|
|
196
|
+
### `/status` details
|
|
197
|
+
|
|
198
|
+
Shows a rich status view including:
|
|
199
|
+
- Roundhouse and agent versions
|
|
200
|
+
- Current model (from active session or configured default)
|
|
201
|
+
- Context token usage with visual progress bar
|
|
202
|
+
- Active sessions, platforms, uptime, memory
|
|
203
|
+
- Debug flags and allowed users
|
|
204
|
+
|
|
205
|
+
### `/compact`
|
|
206
|
+
|
|
207
|
+
Manually triggers context compaction for the current chat's session. Shows before/after token counts. Useful when conversations get long and you want to free up context window space without starting a new session.
|
|
208
|
+
|
|
209
|
+
### `/verbose`
|
|
210
|
+
|
|
211
|
+
Toggles verbose mode for the current chat. When ON, shows tool call status messages (e.g. "⚡ Running `bash`…"). When OFF (default), tool calls execute silently — you only see the agent's text responses. State shown in `/status`.
|
|
212
|
+
|
|
213
|
+
### `/stop`
|
|
214
|
+
|
|
215
|
+
Aborts the current agent run for this chat — stops any in-progress tool calls, LLM generation, and compaction. The session is preserved; send another message to continue the conversation.
|
|
216
|
+
|
|
217
|
+
### Follow-up notifications
|
|
218
|
+
|
|
219
|
+
When extensions (e.g. code review) queue follow-up work after the agent responds, the gateway shows:
|
|
220
|
+
- ⏳ "Hold on — waiting for follow-up messages..." (after 2s delay)
|
|
221
|
+
- ✅ "All done — waiting for your input." (when processing completes)
|
|
222
|
+
|
|
223
|
+
Fast operations that complete within 2 seconds show no extra messages.
|
|
224
|
+
|
|
225
|
+
## File attachments
|
|
226
|
+
|
|
227
|
+
Roundhouse handles voice messages, images, documents, and other file attachments from Telegram:
|
|
228
|
+
|
|
229
|
+
1. Files are downloaded and saved to `~/.roundhouse/incoming/<thread>/<message>/`
|
|
230
|
+
2. A structured `AgentMessage` with typed `MessageAttachment[]` metadata is passed to the agent
|
|
231
|
+
3. The agent receives file paths, MIME types, sizes, and can inspect files with its tools
|
|
232
|
+
4. Files are marked as untrusted user-provided input
|
|
233
|
+
|
|
234
|
+
### Limits
|
|
235
|
+
|
|
236
|
+
| Limit | Value |
|
|
237
|
+
|-------|-------|
|
|
238
|
+
| Max file size | 20 MB per file |
|
|
239
|
+
| Max attachments | 5 per message |
|
|
240
|
+
| Filename length | 100 characters (sanitized to ASCII) |
|
|
241
|
+
|
|
242
|
+
Supported types: voice messages, audio, images, video, documents (PDF, etc.)
|
|
243
|
+
|
|
244
|
+
The incoming directory can be overridden with `ROUNDHOUSE_INCOMING_DIR` environment variable.
|
|
245
|
+
|
|
246
|
+
### How the agent sees attachments
|
|
247
|
+
|
|
248
|
+
The Pi adapter formats attachments as a fenced JSON manifest:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
Chat attachments saved locally. Inspect these files with tools before making claims about their contents.
|
|
252
|
+
```json
|
|
253
|
+
[
|
|
254
|
+
{
|
|
255
|
+
"id": "att_a1b2c3d4",
|
|
256
|
+
"type": "audio",
|
|
257
|
+
"name": "audio.ogg",
|
|
258
|
+
"localPath": "/home/user/.roundhouse/incoming/telegram_c123/1745.../0-audio.ogg",
|
|
259
|
+
"mime": "audio/ogg",
|
|
260
|
+
"sizeBytes": 43520,
|
|
261
|
+
"untrusted": true
|
|
262
|
+
}
|
|
263
|
+
]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Other agent adapters can format attachments differently — the `AgentMessage.attachments` array provides structured data.
|
|
267
|
+
|
|
268
|
+
### Voice transcription (STT)
|
|
269
|
+
|
|
270
|
+
Roundhouse can automatically transcribe voice messages using [OpenAI Whisper](https://github.com/openai/whisper) running locally. No cloud services or API keys required.
|
|
271
|
+
|
|
272
|
+
**Setup:**
|
|
273
|
+
```bash
|
|
274
|
+
pip install openai-whisper
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Or set `autoInstall: true` in config to have roundhouse install whisper automatically on first voice message.
|
|
278
|
+
|
|
279
|
+
**Enable in config:**
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"voice": {
|
|
283
|
+
"stt": {
|
|
284
|
+
"enabled": true,
|
|
285
|
+
"mode": "on",
|
|
286
|
+
"autoInstall": true,
|
|
287
|
+
"chain": ["whisper"],
|
|
288
|
+
"autoTranscribe": {
|
|
289
|
+
"voiceMessages": true,
|
|
290
|
+
"audioFiles": false,
|
|
291
|
+
"maxDurationSec": 120
|
|
292
|
+
},
|
|
293
|
+
"providers": {
|
|
294
|
+
"whisper": {
|
|
295
|
+
"type": "whisper",
|
|
296
|
+
"model": "small",
|
|
297
|
+
"timeoutMs": 30000
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
When enabled, voice messages are automatically transcribed before being sent to the agent. The agent sees both the transcript and the raw audio file path:
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"id": "att_a1b2c3d4",
|
|
310
|
+
"type": "audio",
|
|
311
|
+
"localPath": "/home/user/.roundhouse/incoming/.../0-audio.ogg",
|
|
312
|
+
"transcript": {
|
|
313
|
+
"text": "This will also work in Hebrew",
|
|
314
|
+
"language": "hebrew",
|
|
315
|
+
"provider": "whisper-small",
|
|
316
|
+
"approximate": true
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
For voice-only messages (no typed text), the transcript becomes the message text sent to the agent.
|
|
322
|
+
|
|
323
|
+
Whisper model sizes: `tiny` (fast, English-only reliable), `base`, `small` (recommended — good multilingual), `medium`, `large` (slow but most accurate).
|
|
324
|
+
|
|
325
|
+
Transcripts are always marked `approximate: true` — the agent is instructed to use the raw file if exact wording matters.
|
|
326
|
+
|
|
327
|
+
## Cron Jobs (Scheduled Tasks)
|
|
328
|
+
|
|
329
|
+
Roundhouse includes a built-in cron scheduler for running agent prompts on a schedule.
|
|
330
|
+
|
|
331
|
+
### CLI Commands
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
roundhouse cron add <id> [flags] Create a scheduled job
|
|
335
|
+
roundhouse cron list List all jobs
|
|
336
|
+
roundhouse cron show <id> Show job details + recent runs
|
|
337
|
+
roundhouse cron trigger <id> Run a job now
|
|
338
|
+
roundhouse cron runs <id> Show run history
|
|
339
|
+
roundhouse cron edit <id> [flags] Modify a job
|
|
340
|
+
roundhouse cron pause <id> Disable a job
|
|
341
|
+
roundhouse cron resume <id> Re-enable a job
|
|
342
|
+
roundhouse cron delete <id> Delete a job
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Schedule Types
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Standard cron with timezone
|
|
349
|
+
roundhouse cron add daily-report --cron "0 8 * * *" --tz Asia/Jerusalem --prompt "..."
|
|
350
|
+
|
|
351
|
+
# Fixed interval
|
|
352
|
+
roundhouse cron add health-check --every 6h --prompt "..."
|
|
353
|
+
|
|
354
|
+
# One-shot (relative or absolute)
|
|
355
|
+
roundhouse cron add reminder --at 30m --prompt "..."
|
|
356
|
+
roundhouse cron add meeting --at 2026-04-28T14:00:00 --tz Asia/Jerusalem --prompt "..."
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Notifications
|
|
360
|
+
|
|
361
|
+
Add `--telegram <chatId>` to send results to Telegram:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
roundhouse cron add aws-costs --cron "0 8 * * *" --tz Asia/Jerusalem \\
|
|
365
|
+
--prompt "Check current AWS costs and summarize." \\
|
|
366
|
+
--telegram 123456789
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Telegram Commands
|
|
370
|
+
|
|
371
|
+
| Command | Description |
|
|
372
|
+
|---------|-------------|
|
|
373
|
+
| `/crons` or `/jobs` | List all scheduled jobs |
|
|
374
|
+
| `/crons trigger <id>` | Run a job now |
|
|
375
|
+
| `/crons pause <id>` | Disable a job |
|
|
376
|
+
| `/crons resume <id>` | Re-enable a job |
|
|
377
|
+
|
|
378
|
+
### Conversational Setup
|
|
379
|
+
|
|
380
|
+
Tell the agent: *"set a cron daily 8am Israel time to give me current AWS costs"* — the agent will run `roundhouse cron add` via its bash tool.
|
|
381
|
+
|
|
382
|
+
### Heartbeat
|
|
383
|
+
|
|
384
|
+
Edit `~/.roundhouse/HEARTBEAT.md` with recurring tasks. The scheduler reads it every 30 minutes and runs the instructions as an agent prompt. If the file is empty or contains only the default template, no action is taken.
|
|
385
|
+
|
|
386
|
+
### Config
|
|
387
|
+
|
|
388
|
+
Job configs stored as JSON in `~/.roundhouse/crons/`. State in `~/.roundhouse/cron-state/`. Run history in `~/.roundhouse/cron-runs/`.
|
|
389
|
+
|
|
390
|
+
## Extensions
|
|
391
|
+
|
|
392
|
+
### Code review extension
|
|
393
|
+
|
|
394
|
+
The code review extension has moved to its own package: [pi-autoreview](https://github.com/inceptionstack/pi-autoreview). Install it with:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
pi install git:github.com/inceptionstack/pi-autoreview
|
|
398
|
+
```
|
|
399
|
+
|
|
110
400
|
## Adding a new agent backend
|
|
111
401
|
|
|
112
402
|
1. Create `src/agents/kiro.ts` implementing `AgentAdapter`
|
|
@@ -114,14 +404,14 @@ Messages from Telegram/Slack and from the CLI share the same context.
|
|
|
114
404
|
3. Set `"agent": { "type": "kiro" }` in config
|
|
115
405
|
|
|
116
406
|
```typescript
|
|
117
|
-
// src/agents/kiro.ts
|
|
118
407
|
import type { AgentAdapter, AgentAdapterFactory } from "../types";
|
|
119
408
|
|
|
120
409
|
export const createKiroAgentAdapter: AgentAdapterFactory = (config) => {
|
|
121
410
|
return {
|
|
122
411
|
name: "kiro",
|
|
123
|
-
async prompt(threadId,
|
|
124
|
-
//
|
|
412
|
+
async prompt(threadId, message) {
|
|
413
|
+
// message.text contains user text
|
|
414
|
+
// message.attachments contains saved file metadata
|
|
125
415
|
return { text: "response" };
|
|
126
416
|
},
|
|
127
417
|
async dispose() {},
|
|
@@ -134,7 +424,6 @@ export const createKiroAgentAdapter: AgentAdapterFactory = (config) => {
|
|
|
134
424
|
Add the Chat SDK adapter package and wire it in `gateway.ts`:
|
|
135
425
|
|
|
136
426
|
```typescript
|
|
137
|
-
// In buildChatAdapters():
|
|
138
427
|
if (config.slack) {
|
|
139
428
|
const { createSlackAdapter } = await import("@chat-adapter/slack");
|
|
140
429
|
adapters.slack = createSlackAdapter();
|
|
@@ -150,11 +439,30 @@ No other changes needed — the gateway's unified handler covers all platforms.
|
|
|
150
439
|
| `src/index.ts` | Entry point, config loading, startup |
|
|
151
440
|
| `src/gateway.ts` | Owns Chat SDK, wires events → router → agent |
|
|
152
441
|
| `src/router.ts` | `AgentRouter` interface + `SingleAgentRouter` |
|
|
153
|
-
| `src/types.ts` | Core interfaces: `AgentAdapter`, `AgentRouter`, `GatewayConfig` |
|
|
154
|
-
| `src/util.ts` | Pure utilities: `splitMessage`, `isAllowed`, `threadIdToDir` |
|
|
442
|
+
| `src/types.ts` | Core interfaces: `AgentAdapter`, `AgentStreamEvent`, `AgentRouter`, `GatewayConfig` |
|
|
443
|
+
| `src/util.ts` | Pure utilities: `splitMessage`, `isAllowed`, `threadIdToDir`, `startTypingLoop` |
|
|
444
|
+
| `src/cli/cli.ts` | CLI: start, install, tui, update, logs, etc. |
|
|
445
|
+
| `src/cli/doctor.ts` | CLI doctor command |
|
|
446
|
+
| `src/cli/doctor/runner.ts` | Shared doctor runner (CLI + gateway) |
|
|
447
|
+
| `src/cli/doctor/checks/` | Individual health check modules |
|
|
448
|
+
| `src/cron/` | Cron scheduler, runner, store, schedule, template, format |
|
|
449
|
+
| `src/cron/helpers.ts` | Shared cron constants and utilities |
|
|
450
|
+
| `src/notify/telegram.ts` | Shared Telegram Bot API sender |
|
|
155
451
|
| `src/agents/pi.ts` | Pi agent adapter (persistent sessions via pi SDK) |
|
|
156
452
|
| `src/agents/registry.ts` | Agent type → factory registry |
|
|
157
|
-
| `
|
|
453
|
+
| `src/config.ts` | Shared config loading, defaults, env overrides |
|
|
454
|
+
| `test/` | Unit tests (vitest, 75 passing) |
|
|
455
|
+
|
|
456
|
+
## CI/CD
|
|
457
|
+
|
|
458
|
+
Tests run on every push/PR. Publishing to npm happens automatically on tag push:
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
npm version patch # bumps version, creates git tag
|
|
462
|
+
git push origin main --tags # triggers publish workflow
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Requires `NPM_TOKEN` secret in GitHub repo settings.
|
|
158
466
|
|
|
159
467
|
## Testing
|
|
160
468
|
|
|
@@ -162,3 +470,7 @@ No other changes needed — the gateway's unified handler covers all platforms.
|
|
|
162
470
|
npm test # run once
|
|
163
471
|
npm run test:watch # watch mode
|
|
164
472
|
```
|
|
473
|
+
|
|
474
|
+
## License
|
|
475
|
+
|
|
476
|
+
MIT
|
package/architecture.md
CHANGED
|
@@ -98,12 +98,14 @@ User sends "list files" on Telegram
|
|
|
98
98
|
│ 1. Check isAllowed(message.author, allowedUsers) │
|
|
99
99
|
│ 2. Resolve agent via router.resolve(thread.id) │
|
|
100
100
|
│ 3. thread.startTyping() │
|
|
101
|
-
│ 4.
|
|
101
|
+
│ 4. Save attachments to ~/.roundhouse/incoming/<thread>/ │
|
|
102
|
+
│ 5. Build AgentMessage { text, attachments } │
|
|
103
|
+
│ 6. agent.promptStream(thread.id, agentMessage) │
|
|
104
|
+
│ └─▶ Pi adapter formats text + JSON attachment manifest │
|
|
102
105
|
│ └─▶ Pi SDK creates/resumes session │
|
|
103
106
|
│ └─▶ LLM processes, tools execute │
|
|
104
|
-
│ └─▶
|
|
105
|
-
│
|
|
106
|
-
│ 6. thread.post(chunk) for each chunk │
|
|
107
|
+
│ └─▶ Streams AgentStreamEvent back │
|
|
108
|
+
│ 7. Stream text deltas via thread.handleStream() │
|
|
107
109
|
└──────────────────────────────────────────────────────────────┘
|
|
108
110
|
│
|
|
109
111
|
▼
|
|
@@ -113,9 +115,41 @@ User sends "list files" on Telegram
|
|
|
113
115
|
## Key interfaces
|
|
114
116
|
|
|
115
117
|
```typescript
|
|
118
|
+
interface AgentMessage {
|
|
119
|
+
text: string;
|
|
120
|
+
attachments?: MessageAttachment[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface MessageAttachment {
|
|
124
|
+
id: string;
|
|
125
|
+
mediaType: "audio" | "image" | "file" | "video";
|
|
126
|
+
name: string;
|
|
127
|
+
localPath: string;
|
|
128
|
+
mime: string;
|
|
129
|
+
sizeBytes: number;
|
|
130
|
+
untrusted: true;
|
|
131
|
+
transcript?: AttachmentTranscript;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface AttachmentTranscript {
|
|
135
|
+
text: string;
|
|
136
|
+
provider: string;
|
|
137
|
+
language?: string;
|
|
138
|
+
confidence?: number;
|
|
139
|
+
approximate: true;
|
|
140
|
+
status: "completed" | "failed";
|
|
141
|
+
error?: string;
|
|
142
|
+
durationMs?: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
116
145
|
interface AgentAdapter {
|
|
117
146
|
name: string;
|
|
118
|
-
prompt(threadId: string,
|
|
147
|
+
prompt(threadId: string, message: AgentMessage): Promise<AgentResponse>;
|
|
148
|
+
promptStream?(threadId: string, message: AgentMessage): AsyncIterable<AgentStreamEvent>;
|
|
149
|
+
restart?(threadId: string): Promise<void>;
|
|
150
|
+
compact?(threadId: string): Promise<{ tokensBefore: number; tokensAfter: number | null } | null>;
|
|
151
|
+
abort?(threadId: string): Promise<void>;
|
|
152
|
+
getInfo?(threadId?: string): Record<string, unknown>;
|
|
119
153
|
dispose(): Promise<void>;
|
|
120
154
|
}
|
|
121
155
|
|
|
@@ -142,10 +176,24 @@ gateway.config.json
|
|
|
142
176
|
└── chat # Multiple chat inputs
|
|
143
177
|
├── botUsername: "my_bot"
|
|
144
178
|
├── allowedUsers: [...] # Auth filter (userName or userId)
|
|
179
|
+
├── notifyChatIds: [...] # Telegram chat IDs for startup notifications
|
|
145
180
|
└── adapters
|
|
146
181
|
├── telegram: { mode: "polling" }
|
|
147
182
|
├── slack: { ... } # (future)
|
|
148
183
|
└── discord: { ... } # (future)
|
|
184
|
+
|
|
185
|
+
└── voice # Optional voice features
|
|
186
|
+
└── stt
|
|
187
|
+
├── enabled: true
|
|
188
|
+
├── mode: "on" | "off"
|
|
189
|
+
├── autoInstall: false # auto-install whisper via pip3
|
|
190
|
+
├── chain: ["whisper"] # Provider chain (try in order)
|
|
191
|
+
├── autoTranscribe
|
|
192
|
+
│ ├── voiceMessages: true
|
|
193
|
+
│ ├── audioFiles: false
|
|
194
|
+
│ └── maxDurationSec: 120
|
|
195
|
+
└── providers
|
|
196
|
+
└── whisper: { model: "small", timeoutMs: 30000 }
|
|
149
197
|
```
|
|
150
198
|
|
|
151
199
|
Secrets (`TELEGRAM_BOT_TOKEN`, `ANTHROPIC_API_KEY`) are always env vars, never in config.
|
|
@@ -163,6 +211,9 @@ Secrets (`TELEGRAM_BOT_TOKEN`, `ANTHROPIC_API_KEY`) are always env vars, never i
|
|
|
163
211
|
b. Create Chat instance with all adapters
|
|
164
212
|
c. Wire onDirectMessage / onNewMention / onSubscribedMessage → handle()
|
|
165
213
|
d. chat.initialize() — starts polling / webhooks
|
|
214
|
+
e. registerBotCommands() — register /new, /restart, /status with Telegram
|
|
215
|
+
f. notifyStartup() — send Telegram notification to configured notifyChatIds
|
|
216
|
+
g. Start CronSchedulerService — loads jobs, catches up missed runs, ticks every 60s
|
|
166
217
|
7. Running. Ctrl+C → gateway.stop() → router.dispose() → agent.dispose()
|
|
167
218
|
```
|
|
168
219
|
|
|
@@ -202,13 +253,31 @@ The gateway and agent adapters don't change — only the router.
|
|
|
202
253
|
|
|
203
254
|
```
|
|
204
255
|
index.ts
|
|
256
|
+
├── config.ts (loadConfig, applyEnvOverrides)
|
|
205
257
|
├── agents/registry.ts
|
|
206
258
|
│ └── agents/pi.ts
|
|
207
259
|
│ └── util.ts (threadIdToDir)
|
|
208
260
|
├── router.ts
|
|
209
261
|
├── gateway.ts
|
|
210
|
-
│ └── util.ts (splitMessage, isAllowed)
|
|
211
|
-
└──
|
|
262
|
+
│ └── util.ts (splitMessage, isAllowed, threadIdToDir, generateAttachmentId)
|
|
263
|
+
│ └── voice/stt-service.ts
|
|
264
|
+
│ └── voice/providers/whisper.ts
|
|
265
|
+
│ └── voice/types.ts
|
|
266
|
+
└── types.ts (shared interfaces, pure types only)
|
|
267
|
+
|
|
268
|
+
cli/cli.ts
|
|
269
|
+
├── config.ts (DEFAULT_CONFIG, CONFIG_PATH, loadConfig, etc.)
|
|
270
|
+
├── agents/registry.ts (getAgentSdkPackage)
|
|
271
|
+
├── cli/doctor.ts → cli/doctor/runner.ts → cli/doctor/checks/*
|
|
272
|
+
├── cli/cron.ts → cron/store.ts, cron/runner.ts, cron/helpers.ts
|
|
273
|
+
└── (node:fs, node:child_process for daemon management)
|
|
274
|
+
|
|
275
|
+
gateway.ts also imports:
|
|
276
|
+
→ cli/doctor/runner.ts for /doctor command
|
|
277
|
+
→ cron/scheduler.ts → cron/runner.ts → cron/store.ts
|
|
278
|
+
→ cron/helpers.ts, cron/format.ts
|
|
279
|
+
→ notify/telegram.ts
|
|
212
280
|
```
|
|
213
281
|
|
|
214
|
-
No circular dependencies. `
|
|
282
|
+
No circular dependencies. `types.ts` and `config.ts` are pure leaf modules.
|
|
283
|
+
`util.ts` is a leaf module with runtime helpers (`node:crypto` for attachment IDs).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inceptionstack/roundhouse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Multi-platform chat gateway that routes messages through a configured AI agent",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"start": "tsx src/index.ts",
|
|
27
27
|
"dev": "tsx watch src/index.ts",
|
|
28
28
|
"test": "vitest run",
|
|
29
|
-
"test:watch": "vitest"
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"release": "npm version patch --no-git-tag-version && npm publish --access public && git add -A && git commit -m \"v$(node -p 'require(\"./package.json\").version')\" && git push origin main"
|
|
30
31
|
},
|
|
31
32
|
"files": [
|
|
32
33
|
"src/",
|
|
@@ -36,10 +37,12 @@
|
|
|
36
37
|
"architecture.md"
|
|
37
38
|
],
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@chat-adapter/state-memory": "
|
|
40
|
-
"@chat-adapter/telegram": "
|
|
41
|
-
"@mariozechner/pi-coding-agent": "
|
|
42
|
-
"chat": "
|
|
40
|
+
"@chat-adapter/state-memory": "^4.26.0",
|
|
41
|
+
"@chat-adapter/telegram": "^4.26.0",
|
|
42
|
+
"@mariozechner/pi-coding-agent": "^0.69.0",
|
|
43
|
+
"chat": "^4.26.0",
|
|
44
|
+
"croner": "^10.0.1",
|
|
45
|
+
"p-queue": "^9.2.0",
|
|
43
46
|
"tsx": "^4.0.0"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|