Skip to content

fix(crush): type into panes of agents Herdr does not drive - #47

Closed
galichandrey wants to merge 1 commit into
permgps:mainfrom
galichandrey:fix/crush-not-driven-input
Closed

galichandrey wants to merge 1 commit into
permgps:mainfrom
galichandrey:fix/crush-not-driven-input

Conversation

@galichandrey

Copy link
Copy Markdown
Contributor

Herdr refuses agent.prompt and agent.send_keys for an agent it does not drive itself, one that reports its own state instead of going through an official integration: agent_not_ready, "agent w1:p1 is not an active named agent" on 0.9.3. Crush is such an agent, so from a topic every plain message answered "⚠️ herdr agent.prompt: herdr api agent_not_ready" and no key reached the pane. Reads and waits work there, so the topic itself is fine: status, screens and done posts all worked, only input was dead.

The refusal arrives before anything reaches the pane, so the input is sent the way the agent would have received it:

  • a prompt is typed with pane.send_text and submitted with enter through pane.send_keys;
  • every key the plugin sends (/keys, /stop, /interrupt, the buttons) goes to the pane as well;
  • line breaks are joined with spaces, since typed input submits nothing by itself and an enter inside the text would send the message in pieces;
  • one INFO line per fallback with the pane and the lengths, never the text.

Only agent_not_ready takes this path. agent_blocked still reaches the application, which types into the dialog's own free-text entry, and an agent gone from Herdr still answers as before. A target that names an agent rather than a pane has no pane to address, so its refusal reaches the caller unchanged.

Verified live on Herdr 0.9.3 with Crush 0.98.0 in a Herdr pane: the same pane answers agent.read and agent.wait, refuses both agent input methods, and takes the text through pane.send_text plus enter.

Herdr refuses agent.prompt and agent.send_keys for an agent it does not
drive itself, one that reports its own state instead of going through an
official integration: agent_not_ready, "agent w1:p1 is not an active named
agent" on 0.9.3. Crush is such an agent, so from a topic every plain
message answered "⚠️ herdr agent.prompt: herdr api agent_not_ready" and no
key reached the pane. Reads and waits work there, so the topic itself is
fine: status, screens and done posts all worked, only input was dead.

The refusal arrives before anything reaches the pane, so the input is sent
the way the agent would have received it:

- a prompt is typed with pane.send_text and submitted with enter through
  pane.send_keys;
- every key the plugin sends (/keys, /stop, /interrupt, the buttons) goes
  to the pane as well;
- line breaks are joined with spaces, since typed input submits nothing by
  itself and an enter inside the text would send the message in pieces;
- one INFO line per fallback with the pane and the lengths, never the text.

Only agent_not_ready takes this path. agent_blocked still reaches the
application, which types into the dialog's own free-text entry, and an
agent gone from Herdr still answers as before. A target that names an
agent rather than a pane has no pane to address, so its refusal reaches
the caller unchanged.

Verified live on Herdr 0.9.3 with Crush 0.98.0 in a Herdr pane: the same
pane answers agent.read and agent.wait, refuses both agent input methods,
and takes the text through pane.send_text plus enter.
@permgps

permgps commented Oct 10, 2026

Copy link
Copy Markdown
Owner

Thanks for tracking this down — input into Crush and other self-reporting agents being dead is a real gap, and the diagnosis (refusal arrives before anything reaches the pane) is clear and well tested.

One thing blocks the merge as it stands: the fallback runs regardless of the agent's status.

The whole app relies on Herdr answering agent_blocked to keep plain text out of dialogs (inbound.go CmdPrompt → answerBlocked, private_entryanswer.go prompt): text is never typed blind into a dialog, because the enter confirms the highlighted option, usually the approval. For an agent Herdr does not drive, the identity check ("not an active named agent") most likely comes before any state check, so a Crush that reported blocked would answer agent_not_ready, not agent_blocked. The gateway would then type the message and press enter into Crush's permission dialog — and the typed letters themselves may hit the dialog's shortcut keys. The PR text says agent_blocked still reaches the application, which holds for Claude Code but is not verified for a blocked Crush.

Could you move the decision up into the app layer?

  1. The gateway maps agent_not_ready (with a pane target) to a typed domain.ErrAgentNotDriven instead of falling back itself.
  2. The app types into the pane (pane.send_text + enter, reusing domain.DialogText for the line folding rather than a second replacer) only when the agent's status is not blocked. For a blocked not-driven agent the reply is the same as for other dialogs: buttons, ✏️ or /keys.
  3. Explicit keys (/keys, /stop, /interrupt, buttons) can keep falling back to pane.send_keys as now — the operator chose them.
  4. A test: blocked not-driven agent + plain text → nothing typed.

Smaller points:

  • An empty or whitespace-only prompt currently becomes a bare enter in the pane; DialogText returning "" should mean nothing is sent.
  • If Crush crashes without releasing its reported state, could Herdr keep treating the pane as an agent while it is back at a shell prompt? If so, the next message would run as a shell command. A cheap guard is checking pane.process_info before typing — worth a look if you can reproduce it.
  • The commit is authored as VPS user <user@localhost>; please re-author it with an address linked to your GitHub account.

Happy to merge once the status check is in.

@permgps

permgps commented Oct 14, 2026

Copy link
Copy Markdown
Owner

Thank you, this was a real gap: every message into a Crush topic failed with agent_not_ready.

Your commit landed in main as 8b2ac9e. I changed only the author, from VPS user <user@localhost> to your GitHub noreply address, so the hash differs and GitHub will not mark this PR as merged. That is why I am closing it by hand.

On top of it, 7fedd39 adds one guard. Herdr cannot answer agent_blocked for an agent it does not drive. So when Crush shows its permission dialog, agent.prompt still returns agent_not_ready, and the fallback typed the message plus enter into the dialog, where enter confirms the highlighted option. The same code also answers an agent still blocked during startup. Prompt now checks the status the agent reports through agent.list before typing:

  • blocked: ErrAgentBlocked, so the topic gets the same reply as at any other non-Claude dialog and nothing is typed;
  • idle, working, done: typed as one line (domain.DialogText) plus enter;
  • unknown status, pane not listed, failed lookup, empty text: the original refusal, nothing typed.

Keys and buttons still go to the pane unchecked, since that is how the dialog gets answered. Both commits ship in v0.17.2.

@permgps permgps closed this Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants