Herdr Blocked on Question
The herdr-blocked-on-question extension is a small companion to
herdr-agent-state for Herdr
users. While pi has an ask_user_question open, it tells Herdr the agent is
blocked (needs attention) — so hardware readouts such as the
opendeck-herdr Stream Deck
plugin turn red instead of showing busy/blue.
⚠️ Status: provisional — needs addressing later. This is a stopgap for one specific gap. See Status & what still needs addressing for the known limitations and the eventual upstream fix.
What changed (and what didn’t)
Section titled “What changed (and what didn’t)”Be precise about the scope, because it’s easy to misread:
- The
ask_user_questiontool itself was not modified. No pi source, no tool definition, no built-in behaviour was touched. There is nothing to find “in the tool”. - A new companion extension was added (
herdr-blocked-on-question.ts) that observes the tool’s execution lifecycle and emits a signal on pi’s shared event bus. The existing Herdr-managedherdr-agent-state.tsalready knew how to turn that signal into Herdr’sblockedstate — so no change was needed there either (and that file is Herdr-managed and overwritten on update, so it must not be edited).
How it works
Section titled “How it works”The extension listens to three pi events:
| Event | Action |
|---|---|
tool_execution_start (toolName === "ask_user_question") |
emit herdr:blocked { active: true, label: "awaiting answer" } |
tool_execution_end (toolName === "ask_user_question") |
emit herdr:blocked { active: false } |
agent_end |
drain any question still open (abort safety net) |
herdr-agent-state.ts consumes herdr:blocked on the same bus, increments its
blocked counter, and reports Herdr state blocked. When the question is
answered, the counter returns to zero and — because pi is still mid-turn — state
goes back to working. End-to-end:
ask_user_question opens → herdr:blocked active → Herdr "blocked" → readout REDuser answers → herdr:blocked inactive → Herdr "working" → readout BLUEIt tracks open toolCallIds in a Set, so nested/repeated questions and
duplicate starts stay balanced. It is inert unless pi is running under Herdr
(HERDR_ENV=1); with no listener on the bus the emit is a harmless no-op.
Replicate in a new install
Section titled “Replicate in a new install”Prerequisites
-
The Herdr pi integration is installed (it ships the
herdr-agent-state.tsbridge that consumes the signal):Terminal window herdr integration install pi -
pi is launched from within Herdr (
herdr, orherdr --session <name>) so theHERDR_ENV/HERDR_SOCKET_PATH/HERDR_PANE_IDvariables are set.
Install the extension — pick one (all land in pi’s auto-discovered
~/.pi/agent/extensions/):
# Option 0: one-liner from GitHub (auto-discovers every .ts in the repo,# including this one — re-run to update)curl -fsSL https://raw.githubusercontent.com/JohannesBertens/jreb-pi-extensions/main/install.sh | sh
# Option 1: copymkdir -p ~/.pi/agent/extensionscp herdr-blocked-on-question.ts ~/.pi/agent/extensions/
# Option 2: symlink from a checkout (edits propagate on /reload)mkdir -p ~/.pi/agent/extensionsln -sf ~/projects/jreb-pi-extensions/herdr-blocked-on-question.ts ~/.pi/agent/extensions/Activate — there is no toggle command; it activates automatically on every
session_start when HERDR_ENV=1:
/reloadTo verify end-to-end, trigger an ask_user_question under Herdr: the connected
Stream Deck tile/dial should flip to red while the question is open and back
to blue once answered. (opendeck-herdr polls every 2 s, so allow up to that
for the colour change.)
Status & what still needs addressing
Section titled “Status & what still needs addressing”This extension is a provisional fix for one symptom: an open
ask_user_question is mid-turn, so Herdr correctly reports working and the
readout says “busy”. It is not the durable solution.
- Only
ask_user_questionis covered. Permission / confirmation prompts (ctx.ui.confirm/select/input) and the built-in permission gate are not — they lack a single, stable hook to listen on. - Couples to a Herdr-managed contract. It emits
herdr:blocked, whichherdr-agent-state.tsconsumes. If Herdr renames or drops that event, this silently stops working. - The real fix is upstream. The durable solution is for pi core (or the
Herdr integration) to emit
herdr:blockedfor all awaiting-input states, at which point this extension becomes redundant and should be removed.