code-yeongyu / code-yeongyu/senpi

Async ask-user: clickable pending blocks, question queue, regular-mode mouse capture, herdr awareness

Closed
#1,645 37 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
429
Forks
98
Avg merge
5h 3m
Merged PRs (30d)
526

Description

## Summary
Async `ask_user_question` / `request_user_input` (`waitForAnswer: false`) shows a pending question as a 4-row widget above the editor, but the widget is not clickable, digits typed into the composer become a comment instead of an answer, a second async question cancels the first, the only chord (`alt+a`) is unusable on machines where Option+A is remapped, nothing signals arrival, and terminal workspace managers (herdr) never learn the agent is waiting for a decision.

## Reproduction (senpi main, TUI regular mode)
1. Run `senpi -e packages/coding-agent/test/fixtures/extensions/ask-user-fixture.ts` and type `/askq wait=false n=1`.
2. Click an option in the widget: nothing happens (no mouse capture in regular mode; `TuiMainScreen` never enables SGR reporting).
3. Type `1` and press Enter: the question resolves as `comment-submitted` with comment `"1"` (`interactive-mode.ts` composer text path), i.e. the model receives `The user responded: 1` plus `Unanswered: ...`.
4. Run `/askq wait=false n=1` again: the first question is cancelled (`showAsyncQuestion` supersedes the previous slot).
5. Inside a herdr pane, `herdr agent get ` reports `working`/`idle` while the question is pending (no `blocked` state).

## Expected (ideal state)
- Every pending async question is a first-class block above the editor: clicking an option answers it (single question -> sent immediately), clicking the header expands, "own answer" opens the editor; the same works in fullscreen mode and inside herdr panes.
- Regular mode captures the mouse ONLY while a question block is on screen (`terminal.mouse = whilePending`, with `always`/`off`), clicks are ignored whenever the frame position is uncertain (never a misfire), and nothing changes about scrolling/selection when no question is pending.
- N async questions queue FIFO; arrival never steals focus; `alt+down` cycles the shown question; `/answer` lists them, `/answer skip` dismisses the shown one, `/answer ` picks one.
- A digit typed into an empty composer selects that option (and submits when the question is a single single-select); the composer reply is explicitly bound to one question with a visible `reply to ` label; `alt+enter` sends as an ordinary message.
- `app.question.answer` defaults to `alt+up` (shared with dequeue: pending question wins) while `alt+a` keeps working.
- Arrival is signalled: terminal title `? `, one BEL (`askUser.bell`), hooks Notification kind `ask-user-asked`, and `herdr:blocked` events consumed by a builtin herdr reporter (source `custom:senpi`) so herdr shows the pane as blocked with the question header.
- The goal loop parks on the earliest pending question's deadline; answered questions render as a one-line `↳ : ` chip.
- Zero regression: every existing keyboard path (empty Enter, alt+a / å, Esc collapse, comment reply, wait-mode digit submit), RPC/app-server N-pending contracts, fullscreen mouse behavior and the default `tuiMode` stay unchanged.

## Actual
See Reproduction. Root facts: pi-tui has a shared mouse model (`packages/tui/src/tui.ts` `TuiMouseEvent`, `Container.handleMouse`) but SGR capture is enabled only by `TuiAltScreen`; the ask-user widget/component implement no `handleMouse`; `interactive-mode.ts` keeps one `asyncQuestion` slot; the composer submit path treats any non-slash text as the comment; the goal monitor tracks one aggregate ask-user deadline; hooks fire only on settlement.

## Root cause
Mouse capture and a queue were never designed for regular mode; the async widget advertises numbered options that only the expanded component understands; herdr integration is left to user-installed extensions that do not know about questions.

## Scope + acceptance criteria
Delivered as stacked PRs: (A) pi-tui shared `mouse-input.ts`, lease-based scoped capture, fail-closed frame anchor (viewport / cleared / private DECXCPR) and main-screen dispatch through `TuiMainScreen`; (B) request-id keyed question queue, keyboard model, arrival signals, goal-monitor min-deadline, answer chip; (G) builtin herdr reporter; (C) clickable widget/component, host leases, `terminal.mouse` setting, real-terminal QA matrix (regular short/tall frame, resize, fullscreen, off, wheel, herdr pane, tmux, right-click, zero-capture control). Acceptance: characterization suite pinning today's behavior stays green (two documented intentional changes), alt-screen tests keep their pass counts, pty QA proves click-to-answer in regular mode and `herdr agent get` reports `blocked` while pending.

## Related
Closed: #1573, #1533, #1623, #1620, #1641 (PRs #1576, #1544, #1625, #1621, #1642, #1630, #1562, #1599). Desktop counterpart: code-yeongyu/omo-desktop-app (pending-question inbox, answer-later, notifications) - linked from that issue.

Contributor guide

Open the contributing guide

Research direction

Start with packages/tui/src/tui.ts, TuiMainScreen, and interactive-mode.ts, then reproduce the behavior with packages/coding-agent/test/fixtures/extensions/ask-user-fixture.ts using the listed /askq commands. Review the stacked A, B, G, and C scope and characterization, alt-screen, and PTY QA requirements. Done means queued questions, regular-mode click and keyboard handling, arrival signals, goal-monitor behavior, and herdr blocked reporting meet the stated acceptance criteria without regressions.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, developer-experience, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
15/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.