Daily operations
Canonical commands
bun run start
bun run status
bun run restart
bun run stop
start: starts in the background and waits for PID/socket readiness; invalid config fails before any bot polls.restart: serially stops the deployment’s PID owner and orphan processes, waits for every PID/file/socket to disappear, then starts one replacement.status: verifies that the PID belongs to this repository’s daemon instead of trusting the file alone.stop: gracefully stops bots, agents, and IPC with SIGTERM.
Logs are in data/daemon.log. The controller shows only a bounded credential-redacted tail. Never post a full .env or unreviewed logs.
Configuration changes
Configuration is not hot-reloaded. After changing telegram.config.ts, .env, or a persona, run:
bun run restart
You can also use /tg config in Pi to validate or safely edit an existing source. Replacement retains local .bak-<nonce> files. Confirm the new deployment is ready before applying your backup-retention policy; do not delete backups as incidental cleanup.
Data and backups
Persistent resources default to data/ and local project session directories. SQLite is canonical history; Telegram is not the restore source.
Before backup:
- run
bun run stop; - confirm
bun run statusno longer reports running; - copy configuration, personas, data, and session resources to an access-controlled destination;
- keep
.envand private personas out of public artifacts.
Never start two daemons against one copied database in the same directory.
Telegram group controls
Public read commands are /help and /status.
/status shows only the bot that actually received the command; use /status@bot_username to target one explicitly. Its rich message shows runtime state, provider/model/effective reasoning, current context/window/percentage, system/tool/summary/message/free segments as one red/purple/brown/blue/green square per rounded 1,024 tokens, average tok/s/send/think time, latest conversation request, retained SQLite totals, cache hit rate, latency/cost, routing, and latest compaction. The square bar occupies its own line and the legend follows line by line. Current context comes directly from the Pi session; after compaction it remains unknown until the next main request instead of showing a previous epoch. It shares the unified telemetry semantics with Pi /tg status. If Telegram definitively rejects the rich-message method or format before creating a message, the daemon sends one independently generated plain-text projection instead. It does not resend after uncertain outcomes.
Only telegram_admins may run:
/compact
/set <routing_p|cooldown_ms> <value>
A command acts on the bot that received it; append @bot_username to target a specific bot. The deterministic control plane consumes these commands outside persona/provider context. compact uses the existing auxiliary summarization model and may incur cost. Busy bots are not aborted. set writes through to telegram.config.ts, so the new value survives restarts.
Real verification
Default bun test avoids Telegram/provider calls; the test preload mechanically rejects every non-loopback network access. Networked scripts require a bot selection:
bun run scripts/smoke-pi.ts --bot friend
bun run scripts/e2e-agent.ts --bot friend
bun run scripts/e2e-compaction.ts --bot friend
These commands may incur cost or post group messages. Read the daemon runbook first and record the selected bot, expected side effects, and rollback.
Why working directories must be isolated
One working directory currently hosts one group deployment. This is not merely a UI limitation: the following resources belong to the working directory and have no deployment namespace:
- the single
group_peer_idand canonical SQLite history, including each bot’s consumed cursor, visible references, and reply obligations; - agent sessions and context epochs;
- each poller’s Telegram update offset and the shared router secret;
- the daemon PID, control lock, and Unix socket.
Running a second group in one checkout by only switching configuration files therefore does not create two deployments. It can feed one group’s history into another group’s model context, skip updates through the wrong offset, or make daemons compete for one PID/socket.
Use a separate clone or worktree for a second group. Give it independent .env, config, personas, and Telegram bot tokens, plus a separate data/database, sessions, PID/lock/socket, and daemon working directory. Do not merely copy the database or point both directories back to shared data.
This boundary follows the project’s minimal-design principle: reuse an existing, inspectable filesystem isolation boundary instead of adding namespaces, hot reload, and another control plane for an unrequested multi-tenant product. Read the cost design overview for the philosophy and the project description for the authoritative boundary.
Next: Troubleshooting.