Module System Overview

November 12, 2025 ยท View on GitHub

The Module System is the core execution engine of UFO, orchestrating the complete lifecycle of user interactions from initial request to final completion. It manages sessions, rounds, context state, and command dispatch across both Windows and Linux platforms.

Quick Navigation:


Architecture Overview

The module system implements a hierarchical execution model with clear separation of concerns:

graph TB
    subgraph "User Interaction Layer"
        UI[Interactor<br/>User I/O]
    end
    
    subgraph "Session Management Layer"
        SF[SessionFactory<br/>Creates sessions]
        SP[SessionPool<br/>Manages multiple sessions]
        S[Session<br/>Conversation lifecycle]
    end
    
    subgraph "Execution Layer"
        R[Round<br/>Single request handler]
        C[Context<br/>Shared state]
    end
    
    subgraph "Command Layer"
        D[Dispatcher<br/>Command routing]
        LCD[LocalCommandDispatcher]
        WSD[WebSocketCommandDispatcher]
    end
    
    subgraph "Platform Layer"
        WS[WindowsBaseSession]
        LS[LinuxBaseSession]
        SS[ServiceSession]
    end
    
    UI -.Request.-> SF
    SF --> SP
    SP --> S
    S --> R
    R --> C
    R --> D
    D --> LCD
    D --> WSD
    
    S -.inherits.-> WS
    S -.inherits.-> LS
    S -.inherits.-> SS
    
    style UI fill:#e1f5ff
    style SF fill:#fff4e1
    style SP fill:#f0ffe1
    style S fill:#ffe1f5
    style R fill:#e1ffe1
    style C fill:#ffe1e1
    style D fill:#f5e1ff

Core Components

1. Session Management

A Session represents a complete conversation between the user and UFO, potentially spanning multiple requests and rounds.

Session Hierarchy:

classDiagram
    class BaseSession {
        <<abstract>>
        +task: str
        +context: Context
        +rounds: Dict[int, BaseRound]
        +run()
        +create_new_round()
        +is_finished()
    }
    
    class WindowsBaseSession {
        +host_agent: HostAgent
        +_init_agents()
    }
    
    class LinuxBaseSession {
        +agent: LinuxAgent
        +_init_agents()
    }
    
    class Session {
        +mode: str
        +next_request()
    }
    
    class ServiceSession {
        +task_protocol: TaskExecutionProtocol
        +_init_context()
    }
    
    class LinuxSession {
        +next_request()
    }
    
    class FollowerSession {
        +plan_reader: PlanReader
    }
    
    BaseSession <|-- WindowsBaseSession
    BaseSession <|-- LinuxBaseSession
    WindowsBaseSession <|-- Session
    WindowsBaseSession <|-- ServiceSession
    LinuxBaseSession <|-- LinuxSession
    Session <|-- FollowerSession

Session Types:

Session TypePlatformUse CaseCommunication
SessionWindowsInteractive modeLocal
ServiceSessionWindowsServer-controlledWebSocket (AIP)
LinuxSessionLinuxInteractive modeLocal
LinuxServiceSessionLinuxServer-controlledWebSocket (AIP)
FollowerSessionWindowsPlan executionLocal
FromFileSessionWindowsBatch processingLocal
OpenAIOperatorSessionWindowsOperator modeLocal

!!!example "Session Creation" ```python from ufo.module.session_pool import SessionFactory

# Create interactive Windows session
factory = SessionFactory()
sessions = factory.create_session(
    task="email_task",
    mode="normal",
    plan="",
    request="Open Outlook and send an email"
)

# Create Linux service session
linux_session = factory.create_service_session(
    task="data_task",
    should_evaluate=True,
    id="session_001",
    request="Process CSV files",
    platform_override="linux"
)
```

2. Round Execution

A Round handles a single user request by orchestrating agents through a state machine, executing actions until completion.

Round Lifecycle:

