Know when a worker finishes or asks
Desktop toasts with a sound per event, Claude Code's own Notification hook, Telegram from your phone, and the ask escalation that reaches a human.
Goal#
A worker seals, fails, gets promoted, or stops with a question — know it without staring at the terminal. Four layers, each optional and stacking on the previous one: a toast on this machine, the CLI's own "I stopped" event, Telegram when you walked away, and a digest when you come back.
1. On this machine#
notify.desktop raises an OS toast on every event (sealed, failed,
promoted, needs_approval, needs_answer, notification) — on by default
wherever a display exists:
notify:
desktop: true # default; false turns the toast off
sound: on # on|off|questions (default on)
notify.sound: questions keeps the machine quiet until something is
actually waiting on you — a question or an approval — and silent on a plain
sealed. Each event kind gets its own sound so the ear tells a finished task
from a question without reading the screen.
The toast title is orch · <workspace> · <seat> — the workspace name and the
agent/session that raised it, so several terminals with different Orchemax
seats stay distinguishable at a glance. The body carries the event and the
task's own words.
Prove the channel works, or force it on for a one-off check:
orch notify test --desktop --event failed
Every emission is also appended to .orch/logs/notify.log, whether or not a
toast could actually be shown on this OS.
2. The CLI's own stop#
Some events happen inside the agent CLI itself — it needs a permission
answer, or it has been idle waiting for input — and nothing else in Orchemax
can see that moment. Claude Code exposes it as a Notification hook; wiring
it is one command:
orch guard wire --claude
This adds a Notification entry (project scope, same Orchemax-marked block
as the rest of guard wire) that runs orch hook notification on every
permission prompt or idle wait. The hook raises the desktop toast (and
Telegram, when configured) with the CLI's own message, deduped within 30
seconds, and never blocks the CLI — it always exits 0.
The same command also wires SessionStart to run orch notify-hook — the
idle-return digest from step 5 below fires the moment you reopen a seat,
without waiting on the first tool call.
Other CLIs — cursor-agent, Copilot, opencode, agy — have no equivalent event; they rely entirely on the desktop channel from step 1, which fires on sealing regardless of which CLI ran the session.
3. Away from the machine#
Pair a Telegram bot once, and every event the desktop channel carries also reaches your phone:
-
Talk to
@BotFatheron Telegram, create a bot, copy its token. -
Put the token in an environment variable and name that variable in
orch.yaml:notify: telegram: bot_token_env: ORCH_TELEGRAM_TOKEN -
Pair your chat — no manual chat-id hunting:
orch notify telegram pairThis prints the bot's
t.melink, waits for you to send/start, adds your chat id tonotify.telegram.chat_allowlistinorch.yaml, and sends one test event so you know it worked.
orch daemon starts the long-poll worker automatically once
notify.telegram.bot_token_env is set — nobody has to keep a terminal open
for it. From an allowlisted chat:
/status— how many sessions are running./approve <session>//reject <session>— merge or drop a sealed session./answer <id> <text>— answer a worker's pending question (see below).
A message from any chat not in the allowlist gets orch: unauthorized chat
and nothing else runs.
4. Questions#
A worker's ask blocks that worker until someone answers. If nobody does
within ask.escalate_after_sec (default 60 seconds — well under the ask's
own 600-second timeout), Orchemax escalates it as needs_answer on every
configured channel — desktop, Telegram, webhooks — carrying the question and
its ask id:
ask:
escalate_after_sec: 60
Answer it from Telegram (/answer <id> <text>), or from the CLI on this
machine:
orch ask list
orch ask answer <id> "keep the shared helper, do not duplicate it"
Either path calls the same AnswerAsk — the blocked worker resumes
immediately, no restart.
5. Coming back#
When you open a seat again after being away longer than
notify.idle_digest_min (default 10 minutes), the next hook drain — or
SessionStart, which checks regardless of the gap — injects one summary line
ahead of the regular messages:
while you were away (23m): 2 sealed, 1 failed, 1 question pending (#41)
Nothing to report — no seal, no failure, no pending question — means no line at all: the digest never announces "nothing new", and it never forces another turn just to say so.
notify:
idle_digest_min: 10
What never leaves the machine#
Every channel in this page carries status and summary only — event name,
workspace, session, verdict, the question's own text for needs_answer —
never a diff or file content.