simhacli 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. simhacli-1.0.0/PKG-INFO +216 -0
  2. simhacli-1.0.0/README.md +184 -0
  3. simhacli-1.0.0/agent/agent.py +205 -0
  4. simhacli-1.0.0/agent/events.py +116 -0
  5. simhacli-1.0.0/agent/session.py +95 -0
  6. simhacli-1.0.0/agent/state.py +121 -0
  7. simhacli-1.0.0/client/llm_client.py +247 -0
  8. simhacli-1.0.0/client/response.py +95 -0
  9. simhacli-1.0.0/config/config.py +155 -0
  10. simhacli-1.0.0/config/loader.py +277 -0
  11. simhacli-1.0.0/context/compaction.py +92 -0
  12. simhacli-1.0.0/context/loop_detector.py +50 -0
  13. simhacli-1.0.0/context/manager.py +217 -0
  14. simhacli-1.0.0/hooks/hook_system.py +142 -0
  15. simhacli-1.0.0/main.py +502 -0
  16. simhacli-1.0.0/prompts/system.py +337 -0
  17. simhacli-1.0.0/pyproject.toml +64 -0
  18. simhacli-1.0.0/safety/approval.py +314 -0
  19. simhacli-1.0.0/setup.cfg +4 -0
  20. simhacli-1.0.0/simhacli.egg-info/PKG-INFO +216 -0
  21. simhacli-1.0.0/simhacli.egg-info/SOURCES.txt +46 -0
  22. simhacli-1.0.0/simhacli.egg-info/dependency_links.txt +1 -0
  23. simhacli-1.0.0/simhacli.egg-info/entry_points.txt +2 -0
  24. simhacli-1.0.0/simhacli.egg-info/requires.txt +9 -0
  25. simhacli-1.0.0/simhacli.egg-info/top_level.txt +11 -0
  26. simhacli-1.0.0/tools/base.py +197 -0
  27. simhacli-1.0.0/tools/builtin/__init__.py +43 -0
  28. simhacli-1.0.0/tools/builtin/edit_file.py +211 -0
  29. simhacli-1.0.0/tools/builtin/glob.py +131 -0
  30. simhacli-1.0.0/tools/builtin/grep.py +236 -0
  31. simhacli-1.0.0/tools/builtin/list_dir.py +60 -0
  32. simhacli-1.0.0/tools/builtin/memory.py +115 -0
  33. simhacli-1.0.0/tools/builtin/read_file.py +131 -0
  34. simhacli-1.0.0/tools/builtin/shell.py +217 -0
  35. simhacli-1.0.0/tools/builtin/todo.py +245 -0
  36. simhacli-1.0.0/tools/builtin/web_fetch.py +126 -0
  37. simhacli-1.0.0/tools/builtin/web_search.py +91 -0
  38. simhacli-1.0.0/tools/builtin/write_file.py +107 -0
  39. simhacli-1.0.0/tools/discovery.py +70 -0
  40. simhacli-1.0.0/tools/mcp/client.py +114 -0
  41. simhacli-1.0.0/tools/mcp/mcp_manager.py +91 -0
  42. simhacli-1.0.0/tools/mcp/mcp_tool.py +51 -0
  43. simhacli-1.0.0/tools/registry.py +163 -0
  44. simhacli-1.0.0/tools/subagent.py +404 -0
  45. simhacli-1.0.0/ui/tui.py +848 -0
  46. simhacli-1.0.0/utils/errors.py +49 -0
  47. simhacli-1.0.0/utils/paths.py +40 -0
  48. simhacli-1.0.0/utils/text.py +91 -0
