Files
cleveragents-core/docs/reference/tui_permission_question.md
freemo dd17d0f8e6
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / security (push) Has been cancelled
CI / quality (push) Has been cancelled
CI / benchmark-publish (push) Has been cancelled
CI / unit_tests (push) Has been cancelled
CI / integration_tests (push) Has been cancelled
CI / e2e_tests (push) Has been cancelled
CI / build (push) Has been cancelled
CI / helm (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / status-check (push) Has been cancelled
CI / benchmark-regression (push) Has been cancelled
docs(tui): add shell safety, permission question widget, and first-run docs
Add reference documentation for three new TUI features:

- docs/reference/tui_shell_safety.md: Full reference for the shell danger
  detection subsystem (DangerousPatternDetector, ShellDangerLevel,
  DangerousPattern, DangerousCommandWarning, DEFAULT_PATTERNS registry).
  Covers all built-in patterns across CRITICAL/HIGH/MEDIUM/LOW levels,
  usage examples, and custom pattern extension.

- docs/reference/tui_permission_question.md: Full reference for the inline
  PermissionQuestionWidget (issue #997). Documents InlinePermissionQuestion,
  PermissionRequestType, PermissionDecision, render_permission_question(),
  PermissionDecisionEvent, and PermissionQuestionWidget with key bindings
  and usage examples.

- docs/reference/tui.md: Extended with First-Run Experience section
  (ActorSelectionOverlay, first_run helpers), Inline Permission Questions
  section, shell danger detection note in Shell Mode, updated module table,
  and links to new reference pages.
2026-04-03 18:01:26 +00:00

7.5 KiB
Raw Permalink Blame History

TUI — Permission Question Widget

The Permission Question Widget renders inline permission requests directly in the conversation stream for single-file operations. For multi-file operations, the full PermissionsScreen is used instead.

Introduced in the [Unreleased] milestone (issue #997). See also tui.md for the overall TUI architecture.

Module: cleveragents.tui.widgets.permission_question


Overview

When an actor requests a single-file operation (write, delete, read, shell execution, or network access), the TUI renders a PermissionQuestionWidget inline in the conversation stream. The user can respond with single-key shortcuts or navigate with arrow keys.

Permission Required

The actor wants to write to:
  src/api/main.py

   a  Allow once
    A  Allow always (this session)
    r  Reject once
    R  Reject always (this session)

Press v to open full PermissionsScreen with diff view

Domain Models

PermissionRequestType

Module: cleveragents.domain.models.core.inline_permission_question

StrEnum classifying the type of operation being requested.

Value Description
file_write Actor wants to write to a file
file_delete Actor wants to delete a file
file_read Actor wants to read a file
shell_exec Actor wants to execute a shell command
network Actor wants to access the network

PermissionDecision

Module: cleveragents.domain.models.core.inline_permission_question

StrEnum for the user's decision on a permission request.

Value Description
allow_once Allow this specific operation once
allow_always Allow all operations from this actor for the session
reject_once Reject this specific operation once
reject_always Reject all operations from this actor for the session

InlinePermissionQuestion

Module: cleveragents.domain.models.core.inline_permission_question

Pydantic model representing a single-file permission request.

Field Type Default Description
file_path str Path of the file the actor wants to operate on (non-empty)
request_type PermissionRequestType Type of operation being requested
diff_content str "" Unified diff content showing proposed changes (may be empty)
actor_name str "actor" Name of the actor requesting permission

Properties:

Property Type Description
has_diff bool True when diff_content is non-empty after stripping whitespace

Methods:

Method Returns Description
description_line() str Human-readable one-line description (e.g. "The actor wants to write to:")

render_permission_question

Module: cleveragents.tui.widgets.permission_question

Pure rendering helper that produces the widget's text content.

def render_permission_question(
    question: InlinePermissionQuestion,
    selected_index: int = 0,
    *,
    show_diff: bool = False,
) -> str:
Parameter Type Default Description
question InlinePermissionQuestion The permission question to render
selected_index int 0 Index of the currently highlighted option (03)
show_diff bool False When True, appends the diff content below the options

Returns a multi-line string suitable for display in a Textual Static widget.


PermissionDecisionEvent

Module: cleveragents.tui.widgets.permission_question

Emitted when the user makes a permission decision. The host application listens for this event to act on the user's choice.

Attribute Type Description
question InlinePermissionQuestion The original permission question
decision PermissionDecision The decision made by the user

PermissionQuestionWidget

Module: cleveragents.tui.widgets.permission_question

Textual Static subclass that renders an inline permission question in the conversation stream.

Constructor

PermissionQuestionWidget(question: InlinePermissionQuestion, *args, **kwargs)

Properties

Property Type Description
question InlinePermissionQuestion The associated permission question
selected_index int Index of the currently highlighted option
decision PermissionDecision | None The decision if made, else None
open_full_screen bool True if the user pressed v to open PermissionsScreen

Navigation

Method Returns Description
move_up() None Move the selection cursor up by one option (wraps)
move_down() None Move the selection cursor down by one option (wraps)

Key Handling

Method Returns Description
handle_key(key) PermissionDecisionEvent | None Process a key press; returns a decision event when resolved

Supported keys:

Key Action
/ up Move selection up
/ down Move selection down
Enter Confirm the currently highlighted option
a Allow once
A Allow always (this session)
r Reject once
R Reject always (this session)
v Set open_full_screen = True (host opens PermissionsScreen)

Usage Example

from cleveragents.domain.models.core.inline_permission_question import (
    InlinePermissionQuestion,
    PermissionDecision,
    PermissionRequestType,
)
from cleveragents.tui.widgets.permission_question import (
    PermissionDecisionEvent,
    PermissionQuestionWidget,
    render_permission_question,
)

# Build the domain model
question = InlinePermissionQuestion(
    file_path="src/api/main.py",
    request_type=PermissionRequestType.FILE_WRITE,
    diff_content="--- a/src/api/main.py\n+++ b/src/api/main.py\n@@ -1 +1,2 @@\n+import logging\n",
    actor_name="openai/gpt-4o",
)

# Pure rendering (no Textual dependency)
text = render_permission_question(question, selected_index=0, show_diff=True)
print(text)

# Widget usage (requires Textual)
widget = PermissionQuestionWidget(question)

# Simulate key presses
event = widget.handle_key("down")   # moves cursor, returns None
event = widget.handle_key("enter")  # confirms selection, returns PermissionDecisionEvent
assert event is not None
assert event.decision == PermissionDecision.ALLOW_ALWAYS  # second option

# Single-key shortcut
event = widget.handle_key("r")
assert event.decision == PermissionDecision.REJECT_ONCE

# Open full screen
widget.handle_key("v")
assert widget.open_full_screen is True

Relationship to PermissionsScreen

The PermissionQuestionWidget is designed for single-file operations where a compact inline display is sufficient. When:

  • The operation involves multiple files, or
  • The user presses v to request a detailed diff view,

the host application should push the full PermissionsScreen overlay instead.