Repository navigation
Channels and Telegram
Channels announce what needs you and, for some, let you answer. They get the command, run, node and role, never file contents, diffs or tool output. Failed deliveries retry with backoff through a persistent outbox.
-
macOS notifications (
osascript, orterminal-notifierwhen installed). On by default on macOS. - Telegram with Approve / Deny buttons, reports, and a conversation with the orchestrator.
-
GitHub: a run asked from an issue (
shibaox run --issue N) reports back as a comment on it; never asks approvals there (channels.github.enabled: falseindaemon.yamlturns it off).
- Create a bot with @BotFather (Customize → Plugins → Telegram → Create a bot) and note its token.
- Paste the token on the Telegram card (or
shibaox keys set SHIBAOX_TELEGRAM_TOKEN 123:abc): it goes to the vault. - Click Pair with my Telegram, then open your bot in Telegram and send
/startwithin a minute. The daemon long polls the Bot API for that message, writes its chat tochannels.telegram.chat_idin~/.shibaox/daemon.yaml(comments and the other keys kept) and starts the channel on it at once: no restart. The card says Paired with chat , or why not (no message arrived in 60 s, Telegram refused the token…). Messages the bot received before the pairing are consumed, never answered later; a/startis preferred when several arrive together. One pairing at a time. - Send a test message checks the whole path: the bot writes "Shibaox ✓ test message from the app" to the paired chat.
The card is Ready only when the token is saved, a chat is paired and the channel is running. Pairing writes daemon.yaml, so it works from the daemon's own machine only (see Security); the test message works from any client.
Same thing over the API: POST /plugins/telegram/actions/pair (optional { "timeoutMs": … }, up to 120 000) answers { paired: true, chatId, from? } or { paired: false, reason }; POST /plugins/telegram/actions/test answers { sent: true, chatId }, or 409 not_paired / no_token.
The chat id can still be written yourself (message the bot, then read getUpdates, or use @userinfobot):
- Put the token in the vault:
shibaox keys set SHIBAOX_TELEGRAM_TOKEN 123:abc. The channel starts polling at once; remove the key and it stops; change it and it restarts. No daemon restart. - In
~/.shibaox/daemon.yaml:
channels:
telegram:
chat_id: 123456789
org: ./org # relative paths resolve next to daemon.yaml
project: /path/to/project # optional: pin text messages to this org and project
workflow: chat # default
# adapter: claude-code
# bot_token_env: SHIBAOX_TELEGRAM_TOKEN # defaultorg and project are not written by the pairing and are optional: without them a text message runs on the daemon's home org and home workspace (the same place a new chat without a project uses); set them to pin the chat to one org and project.
shibaox doctor checks the token with getMe.
Roles with telegram in their tools get the daemon's telegram_send(text) tool (the scaffold's assistant has it): "send me a Telegram when the build is done" works. The text goes to the paired chat as plain text (escaped, split under Telegram's limit) through the daemon; the bot token never reaches the model. When nothing is paired the tool says "Telegram is not paired: Customize → Plugins → Telegram → Pair". An org created before this release adds telegram to org/roles/assistant.yaml tools by hand.
Every human node and tool approval arrives as a message with Approve and Deny. The first answer wins; the message is edited to say who answered and how.
Any text you send the bot from your private chat (groups are ignored: the orchestrator writes to the project) is a turn for the orchestrator, one at a time; texts sent while it answers wait their turn. The reply comes back to the chat, and workflows it dispatches report their end there too. /status answers with the daemon, its runs and what needs you; /help lists this.
The thread lives in memory (a restart forgets it; the orchestrator's remember notes do not) and is compacted like any long conversation. Messages that piled up while the daemon was down: only the last one is answered, and the chat is told how many were skipped.
A reply is Markdown from the model, sent rendered the way Telegram shows it: headings and **bold** as bold, *italic*, `code` and fenced blocks, links, bullets. Long replies go out in pieces under Telegram's limit, cut between paragraphs (a code block stays whole). While a turn runs, the chat shows typing… until the reply lands.
telegram_send(text, path?) also sends a file of the run's workspace: images go as photos, videos as videos, audio as audio and anything else as a document (50 MB at most; the text is the caption). Only files of the workspace, never protected ones (.env, the project's protected globs). Ask the assistant to send an image it generated, a report it wrote or a file you attached.
Telegram serves getUpdates to one program per bot. If another program (another daemon, a server, an older install) polls the same bot, Telegram answers 409 Conflict to the other one and messages land in whichever asked last. Pairing waits through a Conflict for the whole wait and then says so; a channel that keeps losing messages has the same cause. Give Shibaox a bot of its own: BotFather → /newbot, then replace the token in Customize → Keys (or shibaox keys set SHIBAOX_TELEGRAM_TOKEN) and pair again.