@@ -0,0 +1,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: simhacli
3
+ Version: 1.0.0
4
+ Summary: SimhaCLI 🦁 — AI Coding Agent that runs inside your terminal
5
+ Author: Narasimha Naidu Korrapti
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/naidu199/SimhaCLI
8
+ Project-URL: Repository, https://github.com/naidu199/SimhaCLI
9
+ Keywords: ai,cli,agent,coding,llm,terminal
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Code Generators
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: click>=8.0.0
24
+ Requires-Dist: rich>=13.0.0
25
+ Requires-Dist: httpx>=0.25.0
26
+ Requires-Dist: anyio>=4.0.0
27
+ Requires-Dist: pydantic>=2.0.0
28
+ Requires-Dist: toml>=0.10.0
29
+ Requires-Dist: ddgs>=6.0.0
30
+ Requires-Dist: fastmcp>=2.0.0
31
+ Requires-Dist: diskcache>=5.0.0
32
+
33
+ <div align="center">
34
+
35
+ # 🦁 SimhaCLI
36
+
37
+ ### AI-Powered Coding Agent for Your Terminal
38
+
39
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
40
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
41
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
42
+
43
+ _Built by Narasimha Naidu Korrapti_
44
+
45
+ [Features](#-features) • [Installation](#-installation)
46
+
47
+ </div>
48
+
49
+ ---
50
+
51
+ ## 📖 Overview
52
+
53
+ **SimhaCLI** is a powerful terminal-based AI coding agent that brings the intelligence of Large Language Models directly into your development workflow. It seamlessly integrates with your codebase, understands context, and executes actions through a comprehensive set of builtin tools.
54
+
55
+ ### Why SimhaCLI?
56
+
57
+ - 🚀 **Session-Based Architecture**: Persistent context and memory across interactions
58
+ - 🛠️ **11 Builtin Tools**: File operations, shell commands, web access, task management
59
+ - 🔒 **Safety First**: Shell command blocking prevents dangerous operations
60
+ - 💾 **Persistent Memory**: Remember user preferences and context
61
+ - 🎨 **Beautiful TUI**: Rich terminal interface with syntax highlighting
62
+ - ⚡ **Streaming Responses**: Real-time output as the agent thinks
63
+ - 🔄 **Event-Driven**: Observable agent actions with full transparency
64
+
65
+ ---
66
+
67
+ ## ✨ Features
68
+
69
+ ### 🤖 Intelligent Agent
70
+
71
+ - **Agentic Loop**: Autonomous multi-turn conversations with tool usage
72
+ - **Context Management**: Tracks conversation history with token counting
73
+ - **Turn Tracking**: Session-based state management with UUIDs
74
+ - **Streaming Output**: Real-time response generation
75
+
76
+ ### 🛠️ Comprehensive Toolset
77
+
78
+ 11 builtin tools across 5 categories:
79
+
80
+ - **📖 Read**: `read_file`, `list_dir`, `glob`, `grep`
81
+ - **✏️ Write**: `write_file`, `edit_file`
82
+ - **🖥️ Shell**: `shell` (with 40+ blocked dangerous commands)
83
+ - **🌐 Web**: `web_search`, `web_fetch`
84
+ - **💾 Memory**: `todos` (task management), `memory` (persistent storage)
85
+
86
+ ### 🔒 Safety & Security
87
+
88
+ - Command blocking for dangerous operations (rm -rf, format, etc.)
89
+ - Timeout protection on shell commands (120s default, 600s max)
90
+ - File validation and error handling
91
+ - Configurable working directory restrictions
92
+
93
+ ### 💾 Persistent Storage
94
+
95
+ - **User Memory**: JSON-based key-value storage (`~/.simhacli/user_memory.json`)
96
+ - **Configuration**: System and project-level TOML configs
97
+ - **Session Tracking**: UUID-based session management with timestamps
98
+
99
+ ### 🎨 Rich Terminal UI
100
+
101
+ - Syntax-highlighted code display
102
+ - Color-coded tool execution (cyan=read, yellow=write, white=shell)
103
+ - Live streaming text output
104
+ - Beautiful welcome banner and formatting
105
+ - Error panels with detailed information
106
+
107
+ ---
108
+
109
+ ## 🚀 Installation
110
+
111
+ ### Prerequisites
112
+
113
+ - **Python 3.10+**
114
+ - **API Key** (OpenAI, Gemini, or compatible API)
115
+
116
+ ### Option 1: Install from PyPI (Recommended)
117
+
118
+ ```bash
119
+ pip install simhacli
120
+ ```
121
+
122
+ After installation, you can run SimhaCLI from anywhere:
123
+
124
+ ```bash
125
+ simhacli # Start interactive mode
126
+ simhacli "explain this code" # Run a single prompt
127
+ simhacli --help # Show help
128
+ ```
129
+
130
+ ### Option 2: Install from Source
131
+
132
+ 1. **Clone the repository**
133
+
134
+ ```bash
135
+ git clone https://github.com/naidu199/SimhaCLI.git
136
+ cd SimhaCLI
137
+ ```
138
+
139
+ 2. **Install globally (access from anywhere)**
140
+
141
+ ```bash
142
+ pip install -e .
143
+ ```
144
+
145
+ Or install without editable mode:
146
+
147
+ ```bash
148
+ pip install .
149
+ ```
150
+
151
+ 3. **Now you can use `simhacli` from anywhere:**
152
+
153
+ ```bash
154
+ simhacli # Start interactive mode
155
+ simhacli "your prompt" # Run a single command
156
+ simhacli --cwd /path/to/dir # Set working directory
157
+ ```
158
+
159
+ ### Option 3: Development Setup
160
+
161
+ 1. **Clone and create virtual environment**
162
+
163
+ ```bash
164
+ git clone https://github.com/naidu199/SimhaCLI.git
165
+ cd SimhaCLI
166
+ python -m venv venv
167
+
168
+ # Windows
169
+ venv\Scripts\activate
170
+
171
+ # Linux/Mac
172
+ source venv/bin/activate
173
+ ```
174
+
175
+ 2. **Install in editable mode**
176
+
177
+ ```bash
178
+ pip install -e .
179
+ ```
180
+
181
+ 3. **Run SimhaCLI**
182
+
183
+ ```bash
184
+ simhacli
185
+ ```
186
+
187
+ ### Configuration
188
+
189
+ Set up your API credentials using one of these methods:
190
+
191
+ **Method 1: Environment Variables**
192
+
193
+ ```bash
194
+ # Windows (PowerShell)
195
+ $env:API_KEY = "your_api_key_here"
196
+ $env:API_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
197
+
198
+ # Windows (CMD)
199
+ set API_KEY=your_api_key_here
200
+ set API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
201
+
202
+ # Linux/Mac
203
+ export API_KEY=your_api_key_here
204
+ export API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
205
+ ```
206
+
207
+ **Method 2: Config File**
208
+
209
+ ```bash
210
+ # Create config directory
211
+ mkdir -p ~/.simhacli
212
+
213
+ # Create/edit config.toml
214
+ ```
215
+
216
+ ---
@@ -0,0 +1,184 @@
1
+ <div align="center">
2
+
3
+ # 🦁 SimhaCLI
4
+
5
+ ### AI-Powered Coding Agent for Your Terminal
6
+
7
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
10
+
11
+ _Built by Narasimha Naidu Korrapti_
12
+
13
+ [Features](#-features) • [Installation](#-installation)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## 📖 Overview
20
+
21
+ **SimhaCLI** is a powerful terminal-based AI coding agent that brings the intelligence of Large Language Models directly into your development workflow. It seamlessly integrates with your codebase, understands context, and executes actions through a comprehensive set of builtin tools.
22
+
23
+ ### Why SimhaCLI?
24
+
25
+ - 🚀 **Session-Based Architecture**: Persistent context and memory across interactions
26
+ - 🛠️ **11 Builtin Tools**: File operations, shell commands, web access, task management
27
+ - 🔒 **Safety First**: Shell command blocking prevents dangerous operations
28
+ - 💾 **Persistent Memory**: Remember user preferences and context
29
+ - 🎨 **Beautiful TUI**: Rich terminal interface with syntax highlighting
30
+ - ⚡ **Streaming Responses**: Real-time output as the agent thinks
31
+ - 🔄 **Event-Driven**: Observable agent actions with full transparency
32
+
33
+ ---
34
+
35
+ ## ✨ Features
36
+
37
+ ### 🤖 Intelligent Agent
38
+
39
+ - **Agentic Loop**: Autonomous multi-turn conversations with tool usage
40
+ - **Context Management**: Tracks conversation history with token counting
41
+ - **Turn Tracking**: Session-based state management with UUIDs
42
+ - **Streaming Output**: Real-time response generation
43
+
44
+ ### 🛠️ Comprehensive Toolset
45
+
46
+ 11 builtin tools across 5 categories:
47
+
48
+ - **📖 Read**: `read_file`, `list_dir`, `glob`, `grep`
49
+ - **✏️ Write**: `write_file`, `edit_file`
50
+ - **🖥️ Shell**: `shell` (with 40+ blocked dangerous commands)
51
+ - **🌐 Web**: `web_search`, `web_fetch`
52
+ - **💾 Memory**: `todos` (task management), `memory` (persistent storage)
53
+
54
+ ### 🔒 Safety & Security
55
+
56
+ - Command blocking for dangerous operations (rm -rf, format, etc.)
57
+ - Timeout protection on shell commands (120s default, 600s max)
58
+ - File validation and error handling
59
+ - Configurable working directory restrictions
60
+
61
+ ### 💾 Persistent Storage
62
+
63
+ - **User Memory**: JSON-based key-value storage (`~/.simhacli/user_memory.json`)
64
+ - **Configuration**: System and project-level TOML configs
65
+ - **Session Tracking**: UUID-based session management with timestamps
66
+
67
+ ### 🎨 Rich Terminal UI
68
+
69
+ - Syntax-highlighted code display
70
+ - Color-coded tool execution (cyan=read, yellow=write, white=shell)
71
+ - Live streaming text output
72
+ - Beautiful welcome banner and formatting
73
+ - Error panels with detailed information
74
+
75
+ ---
76
+
77
+ ## 🚀 Installation
78
+
79
+ ### Prerequisites
80
+
81
+ - **Python 3.10+**
82
+ - **API Key** (OpenAI, Gemini, or compatible API)
83
+
84
+ ### Option 1: Install from PyPI (Recommended)
85
+
86
+ ```bash
87
+ pip install simhacli
88
+ ```
89
+
90
+ After installation, you can run SimhaCLI from anywhere:
91
+
92
+ ```bash
93
+ simhacli # Start interactive mode
94
+ simhacli "explain this code" # Run a single prompt
95
+ simhacli --help # Show help
96
+ ```
97
+
98
+ ### Option 2: Install from Source
99
+
100
+ 1. **Clone the repository**
101
+
102
+ ```bash
103
+ git clone https://github.com/naidu199/SimhaCLI.git
104
+ cd SimhaCLI
105
+ ```
106
+
107
+ 2. **Install globally (access from anywhere)**
108
+
109
+ ```bash
110
+ pip install -e .
111
+ ```
112
+
113
+ Or install without editable mode:
114
+
115
+ ```bash
116
+ pip install .
117
+ ```
118
+
119
+ 3. **Now you can use `simhacli` from anywhere:**
120
+
121
+ ```bash
122
+ simhacli # Start interactive mode
123
+ simhacli "your prompt" # Run a single command
124
+ simhacli --cwd /path/to/dir # Set working directory
125
+ ```
126
+
127
+ ### Option 3: Development Setup
128
+
129
+ 1. **Clone and create virtual environment**
130
+
131
+ ```bash
132
+ git clone https://github.com/naidu199/SimhaCLI.git
133
+ cd SimhaCLI
134
+ python -m venv venv
135
+
136
+ # Windows
137
+ venv\Scripts\activate
138
+
139
+ # Linux/Mac
140
+ source venv/bin/activate
141
+ ```
142
+
143
+ 2. **Install in editable mode**
144
+
145
+ ```bash
146
+ pip install -e .
147
+ ```
148
+
149
+ 3. **Run SimhaCLI**
150
+
151
+ ```bash
152
+ simhacli
153
+ ```
154
+
155
+ ### Configuration
156
+
157
+ Set up your API credentials using one of these methods:
158
+
159
+ **Method 1: Environment Variables**
160
+
161
+ ```bash
162
+ # Windows (PowerShell)
163
+ $env:API_KEY = "your_api_key_here"
164
+ $env:API_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
165
+
166
+ # Windows (CMD)
167
+ set API_KEY=your_api_key_here
168
+ set API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
169
+
170
+ # Linux/Mac
171
+ export API_KEY=your_api_key_here
172
+ export API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
173
+ ```
174
+
175
+ **Method 2: Config File**
176
+
177
+ ```bash
178
+ # Create config directory
179
+ mkdir -p ~/.simhacli
180
+
181
+ # Create/edit config.toml
182
+ ```
183
+
184
+ ---
@@ -0,0 +1,205 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import AsyncGenerator, Awaitable, Callable
4
+
5
+ from agent.events import AgentEvent, AgentEventType
6
+ from agent.session import Session
7
+ from client.response import (
8
+ StreamEventType,
9
+ TokenUsage,
10
+ ToolCall,
11
+ ToolResultMessage,
12
+ parse_tool_call_arguments,
13
+ )
14
+ from config.config import Config
15
+ from prompts.system import create_loop_breaker_prompt
16
+ from tools.base import ToolConfirmation
17
+
18
+
19
+ class Agent:
20
+ def __init__(
21
+ self,
22
+ config: Config,
23
+ confirmation_callback: (
24
+ Callable[[ToolConfirmation], Awaitable[bool]] | None
25
+ ) = None,
26
+ ) -> None:
27
+ self.config = config
28
+ # self.client = LLMClient(config=self.config)
29
+ # self.context_manager = ContextManager(config=self.config)
30
+ # self.tool_registry = create_default_registry(config=self.config)
31
+ self.session: Session | None = Session(config=self.config)
32
+
33
+ self.session.approval_manager.confirmation_callback = confirmation_callback
34
+
35
+ async def run(self, message: str) -> AsyncGenerator[AgentEvent, None]:
36
+ await self.session.hook_system.trigger_before_agent(message)
37
+ yield AgentEvent.agent_start(message=message)
38
+ self.session.context_manager.add_user_message(message)
39
+
40
+ # add user message to the context
41
+ final_message: str | None = None
42
+ try:
43
+ async for event in self._agentic_loop():
44
+ yield event
45
+ if event.type == AgentEventType.TEXT_COMPLETE:
46
+ final_message = event.data.get("content", "")
47
+ # yield AgentEvent.text_complete(content=final_message)
48
+ await self.session.hook_system.trigger_after_agent(
49
+ user_message=message,
50
+ agent_response=final_message or "",
51
+ )
52
+ yield AgentEvent.agent_end(response=final_message)
53
+ except Exception as e:
54
+ yield AgentEvent.agent_error(f"Agent encountered an error: {str(e)}")
55
+ await self.session.hook_system.trigger_on_error(e)
56
+
57
+ async def _agentic_loop(self) -> AsyncGenerator[AgentEvent, None]:
58
+
59
+ max_turns = self.config.max_turns
60
+
61
+ for turn_no in range(max_turns):
62
+ self.session.increment_turn_count()
63
+ response_text = ""
64
+ usage: TokenUsage | None = None
65
+ # checking for the context overflow and trimming if necessary
66
+ if self.session.context_manager.needs_compression():
67
+ summary, usage = await self.session.chat_compressor.compress(
68
+ self.session.context_manager
69
+ )
70
+ if summary:
71
+ self.session.context_manager.replace_with_summary(summary)
72
+ self.session.context_manager.set_latest_usage(usage)
73
+ self.session.context_manager.add_usage(usage)
74
+ tool_schema = self.session.tool_registry.get_schemas()
75
+ tool_calls: list[ToolCall] = []
76
+ has_error = False
77
+ async for event in self.session.client.chat_completion(
78
+ self.session.context_manager.get_messages(),
79
+ tools=tool_schema if tool_schema else None,
80
+ ):
81
+
82
+ if event.type == StreamEventType.TEXT_DELTA:
83
+ content = event.text_delta.content if event.text_delta else ""
84
+ response_text += content
85
+ yield AgentEvent.text_delta(content)
86
+ elif event.type == StreamEventType.TOOL_CALL_COMPLETE:
87
+ if event.tool_call:
88
+ tool_calls.append(event.tool_call)
89
+ elif event.type == StreamEventType.ERROR:
90
+ error_msg = event.error if event.error else "Unknown error"
91
+ has_error = True
92
+ yield AgentEvent.agent_error(error_msg)
93
+ return # Stop processing on error
94
+ elif event.type == StreamEventType.MESSAGE_COMPLETE:
95
+ usage = event.usage
96
+ # Only yield text_complete once at the end with full response
97
+ yield AgentEvent.text_complete(content=response_text)
98
+
99
+ # Convert tool_calls to the format expected by the API
100
+ tool_calls_dict = (
101
+ [
102
+ {
103
+ "id": tc.call_id,
104
+ "type": "function",
105
+ "function": {
106
+ "name": tc.name,
107
+ "arguments": tc.arguments,
108
+ },
109
+ }
110
+ for tc in tool_calls
111
+ ]
112
+ if tool_calls
113
+ else None
114
+ )
115
+
116
+ self.session.context_manager.add_assistant_message(
117
+ response_text or "", tool_calls=tool_calls_dict
118
+ )
119
+
120
+ if response_text:
121
+ yield AgentEvent.text_complete(response_text)
122
+ self.session.loop_detector.record_action(
123
+ "response",
124
+ text=response_text,
125
+ )
126
+
127
+ if not tool_calls:
128
+ if usage:
129
+ self.session.context_manager.set_latest_usage(usage)
130
+ self.session.context_manager.add_usage(usage)
131
+
132
+ self.session.context_manager.prune_tool_outputs()
133
+
134
+ return # No tool calls, end the loop
135
+
136
+ tool_call_results: list[ToolResultMessage] = []
137
+
138
+ for tool_call in tool_calls:
139
+ parsed_args = parse_tool_call_arguments(tool_call.arguments or "")
140
+
141
+ yield AgentEvent.tool_call_start(
142
+ call_id=tool_call.call_id,
143
+ name=tool_call.name,
144
+ arguments=parsed_args,
145
+ )
146
+
147
+ self.session.loop_detector.record_action(
148
+ "tool_call",
149
+ tool_name=tool_call.name,
150
+ args=parse_tool_call_arguments(tool_call.arguments or ""),
151
+ )
152
+
153
+ result = await self.session.tool_registry.invoke(
154
+ tool_call.name or "",
155
+ parsed_args,
156
+ self.config.cwd,
157
+ self.session.approval_manager,
158
+ self.session.hook_system,
159
+ )
160
+
161
+ yield AgentEvent.tool_call_complete(
162
+ tool_call.call_id,
163
+ tool_call.name,
164
+ result,
165
+ )
166
+ tool_call_results.append(
167
+ ToolResultMessage(
168
+ tool_call_id=tool_call.call_id,
169
+ content=result.to_model_output(),
170
+ is_error=not result.success,
171
+ )
172
+ )
173
+
174
+ for tool_result in tool_call_results:
175
+ self.session.context_manager.add_tool_result(
176
+ tool_result.tool_call_id,
177
+ tool_result.content,
178
+ )
179
+ loop_detector_value = self.session.loop_detector.check_for_loop()
180
+ if loop_detector_value:
181
+ loop_prompt = create_loop_breaker_prompt(
182
+ loop_description=loop_detector_value,
183
+ )
184
+ self.session.context_manager.add_user_message(loop_prompt)
185
+ continue # Skip executing the tool and continue the loop
186
+ if usage:
187
+ self.session.context_manager.set_latest_usage(usage)
188
+ self.session.context_manager.add_usage(usage)
189
+
190
+ self.session.context_manager.prune_tool_outputs()
191
+
192
+ yield AgentEvent.agent_error(
193
+ f"Maximum number of turns {self.config.max_turns} reached."
194
+ )
195
+
196
+ async def __aenter__(self) -> Agent:
197
+ await self.session.initialize()
198
+ return self
199
+
200
+ async def __aexit__(self, exc_type, exc, tb) -> None:
201
+ if self.session and self.session.client and self.session.mcp_manager:
202
+ await self.session.client.close_client()
203
+ await self.session.mcp_manager.shutdown_mcp()
204
+ self.session.client = None
205
+ self.session = None