Files
cleveragents-core/docs/specification/tui.md

126 KiB
Raw Permalink Blame History

TUI

TUI Architecture Overview

!!! adr "Architecture Decision" The TUI architecture, framework selection (Textual >= 1.0), screen hierarchy, TuiMaterializer integration, and A2A event subscription model are defined in ADR-044: TUI Architecture and Framework.

The TUI (Terminal User Interface) is the second Presentation-layer surface for CleverAgents, built on Textual (>= 1.0). It provides real-time plan monitoring, multi-session management, interactive decision tree navigation, and rich conversation with actors — capabilities impractical in the stateless CLI.

The TUI communicates with the Application layer exclusively through A2A (ADR-026). It subscribes to A2A events for real-time updates and uses the existing Output Rendering Framework (ADR-021) via a TuiMaterializer that maps ElementHandle events to Textual widget operations — enabling all CLI command producers to render in the TUI without modification.

Key architectural principles:

  • Direct-to-chat launch — no launcher screen; opens directly to the main chat interface
  • Right-side collapsible sidebar — three states cycled by shift+tab: hidden → visible → fullscreen
  • Multi-session tabs — independent sessions with separate personas, conversations, and A2A bindings
  • Escape-cascading navigationescape always moves toward the main screen from any state
  • Keyboard-first — every operation achievable without a mouse; mouse is supplementary
  • Single UI codebase — the same Textual widget tree serves standalone TUI, Web (via Textual Web), and IDE plugin

MainScreen Layout — Sidebar Hidden

When the sidebar is hidden, the conversation takes the full terminal width:

┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│═══════════════════════════════════════════ ◆ ═══════════════════════════════════════════     │
│ Session 1Session 2Session 3                                                          │
│              ┗━━━━━━━━━━━━┛                                                                  │
├──────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                              │
│  You                                                          14:01                         │
│  Can you review the auth module in the                                                      │
│  API service?                                                                               │
│  @project:api-service:src/auth/handler.py                                                   │
│                                                                                             │
│                                                                                             │
│  Actor                                                       14:02                          │
│  I'll review the authentication handler.                                                    │
│  Let me analyze the code structure and                                                      │
│  identify potential issues.                                                                 │
│                                                                                             │
│   🔧 local/code-analysis                                                                  │
│                                                                                             │
│  Found 3 issues in the auth module:                                                         │
│                                                                                             │
│  1. **Missing rate limiting**                                                               │
│  2. **JWT token** not validated                                                             │
│  3. **Session cleanup** missing                                                             │
│                                                                                             │
│   🔧 local/file-read                                                                      │
│   ┌─ src/auth/handler.py ─────────────┐                                                     │
│   │  45 │ def login(self, req):      │                                                      │
│   │  46 │     creds = extract(req)   │                                                      │
│   │  47 │     return auth(creds)     │                                                      │
│   └──────────────────────────────────┘                                                      │
│                                                                                              │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ Agent connected                                                                              │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ ┌─────────────────────────────────────────────────────────────────────────────────────┐      │
│ │  What would you like to do?                                                        │      │
│ │   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                                                 │      │
│ └─────────────────────────────────────────────────────────────────────────────────────┘      │
│ feature-devclaude-4-sonnetthink: high2 projects │ $0.12                             │
├──────────────────────────────────────────────────────────────────────────────────────────────┤
│ F1 Help │ shift+tab Sidebar │ tab Persona │ ctrl+tab Preset │ ctrl+s Sessions │ ctrl+q Quit  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Key elements in this layout:

  • Throbber (top edge): rainbow gradient bar, visible only when the actor is processing. Collapses to zero height when idle.
  • Session Tabs (below throbber): tab bar with underline indicator on the active session. Hidden when only one session exists. Session labels show state icons: (actor working), (awaiting input), plain (idle).
  • Conversation (center): scrollable message stream with block cursor navigation.
  • Prompt (bottom): PromptTextArea with mode-dependent symbol ( normal, $ shell, ☰ multi-line), overlays for @ references and / commands, and the PersonaBar.
  • Footer: context-sensitive hotkey reference, always visible.

MainScreen Layout — Sidebar Visible

When the sidebar is visible (shift+tab from hidden), it docks to the right at 32-40 chars wide:

┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│═══════════════════════════════════════════ ◆ ═══════════════════════════════════════════     │
│ Session 1Session 2Session 3                                                          │
│              ┗━━━━━━━━━━━━┛                                                                  │
├─────────────────────────────────────────────────────────────┬────────────────────────────────┤
│                                                             │ ▼ PLANS                        │
│  You                                  14:01                │ ┌────────────────────────────┐ │
│  Can you review the auth module in the                     │ │  fix-auth-bug             │ │
│  API service?                                              │ │   Phase: Execute ●●●      │ │
│  @project:api-service:src/auth/handler.py                  │ │   Profile: trusted         │ │
│                                                            │ │   Actor: claude-4-sonnet   │ │
│                                                            │ │   Cost: $0.08              │ │
│  Actor                                  14:02              │ │                            │ │
│  I'll review the authentication handler.                   │ │  refactor-models          │ │
│  Let me analyze the code structure and                     │ │   Phase: Strategize ○○○   │ │
│  identify potential issues.                                │ │   Profile: cautious        │ │
│                                                            │ │   Depth: 2 (3 subplans)    │ │
│   🔧 local/code-analysis                                 │ │   Actor: gpt-4o            │ │
│                                                            │ │   ├─ users   (execute)     │ │
│  Found 3 issues in the auth module:                        │ │   ├─ orders  (strategize)  │ │
│                                                            │ │   └─ auth    (pending)     │ │
│  1. **Missing rate limiting**                              │ │                            │ │
│  2. **JWT token** not validated                            │ │  update-deps              │ │
│  3. **Session cleanup** missing                            │ │   Phase: Idle              │ │
│                                                            │ │   Profile: auto            │ │
│   🔧 local/file-read                                     │ └────────────────────────────┘ │
│   ┌─ src/auth/handler.py ─────────────┐                    │                                │
│   │  45 │ def login(self, req):       │                     │ ▼ PROJECTS                     │
│   │  46 │     creds = extract(req)    │                     │ ┌────────────────────────────┐ │
│   │  47 │     return auth(creds)      │                     │ │ ◆ cleveragents        [2p] │ │
│   └───────────────────────────────────┘                     │ │ ◆ api-service         [1p] │ │
│                                                             │ │   frontend-app        [0p] │ │
│─────────────────────────────────────────────────────────────│ │   infra-terraform     [0p] │ │
│ Agent connected                                             │ └────────────────────────────┘ │
│─────────────────────────────────────────────────────────────│                                │
│ ┌────────────────────────────────────────────────────────┐  │                                │
│ │  What would you like to do?                           │  │                                │
│ │   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                    │  │                                │
│ └────────────────────────────────────────────────────────┘  │                                │
│ feature-devclaude-4-sonnetthink: med2 proj │ $0.12 │                                │
├─────────────────────────────────────────────────────────────┴────────────────────────────────┤
│ F1 Help │ shift+tab Sidebar │ tab Persona │ ctrl+tab Preset │ ctrl+s Sessions │ ctrl+q Quit  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Key elements in this layout:

  • Throbber (top edge): rainbow gradient bar, visible only when the actor is processing. Collapses to zero height when idle.
  • Session Tabs (below throbber): tab bar with underline indicator on the active session. Hidden when only one session exists. Session labels show state icons: (actor working), (awaiting input), plain (idle).
  • Conversation (center): scrollable message stream with block cursor navigation.
  • Sidebar (right, 32-40 chars wide): Plans panel and Projects panel in collapsible containers.
  • Prompt (bottom of left panel): same prompt components as hidden mode, constrained to the conversation column width.
  • PersonaBar (below prompt): shows persona name, actor, preset, scope, and cost.

MainScreen Layout — Sidebar Fullscreen

Fullscreen mode (shift+tab from visible) covers the entire terminal for plan/project/persona management:

┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│ PLANS & PROJECTS BROWSER                                                                     │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ PLANSPROJECTS                                     │
│ ┌───────────────────────────────────────────┐ │ ┌──────────────────────────────────────────┐ │
│ │                                           │ │ │                                          │ │
│ │ [x] ► fix-auth-bug                        │ │ │ [x] cleveragents                         │ │
│ │     Phase: Execute ●●●                   │ │ │     Namespace: local/                    │ │
│ │     State: in_progress                    │ │ │     Resources: 5                         │ │
│ │     Profile: trusted                      │ │ │     Plans: 2 active                      │ │
│ │     Actor: anthropic/claude-4-sonnet      │ │ │     Invariants: 3                        │ │
│ │     Started: 2m ago    Cost: $0.08        │ │ │     Validations: 2                       │ │
│ │     Decisions: 6 (4 done, 2 pending)      │ │ │                                          │ │
│ │                                           │ │ │ [ ] api-service                          │ │
│ │ [ ] refactor-models                       │ │ │     Namespace: local/                    │ │
│ │     Phase: Strategize ○○○                │ │ │     Resources: 3                         │ │
│ │     State: in_progress                    │ │ │     Plans: 1 active                      │ │
│ │     Profile: cautious                     │ │ │     Invariants: 1                        │ │
│ │     Actor: openai/gpt-4o                  │ │ │     Validations: 4                       │ │
│ │     Started: 5m ago    Cost: $0.03        │ │ │                                          │ │
│ │     Decisions: 3 (2 done, 1 active)       │ │ │ [ ] frontend-app                         │ │
│ │     Subplans: 3 (depth 2)                 │ │ │     Namespace: local/                    │ │
│ │       ├─ users    Execute ●●●            │ │ │     Resources: 2                         │ │
│ │       ├─ orders   Strategize ○○○         │ │ │     Plans: 0                             │ │
│ │       └─ auth     Pending                 │ │ │                                          │ │
│ │                                           │ │ │ [ ] infra-terraform                      │ │
│ │ [ ] update-deps                           │ │ │     Namespace: local/                    │ │
│ │     Phase: Idle                           │ │ │     Resources: 8                         │ │
│ │     Profile: auto                         │ │ │     Plans: 0                             │ │
│ │                                           │ │ │                                          │ │
│ └───────────────────────────────────────────┘ │ └──────────────────────────────────────────┘ │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ PERSONA CYCLE LIST (tab order)                                                               │
│ ┌──────────────────────────────────────────────────────────────────────────────────────┐     │
│ │  1. feature-dev (claude-4-sonnet, 2 projects)                                        │     │
│ │  2. reviewer (gpt-4o, 1 project)                                                     │     │
│ │  3. infra-admin (claude-4-opus, 1 project)                                           │     │
│ │  [+] Add current selection as persona...                                             │     │
│ └──────────────────────────────────────────────────────────────────────────────────────┘     │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ Selected: 1 plan, 1 project                                                                  │
│ space Select │ enter Details │ ctrl+p Create Persona │ / Search │ d Delete │ esc Back        │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

In fullscreen mode:

  • Selection mode: space toggles selection on the highlighted plan or project. Selected items show [x], deselected show [ ].
  • Persona creation: ctrl+p opens the PersonaEditorModal with the selected plans and projects pre-populated as the scope.
  • Detail inspection: enter on a plan opens the PlanDetailModal; enter on a project opens the ProjectDetailModal.
  • Persona cycle list: The bottom panel shows the ordered list of personas that tab cycles through. Users can reorder (ctrl+up/ctrl+down), remove (d), or add new personas from the current selection.

Sidebar State Transitions

State Layout Input Focus Content
Hidden Sidebar display: none; conversation full width Prompt retains focus No sidebar content visible
Visible Sidebar docked right, 32-40 chars wide Prompt retains focus; ctrl+b focuses sidebar Plans and Projects panels in collapsible containers
Fullscreen Covers entire screen Sidebar takes input focus Extended details, selection mode, persona management

State transitions:

Hidden ──shift+tab──► Visible ──shift+tab──► Fullscreen
  ▲                                              │
  └──────────────── escape (×1-2) ───────────────┘

Persona System

!!! adr "Architecture Decision" The persona abstraction, argument preset cycling, scope behavior, and persona lifecycle are defined in ADR-045: TUI Persona System.

A persona is a TUI-only abstraction that bundles:

  1. Actor reference — a namespaced actor name (e.g., anthropic/claude-4-sonnet)
  2. Base arguments — default arguments passed to the actor on every invocation
  3. Scoped projects — projects always included in the session's context
  4. Scoped plans — plans always included in the session's context
  5. Argument presets — named argument overrides cycled with ctrl+tab
  6. Display metadata — short name, accent color, description

Personas are stored as YAML files in ~/.config/cleveragents/personas/ and are strictly a Presentation-layer concept — they never appear in the domain model, A2A protocol, or database schema.

PersonaBar

The PersonaBar always shows the current state:

┌──────────────────────────────────────────────────────────────────────┐
│  What would you like to do?                                         │
│   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                                  │
└──────────────────────────────────────────────────────────────────────┘
  feature-devclaude-4-sonnetthink: high2 projects       │ $0.12
  ─────────── ─ ─────────────── ─ ─────────── ─ ──────────────── ─ ─────
  persona       actor             preset        scope              cost
PersonaBar Segment Style Updates When
Persona name $text-primary on $primary 10% bg tab cycle
Actor name $text-secondary tab cycle
Preset label $text-warning (non-default) / $text-muted (default) ctrl+tab cycle
Scope indicator $text-muted Persona change or /scope:add/remove
Session cost $secondary 70%, right-aligned After each actor response

Persona Cycling

tab cycles through personas in the configured cycle list. ctrl+tab cycles through the current persona's argument presets:

tab:       persona_1 → persona_2 → persona_3 → persona_1 → ...
ctrl+tab:  default → think: high → think: max → quick → default → ...

When cycling, the PersonaBar updates immediately. The actor binding for the session is updated — subsequent prompts use the new persona's actor and scope.

First-Run Experience

On first launch (no personas configured), a centered overlay guides actor selection:

┌──────────────────────────────────────────────────────────────────────┐
│ Welcome to CleverAgents                                              │
│                                                                      │
│ Select an actor to get started:                                      │
│                                                                      │
│    anthropic/claude-4-sonnet   (recommended)                        │
│     anthropic/claude-4-opus                                          │
│     openai/gpt-4o                                                    │
│     openai/o3                                                        │
│     google/gemini-2                                                  │
│     / to search...                                                   │
│                                                                      │
│ A default persona will be created with this actor.                   │
│ You can add more actors and personas later.                          │
│                                                                      │
│──────────────────────────────────────────────────────────────────────│
│ enter Select │ j/k Navigate │ / Search                               │
└──────────────────────────────────────────────────────────────────────┘

