Class: AgentLoop
- Inherits:
-
Object
- Object
- AgentLoop
- Defined in:
- lib/agent_loop.rb
Overview
Not thread-safe. Callers must serialize concurrent calls to #process (e.g. TUI uses a loading flag, future callers should use session-level locks).
Orchestrates the LLM agent loop: accepts user input, runs the tool-use cycle via LLM::Client, and emits events through Events::Bus.
Extracted from TUI::Screens::Chat so the same agent logic can run from the TUI, a background job, or an Action Cable channel.
Instance Attribute Summary collapse
-
#session ⇒ Session
readonly
The conversation session this loop operates on.
Instance Method Summary collapse
-
#finalize ⇒ Object
Clean up the underlying ShellSession PTY and resources.
-
#initialize(session:, shell_session: nil, client: nil, registry: nil) ⇒ AgentLoop
constructor
A new instance of AgentLoop.
-
#process(input) ⇒ String?
Runs the agent loop for a single user input.
-
#run ⇒ String
Runs the LLM tool-use loop on persisted session messages.
Constructor Details
#initialize(session:, shell_session: nil, client: nil, registry: nil) ⇒ AgentLoop
Returns a new instance of AgentLoop.
36 37 38 39 40 41 |
# File 'lib/agent_loop.rb', line 36 def initialize(session:, shell_session: nil, client: nil, registry: nil) @session = session @shell_session = shell_session || ShellSession.new(session_id: session.id) @client = client @registry = registry end |
Instance Attribute Details
#session ⇒ Session (readonly)
Returns the conversation session this loop operates on.
27 28 29 |
# File 'lib/agent_loop.rb', line 27 def session @session end |
Instance Method Details
#finalize ⇒ Object
Clean up the underlying ShellSession PTY and resources. Safe to call multiple times — subsequent calls are no-ops.
83 84 85 |
# File 'lib/agent_loop.rb', line 83 def finalize @shell_session&.finalize end |
#process(input) ⇒ String?
Runs the agent loop for a single user input.
Emits Events::UserMessage immediately, then delegates to #run. On error emits Events::AgentMessage with the error text.
50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/agent_loop.rb', line 50 def process(input) text = input.to_s.strip return if text.empty? Events::Bus.emit(Events::UserMessage.new(content: text, session_id: @session.id)) run rescue => error = "#{error.class}: #{error.}" Events::Bus.emit(Events::AgentMessage.new(content: , session_id: @session.id)) end |
#run ⇒ String
Runs the LLM tool-use loop on persisted session messages.
Unlike #process, does not emit Events::UserMessage and lets errors propagate — designed for callers like AgentRequestJob that handle retries and need errors to bubble up.
71 72 73 74 75 76 77 78 79 |
# File 'lib/agent_loop.rb', line 71 def run @client ||= LLM::Client.new @registry ||= build_tool_registry = @session. response = @client.chat_with_tools(, registry: @registry, session_id: @session.id) Events::Bus.emit(Events::AgentMessage.new(content: response, session_id: @session.id)) response end |