Documentation Index Fetch the complete documentation index at: https://docs.opper.ai/llms.txt
Use this file to discover all available pages before exploring further.
Agents can create custom sections in the TUI sidebar to show status, progress, metrics, and other dynamic information. This helps users understand what your agent is doing in real-time.
Sidebar sections provide a persistent display of agent state that’s always visible in the TUI:
Agent status - Show whether the agent is idle, working, or has errors
Progress updates - Display progress bars for long-running operations
Live metrics - Show request counts, processing time, etc.
Important alerts - Highlight warnings or critical information
Current activity - Display what the agent is currently doing
Coordinate sidebar updates with System Prompts to keep both the visual display and LLM context synchronized.
Basic Usage
Registering a Section
Create a sidebar section during agent initialization:
class MyAgent ( OpperatorAgent ):
def initialize ( self ):
# Register a status section
self .register_section(
section_id = "status" ,
title = "Agent Status" ,
content = "<c fg='gray'>○ Initializing</c>" ,
collapsed = False
)
Updating a Section
Update the section content as your agent works:
def start ( self ):
# Update to running state
self .update_section(
"status" ,
"<c fg='green'>● Running</c>"
)
def handle_request ( self , args ):
# Update with activity
self .request_count += 1
self .update_section(
"status" ,
f """
<c fg='green'>● Running</c>
<b>Requests:</b> { self .request_count }
<b>Last:</b> { time.strftime( '%H:%M:%S' ) }
""" .strip()
)
SDK Methods
Unique identifier for the section (e.g., “status”, “metrics”)
Display title shown in sidebar
Section content with optional formatting
Start collapsed (default: False)
If you call update_section() for a section that doesn’t exist, it will automatically create it. However, it’s recommended to explicitly register sections first for clarity.
Sidebar sections support HTML-like formatting tags:
Colors
Named Colors
Hex Colors
Background Colors
"<c fg='green'>Success</c>"
"<c fg='red'>Error</c>"
"<c fg='yellow'>Warning</c>"
"<c fg='blue'>Info</c>"
# Available: red, green, blue, yellow, cyan, magenta,
# white, black, gray, grey, orange, purple, pink, brown
Text Styling
"<b>Bold text</b>"
"<i>Italic text</i>"
"<b><i>Bold and italic</i></b>"
# Nested tags work
"<c fg='red'><b>Bold Red Text</b></c>"
Status Indicators and Symbols
Use ASCII symbols for status indicators, progress visualization, and animations:
Status Indicators
Progress Bars
Charts
Sparklines
Spinners
# Status states
"<c fg='green'>● Active</c>"
"<c fg='yellow'>○ Inactive</c>"
"<c fg='green'>✓ Success</c>"
"<c fg='red'>✗ Failed</c>"
"<c fg='yellow'>⚠ Warning</c>"
"<c fg='blue'>ℹ Info</c>"
# Loading states
"<c fg='yellow'>◐ Starting</c>"
"<c fg='yellow'>◓ Paused</c>"
# Full progress bar
"█████████░░░░░░░░░ 45%"
# Complete
"██████████████████ 100%"
# Starting
"███░░░░░░░░░░░░░░░ 15%"
# Bar charts
"Mon ████████░░ 8"
"Tue ███████████ 11"
"Wed █████░░░░░ 5"
"Thu ████████░░ 8"
"Fri ███████░░░ 7"
# Trend over time
"▁▂▃▄▅▆▇█"
# Recent trend
"▁▁▂▃▄▅▃▂"
# Circle spinner (rotate for animation)
"◐ ◓ ◑ ◒"
# Dot spinner
"⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"
Progress Bars
Create progress bars using block characters:
def show_progress ( self , current , total ):
percent = int ((current / total) * 100 )
filled = int ( 20 * percent / 100 )
bar = "█" * filled + "░" * ( 20 - filled)
self .update_section(
"progress" ,
f " { bar } { percent } % \n Item { current } / { total } "
)
# Example output:
# ████████░░░░░░░░░░░░ 40%
# Item 4/10
Common Patterns
Overview + Activity Pattern
Use two sections - one stable, one dynamic:
class TaskAgent ( OpperatorAgent ):
def initialize ( self ):
# Stable overview
self .register_section(
"overview" ,
"Overview" ,
f """
<b>Agent:</b> Task Processor
<b>Version:</b> 1.0
<b>Status:</b> Ready
""" .strip()
)
# Dynamic activity (collapsed by default)
self .register_section(
"activity" ,
"Current Activity" ,
"Idle" ,
collapsed = True
)
def process_task ( self , task_id ):
# Show current activity
self .update_section(
"activity" ,
f """
<c fg='yellow'>◐ Processing task</c>
<b>ID:</b> { task_id }
<b>Started:</b> { time.strftime( '%H:%M:%S' ) }
""" .strip()
)
# Do the work...
result = self .process(task_id)
# Show completion
self .update_section(
"activity" ,
f """
<c fg='green'>✓ Task completed</c>
<b>ID:</b> { task_id }
<b>Result:</b> { result }
""" .strip()
)
# Update overview
self .update_section(
"overview" ,
f """
<b>Agent:</b> Task Processor
<b>Version:</b> 1.0
<b>Status:</b> Ready
<b>Last Task:</b> { task_id }
""" .strip()
)
Lifecycle Status Updates
Show different status as agent moves through lifecycle:
class MonitorAgent ( OpperatorAgent ):
def initialize ( self ):
self .register_section(
"status" ,
"Agent Status" ,
"<c fg='gray'>○ Initializing...</c>"
)
def start ( self ):
# Animate startup
for frame in [ "◐" , "◓" , "◑" , "◒" ] * 2 :
self .update_section(
"status" ,
f "<c fg='yellow'> { frame } Starting...</c>"
)
time.sleep( 0.25 )
# Running state
self .update_section(
"status" ,
"<c fg='green'>● Running</c>"
)
def on_shutdown ( self ):
self .update_section(
"status" ,
"<c fg='yellow'>○ Stopping...</c>"
)
Metrics Dashboard
Show live metrics:
class APIAgent ( OpperatorAgent ):
def initialize ( self ):
self .register_section(
"metrics" ,
"📊 Metrics" ,
"""
<b>Requests:</b> 0
<b>Errors:</b> 0
<b>Avg Response:</b> 0ms
<b>Uptime:</b> 0s
""" .strip()
)
def update_metrics ( self ):
uptime = int (time.time() - self .start_time)
avg_response = sum ( self .response_times) / len ( self .response_times)
self .update_section(
"metrics" ,
f """
<b>Requests:</b> { self .request_count }
<b>Errors:</b> { self .error_count }
<b>Avg Response:</b> { avg_response :.0f} ms
<b>Uptime:</b> { uptime } s
""" .strip()
)
Complete Example
Full agent with multiple sidebar sections:
from opperator_agent import OpperatorAgent, LogLevel
import time
class ProcessingAgent ( OpperatorAgent ):
def __init__ ( self ):
super (). __init__ ( name = "processing_agent" )
self .processed_count = 0
self .error_count = 0
def initialize ( self ):
# Overview section
self .register_section(
"overview" ,
"Processing Agent" ,
"""
<b>Status:</b> <c fg='gray'>Initializing</c>
<b>Processed:</b> 0
<b>Errors:</b> 0
""" .strip()
)
# Activity section (collapsed)
self .register_section(
"activity" ,
"Current Activity" ,
"No activity" ,
collapsed = True
)
# Register command
self .register_command(
"process_file" ,
self .cmd_process_file,
description = "Process a file" ,
async_enabled = True ,
progress_label = "Processing file"
)
def start ( self ):
self .update_section(
"overview" ,
"""
<b>Status:</b> <c fg='green'>● Running</c>
<b>Processed:</b> 0
<b>Errors:</b> 0
""" .strip()
)
def cmd_process_file ( self , args ):
file_path = args.get( "file_path" , "data.csv" )
# Show processing started
self .update_section(
"activity" ,
f """
<c fg='yellow'>◐ Processing file</c>
<b>File:</b> { file_path }
<b>Started:</b> { time.strftime( '%H:%M:%S' ) }
""" .strip()
)
# Simulate processing with progress
items = range ( 10 )
for i, item in enumerate (items):
time.sleep( 0.5 ) # Simulate work
# Update progress
percent = int ((i + 1 ) / len (items) * 100 )
filled = int ( 20 * percent / 100 )
bar = "█" * filled + "░" * ( 20 - filled)
self .report_progress(
text = f "Processing { i + 1 } / { len (items) } " ,
progress = (i + 1 ) / len (items)
)
self .update_section(
"activity" ,
f """
<c fg='yellow'>◐ Processing file</c>
<b>File:</b> { file_path }
{ bar } { percent } %
Item { i + 1 } / { len (items) }
""" .strip()
)
# Complete
self .processed_count += 1
self .update_section(
"activity" ,
f """
<c fg='green'>✓ File processed</c>
<b>File:</b> { file_path }
<b>Items:</b> { len (items) }
<b>Completed:</b> { time.strftime( '%H:%M:%S' ) }
""" .strip()
)
self .update_section(
"overview" ,
f """
<b>Status:</b> <c fg='green'>● Running</c>
<b>Processed:</b> { self .processed_count }
<b>Errors:</b> { self .error_count }
<b>Last File:</b> { file_path }
""" .strip()
)
return { "status" : "success" , "items" : len (items)}
if __name__ == "__main__" :
agent = ProcessingAgent()
agent.run()
Best Practices
Sidebar space is limited. Use short labels and values: Good: """
<b>Status:</b> Running
<b>Requests:</b> 42
<b>Errors:</b> 0
"""
Bad: """
The agent is currently running and has processed
42 requests with 0 errors encountered so far.
"""
Update on meaningful changes
Don’t update too frequently or on trivial changes: Good: # Update when state changes
def handle_request ( self , req ):
self .request_count += 1
if self .request_count % 10 == 0 : # Every 10 requests
self .update_metrics()
Bad: # Updates 10x per second!
def main_loop ( self ):
while self .running:
self .update_section( "time" , f " { time.time() } " )
time.sleep( 0.1 )
Use colors to indicate status, not just for decoration:
🟢 Green: Success, healthy, active
🟡 Yellow: Warning, processing, inactive
🔴 Red: Error, failed, critical
⚪ Gray: Neutral, initializing
if self .error_count > 0 :
status = "<c fg='red'>✗ Errors</c>"
elif self .processing:
status = "<c fg='yellow'>◐ Processing</c>"
else :
status = "<c fg='green'>● Ready</c>"
Use collapsed=True for sections users don’t need to see constantly: # Always visible
self .register_section(
"status" ,
"Status" ,
content,
collapsed = False
)
# Collapsed by default
self .register_section(
"debug" ,
"Debug Info" ,
content,
collapsed = True
)
Troubleshooting
Check :
Agent must be running (not necessarily focused)
Section ID must be non-empty after trimming whitespace
Check agent logs for errors
Debug :def initialize ( self ):
self .log(LogLevel. INFO , "Registering section" )
self .register_section( "test" , "Test" , "Content" )
self .log(LogLevel. INFO , "Section registered" )
Check :
Section ID must match exactly (case-sensitive)
Agent is emitting updates
No exceptions in handler
Section was registered first (or auto-created)
Debug :def update_status ( self ):
self .log(LogLevel. INFO , "Updating section" )
self .update_section( "status" , "New content" )
self .log(LogLevel. INFO , "Section updated" )
Check :
Use quotes around color names: <c fg="green"> or <c fg='green'>
Named colors: red, green, blue, yellow, cyan, magenta, white, black, gray, grey, orange, purple, pink, brown
Hex format: #RRGGBB (6 digits) or #RGB (3 digits)
Must include closing tags: </c>, </b>, </i>
Examples :# Correct
"<c fg='green'>text</c>"
'<c fg="green">text</c>'
"<c fg='#00ff00'>text</c>" # 6-digit hex
"<c fg='#0f0'>text</c>" # 3-digit hex
# Wrong
"<c fg=green>text</c>" # Missing quotes
"<c fg='green'>text" # Missing closing tag
Next Steps
Lifecycle events React to conversation and agent activation events
State management Persist state across conversations and sessions