Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion docs/TUI.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ terminal with nothing optional showing, the transcript floor is 12 rows
(`IDLE_TRANSCRIPT_FLOOR`); with an inset overlay open the floor drops to 8 rows (`OVERLAY_TRANSCRIPT_FLOOR`) so the log stays glanceable
underneath a permission prompt. When space is scarce, collapse follows a
fixed order — transient banners first, then settings/plugin notices, then
task/agents strips, then progress, then the prompt itself shrinks one
task/agents strips, then progress, then the INPUT REQUIRED strip (the last
optional row), then the prompt itself shrinks one
row at a time down to its 3-row base — never the transcript
(`COLLAPSE_ORDER` in `zones.ts`).

Expand Down Expand Up @@ -331,6 +332,31 @@ truncates first, then requester metadata and routing copy shorten, while the
waiting label and the count stay. Primary-session `ask_operator` prompts keep
their own overlay and never use this strip.

### INPUT REQUIRED strip

While a primary-session `ask_operator` question is unanswered, a one-row
**INPUT REQUIRED** strip sits on the prompt box (zone `input_required`):

```
◆ INPUT REQUIRED · operator answer needed · Which environment should I deploy to? (+1 more)
```

It keeps the owed answer in view while the operator scrolls, types, or has
the operator modal open. It is display only: the modal stays the one place
to answer, and the strip never changes focus, key routing, overlay order,
timeouts, or cancellation. Only gates the primary session's `ask_operator`
callback marks with `source: "primary-ask-operator"` raise it; permission
gates, MCP-trust gates, and worker `ask_director` questions never do. An
item's identity is its gate id, and it leaves when that gate settles
(answered, cancelled with Esc, timed out, aborted, or torn down); host
dispose clears every item (`src/tui/operator-input-required.ts`, fed from
`gate-wire.ts` and painted by `paintChrome` in `shell/chrome.ts`). One
question shows at a time, with `(+N more)` for the rest. At narrow widths the
Comment thread
bwachman marked this conversation as resolved.
Resolved
question truncates and then drops first, then the routing copy. Below 26
cells the `(+N more)` count is sliced away (cut at 25, gone at 16), leaving
only the label. When WORKER WAITING is also showing, it sits directly above
this strip.

### Transcript spawn_agent rows (history anchors)

`runtime-bridge` paints each `spawn_agent` call as a transcript stream row for
Expand Down
17 changes: 17 additions & 0 deletions src/tui/gate-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import type {
} from "../permission/types.js";
import type { OperatorResult } from "../agent/tools.js";

/** Provenance minted only by the primary session's ask_operator callback. */
export const PRIMARY_ASK_OPERATOR_SOURCE = "primary-ask-operator" as const;

export type OperatorGateSource = typeof PRIMARY_ASK_OPERATOR_SOURCE;

/** Fail-closed settle when no approval UI can bind the operator's accept. */
export const APPROVAL_UNAVAILABLE_MESSAGE =
"no approval UI available; request denied" as const;
Expand All @@ -27,6 +32,18 @@ export interface OperatorGateEvent {
* queue — the modal cannot outlive a tool that already finished.
*/
signal?: AbortSignal;
/**
* Optional display provenance minted by the session emitter, never inferred
* from an operator question by the TUI.
*/
source?: OperatorGateSource;
}

/** Only an explicitly marked primary ask may enter input-required chrome. */
export function isPrimaryAskOperatorEvent(
event: OperatorGateEvent,
): event is OperatorGateEvent & { readonly source: OperatorGateSource } {
return event.source === PRIMARY_ASK_OPERATOR_SOURCE;
}

export interface PermissionGateEvent {
Expand Down
Loading
Loading