herdr-turn-coordinator
v0.8.1
Published
Preserve interactive Herdr agent TUIs without model-driven status polling
Maintainers
Readme
Herdr Turn Coordinator
Run one interactive coding-agent turn in Herdr without spending parent-model tokens on status polling.
Works with every agent kind supported by your installed Herdr version, including Codex, Claude Code, Grok, Gemini CLI, Kimi, Cursor, OpenCode, and GitHub Copilot.
The plugin keeps the downstream agent's native TUI, splits a dedicated pane without taking focus, waits in a local supervisor process, reads the final output once, and leaves the pane open for human takeover.
Why
herdr agent prompt --wait can report agent_prompt_stalled even when a prompt was delivered and the agent continues working. A parent model that recovers by repeatedly calling agent get, agent read, or agent wait pays for every observation as another model turn.
Turn Coordinator moves that wait into a local process. On a false stall it checks for a new prompt on screen or a recovered working state, then waits. If Grok leaves the confirmed prompt queued while idle, it presses Enter once before waiting. If it only sees 60 seconds of quiet pane output, it returns unknown for human takeover instead of declaring the turn done. It never resends the same prompt.
Requirements
- Herdr 0.8.0 or newer
- Python 3.9 or newer
- macOS or Linux
- A Herdr-supported interactive agent CLI
Supported AI CLIs
--kind is passed directly to Herdr. Use the Herdr kind ID, not the product name.
| Product | --kind | Product | --kind |
| --- | --- | --- | --- |
| Codex | codex | Claude Code | claude |
| Grok | grok | Gemini CLI | gemini |
| Kimi Code CLI | kimi | Cursor Agent CLI | cursor |
| OpenCode | opencode | GitHub Copilot CLI | copilot |
| Cline | cline | Kiro CLI | kiro |
| Qwen Code | qwen | Qoder CLI | qodercli |
| Amp | amp | Droid | droid |
Any other kind accepted by your installed Herdr version also works, including kinds added after this plugin release. Availability and agent detection quality follow Herdr itself. The only extra agent-specific guard is Kimi's first-run folder-trust prompt.
Install
Recommended Herdr plugin installation:
herdr plugin install KarthusLorin/herdr-turn-coordinator
herdr plugin action invoke karthuslorin.turn-coordinator.install-cli
herdr plugin log list --plugin karthuslorin.turn-coordinator --limit 1
herdr-turn doctorPlugin actions are asynchronous. Confirm the install action log says succeeded before running doctor. Plugin install puts herdr-turn in ~/.local/bin, so ensure that directory is on PATH.
Alternatively, install the CLI from npm:
npm install --global herdr-turn-coordinator
herdr-turn doctorChoose one installation method. An npm global install uses the npm prefix bin instead of ~/.local/bin.
Usage
herdr-turn run and herdr-turn prompt must run from a pane inside Herdr (HERDR_ENV=1). herdr-turn doctor can run outside Herdr.
Start a new interactive agent in a dedicated pane:
herdr-turn run \
--kind codex \
--name reviewer \
--prompt "Review the current diff and report only actionable findings."For Claude Code, Grok, Gemini, or Kimi, use --kind claude, --kind grok, --kind gemini, or --kind kimi.
Select a model for a new Claude Code, Codex, or OpenCode session without changing shared CLI configuration:
herdr-turn run --kind claude --model gateway/team-model --name seed-review --prompt "Review the diff."
herdr-turn run --kind codex --model my-codex-model --name sol-worker --prompt "Implement the task."
herdr-turn run --kind opencode --model opencode/muse-spark-1.3-contributor-free --name muse-worker --prompt "Implement the task."--model is forwarded as one native argument through herdr agent start --.
Omitting it preserves the CLI default and existing startup flags. Only claude,
codex, and opencode support this option here; Trae uses herdr-trae-turn --model.
prompt does not accept --model: continue the same session without changing
its model. Model/provider availability and orchestration eligibility remain the
caller's responsibility; accepting a model slug does not verify provider access.
For a new Claude Code session, pin effort without changing shared settings:
herdr-turn run --kind claude --model sonnet --effort low --name tagger --prompt "Label the input."--effort accepts low, medium, high, xhigh, or max and is forwarded to
Claude's native session-only flag. It is supported only by run --kind claude;
prompt keeps the existing session's effort. Omitting it preserves the CLI's
configured effort. Argument forwarding does not verify a provider's reasoning behavior.
Continue an existing settled agent:
herdr-turn prompt \
--target reviewer \
--prompt "Now summarize the top three risks."Both commands block until the turn settles or the timeout expires, then print one JSON object. The default timeout is 1800000 ms (30 minutes). Timeouts accept explicit units such as 600ms, 600s, and 10m; bare values remain milliseconds for compatibility. Ambiguous bare values below 1000 are rejected with a unit hint. The created pane stays open and the agent remains fully interactive.
{
"ok": true,
"pane_id": "w1:p2",
"agent_name": "reviewer",
"agent_status": "idle",
"text": "..."
}On ok: false, a non-zero exit, or a status other than idle/done, stop for human takeover instead of polling Herdr from model turns.
Completion receipts
A settled pane only proves the TUI returned to its prompt box. A worker that hit
a rate limit, ran out of context, or gave up mid-task settles exactly like one
that finished, so agent_status alone cannot separate them. Pass --receipt
with an absolute path to require the worker to write a JSON receipt as its final
action:
herdr-turn run \
--kind codex \
--name reviewer \
--receipt /abs/path/run-1/receipt.json \
--prompt "Review the diff, write findings to /abs/path/run-1/review.md, then write
{\"status\":\"completed\",\"artifacts\":[\"/abs/path/run-1/review.md\"],\"remaining\":[],\"reason\":\"done\"}
to /abs/path/run-1/receipt.json as your final action."The wrapper verifies the receipt itself and adds a receipt object to the
result:
{
"ok": false,
"agent_status": "done",
"receipt": {"path": "...", "present": false, "fresh": false, "parsable": false,
"accepted": false, "problem": "missing"}
}problem is one of missing, stale, unparsable, inconsistent,
not_completed, or artifact_unverified. A receipt is accepted only when it
appeared during this turn, parses as a JSON object, carries all four fields —
status: "completed", a list artifacts, an empty or absent remaining, and a
non-empty reason — and every path in artifacts exists and was modified
during the turn. A receipt that omits reason, or whose artifacts or
remaining is not a list, is rejected as inconsistent: a worker that cannot
say why it stopped has produced nothing a recovery step can act on, whatever its
status claims. A receipt left behind by an earlier attempt is rejected as
stale, so reusing a receipt path across retries cannot produce a false
success. An empty artifacts list is legitimate for investigation or review
turns that deliver only the receipt.
--receipt also makes the wrapper patient about a slow start. A pane that is
still idle right after the prompt lands is ambiguous — the CLI may not have
picked the turn up yet — and without a receipt the only tiebreak is a 1.5s
window, which occasionally reports a false stable_settled for a worker that
was merely slow. With a receipt there is evidence to wait for, so that window is
re-armed until the receipt appears, the agent is first seen working, or 30s
pass. A turn that really was a no-op still ends immediately, because its receipt
is already on disk.
--receipt is opt-in. Without it the result keeps its published shape and ok
keeps its previous meaning; with it, ok additionally requires an accepted
receipt.
Suggested agent instruction
Inside Herdr, preserve downstream agents' native interactive TUIs. Use one
blocking `herdr-turn run --kind <kind> --name <name> --prompt <prompt>` call,
then consume its single final JSON result. Do not poll Herdr from model turns.Behavior
- Preserves each agent's native interactive TUI by using
herdr agent start; it never substitutes a batch or non-interactive mode. - Rejects prompts to agents reported as
working,blocked, orunknown. - Leaves Kimi's first-run folder-trust prompt untouched for manual confirmation.
- Uses Herdr's native blocking wait first.
- On
agent_prompt_stalled, falls back after a revision advance plus a new prompt anchor or a recoveredworkingstate. For an idle Grok pane with the new prompt visible, it presses Enter once and then waits with the original turn timeout. - Uses native lifecycle waiting once Herdr reports
working; otherwise a local 60-second revision-quiet heuristic returnsunknownwithout declaring success. - Reads history once only after confirmed completion. Blocked or uncertain turns read the visible screen without scrolling the live TUI.
- Never resends a stalled prompt automatically.
- With
--receipt, verifies the receipt after the pane settles and folds the verdict intook; without it, behavior is unchanged.
Local A/B result
One parent-Codex-to-Kimi review task on macOS with Herdr 0.8.0 (n=1) produced the following result:
| Metric | Before | With plugin | Change | | --- | ---: | ---: | ---: | | Input tokens | 357,074 | 124,276 | -65.2% | | Parent tool calls | 15 | 4 | -73.3% | | Output tokens | 2,950 | 1,638 | -44.5% | | Wall time | 108 s | 62 s | -42.6% |
Before the plugin, the parent recovered a false stall by polling Herdr. With the plugin, it made one blocking call. This is a single local comparison, not a universal performance guarantee, and it does not measure other agent pairings.
Tests
python3 -m unittest -vnpm test runs the same command.
Scope
This plugin coordinates interactive Herdr agents. Outside Herdr, use the downstream CLI's normal blocking non-interactive mode. It does not modify Herdr or replace agent TUIs. Plugin installation does not need npm; the npm package is an alternative way to install the same herdr-turn CLI.
Uninstall
herdr plugin action invoke karthuslorin.turn-coordinator.uninstall-cli
herdr plugin log list --plugin karthuslorin.turn-coordinator --limit 1
herdr plugin uninstall karthuslorin.turn-coordinatorFor an npm installation:
npm uninstall --global herdr-turn-coordinatorLicense
MIT