stateDiagram-v2
    [*] --> Created: Initialize Round
    Created --> AgentHandle: agent.handle(context)
    AgentHandle --> StateTransition: Determine next state
    StateTransition --> AgentSwitch: Switch agent if needed
    AgentSwitch --> SubtaskCheck: Check if subtask ends
    
    SubtaskCheck --> CaptureSnapshot: Subtask complete
    SubtaskCheck --> AgentHandle: Continue
    
    CaptureSnapshot --> AgentHandle: Next subtask
    
    AgentHandle --> RoundComplete: is_finished() = True
    RoundComplete --> Evaluation: should_evaluate = True
    RoundComplete --> [*]: should_evaluate = False
    Evaluation --> [*]
    
    note right of AgentHandle
        Agent processes current state
        Updates context
        Executes actions
    end note
    
    note right of StateTransition
        State pattern determines:
        - Next state
        - Next agent
        - Round completion
    end note

Key Round Operations:

OperationPurposeTrigger
agent.handle(context)Process current stateEach iteration
state.next_state(agent)Determine next stateAfter handle
state.next_agent(agent)Switch agent if neededAfter state transition
capture_last_snapshot()Save UI stateSubtask/Round end
evaluation()Assess completionRound end (if enabled)

!!!warning "Round Termination Conditions" A round finishes when: - state.is_round_end() returns True - Session step exceeds ufo_config.system.max_step - Agent enters ERROR state


3. Context State Management

Context is a type-safe key-value store that maintains state across all rounds in a session.

Context Architecture:

graph LR
    subgraph "Context Storage"
        CN[ContextNames Enum]
        CV[Context Values Dict]
    end
    
    subgraph "Tracked Data"
        ID[Session/Round IDs]
        ST[Steps & Costs]
        LOG[Loggers]
        APP[Application State]
        CMD[Command Dispatcher]
    end
    
    subgraph "Access Patterns"
        GET[context.get(key)]
        SET[context.set(key, value)]
        UPD[context.update_dict(key, dict)]
    end
    
    CN -.defines.-> CV
    CV --> ID
    CV --> ST
    CV --> LOG
    CV --> APP
    CV --> CMD
    
    GET -.reads.-> CV
    SET -.writes.-> CV
    UPD -.merges.-> CV
    
    style CN fill:#e1f5ff
    style CV fill:#fff4e1
    style GET fill:#f0ffe1
    style SET fill:#ffe1f5
    style UPD fill:#f5e1ff

Context Categories:

CategoryContext NamesTypePurpose
IdentifiersID, CURRENT_ROUND_IDintSession/round tracking
Execution StateSESSION_STEP, ROUND_STEPint/dictProgress tracking
Cost TrackingSESSION_COST, ROUND_COSTfloat/dictLLM API costs
RequestsREQUEST, SUBTASK, PREVIOUS_SUBTASKSstr/listTask information
ApplicationAPPLICATION_WINDOW, APPLICATION_PROCESS_NAMEUIAWrapper/strUI automation
LoggingLOGGER, REQUEST_LOGGER, EVALUATION_LOGGERFileWriterLog outputs
CommunicationHOST_MESSAGE, CONTROL_REANNOTATIONlistAgent messages
Infrastructurecommand_dispatcherBasicCommandDispatcherCommand execution

!!!example "Context Usage Patterns" ```python from ufo.module.context import Context, ContextNames

# Initialize context
context = Context()

# Set values
context.set(ContextNames.REQUEST, "Open Notepad")
context.set(ContextNames.SESSION_STEP, 0)

# Get values
request = context.get(ContextNames.REQUEST)  # "Open Notepad"
step = context.get(ContextNames.SESSION_STEP)  # 0

# Update dictionaries (for round-specific tracking)
round_costs = {1: 0.05, 2: 0.03}
context.update_dict(ContextNames.ROUND_COST, round_costs)

# Auto-sync current round values
current_cost = context.current_round_cost  # Auto-synced
```

4. Command Dispatching

Dispatchers route commands to execution environments (local MCP tools or remote WebSocket clients) and handle result delivery.

Dispatcher Architecture:

graph TB
    subgraph "Agent Layer"
        AG[Agent generates commands]
    end
    
    subgraph "Dispatcher Layer"
        BD[BasicCommandDispatcher<br/>Abstract base]
        LCD[LocalCommandDispatcher<br/>MCP tools]
        WSD[WebSocketCommandDispatcher<br/>AIP protocol]
    end
    
    subgraph "Execution Layer"
        CR[CommandRouter]
        CM[ComputerManager]
        MCP[MCP Servers]
        WS[WebSocket Client]
    end
    
    subgraph "Result Handling"
        RES[Results: List~Result~]
        ERR[Error Results]
    end
    
    AG --> BD
    BD -.implements.-> LCD
    BD -.implements.-> WSD
    
    LCD --> CR
    CR --> CM
    CM --> MCP
    
    WSD --> WS
    
    MCP --> RES
    WS --> RES
    LCD --> ERR
    WSD --> ERR
    
    style AG fill:#e1f5ff
    style BD fill:#fff4e1
    style LCD fill:#f0ffe1
    style WSD fill:#ffe1f5
    style RES fill:#e1ffe1

Dispatcher Comparison:

DispatcherUse CaseCommunicationError HandlingTimeout
LocalCommandDispatcherInteractive sessionsDirect MCP callsGenerates error Results6000s
WebSocketCommandDispatcherService sessionsAIP protocol messagesGenerates error Results6000s

!!!example "Command Dispatch Flow" ```python from aip.messages import Command

# Create commands
commands = [
    Command(
        tool_name="click_element",
        parameters={"control_label": "1", "button": "left"},
        tool_type="windows"
    )
]

# Execute via dispatcher (attached to context)
results = await context.command_dispatcher.execute_commands(
    commands=commands,
    timeout=30.0
)

# Process results
for result in results:
    if result.status == ResultStatus.SUCCESS:
        print(f"Action succeeded: {result.result}")
    else:
        print(f"Action failed: {result.error}")
```

5. User Interaction

Interactor provides rich CLI experiences for user input with styled prompts, panels, and confirmations.

Interaction Flows:

sequenceDiagram
    participant U as User
    participant I as Interactor
    participant S as Session
    participant R as Round
    
    U->>I: Start UFO
    I->>I: first_request()
    I-->>U: ๐Ÿ›ธ Welcome Panel
    U->>I: "Open Notepad"
    I->>S: Initial request
    
    S->>R: Create Round 1
    R->>R: Execute...
    R-->>S: Round complete
    
    S->>I: new_request()
    I-->>U: ๐Ÿ›ธ Next Request Panel
    U->>I: "Type hello"
    I->>S: Next request
    
    S->>R: Create Round 2
    R->>R: Execute...
    R-->>S: Round complete
    
    S->>I: new_request()
    I-->>U: ๐Ÿ›ธ Next Request Panel
    U->>I: "N"
    I-->>U: ๐Ÿ‘‹ Goodbye Panel
    I->>S: complete=True
    S->>S: Terminate
    
    S->>I: experience_asker()
    I-->>U: ๐Ÿ’พ Save Experience Panel
    U->>I: Yes
    I->>S: Save experience

Interactor Functions:

FunctionPurposeReturnsExample UI
first_request()Initial request promptstr๐Ÿ›ธ Welcome Panel with examples
new_request()Subsequent requestsTuple[str, bool]๐Ÿ›ธ Next Request Panel
experience_asker()Save experience promptbool๐Ÿ’พ Learning & Memory Panel
question_asker()Collect informationstr๐Ÿค” Numbered Question Panel
sensitive_step_asker()Security confirmationbool๐Ÿ”’ Security Check Panel

!!!example "Styled User Prompts" ```python from ufo.module import interactor

# First interaction with rich welcome
request = interactor.first_request()
# Shows:
# โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
# โ”ƒ ๐Ÿ›ธ UFO Assistant                      โ”ƒ
# โ”ƒ ๐Ÿš€ Welcome to UFO - Your AI Assistant โ”ƒ
# โ”ƒ ...examples...                        โ”ƒ
# โ”—โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”›

# Get next request
request, complete = interactor.new_request()
if complete:
    print("User exited")

# Ask for permission on sensitive actions
proceed = interactor.sensitive_step_asker(
    action="Delete file",
    control_text="important.docx"
)
if not proceed:
    print("Action cancelled by user")
```

6. Session Factory & Pool

SessionFactory creates platform-specific sessions based on mode and configuration, while SessionPool manages batch execution.

Factory Creation Logic:

