tinyagent-py 0.1.20
🛠️ Build your own AI coding assistant with any model you want. Revolutionary agent framework with secure sandboxed execution, parallel subagents, and freedom to choose any LLM provider - OpenAI, Anthropic, Ollama, or 100+ others.
Requires Python: >=3.8

# TinyAgent
🛠️ **Build Your Own AI Coding Assistant** - Break free from vendor lock-in and create powerful agents with *any* AI model you choose

[![AskDev.AI | Chat with TinyAgent](https://img.shields.io/badge/AskDev.AI-Chat_with_TinyAgent-blue?style=flat-square)](https://askdev.ai/github/askbudi/tinyagent)


![TinyAgent Logo](https://raw.githubusercontent.com/askbudi/tinyagent/main/public/logo.png)


[![AskDev.AI | Chat with TinyAgent](https://img.shields.io/badge/AskDev.AI-Chat_with_TinyAgent-blue?style=flat-square)](https://askdev.ai/github/askbudi/tinyagent)


Inspired by:
- [Tiny Agents blog post](https://huggingface.co/blog/tiny-agents)
- [12-factor-agents repository](https://github.com/humanlayer/12-factor-agents)
- Created by chatting to the source code of JS Tiny Agent using [AskDev.ai](https://askdev.ai/search)

## Quick Links
- [Build your own Tiny Agent](https://askdev.ai/github/askbudi/tinyagent)


## Live Projects using TinyAgent (🔥)
- [AskDev.AI](https://askdev.ai) - Understand, chat, and summarize codebase of any project on GitHub.
- [HackBuddy AI](https://huggingface.co/spaces/ask-dev/HackBuddyAI) - A Hackathon Assistant Agent, built with TinyCodeAgent and Gradio. Match invdividuals to teams based on their skills, interests and organizer preferences.

- [TinyCodeAgent Demo](https://huggingface.co/spaces/ask-dev/TinyCodeAgent) - A playground for TinyCodeAgent, built with tinyagent, Gradio and Modal.com

** Building something with TinyAgent? Let us know and I'll add it here!**


## 🚀 The Vision: Your AI, Your Choice, Your Rules

Tired of being locked into specific AI providers? Want the power of advanced coding assistants without the constraints? TinyAgent gives you **complete freedom** to build intelligent agents that work with *any* AI model - from OpenAI and Anthropic to your own local Ollama models.

**This isn't just another AI wrapper.** It's your gateway to building the coding assistant of your dreams:

### 🎯 Why TinyAgent Changes Everything

- **🔓 Model Freedom**: Switch between GPT-5, Claude-4, Llama, or any 100+ models instantly
- **🏠 Local Privacy**: Run everything locally with Ollama - your code never leaves your machine  
- **🛡️ Production Security**: Enterprise-grade sandboxing across macOS, Linux, and Windows
- **⚡ Parallel Intelligence**: Multiple specialized AI agents working together on complex tasks
- **🔧 Complete Control**: Extend, customize, and hook into every aspect of agent behavior

**Three Revolutionary Components:**
- **TinyAgent**: Your universal AI interface - one API, infinite models
- **TinyCodeAgent**: Secure code execution with cross-platform sandboxing
- **Subagent Swarm**: Parallel specialized workers that collaborate intelligently

### What's new for developers

- **Sandboxed File Tools**: `read_file`, `write_file`, `update_file`, `glob`, `grep` now route through provider sandboxes (Seatbelt/Modal) for secure file operations
- **Enhanced Shell Tool**: Improved `bash` tool with better safety validation, platform-specific tips, and provider-backed execution
- **TodoWrite Tool**: Built-in task management system for tracking progress and organizing complex workflows
- **Provider System**: Pluggable execution backends (Modal.com, Seatbelt sandbox) with unified API
- **Universal Tool Hooks**: Control any tool execution via `before_tool_execution`/`after_tool_execution` callbacks
- **Subagent Tools**: Revolutionary parallel task execution with specialized workers and context isolation
- **Enhanced Security**: Comprehensive validation, sandboxing, and permission controls

## Installation

### Using pip
```bash
# Basic installation
pip install tinyagent-py

# Install with all optional dependencies
pip install tinyagent-py[all]

# Install with Code Agent support
pip install tinyagent-py[code]


# Install with PostgreSQL support
pip install tinyagent-py[postgres]

# Install with SQLite support
pip install tinyagent-py[sqlite]

# Install with Gradio UI support
pip install tinyagent-py[gradio]





```

### Using uv
```bash
# Basic installation
uv pip install tinyagent-py

# Install with Code Agent support
uv pip install tinyagent-py[code]


# Install with PostgreSQL support
uv pip install tinyagent-py[postgres]

# Install with SQLite support
uv pip install tinyagent-py[sqlite]

# Install with Gradio UI support
uv pip install tinyagent-py[gradio]

# Install with all optional dependencies
uv pip install tinyagent-py[all]

```

## Developer Boilerplate & Quick Start

### OpenAI Responses API (optional)

TinyAgent supports OpenAI's Responses API alongside the default Chat Completions flow. To opt in without changing your code, set an environment variable:

```bash
export TINYAGENT_LLM_API=responses
```

Your existing TinyAgent code continues to work. Under the hood, TinyAgent translates your chat `messages`/`tools` to a Responses request and maps the Responses result back to the same structure it already uses (including `tool_calls` and usage accounting). To switch back, unset or set `TINYAGENT_LLM_API=chat`.

Example with explicit toggle:

```python
import os
import asyncio
from tinyagent import TinyAgent

async def main():
    # Option A: via environment variable
    os.environ["TINYAGENT_LLM_API"] = "responses"  # or "chat" (default)
    agent = await TinyAgent.create(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        # Option B: programmatic preference via model_kwargs
        model_kwargs={"llm_api": "responses"},  # or {"use_responses_api": True}
    )
    print(await agent.run("List three safe git commands for a repo"))

asyncio.run(main())
```

Notes:
- The adapter preserves TinyAgent hooks, storage schema, and tool-calling behavior.
- Streaming and semantic events can be added later without changing your code.
- Optional tracing: set `RESPONSES_TRACE_FILE=./responses_trace.jsonl` to capture raw request/response JSON for debugging. Set `DEBUG_RESPONSES=1` to print pairing details.

Examples you can run:
- `examples/openai_sdk_responses_multiturn.py` — baseline SDK multi-turn chaining
- `examples/openai_sdk_responses_extended_tools.py` — SDK multi-turn with function calls
- `examples/litellm_responses_extended_tools.py` — LiteLLM multi-turn with function calls
- `examples/litellm_responses_three_tools.py` — LiteLLM three-tool demo
- `examples/tinyagent_responses_three_tools.py` — TinyAgent three-tool demo (Responses)
- `examples/seatbelt_verbose_tools.py` — TinyCodeAgent + seatbelt, verbose hook stream
- `examples/seatbelt_responses_three_tools.py` — TinyCodeAgent + seatbelt three-tool demo

### 🚀 TinyAgent with New Tools

```python
import asyncio
import os
from tinyagent import TinyAgent
from tinyagent.tools.subagent import create_general_subagent

async def create_enhanced_tinyagent():
    """Create a TinyAgent with all new tools and capabilities."""
    
    # Initialize TinyAgent (TodoWrite is enabled by default)
    agent = TinyAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        enable_todo_write=True  # Enable TodoWrite tool (True by default)
    )
    
    # Add a general-purpose subagent for parallel tasks
    helper_subagent = create_general_subagent(
        name="helper",
        model="gpt-5-mini",
        max_turns=20,
        enable_python=True,
        enable_shell=True
    )
    agent.add_tool(helper_subagent)
    
    # Check available tools
    available_tools = list(agent.custom_tool_handlers.keys())
    print(f"Available tools: {available_tools}")  # ['TodoWrite', 'helper']
    
    # Connect to MCP servers for extended functionality
    await agent.connect_to_server("npx", ["@openbnb/mcp-server-airbnb", "--ignore-robots-txt"])
    
    return agent

async def main():
    agent = await create_enhanced_tinyagent()
    
    try:
        # Example: Complex task with subagent delegation
        result = await agent.run("""
            I need help with a travel planning project:
            1. Create a todo list for this task
            2. Use the helper subagent to find 5 accommodations in Paris for December 2024
            3. Research transportation options between airports and city center
            4. Organize all findings into a structured report
            
            Make sure to track progress with the todo system.
        """)
        
        print("Result:", result)
    finally:
        await agent.close()

# Run the example
asyncio.run(main())
```

### 🛠️ TinyCodeAgent with File Tools & Providers

```python
import asyncio
import os
from tinyagent import TinyCodeAgent
from tinyagent.hooks.rich_code_ui_callback import RichCodeUICallback

async def create_enhanced_code_agent():
    """Create TinyCodeAgent with all file tools and provider features."""
    
    # Option 1: Using Seatbelt Provider (macOS sandbox)
    seatbelt_agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        provider="seatbelt",
        provider_config={
            "python_env_path": "/usr/local/bin/python3",
            "additional_read_dirs": ["/Users/username/projects"],
            "additional_write_dirs": ["/Users/username/projects/output"],
            "environment_variables": {"PROJECT_ROOT": "/Users/username/projects"}
        },
        # Enable all new tools
        enable_python_tool=True,
        enable_shell_tool=True, 
        enable_file_tools=True,
        enable_todo_write=True,
        # REQUIRED: Local execution for Seatbelt provider
        local_execution=True,
        # Working directory for operations
        default_workdir="/Users/username/projects",
        # Auto git checkpoints after shell commands
        auto_git_checkpoint=True,
        # Rich UI for better visualization
        ui="rich",
        # Debug mode control (default: False)
        debug_mode=False  # Set to True to see command execution details
    )
    
    return seatbelt_agent

async def create_modal_code_agent():
    """Create TinyCodeAgent with Modal.com provider."""
    
    modal_agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        provider="modal",
        provider_config={
            "pip_packages": ["requests", "pandas", "matplotlib", "seaborn"]
        },
        authorized_imports=["requests", "pandas", "matplotlib", "seaborn", "numpy"],
        enable_python_tool=True,
        enable_shell_tool=True,
        enable_file_tools=True,
        enable_todo_write=True,
        local_execution=False,  # Use Modal cloud execution
        truncation_config={
            "max_tokens": 5000,
            "max_lines": 300,
            "enabled": True
        }
    )
    
    return modal_agent

async def demonstrate_file_tools():
    """Demonstrate the new file tools functionality."""
    
    agent = await create_enhanced_code_agent()
    
    try:
        # Check available tools
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")
        
        result = await agent.run("""
        I need to analyze a Python project structure:
        
        1. Use glob to find all Python files in the current directory
        2. Use grep to search for "class" definitions across all Python files
        3. Read the main configuration file if it exists
        4. Create a summary report of the project structure
        5. Track progress with todos
        
        Make sure to use the new file tools for secure operations.
        """)
        
        print("Analysis Result:", result)
        
    finally:
        await agent.close()

# Choose your provider
async def main():
    print("Demonstrating TinyCodeAgent with enhanced file tools...")
    await demonstrate_file_tools()

asyncio.run(main())
```

### 📁 File Tools Usage Examples

```python
import asyncio
from tinyagent import TinyCodeAgent

async def file_tools_examples():
    """Examples of using the new sandboxed file tools."""
    
    agent = TinyCodeAgent(
        model="gpt-4o-mini",
        local_execution=True,  # Auto-selects best provider
        enable_file_tools=True
    )
    
    try:
        # Check available tools
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available file tools: {available_tools}")
        
        # Example 1: Project structure analysis
        await agent.run("""
        Use glob to find all Python files in this project:
        - Pattern: "**/*.py" 
        - Search in: "/Users/username/myproject"
        
        Then use grep to find all function definitions:
        - Pattern: "def "
        - Search in the same directory
        
        Finally, read the main.py file to understand the entry point.
        """)
        
        # Example 2: Safe file modification
        await agent.run("""
        I need to update a configuration file:
        1. Read config.json to see current settings
        2. Update the database URL using update_file tool
        3. Verify the changes were applied correctly
        
        Make sure to use exact string matching for safety.
        """)
        
        # Example 3: Code generation and file creation
        await agent.run("""
        Create a new Python module:
        1. Use write_file to create utils/helpers.py
        2. Add utility functions for string manipulation
        3. Include proper docstrings and type hints
        4. Create a simple test file for the utilities
        """)
        
    finally:
        await agent.close()

asyncio.run(file_tools_examples())
```

### 🔧 Grep and Glob Tool Examples

```python
# Glob tool examples
await agent.run("""
Find all JavaScript files in the frontend directory:
Use glob with pattern "**/*.{js,jsx}" in "/path/to/frontend"
""")

await agent.run("""
Find all markdown documentation:
Use glob with pattern "**/*.md" in "/path/to/project"
""")

# Grep tool examples  
await agent.run("""
Search for all TODO comments in the codebase:
Use grep with pattern "TODO|FIXME|XXX" and regex=True
Search in "/path/to/project" directory
Use output_mode="content" to see the actual lines
""")

await agent.run("""
Find all API endpoints in Python files:
Use grep with pattern "@app.route" 
Search only in Python files using glob="**/*.py"
""")
```

### 📋 TodoWrite Tool Integration

```python
import asyncio
from tinyagent import TinyAgent
from tinyagent.tools.todo_write import get_current_todos, get_todo_summary

async def todo_workflow_example():
    """Example of using TodoWrite for task management."""
    
    agent = TinyAgent(
        model="gpt-5-mini",
        enable_todo_write=True  # Enabled by default
    )
    
    try:
        # Check that TodoWrite tool is available
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")  # Should include 'TodoWrite'
        
        # The agent can automatically use TodoWrite during complex tasks
        result = await agent.run("""
        I need to build a web scraping system:
        1. Create a todo list for this project
        2. Research the target website structure
        3. Implement the scraping logic with error handling
        4. Add data validation and cleaning
        5. Create output formatting and export functions
        6. Write tests for each component
        7. Update todos as you progress
        
        Use the TodoWrite tool to track all these steps.
        """)
        
        # Check current todos programmatically
        current_todos = get_current_todos()
        summary = get_todo_summary()
        
        print(f"Project Status: {summary}")
        print(f"Active Todos: {len(current_todos)}")
        
    finally:
        await agent.close()

asyncio.run(todo_workflow_example())
```

### 🐛 Debug Mode Control

TinyAgent supports debug mode to control execution provider debug output, helping you troubleshoot issues or keep production output clean:

```python
import asyncio
import os
from tinyagent import TinyCodeAgent

async def debug_mode_examples():
    """Examples of debug mode control for TinyCodeAgent."""
    
    # Production mode: Clean output (default)
    production_agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        provider="seatbelt",
        local_execution=True,
        debug_mode=False  # Default: No debug prints
    )
    
    # Development mode: Show execution details  
    debug_agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        provider="seatbelt", 
        local_execution=True,
        debug_mode=True   # Shows command execution details
    )
    
    # Environment variable control (overrides constructor)
    os.environ['TINYAGENT_DEBUG_MODE'] = '1'  # Enable globally
    env_agent = TinyCodeAgent(
        model="gpt-5-mini",
        provider="seatbelt",
        local_execution=True
        # debug_mode will be True due to environment variable
    )
    
    try:
        # Production agent: Clean output
        print("=== Production Mode (Clean Output) ===")
        await production_agent.run("Run: echo 'Hello Production'")
        
        # Debug agent: Detailed output with command traces
        print("\n=== Debug Mode (Detailed Output) ===") 
        await debug_agent.run("Run: echo 'Hello Debug'")
        
    finally:
        await production_agent.close()
        await debug_agent.close()
        await env_agent.close()

asyncio.run(debug_mode_examples())
```

**Debug mode shows:**
- 🔍 Shell command execution markers (`#########################<Bash>#########################`)
- 🎨 Color-coded command output (blue for commands, green for success, red for errors)
- 📝 Python code execution details (when `enable_python_tool=True`)
- ⚙️ Provider-specific execution information across all providers (Seatbelt, Docker, Modal, Bubblewrap)

**Environment Variable Control:**
```bash
# Enable debug mode globally
export TINYAGENT_DEBUG_MODE=1        # or 'true', 'yes', 'on'

# Disable debug mode globally  
export TINYAGENT_DEBUG_MODE=0        # or 'false', 'no', 'off'

# Unset to use constructor parameter
unset TINYAGENT_DEBUG_MODE
```

**Use Cases:**
- **🚀 Production**: `debug_mode=False` (default) for clean, user-friendly output
- **🔧 Development**: `debug_mode=True` for troubleshooting execution issues and understanding command flow
- **🧪 CI/CD**: Environment variable control for flexible debugging in different deployment stages
- **📊 Monitoring**: Enable selectively to diagnose specific execution problems

**Cross-Platform Support:**
Debug mode works consistently across all execution providers:
- **macOS**: Seatbelt provider debug output
- **Linux**: Bubblewrap provider debug output  
- **Windows/Universal**: Docker provider debug output
- **Cloud**: Modal provider debug output

### 🔒 Universal Tool Control with Hooks

```python
import asyncio
from tinyagent import TinyCodeAgent
from tinyagent.code_agent.tools.file_tools import FileOperationApprovalHook, ProductionApprovalHook

class CustomFileHook(FileOperationApprovalHook):
    """Custom hook for file operation control."""
    
    async def before_tool_execution(self, event_name: str, agent, **kwargs):
        tool_name = kwargs.get("tool_name")
        tool_args = kwargs.get("tool_args", {})
        
        # Custom logic for file operations
        if tool_name in ["write_file", "update_file"]:
            file_path = tool_args.get("file_path", "")
            
            # Block operations on sensitive files
            if "secret" in file_path.lower() or "password" in file_path.lower():
                print(f"🚫 Blocked file operation on sensitive file: {file_path}")
                return {"proceed": False, "reason": "Sensitive file access denied"}
            
            # Log all file modifications
            print(f"📝 File operation: {tool_name} on {file_path}")
        
        return {"proceed": True}

async def controlled_agent_example():
    """Example of agent with file operation controls."""
    
    agent = TinyCodeAgent(
        model="gpt-5-mini", 
        provider="seatbelt",
        enable_file_tools=True
    )
    
    # Add custom file control hook
    file_hook = CustomFileHook(auto_approve=False)
    agent.add_callback(file_hook)
    
    try:
        await agent.run("""
        Analyze and modify some project files:
        1. Read the main application file
        2. Update version information in package.json
        3. Create a backup of important configuration
        
        The system will control which operations are allowed.
        """)
        
    finally:
        await agent.close()

asyncio.run(controlled_agent_example())
```

## 📚 Complete Examples Collection

<details>
<summary>Click to expand complete working examples for all TinyAgent features</summary>

### 1. Basic TinyAgent Example

```python
import asyncio
import os
from tinyagent import TinyAgent

async def example_1_basic_tinyagent():
    """✅  Basic TinyAgent example."""
    print("Example 1: Basic TinyAgent")
    
    agent = TinyAgent(
        model="gpt-5-mini",
        api_key=os.environ.get("OPENAI_API_KEY"),
        system_prompt="You are a helpful assistant."
    )
    
    try:
        # The TodoWrite tool is automatically enabled by default
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")
        
        # For actual use, you would call:
        # result = await agent.run("Your task here")
        # print(result)
        
        print("✅ Success: Basic TinyAgent initialized correctly")
        
    finally:
        await agent.close()

asyncio.run(example_1_basic_tinyagent())
```

### 2. Enhanced TinyAgent with Subagents

```python
import asyncio
import os
from tinyagent import TinyAgent
from tinyagent.tools.subagent import create_general_subagent, create_coding_subagent

async def example_2_enhanced_tinyagent():
    """✅  Enhanced TinyAgent with subagents."""
    print("Example 2: Enhanced TinyAgent with Subagents")
    
    # Create agent with TodoWrite enabled by default
    agent = TinyAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        enable_todo_write=True  # This is True by default
    )
    
    # Add a general-purpose subagent
    helper_subagent = create_general_subagent(
        name="helper",
        model="gpt-5-mini", 
        max_turns=20,
        enable_python=True,
        enable_shell=True
    )
    agent.add_tool(helper_subagent)
    
    # Add a coding subagent
    coder = create_coding_subagent(
        name="coder",
        model="gpt-5-mini",
        max_turns=25
    )
    agent.add_tool(coder)
    
    try:
        # Check available tools - they are in custom_tool_handlers
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")
        
        # For MCP server connections (if needed):
        # await agent.connect_to_server("npx", ["@openbnb/mcp-server-airbnb", "--ignore-robots-txt"])
        
        print("✅ Success: Enhanced TinyAgent with subagents")
        
    finally:
        await agent.close()

asyncio.run(example_2_enhanced_tinyagent())
```

### 3. Basic TinyCodeAgent Example

```python
import asyncio
import os
from tinyagent import TinyCodeAgent

async def example_3_basic_tinycodeagent():
    """✅  Basic TinyCodeAgent example."""
    print("Example 3: Basic TinyCodeAgent")
    
    agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.environ.get("OPENAI_API_KEY"),
        provider="seatbelt",
        enable_python_tool=True,
        enable_shell_tool=True,
        enable_file_tools=True,
        enable_todo_write=True,
        local_execution=True  # REQUIRED for Seatbelt provider
    )
    
    try:
        # Check available tools - they are in custom_tool_handlers
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")
        
        # For actual use:
        # result = await agent.run("Write a Python function to calculate factorial")
        # print(result)
        
        print("✅ Success: TinyCodeAgent initialized correctly")
        
    finally:
        await agent.close()

asyncio.run(example_3_basic_tinycodeagent())
```

### 4. Enhanced TinyCodeAgent with Full Configuration

```python
import asyncio
import os
from tinyagent import TinyCodeAgent

async def example_4_enhanced_tinycodeagent():
    """✅  Enhanced TinyCodeAgent with all features."""
    print("Example 4: Enhanced TinyCodeAgent")
    
    agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        provider="seatbelt",
        provider_config={
            "python_env_path": "/usr/bin/python3",
            "additional_read_dirs": ["/tmp"],
            "additional_write_dirs": ["/tmp"],
            "environment_variables": {"TEST_VAR": "test_value"}
        },
        enable_python_tool=True,
        enable_shell_tool=True, 
        enable_file_tools=True,
        enable_todo_write=True,
        local_execution=True,  # REQUIRED for Seatbelt
        default_workdir="/tmp",
        auto_git_checkpoint=False,  # Can be enabled if needed
        ui=None  # Can use "rich" for enhanced UI
    )
    
    try:
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")
        
        print("✅ Success: Enhanced TinyCodeAgent with all features")
        
    finally:
        await agent.close()

asyncio.run(example_4_enhanced_tinycodeagent())
```

### 5. Modal Provider Example

```python
import asyncio
import os
from tinyagent import TinyCodeAgent

async def example_5_modal_provider():
    """✅  TinyCodeAgent with Modal provider."""
    print("Example 5: TinyCodeAgent with Modal Provider")
    
    agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        provider="modal",
        provider_config={
            "pip_packages": ["requests", "pandas"],
        },
        enable_python_tool=True,
        enable_shell_tool=True,
        enable_file_tools=True,
        enable_todo_write=True,
        local_execution=False  # Cloud execution for Modal
    )
    
    try:
        available_tools = list(agent.custom_tool_handlers.keys())
        print(f"Available tools: {available_tools}")
        
        print("✅ Success: Modal provider configured")
        
    finally:
        await agent.close()

asyncio.run(example_5_modal_provider())
```

### 6. Storage Persistence Example

```python
import asyncio
import os
import tempfile
from tinyagent import TinyAgent
from tinyagent.storage.sqlite_storage import SqliteStorage

async def example_6_storage_persistence():
    """✅  Storage persistence example."""
    print("Example 6: Storage Persistence")
    
    temp_db = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
    temp_db.close()
    
    try:
        storage = SqliteStorage(db_path=temp_db.name)
        
        # Create agent with storage
        agent = TinyAgent(
            model="gpt-5-mini",
            api_key=os.getenv("OPENAI_API_KEY"),
            session_id="test-session",
            user_id="test-user",
            storage=storage
        )
        
        # Add a message
        agent.messages.append({
            "role": "user", 
            "content": "Test message for persistence"
        })
        
        # Save the session
        await agent.save_agent()
        original_count = len(agent.messages)
        print(f"Saved {original_count} messages")
        
        await agent.close()
        
        # Create new agent with same session to test loading
        agent2 = TinyAgent(
            model="gpt-5-mini",
            api_key=os.getenv("OPENAI_API_KEY"),
            session_id="test-session",
            user_id="test-user", 
            storage=storage
        )
        
        # Load the session
        await agent2.init_async()
        loaded_count = len(agent2.messages)
        
        print(f"Loaded {loaded_count} messages")
        
        if loaded_count == original_count:
            print("✅ Success: Session persistence working")
        else:
            print("❌ Failed: Session not properly loaded")
            
        await agent2.close()
        
    finally:
        if os.path.exists(temp_db.name):
            os.unlink(temp_db.name)

asyncio.run(example_6_storage_persistence())
```

### 7. Hook System Example

```python
import asyncio
import os
from tinyagent import TinyAgent
from tinyagent.hooks.token_tracker import TokenTracker
from tinyagent.hooks import anthropic_prompt_cache

async def example_7_hook_system():
    """✅  Hook system example."""
    print("Example 7: Hook System")
    
    agent = TinyAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY")
    )
    
    # Add various hooks
    token_tracker = TokenTracker(name="test_tracker")
    agent.add_callback(token_tracker)
    
    # Add Anthropic prompt caching for Claude models
    # cache_callback = anthropic_prompt_cache()
    # agent.add_callback(cache_callback)
    
    # Custom hook
    def custom_hook(event_name, agent, **kwargs):
        if event_name == "agent_start":
            print(f"   Custom hook: Agent starting")
    
    agent.add_callback(custom_hook)
    
    try:
        print(f"Callbacks added: {len(agent.callbacks)}")
        print("✅ Success: Hook system working")
        
    finally:
        await agent.close()

asyncio.run(example_7_hook_system())
```

### 8. Ollama Models Example

```python
import asyncio
import os
from tinyagent import TinyAgent, TinyCodeAgent

async def example_8_ollama_models():
    """✅  Ollama models example."""
    print("Example 8: Ollama Models")
    
    # TinyAgent with Ollama
    agent = TinyAgent(
        model="ollama/llama2",
        api_key=None,  # No API key needed for local models
        temperature=0.7
    )
    
    try:
        print(f"Model: {agent.model}")
        print(f"API Key: {agent.api_key}")
        print("✅ Success: Ollama model configured")
        
    finally:
        await agent.close()
    
    # TinyCodeAgent with Ollama
    code_agent = TinyCodeAgent(
        model="ollama/codellama",
        api_key=None,
        provider="seatbelt",
        local_execution=True,
        enable_python_tool=True,
        enable_shell_tool=True,
        enable_file_tools=True
    )
    
    try:
        available_tools = list(code_agent.custom_tool_handlers.keys())
        print(f"CodeAgent tools: {available_tools}")
        print("✅ Success: Ollama CodeAgent configured")
        
    finally:
        await code_agent.close()

asyncio.run(example_8_ollama_models())
```

### 9. File Tools Usage Example

```python
import asyncio
import os
import tempfile
import shutil
from tinyagent import TinyCodeAgent

async def example_9_file_tools_usage():
    """✅  File tools usage example."""
    print("Example 9: File Tools Usage")
    
    temp_dir = tempfile.mkdtemp(prefix="tinyagent_test_")
    
    try:
        agent = TinyCodeAgent(
            model="gpt-5-mini",
            api_key=os.getenv("OPENAI_API_KEY"),
            provider="seatbelt",
            enable_file_tools=True,
            provider_config={
                "additional_read_dirs": [temp_dir],
                "additional_write_dirs": [temp_dir]
            },
            local_execution=True
        )
        
        # Check file tools are available
        file_tools = ['read_file', 'write_file', 'update_file', 'glob', 'grep']
        available_file_tools = [tool for tool in file_tools 
                              if tool in agent.custom_tool_handlers]
        
        print(f"Available file tools: {available_file_tools}")
        
        if len(available_file_tools) == len(file_tools):
            print("✅ Success: All file tools available")
        else:
            print("❌ Some file tools missing")
        
        await agent.close()
        
    finally:
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)

asyncio.run(example_9_file_tools_usage())
```

### 10. Git Checkpoints Example

```python
import asyncio
import os
from tinyagent import TinyCodeAgent

async def example_10_git_checkpoints():
    """✅  Git checkpoints example."""
    print("Example 10: Git Checkpoints")
    
    agent = TinyCodeAgent(
        model="gpt-5-mini",
        api_key=os.getenv("OPENAI_API_KEY"),
        auto_git_checkpoint=True  # Enable auto git checkpoints
    )
    
    try:
        # Test checkpoint controls
        is_enabled = agent.get_auto_git_checkpoint_status()
        print(f"Git checkpoints enabled: {is_enabled}")
        
        # Test toggle
        agent.enable_auto_git_checkpoint(False)
        is_disabled = agent.get_auto_git_checkpoint_status()
        print(f"Git checkpoints after disable: {is_disabled}")
        
        agent.enable_auto_git_checkpoint(True)
        is_reenabled = agent.get_auto_git_checkpoint_status()
        print(f"Git checkpoints after re-enable: {is_reenabled}")
        
        print("✅ Success: Git checkpoint controls working")
        
    finally:
        await agent.close()

asyncio.run(example_10_git_checkpoints())
```

### Key Corrections Summary

**Important Notes from Testing:**

1. **Tools Access**: Tools are stored in `agent.custom_tool_handlers` (dict), not `agent.tools`
2. **Seatbelt Provider**: TinyCodeAgent with Seatbelt provider REQUIRES `local_execution=True`
3. **TodoWrite Tool**: Automatically added when `enable_todo_write=True` (default)
4. **Storage Loading**: Use `agent.init_async()` to load existing sessions
5. **Messages**: Access conversation via `agent.messages` (list)
6. **File Tools**: Added as custom tools, not in mcp_client.tools
7. **Subagents**: Added as custom tools with their names as keys
8. **Modal Provider**: Works with `local_execution=False` (cloud execution)
9. **Hooks**: Added to `agent.callbacks` list
10. **Git Checkpoints**: Have dedicated control methods

</details>

## Using Local Models with Ollama

TinyAgent supports local models through Ollama via LiteLLM integration. This allows you to run models locally without requiring API keys or cloud services.

### Prerequisites

1. Install Ollama from [ollama.ai](https://ollama.ai)
2. Pull the model you want to use:
   ```bash
   ollama pull qwen2.5-coder:7b
   ollama pull codellama
   ollama pull gpt-oss:20b
   # or any other model from Ollama library
   ```

### Basic Usage with Ollama

```python
import asyncio
from tinyagent import TinyAgent

async def main():
    # Initialize TinyAgent with Ollama model
    # Format: "ollama/<model-name>"
    agent = TinyAgent(
        model="ollama/qwen2.5-coder:7b",  # or "ollama/codellama", "ollama/mixtral", etc.
        api_key=None,  # No API key needed for local models
        temperature=0.7,
        system_prompt="You are a helpful AI assistant running locally."
    )
    
    try:
        # Connect to MCP servers if needed
        await agent.connect_to_server("npx", ["@openbnb/mcp-server-airbnb", "--ignore-robots-txt"])
        
        # Run the agent
        result = await agent.run("What can you help me with today?")
        print("Response:", result)
    finally:
        await agent.close()

asyncio.run(main())
```

### TinyCodeAgent with Ollama

```python
import asyncio
from tinyagent import TinyCodeAgent

async def main():
    # Use code-optimized models for better results
    agent = TinyCodeAgent(
        model="ollama/qwen2.5-coder:7b",  # qwen2.5-coder:7b is optimized for code tasks
        api_key=None,
        provider="seatbelt",  # or "modal" for cloud execution
        enable_python_tool=True,
        enable_shell_tool=True,
        enable_file_tools=True
    )
    
    try:
        result = await agent.run("""
        Write a Python function to calculate fibonacci numbers
        and test it with the first 10 numbers.
        """)
        print("Result:", result)
    finally:
        await agent.close()

asyncio.run(main())
```

### Advanced Ollama Configuration

```python
from tinyagent import TinyAgent

# Custom Ollama endpoint (if not using default)
agent = TinyAgent(
    model="ollama/llama2",
    api_key=None,
    model_kwargs={
        "api_base": "http://localhost:11434",  # Custom Ollama server
        "num_predict": 2048,  # Max tokens to generate
        "top_k": 40,
        "top_p": 0.9,
        "repeat_penalty": 1.1
    }
)

# Using with hooks and callbacks
from tinyagent.hooks.rich_ui_callback import RichUICallback

agent = TinyAgent(
    model="ollama/mixtral",
    api_key=None,
    temperature=0.5
)

# Add rich UI for better visualization
ui = RichUICallback()
agent.add_callback(ui)
```

### Recommended Ollama Models

| Model | Best For | Command |
|-------|----------|---------|
| `llama2` | General purpose tasks | `ollama pull llama2` |
| `codellama` | Code generation and analysis | `ollama pull codellama` |
| `mixtral` | Advanced reasoning, larger context | `ollama pull mixtral` |
| `mistral` | Fast, efficient general tasks | `ollama pull mistral` |
| `phi` | Lightweight, fast responses | `ollama pull phi` |
| `deepseek-coder` | Specialized code tasks | `ollama pull deepseek-coder` |

### Performance Tips

1. **Model Selection**: Choose models based on your task:
   - Use `codellama` or `deepseek-coder` for code-heavy tasks
   - Use `mixtral` for complex reasoning
   - Use `phi` or `mistral` for faster responses

2. **Resource Management**: Local models use your machine's resources:
   ```python
   # Adjust temperature for more deterministic outputs
   agent = TinyAgent(
       model="ollama/codellama",
       temperature=0.1,  # Lower = more deterministic
       model_kwargs={
           "num_thread": 8,  # Adjust based on your CPU
           "num_gpu": 1,     # If you have GPU support
       }
   )
   ```

3. **Context Length**: Be mindful of context limits:
   ```python
   # Configure for longer contexts if needed
   agent = TinyAgent(
       model="ollama/mixtral",
       model_kwargs={
           "num_ctx": 4096,  # Context window size
       }
   )
   ```

## Custom Instructions System 📝

TinyAgent supports a flexible custom instruction system that allows you to append project-specific, domain-specific, or context-specific instructions to your agent's system prompt. This feature is perfect for customizing agent behavior, adding specialized knowledge, or maintaining consistent behavior across your project.

### Key Features

- **🎯 Flexible Input**: Support for both string input and file paths
- **📁 Automatic AGENTS.md Loading**: Auto-detects project instructions
- **🔧 Enable/Disable Control**: Runtime configuration with proper logging
- **🏷️ Placeholder Support**: Smart insertion at specific locations in system prompts
- **🎛️ Configurable Paths**: Custom filenames and locations
- **🔗 Subagent Integration**: Control inheritance for specialized workers

### Quick Start

#### Basic Usage with String Instructions

```python
import asyncio
from tinyagent import TinyAgent

async def main():
    # Add custom instructions directly as a string
    custom_instructions = """
    You are working on a Python web application project.
    Always consider:
    - Security best practices
    - Performance implications
    - Code maintainability
    - Follow PEP 8 style guidelines
    """
    
    agent = TinyAgent(
        model="gpt-5-mini",
        api_key="your-api-key",
        custom_instruction=custom_instructions,
        enable_custom_instruction=True
    )
    
    result = await agent.run("Help me refactor this Django view function")
    print(result)

asyncio.run(main())
```

#### Automatic AGENTS.md Loading

Cr