Selection creates a "default" persona with the chosen actor and auto-generated argument presets (thinking effort levels are auto-detected from the actor's argument schema). Subsequent launches restore the last active persona.

Reference and Command System

!!! adr "Architecture Decision" The @ reference notation grammar, fuzzy search algorithm, / command system, ! shell mode, and ACMS integration are defined in ADR-046: TUI Reference and Command System.

The TUI prompt supports three input modes, each activated by a distinct prefix:

First Character Mode Prompt Symbol Sent To Overlay
(any other) Normal Actor via A2A
@ (inline) Normal + Reference Actor (with CRP directives) ReferencePickerOverlay
/ Command / TUI command processor SlashCommandOverlay
! Shell $ Host OS subprocess

Reference Picker (@)

Typing @ anywhere in a normal-mode prompt activates the Reference Picker — a fuzzy-search overlay for projects, plans, and resources:

┌─ Reference Picker ───────────────────────────────────────────────────┐
│ @hand                                                                │
│ ──────────────────────────────────────────────────────────────────── │
│  PROJECT  api-service:src/auth/handler.py                            │
│           local/api-service • Python • 245 lines                     │
│                                                                      │
│  PROJECT  cleveragents:git_dir/src/cli/commands/handler.py           │
│           local/cleveragents • Python • 189 lines                    │
│                                                                      │
│  PROJECT  api-service:src/auth/middleware/handler_base.py            │
│           local/api-service • Python • 67 lines                      │
│                                                                      │
│  PLAN     fix-auth-handler (01HXM8C2...)                             │
│           Phase: Execute • Actor: claude-4-sonnet                    │
│                                                                      │
│ ──────────────────────────────────────────────────────────────────── │
│ enter Select │ tab Tree │ ctrl+p Projects │ ctrl+l Plans             │
└──────────────────────────────────────────────────────────────────────┘

Resolved references use a canonical notation: @project:local/api-service:src/auth/handler.py. Fuzzy input like @handler.py auto-expands to the canonical form on selection.

Resolved @ references are translated into CRP (Context Request Protocol) directives before the prompt reaches the actor — they semantically direct the ACMS to prioritize referenced resources in context assembly.

Reference Picker — Tree Browser Mode

Pressing tab in the Reference Picker switches to tree browser mode for hierarchical navigation:

┌─ Reference Picker (Tree) ────────────────────────────────────────────┐
│                                                                      │
│  ▼ Projects                                                          │
│    ▼ local/cleveragents                                              │
│      ▼ git_dir (git-checkout)                                        │
│        ▸ src/                                                        │
│        ▸ tests/                                                      │
│        ▸ docs/                                                       │
│        ─ pyproject.toml                                              │
│        ─ README.md                                                   │
│      ▸ database (sqlite-db)                                          │
│    ▸ local/api-service                                               │
│    ▸ local/frontend-app                                              │
│  ▼ Plans                                                             │
│    ▸ fix-auth-bug (01HXM8C2)                                         │
│    ▸ refactor-models (01HXM9D3)                                      │
│                                                                      │
│ enter Select │ tab Search │ space Expand │ / Filter                  │
└──────────────────────────────────────────────────────────────────────┘

Slash Command Overlay (/)

Typing / as the first character activates command mode. Commands are TUI operations executed locally — they are not sent to the actor.

Slash Command Overlay
┌─ Commands ─────────────────────────────────────────────────────────────┐
│ /se                                                                    │
│ ───────────────────────────────────────────────────────────────────────│
│  /session:create         Create a new session tab                      │
│  /session:list           Show all sessions                             │
│  /session:show           Show session details                          │
│  /session:switch         Switch to session by ID                       │
│  /session:close          Close current session                         │
│  /session:delete         Delete a saved session                        │
│  /session:rename         Rename current session                        │
│  /session:export         Export session to file                        │
│  /session:import         Import session from file                      │
│  /settings               Open settings screen                          │
│                                                                        │
│ enter Execute │ tab Complete │ escape Dismiss                          │
└────────────────────────────────────────────────────────────────────────┘

TUI slash commands mirror CLI command patterns where applicable. The CLI uses agents <noun> <verb>; the TUI uses /<noun>:<verb>. Commands that exist in both CLI and TUI use the same verb names. TUI-only commands (persona, scope, TUI utilities) have no CLI equivalent.

Complete Command Reference

Session Commandsmirrors CLI agents session <verb>

Command Arguments Description
/session:create [--persona <name>] Create a new session tab
/session:list Display all sessions
/session:show <id> <id> Show session details
/session:switch <id> <id> Switch to session by ID or tab index
/session:close [--force] Close the current session tab
/session:delete <id> <id>, [--yes/-y] Delete a saved session
/session:rename <name> <name> Rename the current session
/session:export [path] [path] Export session to JSON
/session:import <path> <path> Import session from JSON

Persona CommandsTUI-only

Command Arguments Description
/persona:list Display all personas
/persona:set <name> <name> Switch to persona
/persona:create Open PersonaEditorModal
/persona:edit [name] [name] Edit persona
/persona:delete <name> <name> Delete persona
/persona:export <name> <name> Export persona YAML
/persona:import <path> <path> Import persona YAML

Scope CommandsTUI-only

Command Arguments Description
/scope:add <ref> <ref> Add project/plan to session scope
/scope:remove <ref> <ref> Remove from session scope
/scope:clear Clear session-level scope additions
/scope:show Show effective scope

Plan Commandsmirrors CLI agents plan <verb>

Command Arguments Description
/plan:use <action> <action>, [projects...], [--arg/-a <key=value>] Start a new plan
/plan:list [--phase] [--state] [--project] [--action] List plans
/plan:status [id] [id] Show plan status
/plan:tree <id> <id>, [--show-superseded] [--depth] Show decision tree
/plan:execute [id] [id] Execute a plan
/plan:apply [id] [id] Apply a completed plan
/plan:cancel <id> <id>, [--reason/-r] Cancel a plan
/plan:diff <id> <id>, [--correction] Show plan diff
/plan:correct <did> <did>, --mode, --guidance Correct a decision
/plan:resume <id> <id>, [--dry-run] Resume a plan
/plan:revert <id> <id>, [--to-phase] Revert a plan
/plan:rollback <id> <cp> both required Rollback to checkpoint
/plan:explain <did> <did>, [--show-context] Explain a decision
/plan:errors <id> <id> Show plan errors
/plan:artifacts <id> <id> Show plan artifacts
/plan:inspect [id] [id] Open PlanDetailModal

Project Commandsmirrors CLI agents project <verb>

Command Arguments Description
/project:list [--namespace/-n] List all projects
/project:create <name> <name>, [--description/-d] [--resource/-r] Create project
/project:show <name> <name> Show project details
/project:delete <name> <name>, [--yes/-y] Delete project
/project:inspect <name> <name> Open ProjectDetailModal
/project:context:show <name> <name> Show project context config

Actor, Resource, Config, Tool, Skill, Invariant, Profile Commandsmirrors CLI

Command Description
/actor:list, /actor:show, /actor:set-default Actor management
/resource:list, /resource:show, /resource:tree, /resource:inspect Resource management
/config:list, /config:get, /config:set Configuration management
/tool:list, /tool:show Tool management
/skill:list, /skill:show Skill management
/invariant:list, /invariant:add, /invariant:remove Invariant management
/profile:list, /profile:show Automation profile management
/context:inspect, /context:set, /context:simulate Context management (inspect state, set policy, simulate assembly)

TUI Utility CommandsTUI-only

Command Arguments Description
/clear Clear conversation display
/theme [name] [name] Switch color theme
/settings Open SettingsScreen
/help [command] [command] Show help
/about Show version and system info
/debug Toggle debug mode

Shell Mode (!)

Shell mode provides direct OS command execution from the TUI prompt. Typing ! as the first character changes the prompt symbol to $ and enables shell syntax highlighting, file/directory tab completion, and separate shell history.

┌────────────────────────────────────────────────────────────────────┐
│ $ git status                                                       │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ On branch main                                                 │ │
│ │ Your branch is up to date with 'origin/main'.                  │ │
│ │                                                                │ │
│ │ Changes not staged for commit:                                 │ │
│ │   modified:   src/auth/handler.py                              │ │
│ │   modified:   src/auth/middleware.py                           │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ (exit code: 0)                                                     │
└────────────────────────────────────────────────────────────────────┘

Shell results appear as ShellResult blocks in the conversation stream with a $primary left border and $foreground 4% background. Long-running commands stream output in real-time; ctrl+c sends SIGINT.

Plan Detail Modal

The PlanDetailModal shows a plan's decision tree alongside decision details:

┌───────────────────────────────────── Plan: fix-auth-bug ─────────────────────────────────────┐
│ ID: 01HXM8C2ABCD     Phase: Execute       State: in_progress                                 │
│ Profile: trusted      Actor: claude-4-sonnet   Started: 2m ago    Cost: $0.08                │
│ Projects: local/api-service                                                                  │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ DECISION TREEDECISION DETAIL                                   │
│ ┌──────────────────────────────────────┐ │ ┌────────────────────────────────────────┐        │
│ │                                      │ │ │ Decision: D3                           │        │
│ │ ▼ D1 strategy_choice                │ │ │ Type: implementation_choice            │        │
│ │   ├─ D2 invariant_enforced          │ │ │ Phase: Execute                         │        │
│ │   ├─▶D3 implementation_choice      │ │ │                                        │        │
│ │   │   ├─ D4 tool_invocation         │ │ │ Question:                              │        │
│ │   │   ├─ D5 tool_invocation        │ │ │ How should rate limiting be            │        │
│ │   │   └─ D6 tool_invocation        │ │ │ implemented on the login endpoint?     │        │
│ │   ├─ D7 resource_selection         │ │ │                                        │        │
│ │   └─ D8 subplan_spawn              │ │ │ Chosen: Token bucket algorithm         │        │
│ │                                      │ │ │ with Redis backing store               │        │
│ │ / Search decisions...                │ │ │                                        │        │
│ │                                      │ │ │ Alternatives:                          │        │
│ └──────────────────────────────────────┘ │ │ • Sliding window counter               │        │
│                                          │ │ • Fixed window counter                 │        │
│ Legend:                                  │ │ • IP-based blocking                    │        │
│   completed   active                  │ │                                        │        │
│   pending    failed                   │ │ Confidence: 0.85                       │        │
│   superseded                            │ │ Rationale: Token bucket provides       │        │
│                                          │ │ fine-grained control with burst        │        │
│ ───────────────────────────────────────  │ │ allowance and Redis enables            │        │
│ Phase: ●●●                              │ │ distributed rate limiting...           │        │
│ Decisions: 8 total                       │ │                                        │        │
│  4 completed, 1 active, 3 pending        │ │ Dependencies:                          │        │
│                                          │ │  ← D1 (strategy_choice)                │        │
│                                          │ │  → D4, D5, D6 (tool_invocations)       │        │
│                                          │ │                                        │        │
│                                          │ │ Context Snapshot: 2.3 KB               │        │
│                                          │ └────────────────────────────────────────┘        │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ j/k Nav │ enter Inspect │ c Correct │ d Diff │ h History │ t Toggle │ / Search               │
│ s Superseded │ x Expand all │ z Collapse all │ escape Close                                  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Decision Tree Interaction

Key Action
j / down Move to next decision
k / up Move to previous decision
enter Select decision → show detail in right panel
space Expand/collapse subtree
c Correct selected decision (opens correction dialog)
d Show diff for selected decision
h Show decision history / superseded chain
t Toggle detail panel visibility
x Expand all nodes
z Collapse all nodes
s Show superseded decisions (hidden by default)
/ Search decisions by type, content, or ID
escape Close modal

Project Detail Modal

The ProjectDetailModal shows a project's resource DAG, invariants, and running plans:

┌───────────────────────────────── Project: local/api-service ─────────────────────────────────┐
│ Namespace: local/        Resources: 3          Plans: 1 active                               │
│ Invariants: 1            Validations: 4        Automation: trusted                           │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ RESOURCESDETAIL                                            │
│ ┌──────────────────────────────────────┐ │ ┌────────────────────────────────────────┐        │
│ │                                      │ │ │ Resource: api-repo                     │        │
│ │ ▼ api-repo (git-checkout)            │ │ │ Type: git-checkout                     │        │
│ │   ├─ src/ (fs-directory)             │ │ │ ID: 01HXYZ...                          │        │
│ │   │   ├─ auth/                       │ │ │                                        │        │
│ │   │   │   ├─ handler.py              │ │ │ URL: git@github.com:org/api.git        │        │
│ │   │   │   ├─ middleware.py           │ │ │ Branch: main                           │        │
│ │   │   │   └─ limiter.py              │ │ │ Commit: a1b2c3d (2h ago)               │        │
│ │   │   ├─ models/                     │ │ │                                        │        │
│ │   │   └─ routes/                     │ │ │ Sandbox: git_worktree                  │        │
│ │   ├─ tests/ (fs-directory)           │ │ │ Checkpoint: enabled                    │        │
│ │   └─ pyproject.toml                  │ │ │                                        │        │
│ │ ─ api-db (container-instance)        │ │ │ Children: 2 (src/, tests/)             │        │
│ │ ─ api-cache (container-instance)     │ │ │ Validations: 2 attached                │        │
│ │                                      │ │ │                                        │        │
│ └──────────────────────────────────────┘ │ └────────────────────────────────────────┘        │
│                                          │                                                   │
│ INVARIANTSRUNNING PLANS                                     │
│ ┌──────────────────────────────────────┐ │ ┌────────────────────────────────────────┐        │
│ │ 1. All API endpoints must have       │ │ │ fix-auth-bug  Execute ●●●  $0.08      │        │
│ │    rate limiting (project-level)     │ │ │                                        │        │
│ └──────────────────────────────────────┘ │ └────────────────────────────────────────┘        │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ j/k Nav │ enter Drill │ i Invariants │ v Validations │ p Plans │ / Search │ esc Close        │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Persona Editor Modal

The PersonaEditorModal is used for creating and editing personas:

┌─────────────────────────────────────── Create Persona ───────────────────────────────────────┐
│                                                                                              │
│ Name:        ┌──────────────────────────────────────┐                                        │
│              │ feature-dev                          │                                        │
│              └──────────────────────────────────────┘                                        │
│                                                                                              │
│ Description: ┌────────────────────────────────────────────────────────┐                      │
│              │ Feature development with Claude on main projects       │                      │
│              └────────────────────────────────────────────────────────┘                      │
│                                                                                              │
│ Actor:       ┌──────────────────────────────────────────┐                                    │
│              │  anthropic/claude-4-sonnet              │ ▼                                  │
│              └──────────────────────────────────────────┘                                    │
│                                                                                              │
│ Color:   ● $primary   ○ $secondary   ○ $success   ○ $warning   ○ $error                      │
│                                                                                              │
│ ── Base Arguments (from actor schema) ──────────────────────────────────────                 │
│ thinking_effort:  ┌──────────┐   temperature:  ┌──────────┐                                  │
│                   │ medium   │ ▼               │ 0.7      │                                  │
│                   └──────────┘                 └──────────┘                                  │
│ max_tokens:       ┌──────────┐                                                               │
│                   │ 16384    │                                                               │
│                   └──────────┘                                                               │
│                                                                                              │
│ ── Scoped Projects ─────────────────────────────────────────────────────────                 │
│ [x] local/cleveragents        [x] local/api-service                                          │
│ [ ] local/frontend-app        [ ] local/infra-terraform                                      │
│                                                                                              │
│ ── Scoped Plans ────────────────────────────────────────────────────────────                 │
│ [ ] fix-auth-bug (01HXM8C2)   [ ] refactor-models (01HXM9D3)                                 │
│                                                                                              │
│ ── Argument Presets (ctrl+tab cycling) ─────────────────────────────────────                 │
│ ┌────────────────────────────────────────────────────────────────────────────────────┐       │
│ │  1. default      │ (base arguments)                                                │       │
│ │  2. think: high  │ thinking_effort=high                                            │       │
│ │  3. think: max   │ thinking_effort=max, temperature=0.3                            │       │
│ │  4. quick        │ thinking_effort=low, max_tokens=4096                            │       │
│ │  [+] Add preset                                                                    │       │
│ └────────────────────────────────────────────────────────────────────────────────────┘       │
│                                                                                              │
│ Cycle Order: ┌──────┐  (0 = not in tab cycle)                                                │
│              │  1   │                                                                        │
│              └──────┘                                                                        │
│                                                                                              │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ tab Next field │ enter Save │ escape Cancel                                                  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Permissions Screen

When a tool requests permission to modify resources, the PermissionsScreen shows the requested changes with a diff view:

┌───────────────────────────────────── Permission Request ─────────────────────────────────────┐
│                                                                                              │
│ local/file-write wants to modify 3 files in local/api-service:                               │
│                                                                                              │
│ ┌─────────────────────────────┐ ┌──────────────────────────────────────────────────────┐     │
│ │Files (3 changes):           │ │src/auth/handler.py                                   │     │
│ │                             │ │──────────────────────────────────────────────────────│     │
│ │ src/auth/handler.py [M]    │ │ @@ -45,6 +45,12 @@                                   │     │
│ │  src/auth/middleware.py [M] │ │  def login(self, request):                           │     │
│ │  src/auth/limiter.py   [A]  │ │ +    @rate_limit(max_calls=5, period=60)             │     │
│ │                             │ │      credentials = self.extract(request)             │     │
│ │                             │ │ -    return self.authenticate(credentials)           │     │
│ │                             │ │ +    result = self.authenticate(credentials)         │     │
│ │                             │ │ +    self.log_attempt(request, result)               │     │
│ │                             │ │ +    return result                                   │     │
│ │                             │ │                                                      │     │
│ │                             │ │ @@ -78,3 +84,8 @@                                    │     │
│ │                             │ │  def logout(self, request):                          │     │
│ │                             │ │ +    self.cleanup_session(request)                   │     │
│ │                             │ │      return Response(status=200)                     │     │
│ │                             │ │                                                      │     │
│ └─────────────────────────────┘ └──────────────────────────────────────────────────────┘     │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ [M] Modified  [A] Added  [D] Deleted                                                         │
│ a Allow │ A Allow Always │ r Reject │ R Reject Always │ j/k Nav │ d Diff │ esc               │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

The diff view supports three display modes, toggled with d:

Mode Behavior
Unified Standard unified diff with +/- lines (default)
Side-by-side Two-column view with old content left, new content right
Context Shows only changed lines with surrounding context (3 lines default)

Additional UI Components

Throbber

A rainbow gradient animated bar spans the full width at the top, visible only when the actor is processing:

┌───────────────────────────────────────────────────────────────────────────┐
│ ════════════════════════════════════ ◆ ═══════════════════════════════════│
│ (actor processing...)                                                     │
│                                                                           │
│ (idle — throbber collapses to zero height)                                │
└───────────────────────────────────────────────────────────────────────────┘

The gradient cycles through 12 colors at 15fps: #881177#aa3355#cc6666#ee9944#eedd00#99dd55#44dd88#22ccbb#00bbcc#0099cc#3366bb#663399

Session Tabs

┌────────────────────────────────────────────────────────────────────────────┐
│ Session 1Session 2Session 3⌛ Session 4                       │
│              ┗━━━━━━━━━━━┛                                                 │
│                                                                            │
│ ctrl+[ Previous │ ctrl+] Next │ ctrl+n New │ ctrl+w Close                  │
│ ctrl+s Sessions screen │ 1-9 Jump to tab                                   │
│                                                                            │
│ Indicators:   actor working  │   awaiting input  │  (plain) idle        │
│              ━━━ active tab underline                                      │
└────────────────────────────────────────────────────────────────────────────┘

Notifications (Flash)

Notifications appear as a single-line Flash bar above the prompt, auto-dismissing after a configurable timeout (default 5s):

┌────────────────────────────────────────┐
│ ✔ Plan fix-auth-bug completed          │
│ (auto-dismiss in 5s)                   │
│                                        │
│ ✗ Plan refactor-models failed          │
└────────────────────────────────────────┘

Help Panel (F1)

F1 opens a context-sensitive help overlay showing available hotkeys for the current screen/widget:

┌─ Help: Main Screen ─────────────────────────────────────────────────────┐
│                                                                         │
│ Navigation                                                              │
│  shift+tab     Cycle sidebar: hidden → visible → fullscreen             │
│  tab           Cycle to next persona                                    │
│  ctrl+tab      Cycle to next argument preset                            │
│  ctrl+[/]      Switch session tab                                       │
│  escape        Close overlay / sidebar / return to prompt               │
│                                                                         │
│ Prompt                                                                  │
└─────────────────────────────────────────────────────────────────────────┘

Escape Cascade

The escape key always cascades toward the main screen:

┌───────────────────────────────────────────────────────────────────────┐
│ Modal (PlanDetail, ProjectDetail, PersonaEditor, ...)                 │
│   ↓ escape                                                            │
│ Overlay (ReferencePicker, SlashCommand)                               │
│   ↓ escape                                                            │
│ Sidebar Fullscreen                                                    │
│   ↓ escape                                                            │
│ Sidebar Visible                                                       │
└───────────────────────────────────────────────────────────────────────┘

Multiple escape presses from any state eventually reach the main chat prompt. No modal or screen may trap the user without an escape path.

Conversation Stream

The conversation displays a chronological stream of typed message blocks:

Block Type Visual Treatment Source
Welcome ASCII art + instructions in $text-success App startup (first message)
UserInput Left border $secondary, 15% background tint, Markdown User prompt submission
ActorResponse Streaming Markdown with syntax-highlighted code fences session.message events
ActorThought $primary-muted 20% bg, max 10 lines (expandable), italic Actor reasoning
ToolCall Expandable: icon + status pill header, collapsible content tool.invoked / tool.completed
PlanProgress Grid layout with status icons per step Plan phase changes
DiffView Unified or side-by-side diff, syntax highlighting Tool results with diffs
TerminalEmbed Bordered terminal, $primary 50% border, green/red tint Shell or tool terminal output
ShellResult Left border $primary, 4% foreground bg User shell command (!) output
Note Semantic: info ($primary), warning ($warning), error ($error) System notifications

The conversation uses a 2-column grid: a 1-character cursor column (left, navigable via alt+up/alt+down) and the content stream (right). The block cursor provides keyboard-driven message navigation — enter or space expands/collapses a block.

Theme and Styling

The TUI uses Textual's CSS (TCSS) system with semantic color tokens:

Token Usage
$primary Active elements, sidebar borders, plan status
$secondary User input highlights, prompt accent
$success Completed states, passed validations
$error Failed states, error notifications
$warning Caution states, cost warnings
$text, $text-muted Primary and secondary text

Default theme is Dracula. All 17+ Textual built-in themes are supported and switchable via /theme <name>. The TCSS file hierarchy follows the screen/widget module structure.

Block Cursor and Context Menu

The conversation uses a 2-column grid: a 1-character-wide cursor column on the left and the content stream on the right. The block cursor (a gutter indicator) is navigated with alt+up/alt+down and provides keyboard-driven interaction with message blocks.

Cursor Navigation

Key Action
alt+up Move cursor to previous block
alt+down Move cursor to next block
space Expand or collapse the focused block (if expandable)
enter Open context menu for the focused block
escape Clear cursor selection, return focus to prompt
Click on block Position cursor on clicked block

Blocks that implement the ExpandProtocol (ToolCall, ActorThought, DiffView) can be toggled with space. The indicator (collapsed) changes to (expanded) on toggle.

Context Menu

Pressing enter with the block cursor on any conversation block opens a floating context menu:

┌─ Block Actions ──────────────────────────────────────────────────────┐
│                                                                      │
│  c   Copy to clipboard                                               │
│  p   Copy to prompt                                                  │
│  e   Export as Markdown                                              │
│  v   Export as SVG (opens browser)                                   │
│  m   Maximize / restore block                                        │
│  r   Retry (ActorResponse only)                                      │
│  d   Show raw data                                                   │
│                                                                      │
│──────────────────────────────────────────────────────────────────────│
│ escape Dismiss                                                       │
└──────────────────────────────────────────────────────────────────────┘
Action Key Applies To Description
Copy to clipboard c All blocks Copies the block's text content (Markdown source for ActorResponse, plain text for others) to the system clipboard
Copy to prompt p All blocks Inserts the block's text content into the prompt TextArea at the cursor position
Export as Markdown e All blocks Exports the block's content to a .md file in the current working directory
Export as SVG v All blocks Renders the block to an SVG file and opens it in the default browser
Maximize / restore m ActorThought, ToolCall, DiffView Toggles between collapsed/default height and full-screen height for the block
Retry r ActorResponse Re-sends the preceding user prompt to the actor, replacing this response
Show raw data d All blocks Shows the raw A2A message data for debugging

Conversation Block Details

Tool Call States

Tool calls progress through pending → completed/failed states. The expand behavior is controlled by the tools.expand setting:

┌────────────────────────────────────────────────────────────────────────┐
│  Pending:                                                              │
│   🔧 local/code-analysis                                            │
│                                                                        │
│  Completed (collapsed):                                                │
│   🔧 local/code-analysis                                             │
│                                                                        │
│  Completed (expanded):                                                 │
│   🔧 local/code-analysis                                             │
│   ┌─ src/auth/handler.py ────────────────────────────────────────────┐ │
│   │  45 │ def login(self, req):                                      │ │
│   │  46 │     creds = extract(req)                                   │ │
│   │  47 │     return auth(creds)                                     │ │
│   └──────────────────────────────────────────────────────────────────┘ │
│                                                                        │
│  Failed:                                                               │
│   🔧 local/code-analysis ✗ failed                                     │
└────────────────────────────────────────────────────────────────────────┘
tools.expand Setting Behavior
never All tool calls start collapsed
always All tool calls auto-expand on completion
success Only successful tool calls auto-expand
fail (default) Only failed tool calls auto-expand
both Both successful and failed auto-expand

Tool calls with kind: "read" (e.g., file reads, directory listings) never auto-expand regardless of setting, as they produce verbose output that would dominate the conversation.

Actor Thought Block

Actor thoughts (reasoning traces) display in a muted container with configurable visibility:

┌──────────────────────────────────────────────────────────────────────┐
│  Collapsed (default, max 10 lines):                                  │
│  ┌──────────────────────────────────────────────────────────────┐    │
│   I need to analyze the authentication flow. The handler           │
│   uses a custom JWT implementation that may have...                │
│                      (8 more lines — space to expand)              │
│  └──────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  Expanded (space or m to maximize):                                  │
│  ┌──────────────────────────────────────────────────────────────┐    │
│   I need to analyze the authentication flow. The handler           │
│   uses a custom JWT implementation that may have timing            │
│   vulnerabilities. Let me check the token validation...            │
│   ...(full content shown)...                                       │
│  └──────────────────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────────────────┘
Property Value
Default height Max 10 lines (CSS max-height: 10)
Expanded height Full content (CSS max-height: 100vh)
Background $primary-muted 20%
Text style Italic
Toggle visibility agent.thoughts setting or ctrl+shift+t
Expand/collapse space with block cursor, or m in context menu

Permission Question Widget

When an actor requests permission for a single-file operation, a Question widget replaces the prompt area temporarily. For multi-file operations (diffs), the full PermissionsScreen is pushed instead.

┌──────────────────────────────────────────────────────────────────────┐
│ Permission Required                                                  │
│                                                                      │
│ The actor wants to write to:                                         │
│   src/auth/handler.py                                                │
│                                                                      │
│    a  Allow once                                                    │
│     A  Allow always (this session)                                   │
│     r  Reject once                                                   │
│     R  Reject always (this session)                                  │
│                                                                      │
│ For multi-file diffs, press v to open full PermissionsScreen         │
└──────────────────────────────────────────────────────────────────────┘
Key Action
a Allow this operation once
A Allow all operations of this type for the remainder of the session
r Reject this operation once
R Reject all operations of this type for the remainder of the session
v Open full PermissionsScreen with diff view (for file writes)
up / down Navigate between options
enter Select the highlighted option

The Question widget uses a blinking caret animation on the selected option to draw attention. Multiple permission requests are queued and presented sequentially — the next question appears immediately after the current one is answered.

Prompt Architecture Detail

History Navigation

The prompt maintains two independent JSONL history files per project:

History File Location Trigger
Prompt history ~/.local/state/cleveragents/history/<project-hash>/prompt.jsonl up/down arrows when cursor is at first/last row of the TextArea
Shell history ~/.local/state/cleveragents/history/<project-hash>/shell.jsonl up/down arrows in shell mode (!/$ prompt)

History navigation rules:

  • up at row 0, column 0: replaces TextArea content with the previous history entry
  • down at the last row: replaces TextArea content with the next history entry (or clears to the original input if at the end)
  • History entries are stored as-is (including multi-line prompts)
  • History files are capped at 10,000 entries; oldest entries are pruned on write
  • Each prompt mode (normal, shell) has independent history and independent cursor position within that history

Key Passthrough

When the conversation Window widget has focus (e.g., during block cursor navigation), any printable character typed is forwarded to the prompt TextArea. This ensures the user can always start typing without explicitly focusing the prompt — the focus shifts automatically on the first printable keypress.

Multiline Detection

The prompt auto-detects multiline input and switches the prompt symbol to :

Condition Result
Input contains \n (newline character) Switch to multiline mode
Input contains triple backticks (```) Switch to multiline mode
shift+enter or ctrl+j pressed Insert newline, switch to multiline
All newlines removed Revert to single-line mode

In multiline mode, enter inserts a newline; ctrl+enter submits. In single-line mode, enter submits.

Shell Danger Detection

When shell mode is active (!/$ prefix), the prompt performs heuristic analysis of the command to detect potentially destructive operations. Dangerous commands are highlighted with $error styling and a warning indicator appears below the prompt:

Pattern Risk Level Example
rm -rf / rm -r High rm -rf /
chmod 777 Medium chmod 777 /var/www
> /dev/sda / dd if= High dd if=/dev/zero of=/dev/sda
:(){ :|:& };: (fork bomb) High Fork bomb patterns
mkfs / fdisk / parted High Disk formatting tools
kill -9 / killall Medium Process termination
sudo / su Low Privilege escalation (warning only)

Danger detection is controlled by the shell.warn_dangerous setting (default: true). The detection is advisory only — it never prevents command execution. The warning text reads: ⚠ Potentially destructive command detected.

Notification System

The TUI uses a multi-tier notification system to keep users informed across different attention contexts:

Flash Messages (In-App)

Flash messages are single-line notifications displayed between the conversation and the prompt. They auto-dismiss after the configured ui.flash_duration (default: 5s).

Style CSS Class Use Case Example
Default -default Informational Press ctrl+f to focus terminal
Success -success Positive outcomes ✔ Plan fix-auth-bug completed ($0.08)
Warning -warning Caution ⚠ Plan approaching cost limit ($4.50/$5.00)
Error -error Failures ✗ Plan refactor-models failed: validation error

Desktop Notifications

Desktop notifications alert the user when the TUI is not in the foreground. They are delivered via the OS notification system (using notifypy on Linux/macOS, native win10toast on Windows).

Setting Value Behavior
never Desktop notifications are disabled entirely
on-blur (default) Notifications are sent only when the terminal window does not have focus
always Notifications are sent regardless of focus state

Events that trigger desktop notifications:

  • Actor turn completed (response finished)
  • Permission requested (actor wants to perform a gated action)
  • Plan phase transition (idle → strategize, execute → completed, etc.)
  • Plan error or failure
  • Session cost threshold exceeded (configurable)

Each notification includes: the CleverAgents icon, a title (event type), and a body (session name + brief detail). Low-severity events (informational, routine phase transitions) are suppressed by default (notifications.hide_low_severity: true).

Terminal Title Management

The TUI updates the terminal window title via VT escape sequences (\033]0;...\007) to provide at-a-glance status:

State Title Format Example
Idle ◆ CleverAgents — <session_name> ◆ CleverAgents — Session 2
Actor working ⌛ CleverAgents — <session_name> ⌛ CleverAgents — Session 2
Awaiting input Alternates between and 👉 at 0.5s interval Blinking title draws attention
Permission requested Alternates between and 🔐 at 0.5s interval Urgent attention for permission

Title blinking is controlled by the notifications.blink_title setting (default: true). When disabled, the title updates but does not blink.

Sound Effects

Optional sound effects play on significant events (controlled by notifications.enable_sounds setting, default: true):

  • turn_complete.wav — Actor finished a response
  • permission_required.wav — Permission requested
  • plan_complete.wav — Plan reached terminal state
  • error.wav — Error or failure occurred

Sound files are bundled in src/cleveragents/tui/data/sounds/. The notification system gracefully degrades if audio playback is unavailable (e.g., headless server).

Clipboard Operations

The TUI provides clipboard integration for copying content from the conversation and other widgets:

Operation Trigger Behavior
Auto-copy on selection Mouse text selection When ui.auto_copy is true (default), any text selected with the mouse is automatically copied to the system clipboard
Block copy c in block context menu Copies the focused block's text content to the clipboard
Block copy to prompt p in block context menu Inserts the focused block's text into the prompt TextArea at the cursor
Manual copy OS-native selection + copy (e.g., ctrl+shift+c in most terminals) Standard terminal copy behavior

Clipboard access uses pyperclip when available, with fallback to OSC 52 escape sequences for terminal-native clipboard access (works over SSH and in tmux/screen). If neither is available, copy operations display a flash message with the content for manual copying.

Session Persistence and Resume

TUI sessions are persisted to enable resumption across TUI restarts:

Storage

Sessions are stored in an SQLite database at ~/.local/state/cleveragents/tui.db:

Column Type Description
id TEXT (PK) UUID matching the domain Session entity ID
persona_name TEXT Name of the persona active when the session was last used
actor_identity TEXT Namespaced actor identity (e.g., anthropic/claude-4-sonnet)
title TEXT User-assigned or auto-generated session title
prompt_count INTEGER Number of user prompts sent in this session
total_cost REAL Cumulative cost in USD for this session
created_at TEXT (ISO 8601) Session creation timestamp
last_used TEXT (ISO 8601) Last interaction timestamp
project_path TEXT Working directory at session creation (nullable)
meta_json TEXT JSON blob for extensible metadata (preset, sidebar state, scroll position)

Resume Workflow

  1. User presses ctrl+r (from SessionsScreen) or uses /session:import
  2. The TUI reads the session record from SQLite
  3. A new A2A Task is initiated with the stored session ID — the Application layer recognizes this as a resume request and loads the existing Session entity
  4. The actor receives the session's conversation history via A2A's multi-turn interaction pattern
  5. The conversation stream is populated with historical messages (rendered from stored A2A message data)
  6. The persona is restored to the one stored in the session record

Not all actors support session resume — actors that do not support it will start a fresh conversation. A flash warning is shown: ⚠ This actor does not support session resume. Starting fresh conversation.

Sessions Screen

The Sessions screen (ctrl+s) is a modal overlay showing all active and saved sessions:

┌─ Sessions ─────────────────────────────────────────────────────────────────────────────┐
│                                                                                        │
│   Session 2   feature-dev   claude-4-sonnet    awaiting input     14:02           │
│    Last: Can you review the auth module...                                             │
│                                                                                        │
│    Session 1   reviewer      gpt-4o            ⌛ working              13:58        │
│    Last: Analyze the test coverage for...                                              │
│                                                                                        │
│    Session 3   infra-admin    claude-4-opus     idle                    13:45       │
│    Last: Update the terraform modules...                                               │
│                                                                                        │
│────────────────────────────────────────────────────────────────────────────────────────│
│  Saved Sessions (from previous runs)                                                   │
│                                                                                        │
│    debug-session   feature-dev   claude-4-sonnet   23 prompts   Mar 10 09:15       │
│    refactor-v2    reviewer      gpt-4o            8 prompts    Mar 9 16:30         │
│                                                                                        │
│────────────────────────────────────────────────────────────────────────────────────────│
│ enter Switch │ ctrl+r Resume saved │ ctrl+n New │ d Delete │ r Rename │ esc Back       │
└────────────────────────────────────────────────────────────────────────────────────────┘

The screen is divided into two sections:

  • Active Sessions (top): Currently open session tabs with their state indicators, persona, actor, and last prompt preview. The current session is highlighted with .
  • Saved Sessions (bottom): Previously persisted sessions from the SQLite database, showing prompt count and last-used timestamp. These can be resumed with ctrl+r or enter.

Time formatting follows human-readable relative time: just now, 2m ago, 1h ago, or full date for >24h.

Settings Screen

The Settings screen (F2 or ctrl+,) is a modal overlay with search-driven navigation through all TUI settings:

┌─ Settings ─────────────────────────────────────────────────────────────────────────┐
│  🔍 Search: flash                                                                  │
│────────────────────────────────────────────────────────────────────────────────────│
│                                                                                    │
│  ▼ UI                                                                              │
│    Theme                   [ dracula          ]  17 themes available               │
│    Column width            [ 100              ]  Fixed column width (40-300)       │
│    Scrollbar               [ normal           ]  normal / thin / hidden            │
│    Flash duration           [ 5.0              ]  Seconds (0.5-30)                 │
│    Auto-copy on select     [                 ]  Copy selected text to clipboard   │
│                                                                                    │
│  ▼ Notifications                                                                   │
│    Desktop notifications   [ on-blur          ]  never / on-blur / always          │
│    Blink terminal title    [                 ]  Blink when input needed           │
│    Sound effects           [                 ]  Play sounds on events             │
│                                                                                    │
│────────────────────────────────────────────────────────────────────────────────────│
│ enter Edit │ tab Next │ ctrl+s Focus search │ esc Back (auto-saves)                │
└────────────────────────────────────────────────────────────────────────────────────┘

Settings are schema-driven: each setting has a type, default value, validation rules, and description. The schema is used to auto-generate the settings UI — new settings added to the schema automatically appear in the screen.

Settings are persisted as JSON in ~/.config/cleveragents/tui-settings.json and saved automatically when the settings screen is dismissed.

Complete Settings Schema

UI Settings (ui.*)

Setting Type Default Range / Choices Description
ui.theme choices dracula 17+ Textual themes Color theme for the entire TUI
ui.column boolean true Enable fixed column width for conversation content
ui.column_width integer 100 40300 Maximum column width in characters (when column is true)
ui.scrollbar choices normal normal / thin / hidden Scrollbar appearance for all scrollable widgets
ui.compact_input boolean false Remove prompt border and margin for minimal appearance
ui.sessions_bar choices multiple always / multiple / never When to show the session tab bar (multiple = only when ≥2 sessions)
ui.footer boolean true Show the hotkey footer bar at the bottom
ui.info_bar boolean true Show the info bar below the prompt (PersonaBar)
ui.status_line boolean true Show token count and cost in the info bar
ui.flash_duration number 5.0 0.530.0 Seconds before flash messages auto-dismiss
ui.auto_copy boolean true Automatically copy mouse-selected text to clipboard
ui.prune_low_mark integer 1500 10010000 Target conversation line count after pruning
ui.prune_excess integer 1000 1005000 Lines over prune_low_mark that trigger pruning
ui.throbber choices rainbow rainbow / quotes Throbber style: animated gradient or rotating text quotes

Notification Settings (notifications.*)

Setting Type Default Range / Choices Description
notifications.desktop choices on-blur never / on-blur / always When to send desktop notifications
notifications.blink_title boolean true Blink terminal window title when input is needed
notifications.enable_sounds boolean true Play sound effects on events
notifications.turn_complete boolean true Notify when actor finishes a response
notifications.hide_low_severity boolean true Suppress desktop notifications for low-severity events

Sidebar Settings (sidebar.*)

Setting Type Default Range / Choices Description
sidebar.auto_hide boolean false Auto-hide sidebar when it loses focus (slides back on ctrl+b)
sidebar.width integer 36 2460 Sidebar width in characters when in visible state

Agent Settings (agent.*)

Setting Type Default Range / Choices Description
agent.thoughts boolean true Show actor thinking/reasoning blocks in conversation

Tool Settings (tools.*)

Setting Type Default Range / Choices Description
tools.expand choices fail never / always / success / fail / both When to auto-expand tool call results

Shell Settings (shell.*)

Setting Type Default Range / Choices Description
shell.command string /bin/sh Shell executable for ! mode
shell.startup_commands text PS1="" Commands run at shell startup (newline-separated)
shell.warn_dangerous boolean true Highlight potentially dangerous commands
shell.allow_commands text git,ls,cat,... Commands recognized as shell (auto-switch to $ mode)
shell.directory_commands text cd,rmdir Commands whose arguments are directories (for tab completion)
shell.file_commands text cat,less,head Commands whose arguments are files (for tab completion)

Diff Settings (diff.*)

Setting Type Default Range / Choices Description
diff.view choices auto unified / split / auto Diff display mode (auto selects based on available width)

Conversation Content Pruning

Long conversations accumulate widgets that consume memory and degrade rendering performance. The TUI implements automatic content pruning to maintain responsiveness:

Parameter Setting Default Description
Target line count ui.prune_low_mark 1500 After pruning, the conversation is trimmed to approximately this many lines
Trigger threshold ui.prune_excess 1000 Pruning activates when the conversation exceeds prune_low_mark + prune_excess lines

Pruning behavior:

  1. When the conversation stream exceeds 2500 lines (1500 + 1000), pruning activates
  2. The oldest message blocks are removed from the DOM (not just hidden) until the line count drops to approximately prune_low_mark
  3. A Note block is inserted at the top: (Earlier messages pruned — see session history for full conversation)
  4. Pruning never removes the most recent 500 lines regardless of settings
  5. Pruned messages remain in the session history (SQLite) and can be viewed via /session:export
  6. Welcome blocks, system notes, and the current actor response are never pruned

Safety Behaviors

Double-Tap Quit

ctrl+c does not immediately quit the TUI. Instead:

  1. First press: If an actor is processing, sends an interrupt/cancel signal to the actor. If idle, shows a flash message: Press ctrl+c again within 5s to quit
  2. Second press within 5s: Quits the TUI (after saving session state)
  3. Second press after 5s: Resets — treated as a new first press

ctrl+q always quits immediately (after saving session state) without requiring a double-tap.

Double-Tap Escape for Terminal

When a terminal widget (TerminalEmbed or ShellTerminal) has focus, a single escape press is ambiguous — the terminal itself may use escape sequences. The TUI requires a double-tap within 400ms:

  1. First escape: Starts a 400ms timer
  2. Second escape within 400ms: Exits terminal focus, returns focus to the prompt
  3. No second escape within 400ms: The escape keypress is forwarded to the terminal as a normal escape sequence

A flash hint appears on the first escape: Press escape again to exit terminal.

Loading States

The TUI supports two loading indicator styles, configurable via ui.throbber:

Style Setting Value Description
Rainbow gradient rainbow (default) Animated horizontal bar cycling through 12 colors at 15fps (see Throbber diagram)
Rotating quotes quotes Displays shuffled quotes from a curated collection, rotating every 3 seconds

The quotes collection contains ~200 entries drawn from science fiction (curated for relevance to AI/coding themes). Example quotes:

  • "I'm sorry, Dave. I'm afraid I can't do that." — HAL 9000
  • "The only way to do great work is to love what you do." — Steve Jobs
  • "Any sufficiently advanced technology is indistinguishable from magic." — Arthur C. Clarke

Both styles occupy 1 row at the top of the screen and collapse to zero height when idle.

Conversation Export

Conversations can be exported in multiple formats:

Format Command / Trigger Output
JSON /session:export [path] Full session data including A2A messages, metadata, timestamps
Markdown /session:export --format md [path] Human-readable conversation transcript with Markdown formatting
SVG Context menu → v (per block) Single block rendered as SVG image

The JSON export format is the canonical format for /session:import — it contains all data needed to fully restore a session. The Markdown format is a lossy export intended for sharing and documentation.

Hotkey Reference

Hotkeys vary by the current screen and focused widget. The help panel (F1) always reflects the currently applicable hotkeys.

Global Hotkeys (Available Everywhere)

Key Action
ctrl+q Quit TUI immediately (saves session state)
ctrl+c Interrupt actor / double-tap to quit (5s window)
F1 Toggle help panel
F2 / ctrl+, Open settings
ctrl+s Open Sessions screen
ctrl+n Create new session tab
ctrl+w Close current session tab
ctrl+[ / ctrl+] Switch to previous / next session tab
shift+tab Cycle sidebar: hidden → visible → fullscreen
escape Close current overlay / modal / sidebar toward main screen

MainScreen — Prompt Focused

Key Action
enter Submit prompt (single-line mode) / insert newline (multi-line mode)
ctrl+enter Submit prompt (multi-line mode)
shift+enter / ctrl+j Insert newline (switch to multi-line mode)
tab Cycle to next persona
ctrl+tab Cycle to next argument preset
up (at row 0) Previous prompt history entry
down (at last row) Next prompt history entry
@ Open Reference Picker overlay
/ (at position 0) Open Slash Command overlay
! or $ (at position 0) Activate shell mode
backspace (at position 0 in shell mode) Deactivate shell mode, return to normal
tab (in shell mode) Tab-complete file/directory path
ctrl+b Focus sidebar (when visible)

MainScreen — Conversation / Block Cursor

Key Action
alt+up Move block cursor to previous block
alt+down Move block cursor to next block
space Expand / collapse focused block
enter Open context menu for focused block
escape Clear block cursor, return focus to prompt
Any printable character Forward to prompt (key passthrough)

MainScreen — Block Context Menu

Key Action
c Copy block content to clipboard
p Copy block content to prompt
e Export block as Markdown file
v Export block as SVG (opens browser)
m Maximize / restore block height
r Retry prompt (ActorResponse blocks only)
d Show raw A2A message data
escape Dismiss menu

MainScreen — Terminal Focused

Key Action
escape escape (within 400ms) Exit terminal focus, return to prompt
ctrl+c Send SIGINT to terminal process
All other keys Forwarded to terminal PTY

Sidebar — Visible (Partial)

Key Action
tab / shift+tab (within sidebar) Navigate between Plans and Projects panels
enter Expand / collapse the highlighted collapsible section
up / down Navigate items within a panel
escape Dismiss sidebar, return focus to prompt
shift+tab (global) Transition to fullscreen sidebar

Sidebar — Fullscreen

Key Action
space Toggle selection on highlighted plan/project
enter Open detail modal (PlanDetailModal or ProjectDetailModal)
up / down Navigate items
left / right Switch between Plans and Projects columns
ctrl+p Create persona from current selection
ctrl+up / ctrl+down Reorder personas in the cycle list
d Delete highlighted persona from cycle list
/ Open search/filter within sidebar
escape Return to visible sidebar state

Reference Picker Overlay

Key Action
typing Fuzzy search across projects, plans, and resources
enter Insert selected reference into prompt
up / down Navigate results
tab Switch between fuzzy search and tree browser modes
ctrl+p Filter to projects only
ctrl+l Filter to plans only
escape Dismiss picker, remove @ trigger character

Reference Picker — Tree Browser Mode

Key Action
enter Select highlighted item as reference
space Expand / collapse tree node
up / down Navigate tree
/ Open inline filter
tab Switch back to fuzzy search mode
escape Dismiss picker

Slash Command Overlay

Key Action
typing Fuzzy search commands
enter Execute selected command
tab Auto-complete command name
up / down Navigate command list
escape Dismiss overlay

Plan Detail Modal

Key Action
up / down Navigate decision tree nodes
enter Select decision node (show detail in right panel)
space Expand / collapse subtree
/ Search within decision tree
c Correct the selected decision (opens correction dialog)
a Approve pending decision
r Reject pending decision
d Show diff for the selected decision
e Explain decision (show reasoning)
x Cancel the plan
escape Close modal

Project Detail Modal

Key Action
up / down Navigate resource tree
enter View resource details (right panel)
space Expand / collapse resource tree node
/ Search within resource tree
i Show invariants for selected resource
v Show validations for selected resource
escape Close modal

Persona Editor Modal

Key Action
tab / shift+tab Navigate form fields
enter Edit the focused field / confirm selection
ctrl+a Add a new argument preset
ctrl+d Delete the selected argument preset
ctrl+s Save persona and close editor
escape Cancel and close editor (prompts to save if modified)

Permissions Screen

Key Action
j / down Next file diff
k / up Previous file diff
tab / shift+tab Cycle focus: file list → diff view → action buttons
a Allow once
A Allow always (this session)
r Reject once
R Reject always (this session)
d Toggle diff view mode: unified → split → auto
escape Reject and close (equivalent to r)

Settings Screen

Key Action
ctrl+s Focus search input
tab / shift+tab Navigate settings fields
enter Edit the focused setting
escape Close settings (auto-saves)

Sessions Screen

Key Action
up / down Navigate session list
enter Switch to highlighted active session / resume saved session
ctrl+r Resume a saved (historical) session
ctrl+n Create new session
d Delete highlighted session
r Rename highlighted session
escape Close sessions screen

Question / Permission Widget (Inline)

Key Action
a Allow once
A Allow always (this session)
r Reject once
R Reject always (this session)
v Open full Permissions Screen (for file writes)
up / down Navigate options
enter Select highlighted option

Help Panel (F1)

Key Action
F1 Close help panel
escape Close help panel
up / down Scroll help content