The agent loop is the core processing engine. It: 1. Receives messages from the bus 2. Builds context with history, memory, skills 3. Calls the LLM 4. Executes tool calls 5. Sends responses back
| 30 | |
| 31 | |
| 32 | class AgentLoop: |
| 33 | """ |
| 34 | The agent loop is the core processing engine. |
| 35 | |
| 36 | It: |
| 37 | 1. Receives messages from the bus |
| 38 | 2. Builds context with history, memory, skills |
| 39 | 3. Calls the LLM |
| 40 | 4. Executes tool calls |
| 41 | 5. Sends responses back |
| 42 | """ |
| 43 | |
| 44 | def __init__( |
| 45 | self, |
| 46 | bus: MessageBus, |
| 47 | provider: LLMProvider, |
| 48 | workspace: Path, |
| 49 | model: str | None = None, |
| 50 | max_iterations: int = 20, |
| 51 | brave_api_key: str | None = None, |
| 52 | exec_config: ExecToolConfig | None = None, |
| 53 | cron_service: CronService | None = None, |
| 54 | restrict_to_workspace: bool = False, |
| 55 | session_manager: SessionManager | None = None, |
| 56 | ): |
| 57 | from nanobot.config.schema import ExecToolConfig |
| 58 | |
| 59 | self.bus = bus |
| 60 | self.provider = provider |
| 61 | self.workspace = workspace |
| 62 | self.model = model or provider.get_default_model() |
| 63 | self.max_iterations = max_iterations |
| 64 | self.brave_api_key = brave_api_key |
| 65 | self.exec_config = exec_config or ExecToolConfig() |
| 66 | self.cron_service = cron_service |
| 67 | self.restrict_to_workspace = restrict_to_workspace |
| 68 | |
| 69 | self.context = ContextBuilder(workspace) |
| 70 | self.sessions = session_manager or SessionManager(workspace) |
| 71 | self.tools = ToolRegistry() |
| 72 | self.subagents = SubagentManager( |
| 73 | provider=provider, |
| 74 | workspace=workspace, |
| 75 | bus=bus, |
| 76 | model=self.model, |
| 77 | brave_api_key=brave_api_key, |
| 78 | exec_config=self.exec_config, |
| 79 | restrict_to_workspace=restrict_to_workspace, |
| 80 | ) |
| 81 | |
| 82 | self._running = False |
| 83 | self._register_default_tools() |
| 84 | |
| 85 | def _register_default_tools(self) -> None: |
| 86 | """Register the default set of tools.""" |
| 87 | # File tools (restrict to workspace if configured) |
| 88 | allowed_dir = self.workspace if self.restrict_to_workspace else None |
| 89 | self.tools.register(ReadFileTool(allowed_dir=allowed_dir)) |