The core agent loop that drives miniClaudeCode. Mirrors Claude Code's loop: prompt -> LLM -> tool_use? -> execute -> loop until the LLM produces a final text-only response.
| 34 | |
| 35 | |
| 36 | class AgentLoop: |
| 37 | """The core agent loop that drives miniClaudeCode. |
| 38 | |
| 39 | Mirrors Claude Code's loop: prompt -> LLM -> tool_use? -> execute -> loop |
| 40 | until the LLM produces a final text-only response. |
| 41 | """ |
| 42 | |
| 43 | def __init__( |
| 44 | self, |
| 45 | config: Config | None = None, |
| 46 | registry: ToolRegistry | None = None, |
| 47 | ) -> None: |
| 48 | self.config = config or Config() |
| 49 | self.registry = registry or ToolRegistry.default() |
| 50 | self.permission_gate = PermissionGate(self.config) |
| 51 | self.context = ConversationContext(config=self.config) |
| 52 | self.client = anthropic.Anthropic() |
| 53 | |
| 54 | system_prompt = build_system_prompt( |
| 55 | self.registry, |
| 56 | permission_mode=self.config.permission_mode.value, |
| 57 | ) |
| 58 | self.context.set_system_prompt(system_prompt) |
| 59 | |
| 60 | def run(self, user_message: str) -> str: |
| 61 | """Process a user message through the agent loop, returning the final text response.""" |
| 62 | self.context.add_user_message(user_message) |
| 63 | final_text = "" |
| 64 | |
| 65 | for turn in range(self.config.max_turns): |
| 66 | response = self._call_api() |
| 67 | tool_calls, text_parts = self._parse_response(response) |
| 68 | |
| 69 | if text_parts: |
| 70 | final_text = "\n".join(text_parts) |
| 71 | |
| 72 | if not tool_calls: |
| 73 | # No tool calls -- the loop ends, return the text |
| 74 | self.context.add_assistant_message(response.content) |
| 75 | break |
| 76 | |
| 77 | # There are tool calls -- execute them and continue the loop |
| 78 | self.context.add_assistant_message(response.content) |
| 79 | self._execute_tool_calls(tool_calls) |
| 80 | else: |
| 81 | if not final_text: |
| 82 | final_text = "(max turns reached without a final response)" |
| 83 | |
| 84 | return final_text |
| 85 | |
| 86 | def _call_api(self) -> Any: |
| 87 | """Call the Anthropic API with current context.""" |
| 88 | return self.client.messages.create( |
| 89 | model=self.config.model, |
| 90 | max_tokens=8192, |
| 91 | system=self.context.system_prompt, |
| 92 | tools=self.registry.api_schemas(), |
| 93 | messages=self.context.get_api_messages(), |