Approvals & questionnaires
When the agent needs a human decision, Mayfly answers with full-width pull-up panels. Panels mount by editor-slot replacement: the panel genuinely takes over the editor's dock slot — the editor leaves the tree (state intact), and only the two-row footer remains below the panel. The editor frame never peeks out from behind. Closing the panel restores the editor with focus and draft intact.
Approval panel
When a tool call needs authorization, a four-choice panel opens: an amber rule + ▶ Approve {tool}? title + numbered choices:
1. Allow once
2. Allow {tool} for this session
3. Reject
4. Reject with feedback(Number keys select directly; non-wrapping ↑↓ navigates and Enter confirms; the selected row carries a ▶ pointer.)
- Session-level remember — choice 2 records the tool in a session allowance table; later requests for the same agent + tool skip the panel entirely and pass through.
- Reject with feedback — choice 4 swaps the menu for an inline reason editor; submitting steers the agent with a user message (
User rejected …: <reason>), so the agent sees why. An empty reason is a plain Reject (no steering). - Escape rejects; an aborted request signal settles as cancelled.
- FIFO serialization — concurrent approval requests queue; one panel shows at a time.
Requests from other agents (not the one mounted in the UI) don't open a panel — they pass down the waterfall to the next answerer.
Questionnaire panel
ctx.userQuestions requests (clarifying questions and the like) open a per-question panel that focuses one question at a time:
- the panel title reads
Question {i} of {N}; the first row carries the progress{i}/{N}followed by every question's header (orQ{i+1}) —●current,✓answered,○unanswered; - the question tab strip uses non-wrapping Left / Right, with Enter descending into content; Tab / Shift-Tab is inert on the question tabs and switches semantic groups only inside content. Unsubmitted editor text is kept as a per-question draft and restored on return; Enter records the answer and jumps to the next unanswered question;
- single-choice via ↑↓ + Enter, multi-choice via Space + Enter; the cursor row is highlighted full-width (the Other row included);
- every question ends with a fixed Other pseudo-entry that opens a compact single-line input — a highlighted
> Answerrow; multi-line text is flattened to one line; - questions without options go straight to the same single-line input;
- the footer follows the state: editing shows confirmation and return operations; the option list adds
space toggleonly for multi-choice; - answering everything resolves automatically. Escape rejects the whole request — though inside the Other editor, Escape first saves the draft and returns to the option list; an aborted signal closes and rejects it too.
Questionnaire answers enter the session as user-visible content the model can see.
Multi-field forms
Custom provider onboarding and the typed y confirmation for the danger permission preset use a multi-field form panel:
- each field starts on one compact
label · hint: valuerow; the selected row carries→, and wrapped values continue under that row; - non-wrapping Up / Down moves between fields only in navigation and remains available for cursor movement while editing; typing starts editing, the first Enter on an untouched field enters edit mode, and Enter or a valid Tab while editing confirms and advances or submits the last field; invalid input stays active; Tab switches semantic groups only in content navigation, and Escape climbs one layer at a time;
- a validation error renders directly below the failing field without closing the panel; any edit clears it;
- values truncate to the panel width, so long pasted keys never break the frame.
Plan-review panel
When the agent calls exit_plan_mode to wrap up a plan, the review request opens in a dedicated question shape (the plan-review intent over ctx.userQuestions): a framed, scrollable window with the full rendered plan plus a numbered triple:
1. Approve
2. Reject
3. Revise (inline feedback editor)- the Revise row carries the feedback input; submitting answers with a decline-with-feedback (the harness folds it into "their feedback: …"), so the agent iterates on the plan with the notes; an empty submission equals a plain Reject;
- Approve/Reject settle directly; an aborted signal closes the panel with the cancellation code (
ASK_CANCELLED).
Plan mode itself enters/exits through the Shift+Tab three-state cycle (normal → plan → yolo, see Session modes); the footer's mode badge reflects it live.
Permission-preset panel
/permission opens the permission-preset selector (the same single-select list shape as /sessions and /preset): one row per preset (a named bundle of sandbox mode + approval policy), the active one marked ← current; Enter switches through the host's same write path, and a danger-level preset requires a typed y (no accidental switches). A bare invocation is intercepted by the input layer to open the panel; the command itself is registered by the upstream dsh-permission-presets, and argumented calls pass through.