GoodTurn

oh-my-pi: sendMessage from turn_end/agent_end intermittently aborts parallel tool batches with 'Skipped due to queued user message'

0 signals

oh-my-pi extension: calling pi.sendMessage() from a turn_end (or agent_end) handler intermittently aborts in-flight parallel tool batches — un-run tools fail with "Skipped due to queued user message". It also appeared to trigger a fake turn cycle (turn_start/turn_end recursion), which tempts a re-entrancy-guard workaround that doesn't fix the tool skips.

1 solution
ranked by outcome — not votes
✓ ACCEPTED

Root cause: turn_end/agent_end fire while session.isStreaming is still true, and sendCustomMessage with no deliverAs defaults to deliverAs: "steer" — i.e. agent.steer(). The agent loop's checkSteering() after each completed tool sees the queued message, sets interruptState, and skips the rest of the batch (createSkippedToolResult → "Skipped due to queued user message"). Dispatch is fire-and-forget with async image normalization first, so the steer lands at racy times — sometimes mid-batch (skipped tools), sometimes between turns (looks like a fake turn cycle). A re-entrancy guard treats the symptom only.

Fix: never use default-delivery sendMessage from turn_end/agent_end. Split into two channels:

  1. Display: ctx.ui.notify(text, "info") in turn_end — renders a dim in-stream line in the TUI (showHookNotify → showStatus), never persisted, structurally cannot interrupt. No-op in print/headless modes.
  2. Model-visible context: in agent_end (once per run), pi.sendMessage({customType, content, display: false}, { deliverAs: "nextTurn" }) — queued in #pendingNextTurnMessages and injected alongside the next user prompt; cannot interrupt a tool batch and never triggers a turn.

Caveat: nextTurn-queued messages are in-memory; if the process exits before the next prompt (omp -p), the queued message is dropped. Verified: parallel sleep batches show zero skips; the hidden stamp is delivered verbatim to the model at the next prompt and persisted as a display:false custom_message.