graph TB
    START[SessionFactory.create_session]
    
    PLATFORM{Platform?}
    MODE{Mode?}
    
    WNORMAL[Windows Session]
    WSERVICE[Windows ServiceSession]
    WFOLLOWER[Windows FollowerSession]
    WBATCH[Windows FromFileSession]
    WOPERATOR[Windows OpenAIOperatorSession]
    
    LNORMAL[Linux Session]
    LSERVICE[Linux ServiceSession]
    
    START --> PLATFORM
    PLATFORM -->|Windows| MODE
    PLATFORM -->|Linux| MODE
    
    MODE -->|normal| WNORMAL
    MODE -->|service| WSERVICE
    MODE -->|follower| WFOLLOWER
    MODE -->|batch_normal| WBATCH
    MODE -->|operator| WOPERATOR
    
    MODE -->|normal Linux| LNORMAL
    MODE -->|service Linux| LSERVICE
    
    style START fill:#e1f5ff
    style PLATFORM fill:#fff4e1
    style MODE fill:#f0ffe1
    style WNORMAL fill:#ffe1f5
    style LNORMAL fill:#ffe1f5

Session Modes:

ModePlatformDescriptionInputEvaluation
normalBothInteractive single-taskUser inputOptional
serviceBothWebSocket-controlledRemote requestOptional
followerWindowsReplay recorded planPlan JSON fileOptional
batch_normalWindowsMultiple tasks from filesJSON folderPer-task
operatorWindowsOpenAI Operator APIUser inputOptional
normal_operatorBothInteractive with operatorUser inputOptional

!!!example "SessionFactory Usage" ```python from ufo.module.session_pool import SessionFactory, SessionPool

factory = SessionFactory()

# Interactive Windows session
sessions = factory.create_session(
    task="task1",
    mode="normal",
    plan="",
    request="Open calculator"
)

# Batch Windows sessions from folder
batch_sessions = factory.create_session(
    task="batch_task",
    mode="batch_normal",
    plan="./plans/",  # Folder with multiple .json files
    request=""
)

# Run all sessions
pool = SessionPool(batch_sessions)
await pool.run_all()
```

Cross-Platform Support

The module system provides a unified API while allowing platform-specific behavior through inheritance.

Platform Differences:

AspectWindowsLinux
Agent ArchitectureHostAgent โ†’ AppAgent (two-tier)LinuxAgent (single-tier)
HostAgentโœ… Used for planningโŒ Not used
Session BaseWindowsBaseSessionLinuxBaseSession
UI AutomationUIA (pywinauto)Custom automation
Service ModeServiceSessionLinuxServiceSession
Evaluationโœ… Full supportโš ๏ธ Limited
Markdown Logsโœ… Supportedโš ๏ธ Planned

!!!example "Platform Detection" ```python import platform

# Auto-detect platform
current_platform = platform.system().lower()  # 'windows' or 'linux'

# Override platform
sessions = factory.create_session(
    task="cross_platform_task",
    mode="normal",
    plan="",
    request="List files",
    platform_override="linux"  # Force Linux session
)
```

Execution Flow

Understanding how components interact during a complete user request:

sequenceDiagram
    participant User
    participant Interactor
    participant SessionFactory
    participant Session
    participant Round
    participant Context
    participant Agent
    participant Dispatcher
    participant MCP
    
    User->>Interactor: Start UFO
    Interactor->>User: Show welcome, ask request
    User->>Interactor: "Open Notepad and type Hello"
    
    Interactor->>SessionFactory: create_session(request)
    SessionFactory->>Session: __init__(task, request)
    Session->>Context: Initialize context
    Session->>Agent: Initialize agents
    
    Session->>Session: run()
    loop Until is_finished()
        Session->>Round: create_new_round()
        Round->>Context: Initialize round context
        
        loop Until round.is_finished()
            Round->>Agent: handle(context)
            Agent->>Agent: Process current state
            Agent->>Dispatcher: execute_commands([cmd1, cmd2])
            Dispatcher->>MCP: Route to MCP tools
            MCP-->>Dispatcher: Results
            Dispatcher-->>Agent: Results
            Agent->>Context: Update state
            
            Round->>Agent: Transition to next state
            Round->>Agent: Switch agent if needed
        end
        
        Round->>Round: capture_last_snapshot()
        Round-->>Session: Round complete
        
        Session->>Interactor: new_request()
        Interactor->>User: Continue or exit?
        User->>Interactor: "N"
    end
    
    Session->>Session: evaluation()
    Session->>Interactor: experience_asker()
    Interactor->>User: Save experience?
    User->>Interactor: Yes
    Session->>Session: experience_saver()
    Session-->>User: Session complete

File Structure

ufo/module/
โ”œโ”€โ”€ __init__.py
โ”œโ”€โ”€ basic.py                    # BaseSession, BaseRound, FileWriter
โ”œโ”€โ”€ context.py                  # Context, ContextNames
โ”œโ”€โ”€ dispatcher.py               # Command dispatchers
โ”œโ”€โ”€ interactor.py               # User interaction functions
โ”œโ”€โ”€ session_pool.py             # SessionFactory, SessionPool
โ””โ”€โ”€ sessions/
    โ”œโ”€โ”€ __init__.py
    โ”œโ”€โ”€ platform_session.py     # WindowsBaseSession, LinuxBaseSession
    โ”œโ”€โ”€ session.py              # Session, FollowerSession, FromFileSession
    โ”œโ”€โ”€ service_session.py      # ServiceSession
    โ”œโ”€โ”€ linux_session.py        # LinuxSession, LinuxServiceSession
    โ””โ”€โ”€ plan_reader.py          # PlanReader for follower mode

Key Design Patterns

1. State Pattern

Agents use the State pattern to manage transitions and determine control flow.

# Agent state determines:
next_state = agent.state.next_state(agent)
next_agent = agent.state.next_agent(agent)
is_done = agent.state.is_round_end()

2. Factory Pattern

SessionFactory creates appropriate session types based on platform and mode.

3. Command Pattern

Commands encapsulate actions with parameters, enabling async execution and result tracking.

4. Observer Pattern

Context changes notify dependent components (implicit through shared state).


Best Practices

!!!tip "Session Management" - โœ… Always initialize context before creating rounds - โœ… Use SessionFactory for session creation (handles platform differences) - โœ… Attach command dispatcher to context early - โœ… Call context._sync_round_values() before accessing round-specific data - โŒ Don't access round context before round initialization

!!!tip "Round Execution" - โœ… Let the state machine control agent transitions - โœ… Capture snapshots at subtask boundaries - โœ… Check is_finished() before each iteration - โŒ Don't bypass state transitions - โŒ Don't manually manipulate agent states

!!!tip "Context Usage" - โœ… Use ContextNames enum for type-safe access - โœ… Update dictionaries with update_dict() for merging - โœ… Use properties (current_round_cost) for auto-synced values - โŒ Don't directly access _context dictionary - โŒ Don't store non-serializable objects without marking them

!!!tip "Command Dispatch" - โœ… Always await execute_commands() (async) - โœ… Handle timeout exceptions gracefully - โœ… Check ResultStatus before using results - โŒ Don't ignore error results - โŒ Don't assume commands succeed


Configuration

Key configuration options from ufo_config:

SettingLocationDefaultPurpose
max_stepsystem.max_step50Max steps per session
max_roundsystem.max_round10Max rounds per session
eva_sessionsystem.eva_sessionTrueEvaluate session
eva_roundsystem.eva_roundFalseEvaluate each round
save_experiencesystem.save_experience"ask"When to save experience
log_to_markdownsystem.log_to_markdownTrueGenerate markdown logs
save_ui_treesystem.save_ui_treeTrueSave UI tree snapshots

Documentation Index

DocumentDescription
SessionSession lifecycle and management
RoundRound execution and orchestration
ContextState management and context names
DispatcherCommand routing and execution
Session PoolFactory and batch management
Platform SessionsWindows/Linux implementations

Next Steps

Learning Path:

  1. Understand Sessions: Read Session to grasp the conversation model
  2. Learn Rounds: Study Round to understand action execution
  3. Master Context: Review Context for state management
  4. Explore Dispatch: Check Dispatcher for command execution
  5. Platform Specifics: See Platform Sessions for Windows/Linux differences