Showing preview only (815K chars total). Download the full file or copy to clipboard to get everything.
Repository: anthropics/claude-quickstarts
Branch: main
Commit: 4b2549e8093a
Files: 197
Total size: 761.7 KB
Directory structure:
gitextract_5euojtzz/
├── .github/
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build.yaml
│ ├── reusable_build_step.yaml
│ └── tests.yaml
├── .pre-commit-config.yaml
├── CLAUDE.md
├── LICENSE
├── README.md
├── agents/
│ ├── .gitignore
│ ├── README.md
│ ├── __init__.py
│ ├── agent.py
│ ├── agent_demo.ipynb
│ ├── test_message_params.py
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── calculator_mcp.py
│ │ ├── code_execution.py
│ │ ├── file_tools.py
│ │ ├── mcp_tool.py
│ │ ├── think.py
│ │ └── web_search.py
│ └── utils/
│ ├── __init__.py
│ ├── connections.py
│ ├── history_util.py
│ └── tool_util.py
├── autonomous-coding/
│ ├── .gitignore
│ ├── README.md
│ ├── agent.py
│ ├── autonomous_agent_demo.py
│ ├── client.py
│ ├── progress.py
│ ├── prompts/
│ │ ├── app_spec.txt
│ │ ├── coding_prompt.md
│ │ └── initializer_prompt.md
│ ├── prompts.py
│ ├── requirements.txt
│ ├── security.py
│ └── test_security.py
├── browser-use-demo/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── NOTICE
│ ├── README.md
│ ├── browser_use_demo/
│ │ ├── __init__.py
│ │ ├── browser_tool_utils/
│ │ │ ├── __init__.py
│ │ │ ├── browser_dom_script.js
│ │ │ ├── browser_element_script.js
│ │ │ ├── browser_form_input_script.js
│ │ │ ├── browser_key_map.py
│ │ │ └── browser_text_script.js
│ │ ├── display_constants.py
│ │ ├── loop.py
│ │ ├── message_handler.py
│ │ ├── message_renderer.py
│ │ ├── requirements.txt
│ │ ├── streamlit.py
│ │ └── tools/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── browser.py
│ │ ├── collection.py
│ │ └── coordinate_scaling.py
│ ├── build.sh
│ ├── docker-compose.yml
│ ├── image/
│ │ ├── .config/
│ │ │ └── tint2/
│ │ │ ├── applications/
│ │ │ │ ├── firefox-custom.desktop
│ │ │ │ ├── gedit.desktop
│ │ │ │ └── terminal.desktop
│ │ │ └── tint2rc
│ │ ├── entrypoint.sh
│ │ ├── http_server.py
│ │ ├── index.html
│ │ ├── mutter_startup.sh
│ │ ├── novnc_startup.sh
│ │ ├── start_all.sh
│ │ ├── static_content/
│ │ │ └── index.html
│ │ ├── tint2_startup.sh
│ │ ├── x11vnc_startup.sh
│ │ └── xvfb_startup.sh
│ ├── pytest.ini
│ ├── setup.py
│ ├── test-requirements.txt
│ ├── tests/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_integration.py
│ │ ├── test_message_renderer.py
│ │ ├── test_sampling_loop.py
│ │ └── test_streamlit_helpers.py
│ └── validate_env.py
├── computer-use-demo/
│ ├── .gitignore
│ ├── .zed/
│ │ └── settings.json
│ ├── CONTRIBUTING.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── README.md
│ ├── computer_use_demo/
│ │ ├── __init__.py
│ │ ├── loop.py
│ │ ├── requirements.txt
│ │ ├── streamlit.py
│ │ └── tools/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── bash.py
│ │ ├── collection.py
│ │ ├── computer.py
│ │ ├── edit.py
│ │ ├── groups.py
│ │ └── run.py
│ ├── dev-requirements.txt
│ ├── image/
│ │ ├── .config/
│ │ │ └── tint2/
│ │ │ ├── applications/
│ │ │ │ ├── firefox-custom.desktop
│ │ │ │ ├── gedit.desktop
│ │ │ │ └── terminal.desktop
│ │ │ └── tint2rc
│ │ ├── .streamlit/
│ │ │ └── config.toml
│ │ ├── entrypoint.sh
│ │ ├── http_server.py
│ │ ├── index.html
│ │ ├── mutter_startup.sh
│ │ ├── novnc_startup.sh
│ │ ├── start_all.sh
│ │ ├── static_content/
│ │ │ └── index.html
│ │ ├── tint2_startup.sh
│ │ ├── x11vnc_startup.sh
│ │ └── xvfb_startup.sh
│ ├── pyproject.toml
│ ├── ruff.toml
│ ├── setup.sh
│ └── tests/
│ ├── conftest.py
│ ├── loop_test.py
│ ├── streamlit_test.py
│ └── tools/
│ ├── bash_test.py
│ ├── computer_test.py
│ └── edit_test.py
├── customer-support-agent/
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── amplify.yml
│ ├── app/
│ │ ├── api/
│ │ │ └── chat/
│ │ │ └── route.ts
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── lib/
│ │ │ ├── customer_support_categories.json
│ │ │ └── utils.ts
│ │ └── page.tsx
│ ├── components/
│ │ ├── ChatArea.tsx
│ │ ├── FullSourceModal.tsx
│ │ ├── LeftSidebar.tsx
│ │ ├── RightSidebar.tsx
│ │ ├── TopNavBar.tsx
│ │ ├── theme-provider.tsx
│ │ └── ui/
│ │ ├── avatar.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── dialog.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── input.tsx
│ │ └── textarea.tsx
│ ├── components.json
│ ├── config.ts
│ ├── lib/
│ │ └── utils.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── styles/
│ │ └── themes.js
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── financial-data-analyst/
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── README.md
│ ├── app/
│ │ ├── api/
│ │ │ └── finance/
│ │ │ └── route.ts
│ │ ├── finance/
│ │ │ └── page.tsx
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── components/
│ │ ├── ChartRenderer.tsx
│ │ ├── FilePreview.tsx
│ │ ├── TopNavBar.tsx
│ │ ├── theme-provider.tsx
│ │ └── ui/
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── chart.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── textarea.tsx
│ │ ├── toast.tsx
│ │ └── toaster.tsx
│ ├── components.json
│ ├── hooks/
│ │ └── use-toast.ts
│ ├── lib/
│ │ └── utils.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.mjs
│ ├── tailwind.config.ts
│ ├── tsconfig.json
│ ├── types/
│ │ └── chart.ts
│ └── utils/
│ └── fileHandling.ts
└── pyproject.toml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/pull_request_template.md
================================================
## Description
<!-- Describe the changes you've made -->
## Quickstart
- [ ] Computer Use Demo
- [ ] Customer Support Agent
- [ ] Financial Data Analyst
- [ ] N/A
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Other (please describe):
## Testing
<!-- Describe the testing you've done -->
- [ ] Added/updated unit tests
- [ ] Tested manually
- [ ] Verified in development environment
## Screenshots
<!-- If applicable, add screenshots to help explain your changes -->
## Additional Notes
<!-- Add any additional context or notes about the changes -->
================================================
FILE: .github/workflows/build.yaml
================================================
env:
REGISTRY: ghcr.io/anthropics/anthropic-quickstarts
name: build
on:
pull_request:
paths:
- .github/**
- computer-use-demo/**
push:
branches:
- main
paths:
- .github/**
- computer-use-demo/**
jobs:
build-amd64:
uses: ./.github/workflows/reusable_build_step.yaml
with:
platform: amd64
builder: ubuntu-latest-16core
registry: ghcr.io/anthropics/anthropic-quickstarts
tag_prefix: computer-use-demo-
context: computer-use-demo
permissions:
contents: read
packages: write
build-arm64:
uses: ./.github/workflows/reusable_build_step.yaml
with:
platform: arm64
builder: ubuntu-22.04-arm64-16core
registry: ghcr.io/anthropics/anthropic-quickstarts
tag_prefix: computer-use-demo-
context: computer-use-demo
permissions:
contents: read
packages: write
merge:
runs-on: ubuntu-latest
needs:
- build-arm64
- build-amd64
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set image tag
run: |
echo "SHORT_SHA=$(git rev-parse --short ${{ github.sha }})" >> "$GITHUB_ENV"
- name: Create SHA manifest and push
run: |
docker buildx imagetools create -t \
${REGISTRY}:computer-use-demo-${SHORT_SHA} \
${REGISTRY}:computer-use-demo-${SHORT_SHA}-amd64 \
${REGISTRY}:computer-use-demo-${SHORT_SHA}-arm64
- name: Create latest manifest and push
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
docker buildx imagetools create -t \
${REGISTRY}:computer-use-demo-latest \
${REGISTRY}:computer-use-demo-latest-amd64 \
${REGISTRY}:computer-use-demo-latest-arm64
================================================
FILE: .github/workflows/reusable_build_step.yaml
================================================
on:
workflow_call:
inputs:
platform:
required: true
type: string
builder:
required: true
type: string
registry:
required: true
type: string
tag_prefix:
required: false
type: string
context:
required: false
type: string
jobs:
build:
runs-on: ${{ inputs.builder }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set image tag
run: |
short_sha=$(git rev-parse --short ${{ github.sha }})
echo "TAG=${{ inputs.registry }}:${{ inputs.tag_prefix }}${short_sha}" >> "$GITHUB_ENV"
- name: Build Docker image
uses: docker/build-push-action@v5
with:
platforms: linux/${{ inputs.platform }}
context: ${{ inputs.context || '.' }}
push: false
tags: ${{ env.TAG }}
cache-from: type=gha,scope=computer-use-${{ inputs.platform }}
cache-to: type=gha,mode=max,scope=computer-use-${{ inputs.platform }}
load: true
- name: Run container
run: docker run -d -p 8051:8051 ${{ env.TAG }}
- name: Check streamlit
run: |
timeout=60
start_time=$(date +%s)
docker_id=$(docker ps --filter "ancestor=${{ env.TAG }}" --format "{{.ID}}")
echo "docker_id=$docker_id" >> "$GITHUB_ENV"
while true; do
current_time=$(date +%s)
elapsed=$((current_time - start_time))
if [ $elapsed -ge $timeout ]; then
echo "Timeout reached. Container did not respond within $timeout seconds."
exit 1
fi
response=$(docker exec $docker_id curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8501 || echo "000")
if [ "$response" = "200" ]; then
echo "Container responded with 200 OK"
exit 0
fi
done
- name: Check VNC
run: docker exec $docker_id nc localhost 5900 -z
- name: Check noVNC
run: docker exec $docker_id curl -s -o /dev/null -w "%{http_code}" http://localhost:6080 | grep -q 200 || exit 1
- name: Check landing page
run: docker exec $docker_id curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 | grep -q 200 || exit 1
- name: Determine push tags
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
echo "PUSH_TAGS=${TAG}-${{ inputs.platform }}" >> "$GITHUB_ENV"
else
echo "PUSH_TAGS=${TAG}-${{ inputs.platform }},${{ inputs.registry }}:${{ inputs.tag_prefix }}latest-${{ inputs.platform }}" >> "$GITHUB_ENV"
fi
- name: Push Docker image
uses: docker/build-push-action@v5
with:
platforms: linux/${{ inputs.platform }}
context: ${{ inputs.context || '.' }}
push: true
tags: ${{ env.PUSH_TAGS }}
cache-from: type=gha,scope=computer-use-${{ inputs.platform }}
cache-to: type=gha,mode=max,scope=computer-use-${{ inputs.platform }}
================================================
FILE: .github/workflows/tests.yaml
================================================
name: tests
on:
pull_request:
paths:
- .github/**
- computer-use-demo/**
push:
branches:
- main
paths:
- .github/**
- computer-use-demo/**
jobs:
ruff:
runs-on: ubuntu-latest
defaults:
run:
working-directory: computer-use-demo
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v1
with:
src: "computer-use-demo"
pyright:
runs-on: ubuntu-latest
defaults:
run:
working-directory: computer-use-demo
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
cache: "pip"
python-version: "3.11.6"
- run: |
python -m venv .venv
source .venv/bin/activate
pip install -r dev-requirements.txt
- run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
- uses: jakebailey/pyright-action@v1
with:
working-directory: computer-use-demo
pytest:
runs-on: ubuntu-latest
defaults:
run:
working-directory: computer-use-demo
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
cache: "pip"
python-version: "3.11.6"
- run: |
python -m venv .venv
source .venv/bin/activate
pip install -r dev-requirements.txt
- run: echo "$PWD/.venv/bin" >> $GITHUB_PATH
- run: pytest tests --junitxml=junit/test-results.xml
================================================
FILE: .pre-commit-config.yaml
================================================
files: ^computer-use-demo/
default_stages: [pre-commit, pre-push]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.7
hooks:
- id: ruff
name: Run `ruff` to autofix lint errors
args: [--fix-only]
- id: ruff
name: Run `ruff` to format code
entry: ruff format --force-exclude
- id: ruff
name: Run `ruff` to lint code
- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.384
hooks:
- id: pyright
================================================
FILE: CLAUDE.md
================================================
# Claude Quickstarts Development Guide
## Legal
- When changes are made to files that have a copyright notice add them to that subdirectory's CHANGELOG.md file.
## Computer-Use Demo
### Setup & Development
- **Setup environment**: `./setup.sh`
- **Build Docker**: `docker build . -t computer-use-demo:local`
- **Run container**: `docker run -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY -v $(pwd)/computer_use_demo:/home/computeruse/computer_use_demo/ -v $HOME/.anthropic:/home/computeruse/.anthropic -p 5900:5900 -p 8501:8501 -p 6080:6080 -p 8080:8080 -it computer-use-demo:local`
### Testing & Code Quality
- **Lint**: `ruff check .`
- **Format**: `ruff format .`
- **Typecheck**: `pyright`
- **Run tests**: `pytest`
- **Run single test**: `pytest tests/path_to_test.py::test_name -v`
### Code Style
- **Python**: snake_case for functions/variables, PascalCase for classes
- **Imports**: Use isort with combine-as-imports
- **Error handling**: Use custom ToolError for tool errors
- **Types**: Add type annotations for all parameters and returns
- **Classes**: Use dataclasses and abstract base classes
## Customer Support Agent
### Setup & Development
- **Install dependencies**: `npm install`
- **Run dev server**: `npm run dev` (full UI)
- **UI variants**: `npm run dev:left` (left sidebar), `npm run dev:right` (right sidebar), `npm run dev:chat` (chat only)
- **Lint**: `npm run lint`
- **Build**: `npm run build` (full UI), see package.json for variants
### Code Style
- **TypeScript**: Strict mode with proper interfaces
- **Components**: Function components with React hooks
- **Formatting**: Follow ESLint Next.js configuration
- **UI components**: Use shadcn/ui components library
## Financial Data Analyst
### Setup & Development
- **Install dependencies**: `npm install`
- **Run dev server**: `npm run dev`
- **Lint**: `npm run lint`
- **Build**: `npm run build`
### Code Style
- **TypeScript**: Strict mode with proper type definitions
- **Components**: Function components with type annotations
- **Visualization**: Use Recharts library for data visualization
- **State management**: React hooks for state
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Anthropic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Claude Quickstarts
Claude Quickstarts is a collection of projects designed to help developers quickly get started with building applications using the Claude API. Each quickstart provides a foundation that you can easily build upon and customize for your specific needs.
## Getting Started
To use these quickstarts, you'll need an Claude API key. If you don't have one yet, you can sign up for free at [console.anthropic.com](https://console.anthropic.com).
## Available Quickstarts
### Customer Support Agent
A customer support agent powered by Claude. This project demonstrates how to leverage Claude's natural language understanding and generation capabilities to create an AI-assisted customer support system with access to a knowledge base.
[Go to Customer Support Agent Quickstart](./customer-support-agent)
### Financial Data Analyst
A financial data analyst powered by Claude. This project demonstrates how to leverage Claude's capabilities with interactive data visualization to analyze financial data via chat.
[Go to Financial Data Analyst Quickstart](./financial-data-analyst)
### Computer Use Demo
An environment and tools that Claude can use to control a desktop computer. This project demonstrates how to leverage the computer use capabilities of Claude, including support for the latest `computer_use_20251124` tool version with zoom actions.
[Go to Computer Use Demo Quickstart](./computer-use-demo)
### Browser Tools API Demo
A complete reference implementation for browser automation powered by Claude. This project demonstrates how to leverage Claude's browser tools API for web interaction, including navigation, DOM inspection, and form manipulation using Playwright.
[Go to Browser Tools API Demo Quickstart](./browser-tools-api-demo)
### Autonomous Coding Agent
An autonomous coding agent powered by the Claude Agent SDK. This project demonstrates a two-agent pattern (initializer + coding agent) that can build complete applications over multiple sessions, with progress persisted via git and a feature list that the agent works through incrementally.
[Go to Autonomous Coding Agent Quickstart](./autonomous-coding)
## General Usage
Each quickstart project comes with its own README and setup instructions. Generally, you'll follow these steps:
1. Clone this repository
2. Navigate to the specific quickstart directory
3. Install the required dependencies
4. Set up your Claude API key as an environment variable
5. Run the quickstart application
## Explore Further
To deepen your understanding of working with Claude and the Claude API, check out these resources:
- [Claude API Documentation](https://docs.claude.com)
- [Claude Cookbooks](https://github.com/anthropics/claude-cookbooks) - A collection of code snippets and guides for common tasks
- [Claude API Fundamentals Course](https://github.com/anthropics/courses/tree/master/anthropic_api_fundamentals)
## Contributing
We welcome contributions to the Claude Quickstarts repository! If you have ideas for new quickstart projects or improvements to existing ones, please open an issue or submit a pull request.
## Community and Support
- Join our [Anthropic Discord community](https://www.anthropic.com/discord) for discussions and support
- Check out the [Anthropic support documentation](https://support.anthropic.com) for additional help
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
================================================
FILE: agents/.gitignore
================================================
# Python bytecode
__pycache__/
*.py[cod]
*$py.class
# Jupyter Notebook
.ipynb_checkpoints
*/.ipynb_checkpoints/*
# MacOS
.DS_Store
# Environment variables
.env
# Virtual environments
venv/
env/
.venv/
# MCP logs
*.log
# Distribution / packaging
dist/
build/
*.egg-info/
================================================
FILE: agents/README.md
================================================
# Agents
A minimal educational implementation of LLM agents using the Claude API.
> **Note:** This is NOT an SDK, but a reference implementation of key concepts
## Overview & Core Components
This repo demonstrates how to [build effective agents](https://www.anthropic.com/engineering/building-effective-agents) with the Claude API. It shows how sophisticated AI behaviors can emerge from a simple foundation: LLMs using tools in a loop. This implementation is not prescriptive - the core logic is <300 lines of code and deliberately lacks production features. Feel free to translate these patterns to your language and production stack ([Claude Code](https://docs.claude.com/en/docs/agents-and-tools/claude-code/overview) can help!)
It contains three components:
- `agent.py`: Manages Claude API interactions and tool execution
- `tools/`: Tool implementations (both native and MCP tools)
- `utils/`: Utilities for message history and MCP server connections
## Usage
```python
from agents.agent import Agent
from agents.tools.think import ThinkTool
# Create an agent with both local tools and MCP server tools
agent = Agent(
name="MyAgent",
system="You are a helpful assistant.",
tools=[ThinkTool()], # Local tools
mcp_servers=[
{
"type": "stdio",
"command": "python",
"args": ["-m", "mcp_server"],
},
]
)
# Run the agent
response = agent.run("What should I consider when buying a new laptop?")
```
From this foundation, you can add domain-specific tools, optimize performance, or implement custom response handling. We remain deliberately unopinionated - this backbone simply gets you started with fundamentals.
## Requirements
- Python 3.8+
- Claude API key (set as `ANTHROPIC_API_KEY` environment variable)
- `anthropic` Python library
- `mcp` Python library
================================================
FILE: agents/__init__.py
================================================
"""Core agent implementations."""
from .agent import Agent, ModelConfig
from .tools.base import Tool
__all__ = ["Agent", "ModelConfig", "Tool"]
================================================
FILE: agents/agent.py
================================================
"""Agent implementation with Claude API and tools."""
import asyncio
import os
from contextlib import AsyncExitStack
from dataclasses import dataclass
from typing import Any
from anthropic import Anthropic
from .tools.base import Tool
from .utils.connections import setup_mcp_connections
from .utils.history_util import MessageHistory
from .utils.tool_util import execute_tools
@dataclass
class ModelConfig:
"""Configuration settings for Claude model parameters."""
# Available models include:
# - claude-sonnet-4-20250514 (default)
# - claude-opus-4-20250514
# - claude-haiku-4-5-20251001
# - claude-3-5-sonnet-20240620
# - claude-3-haiku-20240307
model: str = "claude-sonnet-4-20250514"
max_tokens: int = 4096
temperature: float = 1.0
context_window_tokens: int = 180000
class Agent:
"""Claude-powered agent with tool use capabilities."""
def __init__(
self,
name: str,
system: str,
tools: list[Tool] | None = None,
mcp_servers: list[dict[str, Any]] | None = None,
config: ModelConfig | None = None,
verbose: bool = False,
client: Anthropic | None = None,
message_params: dict[str, Any] | None = None,
):
"""Initialize an Agent.
Args:
name: Agent identifier for logging
system: System prompt for the agent
tools: List of tools available to the agent
mcp_servers: MCP server configurations
config: Model configuration with defaults
verbose: Enable detailed logging
client: Anthropic client instance
message_params: Additional parameters for client.messages.create().
These override any conflicting parameters from config.
"""
self.name = name
self.system = system
self.verbose = verbose
self.tools = list(tools or [])
self.config = config or ModelConfig()
self.mcp_servers = mcp_servers or []
self.message_params = message_params or {}
self.client = client or Anthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY", "")
)
self.history = MessageHistory(
model=self.config.model,
system=self.system,
context_window_tokens=self.config.context_window_tokens,
client=self.client,
)
if self.verbose:
print(f"\n[{self.name}] Agent initialized")
def _prepare_message_params(self) -> dict[str, Any]:
"""Prepare parameters for client.messages.create() call.
Returns a dict with base parameters from config, with any
message_params overriding conflicting keys.
"""
return {
"model": self.config.model,
"max_tokens": self.config.max_tokens,
"temperature": self.config.temperature,
"system": self.system,
"messages": self.history.format_for_api(),
"tools": [tool.to_dict() for tool in self.tools],
**self.message_params,
}
async def _agent_loop(self, user_input: str) -> list[dict[str, Any]]:
"""Process user input and handle tool calls in a loop"""
if self.verbose:
print(f"\n[{self.name}] Received: {user_input}")
await self.history.add_message("user", user_input, None)
tool_dict = {tool.name: tool for tool in self.tools}
while True:
self.history.truncate()
params = self._prepare_message_params()
# Merge headers properly - default beta header can be overridden by message_params
default_headers = {"anthropic-beta": "code-execution-2025-05-22"}
if "extra_headers" in params:
# Pop extra_headers from params and merge with defaults
custom_headers = params.pop("extra_headers")
merged_headers = {**default_headers, **custom_headers}
else:
merged_headers = default_headers
response = self.client.messages.create(
**params,
extra_headers=merged_headers
)
tool_calls = [
block for block in response.content if block.type == "tool_use"
]
if self.verbose:
for block in response.content:
if block.type == "text":
print(f"\n[{self.name}] Output: {block.text}")
elif block.type == "tool_use":
params_str = ", ".join(
[f"{k}={v}" for k, v in block.input.items()]
)
print(
f"\n[{self.name}] Tool call: "
f"{block.name}({params_str})"
)
await self.history.add_message(
"assistant", response.content, response.usage
)
if tool_calls:
tool_results = await execute_tools(
tool_calls,
tool_dict,
)
if self.verbose:
for block in tool_results:
print(
f"\n[{self.name}] Tool result: "
f"{block.get('content')}"
)
await self.history.add_message("user", tool_results)
else:
return response
async def run_async(self, user_input: str) -> list[dict[str, Any]]:
"""Run agent with MCP tools asynchronously."""
async with AsyncExitStack() as stack:
original_tools = list(self.tools)
try:
mcp_tools = await setup_mcp_connections(
self.mcp_servers, stack
)
self.tools.extend(mcp_tools)
return await self._agent_loop(user_input)
finally:
self.tools = original_tools
def run(self, user_input: str) -> list[dict[str, Any]]:
"""Run agent synchronously"""
return asyncio.run(self.run_async(user_input))
================================================
FILE: agents/agent_demo.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Agent Demo with Simplified Connection Management\n",
"\n",
"This notebook demonstrates an agent using multiple tool types with simplified connection management:\n",
"1. **Local Calculator**: Mathematical expressions via MCP\n",
"2. **Brave Search**: Web search via MCP (if API key available)\n",
"3. **Think Tool**: Internal reasoning tool"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import sys\n",
"import nest_asyncio\n",
"nest_asyncio.apply()\n",
"\n",
"parent_dir = os.path.dirname(os.getcwd())\n",
"sys.path.insert(0, parent_dir)\n",
"from agents.agent import Agent, ModelConfig\n",
"from agents.tools.think import ThinkTool\n",
"from agents.tools.web_search import WebSearchServerTool\n",
"from agents.tools.code_execution import CodeExecutionServerTool"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 1. Configure tools and MCP servers"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Calculator server configured: Yes\n",
"Brave API key available: Yes\n",
"Brave search server configured: Yes\n"
]
}
],
"source": [
"# Standard Python tool\n",
"think_tool = ThinkTool()\n",
"\n",
"# Python MCP server\n",
"calculator_server_path = os.path.abspath(os.path.join(os.getcwd(), \"tools/calculator_mcp.py\"))\n",
"calculator_server = {\n",
" \"type\": \"stdio\",\n",
" \"command\": \"python\",\n",
" \"args\": [calculator_server_path]\n",
"}\n",
"print(f\"Calculator server configured: {'Yes' if calculator_server else 'No'}\")\n",
"\n",
"\n",
"# Brave MCP server written in TypeScript\n",
"\n",
"brave_api_key = os.environ.get(\"BRAVE_API_KEY_BASE_DATA\", \"\")\n",
"print(f\"Brave API key available: {'Yes' if brave_api_key else 'No'}\")\n",
"brave_search_server = {\n",
" \"type\": \"stdio\",\n",
" \"command\": \"npx\",\n",
" \"args\": [\"-y\", \"@modelcontextprotocol/server-brave-search\"],\n",
" \"env\": {\n",
" \"BRAVE_API_KEY\": brave_api_key, \n",
" \"PATH\": f\"{os.path.dirname('npx')}:\" + os.environ.get(\"PATH\", \"\")\n",
" }\n",
"}\n",
"print(f\"Brave search server configured: {'Yes' if brave_search_server else 'No'}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2. Initialize agent with combined tools"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[Multi-Tool Agent] Agent initialized\n"
]
}
],
"source": [
"# Create agent config \n",
"system_prompt = \"\"\"\n",
"You are a helpful assistant with access to:\n",
"1. Web search (brave_web_search, brave_local_search)\n",
"2. Mathematical calculator (calculate) \n",
"3. A tool to think and reason (think)\n",
"\n",
"Always use the most appropriate tool for each task.\n",
"\"\"\"\n",
"\n",
"\n",
"# Initialize agent with standard tools and MCP servers\n",
"agent = Agent(\n",
" name=\"Multi-Tool Agent\",\n",
" system=system_prompt,\n",
" tools=[think_tool], \n",
" mcp_servers=[brave_search_server, calculator_server], \n",
" config=ModelConfig(\n",
" model=\"claude-3-7-sonnet-20250219\", \n",
" max_tokens=4096,\n",
" temperature=1.0\n",
" ),\n",
" verbose=True\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 3. Run queries"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loaded 3 MCP tools from 2 servers.\n",
"\n",
"[Multi-Tool Agent] Received: What's the square root of the OKC population in 2022\n",
"\n",
"[Multi-Tool Agent] Output: I'll help you find the square root of Oklahoma City's population in 2022. First, I need to look up the population data, then I can calculate the square root.\n",
"\n",
"[Multi-Tool Agent] Tool call: brave_web_search(query=Oklahoma City population 2022 census data)\n",
"\n",
"[Multi-Tool Agent] Tool result: Title: Oklahoma Cities by Population (2025)\n",
"Description: The most populous cities in Oklahoma are <strong>Oklahoma City with 702,767</strong>, Tulsa at 411,894, Norman with 130,046, Broken Arrow at 119,194, and Edmond with 98,103. You can get the most current Census population data for all 834 Oklahoma cities, sorted from the most populous cities in Oklahoma to the ...\n",
"URL: https://www.oklahoma-demographics.com/cities_by_population\n",
"\n",
"Title: U.S. Census Bureau QuickFacts: Oklahoma City city, Oklahoma\n",
"Description: QuickFacts provides statistics for all states and counties. Also for cities and towns with a population of <strong>5,000 or more</strong>. ... Methodology differences may exist between data sources, and so estimates from different sources are not comparable.\n",
"URL: https://www.census.gov/quickfacts/fact/table/oklahomacitycityoklahoma/PST045224\n",
"\n",
"Title: Oklahoma City lands in top 20 of largest US cities by population: New census data\n",
"Description: More: 10 Oklahoma cities where population is growing the fastest · Oklahoma City grew by about 1% between 2022 and 2023, adding <strong>7,589 people</strong>. And between the 2010 and 2020 Censuses, Oklahoma City was one of 14 American cities that added more than 100,000 people.\n",
"URL: https://www.oklahoman.com/story/news/2024/05/17/oklahoma-city-population-top-20-us-census-data-growth/73729737007/\n",
"\n",
"Title: Oklahoma City - Wikipedia\n",
"Description: Oklahoma City (/ˌoʊkləˈhoʊmə ... it ranks 20th in population among U.S. cities and 8th in the Southern United States, with a population of <strong>681,054 at the 2020 census</strong>....\n",
"URL: https://en.wikipedia.org/wiki/Oklahoma_City\n",
"\n",
"Title: Oklahoma City city, Oklahoma - Census Bureau Profile\n",
"Description: We're sorry but this website doesn't work properly without JavaScript enabled. Please enable it to continue\n",
"URL: https://data.census.gov/profile/Oklahoma_City_city,_Oklahoma?g=160XX00US4055000\n",
"\n",
"Title: Oklahoma City, Oklahoma Population 2025\n",
"Description: Oklahoma City is a city located in Cleveland County, Oklahoma. Oklahoma City has a <strong>2025 population of 715,893</strong>. Oklahoma City is currently growing at a rate of 0.93% annually and its population has increased by 4.8% since the most recent census, which recorded a population of 683,078 in 2020.\n",
"URL: https://worldpopulationreview.com/us-cities/oklahoma/oklahoma-city\n",
"\n",
"Title: Oklahoma Demographics | Current Census Data\n",
"Description: Get the Oklahoma demographic data that you need. You can get the most current Oklahoma data from the US Census Bureau without spending 40 high-value hours searching the internet. The Oklahoma population is <strong>4,053,824 people</strong> according to the US Census Bureau's 2023 Population Estimates Program.\n",
"URL: https://www.oklahoma-demographics.com/\n",
"\n",
"Title: Oklahoma City, OK - Profile data - Census Reporter\n",
"Description: a little less than the figure in the Oklahoma City, OK Metro Area: <strong>23.5 14,717,310</strong> (±0.2 / ±145,724)\n",
"URL: https://censusreporter.org/profiles/16000US4055000-oklahoma-city-ok/\n",
"\n",
"Title: Oklahoma population by year, county, race, & more | USAFacts\n",
"Description: The ages, races, and <strong>population</strong> density of <strong>Oklahoma</strong> tell a story. Understand the shifts in demographic trends with these charts visualizing decades of <strong>population</strong> <strong>data</strong>. ... Florida K-12 public teachers are paid the lowest average salary when adjusted for cost of living.Published on November 8, <strong>2022</strong> ... <strong>The</strong> <strong>Census</strong> ...\n",
"URL: https://usafacts.org/data/topics/people-society/population-and-demographics/our-changing-population/state/oklahoma/\n",
"\n",
"Title: Resident Population in Oklahoma City, OK (MSA) (OKCPOP) | FRED | St. Louis Fed\n",
"Description: Graph and download economic <strong>data</strong> for Resident <strong>Population</strong> <strong>in</strong> <strong>Oklahoma</strong> <strong>City</strong>, OK (MSA) (OKCPOP) from 2000 to 2024 about <strong>Oklahoma</strong> <strong>City</strong>, OK, residents, <strong>population</strong>, and USA.\n",
"URL: https://fred.stlouisfed.org/series/OKCPOP\n",
"\n",
"[Multi-Tool Agent] Output: Based on the search results, I can see multiple population figures for Oklahoma City in different years. The most relevant information for 2022 specifically isn't directly provided, but I can see:\n",
"\n",
"- 2020 census: 681,054 people\n",
"- 2023 estimate showing growth of 7,589 people from 2022\n",
"- 2025 projection: 715,893 people\n",
"\n",
"Let me search more specifically for the 2022 population:\n",
"\n",
"[Multi-Tool Agent] Tool call: brave_web_search(query=Oklahoma City population 2022 exact figure)\n",
"\n",
"[Multi-Tool Agent] Tool result: Title: Oklahoma City, Oklahoma Population 2025\n",
"Description: Oklahoma City is a city located in Cleveland County, Oklahoma. Oklahoma City has a <strong>2025 population of 715,893</strong>.\n",
"URL: https://worldpopulationreview.com/us-cities/oklahoma/oklahoma-city\n",
"\n",
"Title: Oklahoma City Metro Area Population 1950-2025\n",
"Description: The metro area population of Oklahoma City in 2022 was <strong>1,008,000</strong>, a 1% increase from 2021.\n",
"URL: https://www.macrotrends.net/cities/23088/oklahoma-city/population\n",
"\n",
"Title: Oklahoma City city, Oklahoma - Census Bureau Profile\n",
"Description: We're sorry but this website doesn't work properly without JavaScript enabled. Please enable it to continue\n",
"URL: https://data.census.gov/profile/Oklahoma_City_city,_Oklahoma?g=160XX00US4055000\n",
"\n",
"Title: Oklahoma City - Wikipedia\n",
"Description: The Oklahoma City metropolitan area has an estimated <strong>1.49 million</strong> residents, the largest metropolitan area in the state and 42nd-most populous in the nation. The city limits extend into Canadian, Cleveland, and Pottawatomie counties; however, areas beyond Oklahoma County primarily consist of ...\n",
"URL: https://en.wikipedia.org/wiki/Oklahoma_City\n",
"\n",
"Title: U.S. Census Bureau QuickFacts: Oklahoma City city, Oklahoma\n",
"Description: QuickFacts provides statistics for all states and counties. Also for cities and towns with a population of <strong>5,000 or more</strong>.\n",
"URL: https://www.census.gov/quickfacts/oklahomacitycityoklahoma\n",
"\n",
"Title: Oklahoma City, OK | Data USA\n",
"Description: As of 2023, 12% of Oklahoma City, OK residents <strong>(82.6k people</strong>) were born outside of the United States, which is lower than the national average of 13.8%. In 2022, the percentage of foreign-born citizens in Oklahoma City, OK was 11.8%, meaning that the rate has been increasing.\n",
"URL: https://datausa.io/profile/geo/oklahoma-city-ok\n",
"\n",
"Title: Oklahoma City lands in top 20 of largest US cities by population: New census data\n",
"Description: More: 10 Oklahoma cities where population is growing the fastest · Oklahoma City grew by about 1% between 2022 and 2023, adding <strong>7,589 people</strong>.\n",
"URL: https://www.oklahoman.com/story/news/2024/05/17/oklahoma-city-population-top-20-us-census-data-growth/73729737007/\n",
"\n",
"Title: Oklahoma Cities by Population (2025)\n",
"Description: The most populous cities in Oklahoma are Oklahoma City with <strong>702,767</strong>, Tulsa at 411,894, Norman with 130,046, Broken Arrow at 119,194, and Edmond with 98,103.\n",
"URL: https://www.oklahoma-demographics.com/cities_by_population\n",
"\n",
"Title: Oklahoma City, OK\n",
"Description: Oklahoma City, Oklahoma has a population of <strong>688,700</strong>. The median household income is $66,700 and 15% of residents live below the federal poverty level. The racial and ethnic composition is 22% Hispanic, 51% White, 13% Black, 4% Asian and 2% American Indian or Alaska Native.\n",
"URL: https://bigcitieshealthdata.org/city/oklahoma-city-ok/\n",
"\n",
"Title: Oklahoma Cities by Population 2025\n",
"Description: Oklahoma City is the biggest city in Oklahoma, with a population of <strong>715,893 in 2025</strong>.\n",
"URL: https://worldpopulationreview.com/us-cities/oklahoma\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[Multi-Tool Agent] Output: I still don't see a specific figure for exactly 2022, but I can gather enough information to make a reasonable estimate.\n",
"\n",
"[Multi-Tool Agent] Tool call: think(thought=From the search results, I can see:\n",
"1. The 2020 census population was 681,054\n",
"2. Oklahoma City grew by about 1% between 2022 and 2023, adding 7,589 people\n",
"3. This implies the 2022 population was approximately 7,589/0.01 = 758,900 people\n",
"4. But this seems too high compared to other figures like 702,767 and 688,700 mentioned\n",
"\n",
"Let me reconsider. If Oklahoma City added 7,589 people between 2022-2023 and that represented a 1% growth, then the 2022 population would be:\n",
"7,589 ÷ 0.01 = 758,900\n",
"\n",
"However, this contradicts other data points. Perhaps the clearest estimate is from the second search result which states: \"The metro area population of Oklahoma City in 2022 was 1,008,000\". But this refers to the metro area (larger than just the city proper).\n",
"\n",
"Another data point from the first search states \"Oklahoma City has a 2025 population of 715,893\" and mentions it \"has increased by 4.8% since the most recent census, which recorded a population of 683,078 in 2020.\"\n",
"\n",
"If the growth was relatively steady from 2020 to 2025, the annual growth rate would be about 0.96% per year. \n",
"Using that rate, the 2022 population would be approximately:\n",
"683,078 × (1.0096)² = 683,078 × 1.0193 = 696,272\n",
"\n",
"This seems most consistent with the other data points. I'll use 696,272 as the best estimate for Oklahoma City's 2022 population.)\n",
"\n",
"[Multi-Tool Agent] Tool result: Thinking complete!\n",
"\n",
"[Multi-Tool Agent] Output: Based on the available data, I can estimate that Oklahoma City's population in 2022 was approximately 696,272 people. Now I'll calculate the square root of this number.\n",
"\n",
"[Multi-Tool Agent] Tool call: calculator(number1=696272, number2=0, operator=sqrt)\n",
"\n",
"[Multi-Tool Agent] Tool result: Result: 834.4291461831855\n",
"\n",
"[Multi-Tool Agent] Output: The square root of Oklahoma City's population in 2022 (estimated at 696,272 people) is approximately 834.43.\n"
]
},
{
"data": {
"text/plain": [
"Message(id='msg_016wLY8JCd5baUgaWEHcrage', content=[TextBlock(text=\"The square root of Oklahoma City's population in 2022 (estimated at 696,272 people) is approximately 834.43.\", type='text')], model='claude-3-7-sonnet-20250219', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=5, output_tokens=34, cache_creation_input_tokens=150, cache_read_input_tokens=3881, service_tier='standard'))"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Example query\n",
"agent.run(\"What's the square root of the OKC population in 2022\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loaded 3 MCP tools from 2 servers.\n",
"\n",
"[Multi-Tool Agent] Received: How many bananas will fit in an Toyota GR86?\n",
"\n",
"[Multi-Tool Agent] Output: That's an interesting question! To answer how many bananas will fit in a Toyota GR86, I'll need to find information about the car's cargo capacity and then estimate how many bananas could fit in that space.\n",
"\n",
"[Multi-Tool Agent] Tool call: brave_web_search(query=Toyota GR86 cargo capacity trunk volume)\n",
"\n",
"[Multi-Tool Agent] Tool result: Title: Cargo space. What can it fit? : r/GR86\n",
"Description: 11 votes, 36 comments. 42K subscribers in the <strong>GR86</strong> community. For anyone interested in the <strong>Toyota</strong> <strong>GR86</strong> & Subaru BRZ ('22+).\n",
"URL: https://www.reddit.com/r/GR86/comments/16xh6ii/cargo_space_what_can_it_fit/\n",
"\n",
"Title: 2024 Toyota GR86 Review, Pricing, and Specs\n",
"Description: The 2024 <strong>Toyota</strong> <strong>GR86</strong> turns every day into an autocross day thanks to its playful chassis and easy-to-slide rear-wheel drive antics.\n",
"URL: https://www.caranddriver.com/toyota/gr-86/specs\n",
"\n",
"Title: 2023 Toyota GR86 Interior, Cargo Space & Seating | U.S. News\n",
"Description: "You might not think practicality was a consideration with the GR86, but it is surprisingly useful for weekly errands. Its relatively small <strong>6.3 cubic feet</strong> of trunk space is hampered by its short height. But when the seats are folded, Toyota says the GR86 can hold a set of its own replacement ...\n",
"URL: https://cars.usnews.com/cars-trucks/toyota/gr86/2023/interior\n",
"\n",
"Title: 2025 Toyota GR86 Review, Pricing, and Specs\n",
"Description: DIMENSIONS Wheelbase: 101.4 in Length: 167.9 in Width: 69.9 in Height: 51.6 in Passenger Volume: <strong>78 ft3 Trunk Volume: 6 ft3</strong> Curb Weight: 2838 lb · C/D TEST RESULTS 60 mph: 5.4 sec 100 mph: 13.8 sec 1/4-Mile: 14.0 sec @ 101 mph 130 mph: 27.2 sec Results above omit 1-ft rollout of 0.3 sec.\n",
"URL: https://www.caranddriver.com/toyota/gr-86\n",
"\n",
"Title: Toyota GR86 dimensions, boot space and similars\n",
"Description: <strong>Toyota</strong> <strong>GR86</strong> dimensions with interior photos, boot space and measurements of length, width and height. Previous cars and similars in size.\n",
"URL: https://www.automobiledimension.com/model/toyota/gr86\n",
"\n",
"Title: Trunk Size | Toyota GR86 Forum - GT86 Forum, Subaru BRZ Forum, Scion ...\n",
"Description: It still surprises me that I can fit so much in the <strong>trunk</strong>. I am able to fit my entire paintball gear bag, pods, cleats, 2 cases of paint, mask case, small ice chest, and my extra clothes and smaller items. Gotta enjoy the little things with these beautiful cars!\n",
"URL: https://www.gr86.org/threads/trunk-size.8574/\n",
"\n",
"Title: Toyota GR86 Cargo Space | Toyota GR86 Forum - GT86 Forum, Subaru BRZ Forum, Scion ...\n",
"Description: Not sure if serious or market differences, but it is mandated in all cars in the U.S. if the <strong>trunk</strong> is much bigger than a shoebox lol. ... This is an older thread, you may not receive a response, and could be reviving an old thread. Please consider creating a new thread. ... <strong>Toyota</strong> <strong>GR86</strong> Forum ...\n",
"URL: https://www.gr86.org/threads/toyota-gr86-cargo-space.372/\n",
"\n",
"Title: GR86 - GR86 Boot Dimensions | Toyota GR Forum for owners of GR Yaris, GR86, GR Supra and GR Corolla\n",
"Description: During my time with the GR86, I tried to take accurate measurements of the boot dimensions (in centimetres), as shown below. Sorry the pictures are not exactly to scale - my MS Paint skills are rather limited! = The boot opening is <strong>60cm at its narrowest (at the bottom), 80cm at the widest</strong>...\n",
"URL: https://www.gr-zoo.com/threads/gr86-boot-dimensions.2567/\n",
"\n",
"Title: 2025 Toyota GR86 Interior, Cargo Space & Seating | U.S. News\n",
"Description: Few sports cars have large trunks, and the GR86 is no exception. It has just <strong>6.3 cubic feet</strong> of trunk space, which is not much even for this class. You can fit several grocery bags or a couple of carry-on bags with the rear seats upright.\n",
"URL: https://cars.usnews.com/cars-trucks/toyota/gr86/interior\n",
"\n",
"Title: Toyota GR86 (2025) boot space & practicality\n",
"Description: Read what our experts think about the <strong>Toyota</strong> <strong>GR86</strong>'s practicality, boot size and overall dimensions, how it compares to its rivals and more.\n",
"URL: https://www.parkers.co.uk/toyota/gr86/review/practicality/\n",
"\n",
"[Multi-Tool Agent] Output: Now I need to get information about the average size of a banana to make this calculation:\n",
"\n",
"[Multi-Tool Agent] Tool call: brave_web_search(query=average banana size dimensions volume)\n",
"\n",
"[Multi-Tool Agent] Tool result: Title: Banana for scale - Universal converter - Unit conversion for all!\n",
"Description: Introducing the <strong>banana</strong> as the universal measurement unit, all conversion-related confusion can be avoided. Use this handy app to convert imperial and metric measurements to universal <strong>bananas</strong>.\n",
"URL: http://bananaforscale.info/\n",
"\n",
"Title: How big is a banana? - Quora\n",
"Description: Answer (1 of 10): How big is a <strong>banana</strong>? Is that your <strong>banana</strong> or my <strong>banana</strong>? Musa Ingens, the world's largest <strong>banana</strong> tree growing in the forest of Papua New Guinea and Indonesia. It is the tallest species of <strong>bananas</strong> and the world's largest herbaceous plant. The <strong>size</strong> of the Musa Ingens <strong>banana</strong> is imp...\n",
"URL: https://www.quora.com/How-big-is-a-banana\n",
"\n",
"Title: How Many Calories and Carbs Are in a Banana?\n",
"Description: <strong>Bananas</strong> are extremely healthy and nutritious. Most people know this, but many wonder how many calories and carbs <strong>bananas</strong> contain.\n",
"URL: https://www.healthline.com/nutrition/bananas-calories-carbs\n",
"\n",
"Title: Banana - Wikipedia\n",
"Description: As of 2018, <strong>bananas</strong> are exported in larger <strong>volume</strong> and to a larger value than any other fruit. In 2022, world production of <strong>bananas</strong> and plantains combined was 179 million tonnes, led by India and China with a combined total of 26% of global production. Other major producers were Uganda, Indonesia, ...\n",
"URL: https://en.wikipedia.org/wiki/Banana\n",
"\n",
"Title: On the Usefulness of Bananas: A New System of Measurement | by Liam Foley | Medium\n",
"Description: According to a study conducted by Mahmoud Soltani of the University of Tehran, on average, a fully ripe banana fruit of the Cavendish variety measures 200.25 millimetres in length, with a mass of 180.56 grams, and an ellipsoid volume of <strong>156.10 cubic centimetres</strong>.\n",
"URL: https://medium.com/@liamfoley2430/on-the-usefulness-of-bananas-a-new-system-of-measurement-76c50d8bb89a\n",
"\n",
"Title: Banana-based Length & Distance Unit Converter | Convert To Bananas\n",
"Description: Convert any length to <strong>bananas</strong> (and to useful units as well)\n",
"URL: https://www.converttobananas.com/\n",
"\n",
"Title: r/atlanticdiscussions on Reddit: HOW LONG IS THE, UM, AVERAGE BANANA? And why do you all want to know so bad?\n",
"Description: As a writer on the internet, I often feel as though I’m getting a peek into the American mind. Recently, while looking at search data, I discovered…\n",
"URL: https://www.reddit.com/r/atlanticdiscussions/comments/pbenqf/how_long_is_the_um_average_banana_and_why_do_you/\n",
"\n",
"Title: How Long Is the, Um, Average Banana?\n",
"Description: <strong>Bananas</strong> come in a total of five <strong>sizes</strong> — extra small, small, medium, large and extra large. But how long is the <strong>average</strong> <strong>banana</strong>?\n",
"URL: https://melmagazine.com/en-us/story/how-long-is-a-banana\n",
"\n",
"Title: r/theydidthemath on Reddit: [Request] Assuming the bananas are normal size how many rooms would 102,769 bananas fill?\n",
"Description: The average volume of a banana is <strong>156.1 cm³</strong>. Assuming you mean whole, uncrushed bananas, google search results peg the packing effeciency around 65%.\n",
"URL: https://www.reddit.com/r/theydidthemath/comments/rdiyis/request_assuming_the_bananas_are_normal_size_how/\n",
"\n",
"Title: How big is your banana?\n",
"Description: BMI Calculator Enter your details: Height (cm): Weight (kg): BMI (kg/m2) is Category is\n",
"URL: https://healthieryou.net.au/wp-content/uploads/2015/11/How-big-is-your-banana-Article.pdf\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[Multi-Tool Agent] Output: Now I have the information I need to calculate how many bananas will fit in a Toyota GR86. Let me think through this:\n",
"\n",
"[Multi-Tool Agent] Tool call: think(thought=From the search results, I found:\n",
"\n",
"1. Toyota GR86 trunk capacity:\n",
" - 6.3 cubic feet of trunk space (according to multiple sources)\n",
" - This equals approximately 178,385 cubic centimeters (6.3 ft³ × 28,316.85 cm³/ft³)\n",
"\n",
"2. Average banana volume:\n",
" - A fully ripe Cavendish banana has a volume of approximately 156.10 cubic centimeters\n",
"\n",
"3. Packing efficiency:\n",
" - When packing irregular objects like bananas, we need to account for the space between them\n",
" - A Reddit comment mentioned packing efficiency of around 65% for bananas\n",
" - This means only about 65% of the total volume can be filled with bananas due to gaps\n",
"\n",
"So to calculate how many bananas will fit in the Toyota GR86 trunk:\n",
"- Available volume with packing efficiency: 178,385 cm³ × 0.65 = 115,950 cm³\n",
"- Number of bananas: 115,950 cm³ ÷ 156.10 cm³ = 743 bananas\n",
"\n",
"However, the Toyota GR86 also has an interior cabin with a passenger volume of approximately 78 ft³ (according to one of the search results). If we were to fill the entire car with bananas (which is not practical but answers the hypothetical question):\n",
"\n",
"Total car volume = 78 ft³ (passenger) + 6.3 ft³ (trunk) = 84.3 ft³ = 2,387,110 cm³\n",
"\n",
"With 65% packing efficiency:\n",
"- Available volume: 2,387,110 cm³ × 0.65 = 1,551,622 cm³\n",
"- Number of bananas: 1,551,622 cm³ ÷ 156.10 cm³ = 9,940 bananas\n",
"\n",
"So, approximately 9,940 bananas could theoretically fit in an entire Toyota GR86 if completely filled, or about 743 bananas in just the trunk.)\n",
"\n",
"[Multi-Tool Agent] Tool result: Thinking complete!\n",
"\n",
"[Multi-Tool Agent] Output: # How Many Bananas Will Fit in a Toyota GR86\n",
"\n",
"Based on my research, I can provide you with an estimate of how many bananas would fit in a Toyota GR86.\n",
"\n",
"## The Data:\n",
"\n",
"- **Toyota GR86 trunk capacity**: 6.3 cubic feet (approximately 178,385 cubic centimeters)\n",
"- **Toyota GR86 passenger volume**: 78 cubic feet\n",
"- **Average banana volume**: 156.10 cubic centimeters per banana\n",
"- **Packing efficiency**: Approximately 65% (accounting for inevitable gaps between irregularly shaped bananas)\n",
"\n",
"## The Calculation:\n",
"\n",
"If we're talking about filling just the trunk:\n",
"- Available volume with packing efficiency: 178,385 cm³ × 0.65 = 115,950 cm³\n",
"- Number of bananas in trunk: 115,950 cm³ ÷ 156.10 cm³ = approximately **743 bananas**\n",
"\n",
"If we're considering the entire car (including passenger compartment):\n",
"- Total car volume: 84.3 cubic feet (2,387,110 cm³)\n",
"- Available volume with packing efficiency: 2,387,110 cm³ × 0.65 = 1,551,622 cm³\n",
"- Total number of bananas: 1,551,622 cm³ ÷ 156.10 cm³ = approximately **9,940 bananas**\n",
"\n",
"So, you could fit approximately 743 bananas in the trunk alone, or nearly 10,000 bananas if you filled the entire Toyota GR86 (though that would leave no room for a driver!).\n"
]
},
{
"data": {
"text/plain": [
"Message(id='msg_01NC46TLNZSG97ZH5v7b7YcN', content=[TextBlock(text=\"# How Many Bananas Will Fit in a Toyota GR86\\n\\nBased on my research, I can provide you with an estimate of how many bananas would fit in a Toyota GR86.\\n\\n## The Data:\\n\\n- **Toyota GR86 trunk capacity**: 6.3 cubic feet (approximately 178,385 cubic centimeters)\\n- **Toyota GR86 passenger volume**: 78 cubic feet\\n- **Average banana volume**: 156.10 cubic centimeters per banana\\n- **Packing efficiency**: Approximately 65% (accounting for inevitable gaps between irregularly shaped bananas)\\n\\n## The Calculation:\\n\\nIf we're talking about filling just the trunk:\\n- Available volume with packing efficiency: 178,385 cm³ × 0.65 = 115,950 cm³\\n- Number of bananas in trunk: 115,950 cm³ ÷ 156.10 cm³ = approximately **743 bananas**\\n\\nIf we're considering the entire car (including passenger compartment):\\n- Total car volume: 84.3 cubic feet (2,387,110 cm³)\\n- Available volume with packing efficiency: 2,387,110 cm³ × 0.65 = 1,551,622 cm³\\n- Total number of bananas: 1,551,622 cm³ ÷ 156.10 cm³ = approximately **9,940 bananas**\\n\\nSo, you could fit approximately 743 bananas in the trunk alone, or nearly 10,000 bananas if you filled the entire Toyota GR86 (though that would leave no room for a driver!).\", type='text')], model='claude-3-7-sonnet-20250219', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=4, output_tokens=385, cache_creation_input_tokens=574, cache_read_input_tokens=6630, service_tier='standard'))"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"await agent.run_async(\"How many bananas will fit in an Toyota GR86?\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 4. Example with Anthropic Server Tools\n",
"\n",
"This example demonstrates using Anthropic's native server tools for web search and code execution."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[Server Tools Agent] Agent initialized\n"
]
}
],
"source": [
"# Create Anthropic server tools\n",
"web_search_tool = WebSearchServerTool(\n",
" name=\"web_search\",\n",
" max_uses=5, # Limit to 5 searches per request\n",
" blocked_domains=[\"example.com\"] # Example of blocking specific domains\n",
")\n",
"\n",
"code_execution_tool = CodeExecutionServerTool()\n",
"\n",
"# Initialize agent with server tools\n",
"server_agent = Agent(\n",
" name=\"Server Tools Agent\",\n",
" system=\"\"\"\n",
"You are a helpful assistant with access to:\n",
"1. Web search for finding current information\n",
"2. Code execution for running Python code\n",
"3. Think tool for complex reasoning\n",
"\n",
"Use these tools effectively to answer questions that require current data or calculations.\n",
"\"\"\",\n",
" tools=[think_tool, web_search_tool, code_execution_tool],\n",
" config=ModelConfig(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" max_tokens=4096,\n",
" temperature=0.7\n",
" ),\n",
" verbose=True\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[Server Tools Agent] Received: \n",
"Search for the current population of Tokyo, Japan. \n",
"Then write and execute Python code to calculate how many people that would be per square kilometer, \n",
"given that Tokyo's area is approximately 2,194 square kilometers.\n",
"\n",
"\n",
"[Server Tools Agent] Output: I'll help you find Tokyo's current population and then calculate the population density. Let me start by searching for the current population data.\n",
"\n",
"[Server Tools Agent] Output: Now let me search for more specific information about Tokyo's current population, as the results show different figures for different definitions of \"Tokyo\" (city proper vs metropolitan area).\n",
"\n",
"[Server Tools Agent] Tool call: think(thought=Now I have a clearer picture of Tokyo's population. From the search results, I can see there are different definitions of \"Tokyo\":\n",
"\n",
"1. Tokyo Metropolis (Tokyo-to) - the prefecture/administrative area: Around 14 million people as of 2024\n",
"2. The 23 Special Wards (central Tokyo): Around 9.2-9.7 million people\n",
"3. Greater Tokyo Area (metropolitan area): Around 37 million people\n",
"\n",
"For the calculation with the given area of 2,194 square kilometers, this seems to refer to the Tokyo Metropolis (Tokyo-to) area, as the search results mention:\n",
"- \"metropolis population is 13.9 Million as on 2019 and has area of 2,194 Sq km\" (from source 17)\n",
"- The surface area of the city metropolis comes to approximately 2,187.66 km² (from source 2)\n",
"\n",
"So I should use approximately 14 million as the current population for Tokyo Metropolis for the calculation with the 2,194 sq km area.\n",
"\n",
"Let me write Python code to calculate the population density.)\n",
"\n",
"[Server Tools Agent] Tool result: Thinking complete!\n",
"\n",
"[Server Tools Agent] Output: Now I'll write and execute Python code to calculate Tokyo's population density based on the information I found.\n",
"\n",
"[Server Tools Agent] Output: Based on my search and calculations, here are the key findings:\n",
"\n",
"## Tokyo's Current Population\n",
"\n",
"\n",
"\n",
"[Server Tools Agent] Output: Tokyo Metropolis (Tokyo-to) has a population of over 14 million people as of 2023-2024\n",
"\n",
"[Server Tools Agent] Output: . The search results show different figures depending on the specific definition of \"Tokyo\":\n",
"\n",
"- \n",
"\n",
"[Server Tools Agent] Output: The 23 wards have a population of 9.2 million\n",
"\n",
"[Server Tools Agent] Output: \n",
"- \n",
"\n",
"[Server Tools Agent] Output: The metropolis has a population that exceeds 13 million\n",
"\n",
"[Server Tools Agent] Output: \n",
"- \n",
"\n",
"[Server Tools Agent] Output: The Greater Tokyo Area has 41 million residents as of 2024\n",
"\n",
"[Server Tools Agent] Output: \n",
"\n",
"## Population Density Calculation\n",
"\n",
"Using the current population of approximately **14 million people** for Tokyo Metropolis and the given area of **2,194 square kilometers**, the calculation shows:\n",
"\n",
"**Tokyo's population density is approximately 6,381 people per square kilometer**\n",
"\n",
"This aligns well with existing data, as \n",
"\n",
"[Server Tools Agent] Output: one source indicates Tokyo metropolis has a density of 6,349 persons per square kilometer\n",
"\n",
"[Server Tools Agent] Output: , confirming our calculation is accurate.\n",
"\n",
"## Context\n",
"\n",
"This makes Tokyo one of the most densely populated metropolitan areas in the world. \n",
"\n",
"[Server Tools Agent] Output: The greater Tokyo metropolitan area is estimated to be over 36 million people and is home to 25% of Japan's population, making it the most populous metropolitan area in the world\n",
"\n",
"[Server Tools Agent] Output: .\n"
]
},
{
"data": {
"text/plain": [
"Message(id='msg_018nAgEXuVMSJCTSrSjcZtrj', content=[TextBlock(text=\"Now I'll write and execute Python code to calculate Tokyo's population density based on the information I found.\", type='text'), TextBlock(text=None, type='server_tool_use', id='srvtoolu_01JAxi9fcajoScWQWEG1bJ2S', name='code_execution', input={'code': '# Tokyo population and area data\\n# Based on search results, Tokyo Metropolis (Tokyo-to) has approximately 14 million people\\n# and covers 2,194 square kilometers\\n\\ntokyo_population = 14_000_000 # Current population of Tokyo Metropolis (2024)\\ntokyo_area_sq_km = 2194 # Area in square kilometers\\n\\n# Calculate population density\\npopulation_density = tokyo_population / tokyo_area_sq_km\\n\\nprint(\"Tokyo Population Density Calculation\")\\nprint(\"=\" * 40)\\nprint(f\"Tokyo Metropolis Population: {tokyo_population:,} people\")\\nprint(f\"Tokyo Metropolis Area: {tokyo_area_sq_km:,} square kilometers\")\\nprint(f\"Population Density: {population_density:,.2f} people per square kilometer\")\\n\\n# Let\\'s also show some context with rounded numbers\\nprint(f\"\\\\nRounded: Approximately {population_density:,.0f} people per square kilometer\")\\n\\n# For comparison, let\\'s also calculate for different population estimates\\nprint(\"\\\\n\" + \"=\"*50)\\nprint(\"COMPARISON WITH DIFFERENT POPULATION ESTIMATES:\")\\nprint(\"=\"*50)\\n\\n# Different population estimates from search results\\nestimates = [\\n (\"Tokyo Metropolis (conservative estimate)\", 13_500_000),\\n (\"Tokyo Metropolis (current estimate)\", 14_000_000),\\n (\"Tokyo Metropolis (high estimate)\", 14_300_000),\\n]\\n\\nfor description, population in estimates:\\n density = population / tokyo_area_sq_km\\n print(f\"{description}: {density:,.0f} people/km²\")'}), TextBlock(text=None, type='code_execution_tool_result', tool_use_id='srvtoolu_01JAxi9fcajoScWQWEG1bJ2S', content={'type': 'code_execution_result', 'stdout': 'Tokyo Population Density Calculation\\n========================================\\nTokyo Metropolis Population: 14,000,000 people\\nTokyo Metropolis Area: 2,194 square kilometers\\nPopulation Density: 6,381.04 people per square kilometer\\n\\nRounded: Approximately 6,381 people per square kilometer\\n\\n==================================================\\nCOMPARISON WITH DIFFERENT POPULATION ESTIMATES:\\n==================================================\\nTokyo Metropolis (conservative estimate): 6,153 people/km²\\nTokyo Metropolis (current estimate): 6,381 people/km²\\nTokyo Metropolis (high estimate): 6,518 people/km²\\n', 'stderr': '', 'return_code': 0, 'content': []}), TextBlock(text=\"Based on my search and calculations, here are the key findings:\\n\\n## Tokyo's Current Population\\n\\n\", type='text'), TextBlock(text='Tokyo Metropolis (Tokyo-to) has a population of over 14 million people as of 2023-2024', type='text', citations=[{'type': 'web_search_result_location', 'cited_text': 'With a population of over 14 million in the city proper in 2023, it is one of the most populous urban areas in the world. ', 'url': 'https://en.wikipedia.org/wiki/Tokyo', 'title': 'Tokyo - Wikipedia', 'encrypted_index': 'Eo8BCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDNkmb67meDKwvfNSWBoMey/C2mf4C76VGRm/IjB/p9OlOe00CdTjisZ+Soyr8DL2IDH8J+LioeVOOHK7L/3HfUnClNRaZvtHREGHnFQqE0Pko8rhETBI/USi53r6+Vq2i1sYBA=='}, {'type': 'web_search_result_location', 'cited_text': 'The area within the dashed white line is home to 14 million people, as of 2020. ', 'url': 'http://www.statsmapsnpix.com/2022/09/how-big-is-tokyo.html', 'title': 'Stats, Maps n Pix: How big is Tokyo?', 'encrypted_index': 'EpABCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDN3Dl6f/w5+R2DpZzRoMZiaU1oKXtaWciBkzIjCeqzTw6VNVH9T8pJcQGylS/KdoN8tamtX70LsJ7ygZeDUBnbVxfCebu8Xfpv9tdJAqFN2KZA2z6Q/22MX5gewO0+OfwUtrGAQ='}, {'type': 'web_search_result_location', 'cited_text': 'The area within the dashed white line is home to 14 million people, as of 2020. ', 'url': 'http://www.statsmapsnpix.com/2022/09/how-big-is-tokyo.html', 'title': 'Stats, Maps n Pix: How big is Tokyo?', 'encrypted_index': 'EpABCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDPgOVKuta9qNyug8dRoMzPFdKW3fR2mvZIK7IjCUZybt1VVAMoVOA3VEOCprNID+ZQWavx0Z9ug53tjoeanpJvyGUIqn65McFNAKksEqFDn6oY/apm+AjLJk908w1NyD4w0OGAQ='}]), TextBlock(text='. The search results show different figures depending on the specific definition of \"Tokyo\":\\n\\n- ', type='text'), TextBlock(text='The 23 wards have a population of 9.2 million', type='text', citations=[{'type': 'web_search_result_location', 'cited_text': 'The 23 wards claim a population of 9.2 million, but the metropolis has a population that exceeds 13 million. ', 'url': 'https://worldpopulationreview.com/cities/japan/tokyo', 'title': 'Tokyo Population 2025', 'encrypted_index': 'Eo8BCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDGHmgL6AaaF2lV1RIBoMst2h5K/VIu2RGDTwIjBLqvkNQPtkPP1V49Wf9IxwZBOyM+HgBvi7rd6yR/Wkjr9LpT3HnIQOT5FetsLzqUoqE5k8jG1Xsz7hyicRcL/9UCJHKU8YBA=='}]), TextBlock(text='\\n- ', type='text'), TextBlock(text='The metropolis has a population that exceeds 13 million', type='text', citations=[{'type': 'web_search_result_location', 'cited_text': 'The 23 wards claim a population of 9.2 million, but the metropolis has a population that exceeds 13 million. ', 'url': 'https://worldpopulationreview.com/cities/japan/tokyo', 'title': 'Tokyo Population 2025', 'encrypted_index': 'Eo8BCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDHsQQzoI+Hz+TiOD7xoMgVi2a9XbrLmJlkNKIjCXGiOzb7fzGY/ueYkCuE280j3RfDhLCJ3YWi6gVboWe2BidSHKRwJBoPbhHAaWwVYqEwZ9kwdNZiabocE1Kbh3m6GW/SMYBA=='}, {'type': 'web_search_result_location', 'cited_text': 'The 23 wards claim a population of 9.2 million, but the metropolis has a population that exceeds 13 million. ', 'url': 'https://worldpopulationreview.com/cities/japan/tokyo', 'title': 'Tokyo Population 2025', 'encrypted_index': 'EpEBCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDHolkkUxMX/ORAlhZBoMoDhnLJx+SR+8R8aTIjDBKIO2DJG07HcLucjTH1y1LCGBRw1mv/t061+rSVpTbV7iY5DXREoEUGCcMFVasUgqFT9KXif7+m2jD/0yN42wWd02HG+jbRgE'}]), TextBlock(text='\\n- ', type='text'), TextBlock(text='The Greater Tokyo Area has 41 million residents as of 2024', type='text', citations=[{'type': 'web_search_result_location', 'cited_text': 'The Greater Tokyo Area, which includes Tokyo and parts of six neighboring prefectures, is the most populous metropolitan area in the world, with 41 mi...', 'url': 'https://en.wikipedia.org/wiki/Tokyo', 'title': 'Tokyo - Wikipedia', 'encrypted_index': 'Eo8BCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDPu/83J4h+//6WejfhoMabArnZYopPgIgBadIjABzh8N5tuANZ8z7yijY+HpuAP+iw6WDSwqolJU6364du78SvzlqUXbWLujKQdIhMcqE1fHzX0qTHJmNVRZHIt3AcmEBLwYBA=='}]), TextBlock(text=\"\\n\\n## Population Density Calculation\\n\\nUsing the current population of approximately **14 million people** for Tokyo Metropolis and the given area of **2,194 square kilometers**, the calculation shows:\\n\\n**Tokyo's population density is approximately 6,381 people per square kilometer**\\n\\nThis aligns well with existing data, as \", type='text'), TextBlock(text='one source indicates Tokyo metropolis has a density of 6,349 persons per square kilometer', type='text', citations=[{'type': 'web_search_result_location', 'cited_text': 'Tokyo population in 2023 is estimated to be 35.8 million, population in 2022 is estimated to be 35.6 million, in 2021 is estimated to be 37.5 million,...', 'url': 'https://www.populationu.com/cities/tokyo-population', 'title': 'Tokyo Population 2025', 'encrypted_index': 'EpEBCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDH+CHv/68jATiDYFzRoMKun6Si5c+OHOiG9WIjAq6lZhNchC2RxsNLZMgheItJe0yOT53PlDChrsyxJztTkx8aSELTlrH+2V953cZhYqFUMI2gojH59prn81fC8mSgHNmvaKKBgE'}]), TextBlock(text=', confirming our calculation is accurate.\\n\\n## Context\\n\\nThis makes Tokyo one of the most densely populated metropolitan areas in the world. ', type='text'), TextBlock(text=\"The greater Tokyo metropolitan area is estimated to be over 36 million people and is home to 25% of Japan's population, making it the most populous metropolitan area in the world\", type='text', citations=[{'type': 'web_search_result_location', 'cited_text': 'The greater Tokyo metropolitan area, which is spread over 3 prefectures, is much larger and has a population that is estimated to be over 36 million. ...', 'url': 'https://worldpopulationreview.com/cities/japan/tokyo', 'title': 'Tokyo Population 2025', 'encrypted_index': 'EpEBCioIBBgCIiQ3NmU4ZmI4OC1kOGFhLTQwMzMtYjQ1Mi1lMmZlY2M3MGFiZjkSDC3eHB8MpV+6PvSJ9hoMGBBbLOOC80uQmSO4IjCujon0QmWYTtDWe+Po+0YK+bXYbwcEwKmurDHUK1IuU6w2rvi9Rq2XeSecqpnOhNgqFU8JodAjBnotfaYFd1dee+V5IBAlIxgE'}]), TextBlock(text='.', type='text')], model='claude-sonnet-4-20250514', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=Usage(input_tokens=9, output_tokens=841, cache_creation_input_tokens=928, cache_read_input_tokens=48328, service_tier='standard', server_tool_use={'web_search_requests': 0}), container={'id': 'container_011CQTnr9bhVu3fPTnaT85oA', 'expires_at': '2025-06-24T22:55:24.157374+00:00'})"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Example 1: Use web search to find current information and code execution for analysis\n",
"server_agent.run(\"\"\"\n",
"Search for the current population of Tokyo, Japan. \n",
"Then write and execute Python code to calculate how many people that would be per square kilometer, \n",
"given that Tokyo's area is approximately 2,194 square kilometers.\n",
"\"\"\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
================================================
FILE: agents/test_message_params.py
================================================
#!/usr/bin/env python3
"""Test suite for Agent message_params functionality.
This module tests the ability to pass custom parameters to the Claude API
through the Agent's message_params argument, including headers, metadata,
and API parameters.
"""
import os
import sys
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from agents.agent import Agent, ModelConfig
class TestMessageParams:
"""Test cases for message_params functionality."""
def __init__(self, verbose: bool = True):
"""Initialize test suite.
Args:
verbose: Whether to print detailed output
"""
self.verbose = verbose
self.passed = 0
self.failed = 0
def _print(self, message: str) -> None:
"""Print message if verbose mode is on."""
if self.verbose:
print(message)
def _run_test(self, test_name: str, test_func: callable) -> None:
"""Run a single test and track results.
Args:
test_name: Name of the test
test_func: Test function to execute
"""
self._print(f"\n{'='*60}")
self._print(f"Running: {test_name}")
self._print('='*60)
try:
test_func()
self.passed += 1
self._print(f"✓ {test_name} PASSED")
except Exception as e:
self.failed += 1
self._print(f"✗ {test_name} FAILED: {str(e)}")
if self.verbose:
import traceback
traceback.print_exc()
def test_basic_agent(self) -> None:
"""Test agent without message_params to ensure backward compatibility."""
agent = Agent(
name="BasicAgent",
system="You are a helpful assistant. Be very brief.",
verbose=False
)
response = agent.run("What is 2+2?")
# response is a list of message content blocks
assert any("4" in str(block.get("text", "")) for block in response if block.get("type") == "text")
response_text = next((block["text"] for block in response if block.get("type") == "text"), "")
self._print(f"Response: {response_text}")
def test_custom_headers(self) -> None:
"""Test passing custom headers through message_params."""
agent = Agent(
name="HeaderAgent",
system="You are a helpful assistant. Be very brief.",
verbose=False,
message_params={
"extra_headers": {
"X-Custom-Header": "test-value",
"X-Request-ID": "test-12345"
}
}
)
# Verify headers are stored
assert "extra_headers" in agent.message_params
assert agent.message_params["extra_headers"]["X-Custom-Header"] == "test-value"
response = agent.run("What is 3+3?")
response_text = next((block["text"] for block in response if block.get("type") == "text"), "")
assert "6" in response_text
self._print(f"Response with custom headers: {response_text}")
def test_beta_headers(self) -> None:
"""Test passing beta feature headers."""
agent = Agent(
name="BetaAgent",
system="You are a helpful assistant. Be very brief.",
verbose=False,
message_params={
"extra_headers": {
"anthropic-beta": "files-api-2025-04-14"
}
}
)
# The API call should succeed even with beta headers
response = agent.run("What is 5*5?")
response_text = next((block["text"] for block in response if block.get("type") == "text"), "")
assert "25" in response_text
self._print(f"Response with beta headers: {response_text}")
def test_metadata(self) -> None:
"""Test passing valid metadata fields."""
agent = Agent(
name="MetadataAgent",
system="You are a helpful assistant. Be very brief.",
verbose=False,
message_params={
"metadata": {
"user_id": "test-user-123"
}
}
)
response = agent.run("What is 10/2?")
response_text = next((block["text"] for block in response if block.get("type") == "text"), "")
assert "5" in response_text
self._print(f"Response with metadata: {response_text}")
def test_api_parameters(self) -> None:
"""Test passing various API parameters."""
agent = Agent(
name="ParamsAgent",
system="You are a helpful assistant.",
verbose=False,
message_params={
"top_k": 10,
"top_p": 0.95,
"temperature": 0.7
}
)
# Verify parameters are passed through
params = agent._prepare_message_params()
assert params["top_k"] == 10
assert params["top_p"] == 0.95
assert params["temperature"] == 0.7
response = agent.run("Say 'test'")
response_text = next((block["text"] for block in response if block.get("type") == "text"), "")
assert response_text
self._print(f"Response with custom params: {response_text}")
def test_parameter_override(self) -> None:
"""Test that message_params override config defaults."""
config = ModelConfig(
temperature=1.0,
max_tokens=100
)
agent = Agent(
name="OverrideAgent",
system="You are a helpful assistant.",
config=config,
verbose=False,
message_params={
"temperature": 0.5, # Should override config
"max_tokens": 200 # Should override config
}
)
params = agent._prepare_message_params()
assert params["temperature"] == 0.5
assert params["max_tokens"] == 200
self._print("Parameter override successful")
def test_invalid_metadata_field(self) -> None:
"""Test that invalid metadata fields are properly rejected by the API."""
agent = Agent(
name="InvalidAgent",
system="You are a helpful assistant.",
verbose=False,
message_params={
"metadata": {
"user_id": "valid",
"invalid_field": "should-fail"
}
}
)
try:
agent.run("Test")
# Should not reach here
raise AssertionError("Expected API error for invalid metadata field")
except Exception as e:
assert "invalid_request_error" in str(e) or "metadata" in str(e).lower()
self._print(f"Correctly rejected invalid metadata: {type(e).__name__}")
def test_combined_parameters(self) -> None:
"""Test combining multiple parameter types."""
agent = Agent(
name="CombinedAgent",
system="You are a helpful assistant. Be very brief.",
verbose=False,
message_params={
"extra_headers": {
"X-Test": "combined",
"anthropic-beta": "files-api-2025-04-14"
},
"metadata": {
"user_id": "combined-test"
},
"temperature": 0.8,
"top_k": 5
}
)
params = agent._prepare_message_params()
assert params["extra_headers"]["X-Test"] == "combined"
assert params["metadata"]["user_id"] == "combined-test"
assert params["temperature"] == 0.8
assert params["top_k"] == 5
response = agent.run("What is 1+1?")
response_text = next((block["text"] for block in response if block.get("type") == "text"), "")
assert "2" in response_text
self._print(f"Response with combined params: {response_text}")
def run_all_tests(self) -> None:
"""Run all test cases."""
self._print("\nAgent message_params Test Suite")
self._print("="*60)
tests = [
("Basic Agent (No message_params)", self.test_basic_agent),
("Custom Headers", self.test_custom_headers),
("Beta Feature Headers", self.test_beta_headers),
("Valid Metadata", self.test_metadata),
("API Parameters", self.test_api_parameters),
("Parameter Override", self.test_parameter_override),
("Invalid Metadata Field", self.test_invalid_metadata_field),
("Combined Parameters", self.test_combined_parameters),
]
for test_name, test_func in tests:
self._run_test(test_name, test_func)
self._print(f"\n{'='*60}")
self._print(f"Test Results: {self.passed} passed, {self.failed} failed")
self._print("="*60)
return self.failed == 0
def main():
"""Run the test suite."""
# Check for API key
if not os.environ.get("ANTHROPIC_API_KEY"):
print("Error: Please set ANTHROPIC_API_KEY environment variable")
sys.exit(1)
# Run tests
test_suite = TestMessageParams(verbose=True)
success = test_suite.run_all_tests()
# Exit with appropriate code
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
================================================
FILE: agents/tools/__init__.py
================================================
"""Tools module for agent framework."""
from .base import Tool
from .code_execution import CodeExecutionServerTool
from .file_tools import FileReadTool, FileWriteTool
from .think import ThinkTool
from .web_search import WebSearchServerTool
__all__ = [
"Tool",
"CodeExecutionServerTool",
"FileReadTool",
"FileWriteTool",
"ThinkTool",
"WebSearchServerTool",
]
================================================
FILE: agents/tools/base.py
================================================
"""Base tool definitions for the agent framework."""
from dataclasses import dataclass
from typing import Any
@dataclass
class Tool:
"""Base class for all agent tools."""
name: str
description: str
input_schema: dict[str, Any]
def to_dict(self) -> dict[str, Any]:
"""Convert tool to Claude API format."""
return {
"name": self.name,
"description": self.description,
"input_schema": self.input_schema,
}
async def execute(self, **kwargs) -> str:
"""Execute the tool with provided parameters."""
raise NotImplementedError(
"Tool subclasses must implement execute method"
)
================================================
FILE: agents/tools/calculator_mcp.py
================================================
#!/usr/bin/env python3
"""Simple calculator tool for basic math operations."""
import math
from mcp.server import FastMCP
mcp = FastMCP("Calculator")
@mcp.tool(name="calculator")
def calculator(number1: float, number2: float, operator: str) -> str:
"""Performs basic calculations with two numbers.
Args:
number1: First number in the calculation
number2: Second number in the calculation
operator: Operation symbol to perform (+, -, *, /, ^, sqrt)
Note: Only these exact symbols are supported, not words
Returns:
Result of the calculation
"""
try:
if operator == "+":
result = number1 + number2
elif operator == "-":
result = number1 - number2
elif operator == "*":
result = number1 * number2
elif operator == "/":
if number2 == 0:
return "Error: Division by zero"
result = number1 / number2
elif operator == "^":
result = number1**number2
elif operator == "sqrt":
if number1 < 0:
return "Error: Cannot take square root of negative number"
result = math.sqrt(number1)
else:
return f"Error: Unsupported operator '{operator}'"
# Format the result
if isinstance(result, float) and result.is_integer():
result = int(result)
return f"Result: {result}"
except Exception as e:
return f"Error: {str(e)}"
if __name__ == "__main__":
mcp.run()
================================================
FILE: agents/tools/code_execution.py
================================================
"""Code execution server tool for the agent framework."""
from dataclasses import dataclass
from typing import Any
@dataclass
class CodeExecutionServerTool:
"""Code execution server tool that uses Anthropic's server tool format."""
name: str = "code_execution"
type: str = "code_execution_20250522"
def to_dict(self) -> dict[str, Any]:
"""Convert to Anthropic server tool format."""
return {
"type": self.type,
"name": self.name,
}
================================================
FILE: agents/tools/file_tools.py
================================================
"""File operation tools for reading and writing files."""
import asyncio
import glob
import os
from pathlib import Path
from .base import Tool
class FileReadTool(Tool):
"""Tool for reading files and listing directories."""
def __init__(self):
super().__init__(
name="file_read",
description="""
Read files or list directory contents.
Operations:
- read: Read the contents of a file
- list: List files in a directory
""",
input_schema={
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["read", "list"],
"description": "File operation to perform",
},
"path": {
"type": "string",
"description": "File path for read or directory path",
},
"max_lines": {
"type": "integer",
"description": "Maximum lines to read (0 means no limit)",
},
"pattern": {
"type": "string",
"description": "File pattern to match",
},
},
"required": ["operation", "path"],
},
)
async def execute(
self,
operation: str,
path: str,
max_lines: int = 0,
pattern: str = "*",
) -> str:
"""Execute a file read operation.
Args:
operation: The operation to perform (read or list)
path: The file or directory path
max_lines: Maximum lines to read (for read operation, 0 means no limit)
pattern: File pattern to match (for list operation)
Returns:
Result of the operation as string
"""
if operation == "read":
return await self._read_file(path, max_lines)
elif operation == "list":
return await self._list_files(path, pattern)
else:
return f"Error: Unsupported operation '{operation}'"
async def _read_file(self, path: str, max_lines: int = 0) -> str:
"""Read a file from disk.
Args:
path: Path to the file to read
max_lines: Maximum number of lines to read (0 means read entire file)
"""
try:
file_path = Path(path)
if not file_path.exists():
return f"Error: File not found at {path}"
if not file_path.is_file():
return f"Error: {path} is not a file"
def read_sync():
with open(file_path, encoding="utf-8", errors="replace") as f:
if max_lines > 0:
lines = []
for i, line in enumerate(f):
if i >= max_lines:
break
lines.append(line)
return "".join(lines)
return f.read()
return await asyncio.to_thread(read_sync)
except Exception as e:
return f"Error reading {path}: {str(e)}"
async def _list_files(self, directory: str, pattern: str = "*") -> str:
"""List files in a directory."""
try:
dir_path = Path(directory)
if not dir_path.exists():
return f"Error: Directory not found at {directory}"
if not dir_path.is_dir():
return f"Error: {directory} is not a directory"
def list_sync():
search_pattern = f"{directory}/{pattern}"
files = glob.glob(search_pattern)
if not files:
return f"No files found matching {directory}/{pattern}"
file_list = []
for file_path in sorted(files):
path_obj = Path(file_path)
rel_path = str(file_path).replace(str(dir_path) + "/", "")
if path_obj.is_dir():
file_list.append(f"📁 {rel_path}/")
else:
file_list.append(f"📄 {rel_path}")
return "\n".join(file_list)
return await asyncio.to_thread(list_sync)
except Exception as e:
return f"Error listing files in {directory}: {str(e)}"
class FileWriteTool(Tool):
"""Tool for writing and editing files."""
def __init__(self):
super().__init__(
name="file_write",
description="""
Write or edit files.
Operations:
- write: Create or completely replace a file
- edit: Make targeted changes to parts of a file
""",
input_schema={
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["write", "edit"],
"description": "File operation to perform",
},
"path": {
"type": "string",
"description": "File path to write to or edit",
},
"content": {
"type": "string",
"description": "Content to write",
},
"old_text": {
"type": "string",
"description": "Text to replace (for edit operation)",
},
"new_text": {
"type": "string",
"description": "Replacement text (for edit operation)",
},
},
"required": ["operation", "path"],
},
)
async def execute(
self,
operation: str,
path: str,
content: str = "",
old_text: str = "",
new_text: str = "",
) -> str:
"""Execute a file write operation.
Args:
operation: The operation to perform (write or edit)
path: The file path
content: Content to write (for write operation)
old_text: Text to replace (for edit operation)
new_text: Replacement text (for edit operation)
Returns:
Result of the operation as string
"""
if operation == "write":
if not content:
return "Error: content parameter is required"
return await self._write_file(path, content)
elif operation == "edit":
if not old_text or not new_text:
return (
"Error: both old_text and new_text parameters "
"are required for edit operation"
)
return await self._edit_file(path, old_text, new_text)
else:
return f"Error: Unsupported operation '{operation}'"
async def _write_file(self, path: str, content: str) -> str:
"""Write content to a file."""
try:
file_path = Path(path)
os.makedirs(file_path.parent, exist_ok=True)
def write_sync():
with open(file_path, "w", encoding="utf-8") as f:
f.write(content)
return (
f"Successfully wrote {len(content)} "
f"characters to {path}"
)
return await asyncio.to_thread(write_sync)
except Exception as e:
return f"Error writing to {path}: {str(e)}"
async def _edit_file(self, path: str, old_text: str, new_text: str) -> str:
"""Make targeted changes to a file."""
try:
file_path = Path(path)
if not file_path.exists():
return f"Error: File not found at {path}"
if not file_path.is_file():
return f"Error: {path} is not a file"
def edit_sync():
try:
with open(
file_path, encoding="utf-8", errors="replace"
) as f:
content = f.read()
if old_text not in content:
return (
f"Error: The specified text was not "
f"found in {path}"
)
# Count occurrences to warn about multiple matches
count = content.count(old_text)
if count > 1:
# Edit with warning about multiple occurrences
new_content = content.replace(old_text, new_text)
with open(file_path, "w", encoding="utf-8") as f:
f.write(new_content)
return (
f"Warning: Found {count} occurrences. "
f"All were replaced in {path}"
)
else:
# One occurrence, straightforward replacement
new_content = content.replace(old_text, new_text)
with open(file_path, "w", encoding="utf-8") as f:
f.write(new_content)
return f"Successfully edited {path}"
except UnicodeDecodeError:
return f"Error: {path} appears to be a binary file"
return await asyncio.to_thread(edit_sync)
except Exception as e:
return f"Error editing {path}: {str(e)}"
================================================
FILE: agents/tools/mcp_tool.py
================================================
"""Tools that interface with MCP servers."""
from typing import Any
from .base import Tool
from ..utils.connections import MCPConnection
class MCPTool(Tool):
def __init__(
self,
name: str,
description: str,
input_schema: dict[str, Any],
connection: "MCPConnection",
):
super().__init__(
name=name, description=description, input_schema=input_schema
)
self.connection = connection
async def execute(self, **kwargs) -> str:
"""Execute the MCP tool with the given input_schema.
Note: Currently only supports text results from MCP tools."""
try:
result = await self.connection.call_tool(
self.name, arguments=kwargs
)
if hasattr(result, "content") and result.content:
for item in result.content:
if getattr(item, "type", None) == "text":
return item.text
return "No text content in tool response"
except Exception as e:
return f"Error executing {self.name}: {e}"
================================================
FILE: agents/tools/think.py
================================================
"""Think tool for internal reasoning."""
from .base import Tool
class ThinkTool(Tool):
"""Tool for internal reasoning without executing external actions."""
def __init__(self):
super().__init__(
name="think",
description=(
"Use the tool to think about something. It will not obtain "
"new information or change the database, but just append the "
"thought to the log. Use it when complex reasoning or some "
"cache memory is needed."
),
input_schema={
"type": "object",
"properties": {
"thought": {
"type": "string",
"description": "A thought to think about.",
}
},
"required": ["thought"],
},
)
async def execute(self, thought: str) -> str:
"""Simply returns the thought back to the model."""
return "Thinking complete!"
================================================
FILE: agents/tools/web_search.py
================================================
"""Web search server tool for the agent framework."""
from dataclasses import dataclass
from typing import Any, Optional
@dataclass
class WebSearchServerTool:
"""Web search server tool that uses Anthropic's server tool format."""
name: str = "web_search"
type: str = "web_search_20250305"
max_uses: Optional[int] = None
allowed_domains: Optional[list[str]] = None
blocked_domains: Optional[list[str]] = None
user_location: Optional[dict[str, Any]] = None
def to_dict(self) -> dict[str, Any]:
"""Convert to Anthropic server tool format."""
tool_dict: dict[str, Any] = {
"type": self.type,
"name": self.name,
}
# Add optional parameters if provided
if self.max_uses is not None:
tool_dict["max_uses"] = self.max_uses
if self.allowed_domains is not None:
tool_dict["allowed_domains"] = self.allowed_domains
if self.blocked_domains is not None:
tool_dict["blocked_domains"] = self.blocked_domains
if self.user_location is not None:
tool_dict["user_location"] = self.user_location
return tool_dict
================================================
FILE: agents/utils/__init__.py
================================================
"""Agent utility modules."""
from .history_util import MessageHistory
from .tool_util import execute_tools
__all__ = ["MessageHistory", "execute_tools"]
================================================
FILE: agents/utils/connections.py
================================================
"""Connection handling for MCP servers."""
from abc import ABC, abstractmethod
from contextlib import AsyncExitStack
from typing import Any
from mcp import ClientSession, StdioServerParameters
from mcp.client.sse import sse_client
from mcp.client.stdio import stdio_client
from ..tools.mcp_tool import MCPTool
class MCPConnection(ABC):
"""Base class for MCP server connections."""
def __init__(self):
self.session = None
self._rw_ctx = None
self._session_ctx = None
@abstractmethod
async def _create_rw_context(self):
"""Create the read/write context based on connection type."""
async def __aenter__(self):
"""Initialize MCP server connection."""
self._rw_ctx = await self._create_rw_context()
read_write = await self._rw_ctx.__aenter__()
read, write = read_write
self._session_ctx = ClientSession(read, write)
self.session = await self._session_ctx.__aenter__()
await self.session.initialize()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Clean up MCP server connection resources."""
try:
if self._session_ctx:
await self._session_ctx.__aexit__(exc_type, exc_val, exc_tb)
if self._rw_ctx:
await self._rw_ctx.__aexit__(exc_type, exc_val, exc_tb)
except Exception as e:
print(f"Error during cleanup: {e}")
finally:
self.session = None
self._session_ctx = None
self._rw_ctx = None
async def list_tools(self) -> Any:
"""Retrieve available tools from the MCP server."""
response = await self.session.list_tools()
return response.tools
async def call_tool(
self, tool_name: str, arguments: dict[str, Any]
) -> Any:
"""Call a tool on the MCP server with provided arguments."""
return await self.session.call_tool(tool_name, arguments=arguments)
class MCPConnectionStdio(MCPConnection):
"""MCP connection using standard input/output."""
def __init__(
self, command: str, args: list[str] = [], env: dict[str, str] = None
):
super().__init__()
self.command = command
self.args = args
self.env = env
async def _create_rw_context(self):
return stdio_client(
StdioServerParameters(
command=self.command, args=self.args, env=self.env
)
)
class MCPConnectionSSE(MCPConnection):
"""MCP connection using Server-Sent Events."""
def __init__(self, url: str, headers: dict[str, str] = None):
super().__init__()
self.url = url
self.headers = headers or {}
async def _create_rw_context(self):
return sse_client(url=self.url, headers=self.headers)
def create_mcp_connection(config: dict[str, Any]) -> MCPConnection:
"""Factory function to create the appropriate MCP connection."""
conn_type = config.get("type", "stdio").lower()
if conn_type == "stdio":
if not config.get("command"):
raise ValueError("Command is required for STDIO connections")
return MCPConnectionStdio(
command=config["command"],
args=config.get("args"),
env=config.get("env"),
)
elif conn_type == "sse":
if not config.get("url"):
raise ValueError("URL is required for SSE connections")
return MCPConnectionSSE(
url=config["url"], headers=config.get("headers")
)
else:
raise ValueError(f"Unsupported connection type: {conn_type}")
async def setup_mcp_connections(
mcp_servers: list[dict[str, Any]] | None,
stack: AsyncExitStack,
) -> list[MCPTool]:
"""Set up MCP server connections and create tool interfaces."""
if not mcp_servers:
return []
mcp_tools = []
for config in mcp_servers:
try:
connection = create_mcp_connection(config)
await stack.enter_async_context(connection)
tool_definitions = await connection.list_tools()
for tool_info in tool_definitions:
mcp_tools.append(
MCPTool(
name=tool_info.name,
description=tool_info.description
or f"MCP tool: {tool_info.name}",
input_schema=tool_info.inputSchema,
connection=connection,
)
)
except Exception as e:
print(f"Error setting up MCP server {config}: {e}")
print(
f"Loaded {len(mcp_tools)} MCP tools from {len(mcp_servers)} servers."
)
return mcp_tools
================================================
FILE: agents/utils/history_util.py
================================================
"""Message history with token tracking and prompt caching."""
from typing import Any
class MessageHistory:
"""Manages chat history with token tracking and context management."""
def __init__(
self,
model: str,
system: str,
context_window_tokens: int,
client: Any,
enable_caching: bool = True,
):
self.model = model
self.system = system
self.context_window_tokens = context_window_tokens
self.messages: list[dict[str, Any]] = []
self.total_tokens = 0
self.enable_caching = enable_caching
self.message_tokens: list[tuple[int, int]] = (
[]
) # List of (input_tokens, output_tokens) tuples
self.client = client
# set initial total tokens to system prompt
try:
system_token = (
self.client.messages.count_tokens(
model=self.model,
system=self.system,
messages=[{"role": "user", "content": "test"}],
).input_tokens
- 1
)
except Exception:
system_token = len(self.system) / 4
self.total_tokens = system_token
async def add_message(
self,
role: str,
content: str | list[dict[str, Any]],
usage: Any | None = None,
):
"""Add a message to the history and track token usage."""
if isinstance(content, str):
content = [{"type": "text", "text": content}]
message = {"role": role, "content": content}
self.messages.append(message)
if role == "assistant" and usage:
total_input = (
usage.input_tokens
+ getattr(usage, "cache_read_input_tokens", 0)
+ getattr(usage, "cache_creation_input_tokens", 0)
)
output_tokens = usage.output_tokens
current_turn_input = total_input - self.total_tokens
self.message_tokens.append((current_turn_input, output_tokens))
self.total_tokens += current_turn_input + output_tokens
def truncate(self) -> None:
"""Remove oldest messages when context window limit is exceeded."""
if self.total_tokens <= self.context_window_tokens:
return
TRUNCATION_NOTICE_TOKENS = 25
TRUNCATION_MESSAGE = {
"role": "user",
"content": [
{
"type": "text",
"text": "[Earlier history has been truncated.]",
}
],
}
def remove_message_pair():
self.messages.pop(0)
self.messages.pop(0)
if self.message_tokens:
input_tokens, output_tokens = self.message_tokens.pop(0)
self.total_tokens -= input_tokens + output_tokens
while (
self.message_tokens
and len(self.messages) >= 2
and self.total_tokens > self.context_window_tokens
):
remove_message_pair()
if self.messages and self.message_tokens:
original_input_tokens, original_output_tokens = (
self.message_tokens[0]
)
self.messages[0] = TRUNCATION_MESSAGE
self.message_tokens[0] = (
TRUNCATION_NOTICE_TOKENS,
original_output_tokens,
)
self.total_tokens += (
TRUNCATION_NOTICE_TOKENS - original_input_tokens
)
def format_for_api(self) -> list[dict[str, Any]]:
"""Format messages for Claude API with optional caching."""
result = [
{"role": m["role"], "content": m["content"]} for m in self.messages
]
if self.enable_caching and self.messages:
result[-1]["content"] = [
{**block, "cache_control": {"type": "ephemeral"}}
for block in self.messages[-1]["content"]
]
return result
================================================
FILE: agents/utils/tool_util.py
================================================
"""Tool execution utility with parallel execution support."""
import asyncio
from typing import Any
async def _execute_single_tool(
call: Any, tool_dict: dict[str, Any]
) -> dict[str, Any]:
"""Execute a single tool and handle errors."""
response = {"type": "tool_result", "tool_use_id": call.id}
try:
# Execute the tool directly
result = await tool_dict[call.name].execute(**call.input)
response["content"] = str(result)
except KeyError:
response["content"] = f"Tool '{call.name}' not found"
response["is_error"] = True
except Exception as e:
response["content"] = f"Error executing tool: {str(e)}"
response["is_error"] = True
return response
async def execute_tools(
tool_calls: list[Any], tool_dict: dict[str, Any], parallel: bool = True
) -> list[dict[str, Any]]:
"""Execute multiple tools sequentially or in parallel."""
if parallel:
return await asyncio.gather(
*[_execute_single_tool(call, tool_dict) for call in tool_calls]
)
else:
return [
await _execute_single_tool(call, tool_dict) for call in tool_calls
]
================================================
FILE: autonomous-coding/.gitignore
================================================
# Agent-generated output directories
generations/
# Log files
logs/
================================================
FILE: autonomous-coding/README.md
================================================
# Autonomous Coding Agent Demo
A minimal harness demonstrating long-running autonomous coding with the Claude Agent SDK. This demo implements a two-agent pattern (initializer + coding agent) that can build complete applications over multiple sessions.
## Prerequisites
**Required:** Install the latest versions of both Claude Code and the Claude Agent SDK:
```bash
# Install Claude Code CLI (latest version required)
npm install -g @anthropic-ai/claude-code
# Install Python dependencies
pip install -r requirements.txt
```
Verify your installations:
```bash
claude --version # Should be latest version
pip show claude-code-sdk # Check SDK is installed
```
**API Key:** Set your Anthropic API key:
```bash
export ANTHROPIC_API_KEY='your-api-key-here'
```
## Quick Start
```bash
python autonomous_agent_demo.py --project-dir ./my_project
```
For testing with limited iterations:
```bash
python autonomous_agent_demo.py --project-dir ./my_project --max-iterations 3
```
## Important Timing Expectations
> **Warning: This demo takes a long time to run!**
- **First session (initialization):** The agent generates a `feature_list.json` with 200 test cases. This takes several minutes and may appear to hang - this is normal. The agent is writing out all the features.
- **Subsequent sessions:** Each coding iteration can take **5-15 minutes** depending on complexity.
- **Full app:** Building all 200 features typically requires **many hours** of total runtime across multiple sessions.
**Tip:** The 200 features parameter in the prompts is designed for comprehensive coverage. If you want faster demos, you can modify `prompts/initializer_prompt.md` to reduce the feature count (e.g., 20-50 features for a quicker demo).
## How It Works
### Two-Agent Pattern
1. **Initializer Agent (Session 1):** Reads `app_spec.txt`, creates `feature_list.json` with 200 test cases, sets up project structure, and initializes git.
2. **Coding Agent (Sessions 2+):** Picks up where the previous session left off, implements features one by one, and marks them as passing in `feature_list.json`.
### Session Management
- Each session runs with a fresh context window
- Progress is persisted via `feature_list.json` and git commits
- The agent auto-continues between sessions (3 second delay)
- Press `Ctrl+C` to pause; run the same command to resume
## Security Model
This demo uses a defense-in-depth security approach (see `security.py` and `client.py`):
1. **OS-level Sandbox:** Bash commands run in an isolated environment
2. **Filesystem Restrictions:** File operations restricted to the project directory only
3. **Bash Allowlist:** Only specific commands are permitted:
- File inspection: `ls`, `cat`, `head`, `tail`, `wc`, `grep`
- Node.js: `npm`, `node`
- Version control: `git`
- Process management: `ps`, `lsof`, `sleep`, `pkill` (dev processes only)
Commands not in the allowlist are blocked by the security hook.
## Project Structure
```
autonomous-coding/
├── autonomous_agent_demo.py # Main entry point
├── agent.py # Agent session logic
├── client.py # Claude SDK client configuration
├── security.py # Bash command allowlist and validation
├── progress.py # Progress tracking utilities
├── prompts.py # Prompt loading utilities
├── prompts/
│ ├── app_spec.txt # Application specification
│ ├── initializer_prompt.md # First session prompt
│ └── coding_prompt.md # Continuation session prompt
└── requirements.txt # Python dependencies
```
## Generated Project Structure
After running, your project directory will contain:
```
my_project/
├── feature_list.json # Test cases (source of truth)
├── app_spec.txt # Copied specification
├── init.sh # Environment setup script
├── claude-progress.txt # Session progress notes
├── .claude_settings.json # Security settings
└── [application files] # Generated application code
```
## Running the Generated Application
After the agent completes (or pauses), you can run the generated application:
```bash
cd generations/my_project
# Run the setup script created by the agent
./init.sh
# Or manually (typical for Node.js apps):
npm install
npm run dev
```
The application will typically be available at `http://localhost:3000` or similar (check the agent's output or `init.sh` for the exact URL).
## Command Line Options
| Option | Description | Default |
|--------|-------------|---------|
| `--project-dir` | Directory for the project | `./autonomous_demo_project` |
| `--max-iterations` | Max agent iterations | Unlimited |
| `--model` | Claude model to use | `claude-sonnet-4-5-20250929` |
## Customization
### Changing the Application
Edit `prompts/app_spec.txt` to specify a different application to build.
### Adjusting Feature Count
Edit `prompts/initializer_prompt.md` and change the "200 features" requirement to a smaller number for faster demos.
### Modifying Allowed Commands
Edit `security.py` to add or remove commands from `ALLOWED_COMMANDS`.
## Troubleshooting
**"Appears to hang on first run"**
This is normal. The initializer agent is generating 200 detailed test cases, which takes significant time. Watch for `[Tool: ...]` output to confirm the agent is working.
**"Command blocked by security hook"**
The agent tried to run a command not in the allowlist. This is the security system working as intended. If needed, add the command to `ALLOWED_COMMANDS` in `security.py`.
**"API key not set"**
Ensure `ANTHROPIC_API_KEY` is exported in your shell environment.
## License
Internal Anthropic use.
================================================
FILE: autonomous-coding/agent.py
================================================
"""
Agent Session Logic
===================
Core agent interaction functions for running autonomous coding sessions.
"""
import asyncio
from pathlib import Path
from typing import Optional
from claude_code_sdk import ClaudeSDKClient
from client import create_client
from progress import print_session_header, print_progress_summary
from prompts import get_initializer_prompt, get_coding_prompt, copy_spec_to_project
# Configuration
AUTO_CONTINUE_DELAY_SECONDS = 3
async def run_agent_session(
client: ClaudeSDKClient,
message: str,
project_dir: Path,
) -> tuple[str, str]:
"""
Run a single agent session using Claude Agent SDK.
Args:
client: Claude SDK client
message: The prompt to send
project_dir: Project directory path
Returns:
(status, response_text) where status is:
- "continue" if agent should continue working
- "error" if an error occurred
"""
print("Sending prompt to Claude Agent SDK...\n")
try:
# Send the query
await client.query(message)
# Collect response text and show tool use
response_text = ""
async for msg in client.receive_response():
msg_type = type(msg).__name__
# Handle AssistantMessage (text and tool use)
if msg_type == "AssistantMessage" and hasattr(msg, "content"):
for block in msg.content:
block_type = type(block).__name__
if block_type == "TextBlock" and hasattr(block, "text"):
response_text += block.text
print(block.text, end="", flush=True)
elif block_type == "ToolUseBlock" and hasattr(block, "name"):
print(f"\n[Tool: {block.name}]", flush=True)
if hasattr(block, "input"):
input_str = str(block.input)
if len(input_str) > 200:
print(f" Input: {input_str[:200]}...", flush=True)
else:
print(f" Input: {input_str}", flush=True)
# Handle UserMessage (tool results)
elif msg_type == "UserMessage" and hasattr(msg, "content"):
for block in msg.content:
block_type = type(block).__name__
if block_type == "ToolResultBlock":
result_content = getattr(block, "content", "")
is_error = getattr(block, "is_error", False)
# Check if command was blocked by security hook
if "blocked" in str(result_content).lower():
print(f" [BLOCKED] {result_content}", flush=True)
elif is_error:
# Show errors (truncated)
error_str = str(result_content)[:500]
print(f" [Error] {error_str}", flush=True)
else:
# Tool succeeded - just show brief confirmation
print(" [Done]", flush=True)
print("\n" + "-" * 70 + "\n")
return "continue", response_text
except Exception as e:
print(f"Error during agent session: {e}")
return "error", str(e)
async def run_autonomous_agent(
project_dir: Path,
model: str,
max_iterations: Optional[int] = None,
) -> None:
"""
Run the autonomous agent loop.
Args:
project_dir: Directory for the project
model: Claude model to use
max_iterations: Maximum number of iterations (None for unlimited)
"""
print("\n" + "=" * 70)
print(" AUTONOMOUS CODING AGENT DEMO")
print("=" * 70)
print(f"\nProject directory: {project_dir}")
print(f"Model: {model}")
if max_iterations:
print(f"Max iterations: {max_iterations}")
else:
print("Max iterations: Unlimited (will run until completion)")
print()
# Create project directory
project_dir.mkdir(parents=True, exist_ok=True)
# Check if this is a fresh start or continuation
tests_file = project_dir / "feature_list.json"
is_first_run = not tests_file.exists()
if is_first_run:
print("Fresh start - will use initializer agent")
print()
print("=" * 70)
print(" NOTE: First session takes 10-20+ minutes!")
print(" The agent is generating 200 detailed test cases.")
print(" This may appear to hang - it's working. Watch for [Tool: ...] output.")
print("=" * 70)
print()
# Copy the app spec into the project directory for the agent to read
copy_spec_to_project(project_dir)
else:
print("Continuing existing project")
print_progress_summary(project_dir)
# Main loop
iteration = 0
while True:
iteration += 1
# Check max iterations
if max_iterations and iteration > max_iterations:
print(f"\nReached max iterations ({max_iterations})")
print("To continue, run the script again without --max-iterations")
break
# Print session header
print_session_header(iteration, is_first_run)
# Create client (fresh context)
client = create_client(project_dir, model)
# Choose prompt based on session type
if is_first_run:
prompt = get_initializer_prompt()
is_first_run = False # Only use initializer once
else:
prompt = get_coding_prompt()
# Run session with async context manager
async with client:
status, response = await run_agent_session(client, prompt, project_dir)
# Handle status
if status == "continue":
print(f"\nAgent will auto-continue in {AUTO_CONTINUE_DELAY_SECONDS}s...")
print_progress_summary(project_dir)
await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)
elif status == "error":
print("\nSession encountered an error")
print("Will retry with a fresh session...")
await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)
# Small delay between sessions
if max_iterations is None or iteration < max_iterations:
print("\nPreparing next session...\n")
await asyncio.sleep(1)
# Final summary
print("\n" + "=" * 70)
print(" SESSION COMPLETE")
print("=" * 70)
print(f"\nProject directory: {project_dir}")
print_progress_summary(project_dir)
# Print instructions for running the generated application
print("\n" + "-" * 70)
print(" TO RUN THE GENERATED APPLICATION:")
print("-" * 70)
print(f"\n cd {project_dir.resolve()}")
print(" ./init.sh # Run the setup script")
print(" # Or manually:")
print(" npm install && npm run dev")
print("\n Then open http://localhost:3000 (or check init.sh for the URL)")
print("-" * 70)
print("\nDone!")
================================================
FILE: autonomous-coding/autonomous_agent_demo.py
================================================
#!/usr/bin/env python3
"""
Autonomous Coding Agent Demo
============================
A minimal harness demonstrating long-running autonomous coding with Claude.
This script implements the two-agent pattern (initializer + coding agent) and
incorporates all the strategies from the long-running agents guide.
Example Usage:
python autonomous_agent_demo.py --project-dir ./claude_clone_demo
python autonomous_agent_demo.py --project-dir ./claude_clone_demo --max-iterations 5
"""
import argparse
import asyncio
import os
from pathlib import Path
from agent import run_autonomous_agent
# Configuration
DEFAULT_MODEL = "claude-sonnet-4-5-20250929"
def parse_args() -> argparse.Namespace:
"""Parse command line arguments."""
parser = argparse.ArgumentParser(
description="Autonomous Coding Agent Demo - Long-running agent harness",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Start fresh project
python autonomous_agent_demo.py --project-dir ./claude_clone
# Use a specific model
python autonomous_agent_demo.py --project-dir ./claude_clone --model claude-sonnet-4-5-20250929
# Limit iterations for testing
python autonomous_agent_demo.py --project-dir ./claude_clone --max-iterations 5
# Continue existing project
python autonomous_agent_demo.py --project-dir ./claude_clone
Environment Variables:
ANTHROPIC_API_KEY Your Anthropic API key (required)
""",
)
parser.add_argument(
"--project-dir",
type=Path,
default=Path("./autonomous_demo_project"),
help="Directory for the project (default: generations/autonomous_demo_project). Relative paths automatically placed in generations/ directory.",
)
parser.add_argument(
"--max-iterations",
type=int,
default=None,
help="Maximum number of agent iterations (default: unlimited)",
)
parser.add_argument(
"--model",
type=str,
default=DEFAULT_MODEL,
help=f"Claude model to use (default: {DEFAULT_MODEL})",
)
return parser.parse_args()
def main() -> None:
"""Main entry point."""
args = parse_args()
# Check for API key
if not os.environ.get("ANTHROPIC_API_KEY"):
print("Error: ANTHROPIC_API_KEY environment variable not set")
print("\nGet your API key from: https://console.anthropic.com/")
print("\nThen set it:")
print(" export ANTHROPIC_API_KEY='your-api-key-here'")
return
# Automatically place projects in generations/ directory unless already specified
project_dir = args.project_dir
if not str(project_dir).startswith("generations/"):
# Convert relative paths to be under generations/
if project_dir.is_absolute():
# If absolute path, use as-is
pass
else:
# Prepend generations/ to relative paths
project_dir = Path("generations") / project_dir
# Run the agent
try:
asyncio.run(
run_autonomous_agent(
project_dir=project_dir,
model=args.model,
max_iterations=args.max_iterations,
)
)
except KeyboardInterrupt:
print("\n\nInterrupted by user")
print("To resume, run the same command again")
except Exception as e:
print(f"\nFatal error: {e}")
raise
if __name__ == "__main__":
main()
================================================
FILE: autonomous-coding/client.py
================================================
"""
Claude SDK Client Configuration
===============================
Functions for creating and configuring the Claude Agent SDK client.
"""
import json
import os
from pathlib import Path
from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient
from claude_code_sdk.types import HookMatcher
from security import bash_security_hook
# Puppeteer MCP tools for browser automation
PUPPETEER_TOOLS = [
"mcp__puppeteer__puppeteer_navigate",
"mcp__puppeteer__puppeteer_screenshot",
"mcp__puppeteer__puppeteer_click",
"mcp__puppeteer__puppeteer_fill",
"mcp__puppeteer__puppeteer_select",
"mcp__puppeteer__puppeteer_hover",
"mcp__puppeteer__puppeteer_evaluate",
]
# Built-in tools
BUILTIN_TOOLS = [
"Read",
"Write",
"Edit",
"Glob",
"Grep",
"Bash",
]
def create_client(project_dir: Path, model: str) -> ClaudeSDKClient:
"""
Create a Claude Agent SDK client with multi-layered security.
Args:
project_dir: Directory for the project
model: Claude model to use
Returns:
Configured ClaudeSDKClient
Security layers (defense in depth):
1. Sandbox - OS-level bash command isolation prevents filesystem escape
2. Permissions - File operations restricted to project_dir only
3. Security hooks - Bash commands validated against an allowlist
(see security.py for ALLOWED_COMMANDS)
"""
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
raise ValueError(
"ANTHROPIC_API_KEY environment variable not set.\n"
"Get your API key from: https://console.anthropic.com/"
)
# Create comprehensive security settings
# Note: Using relative paths ("./**") restricts access to project directory
# since cwd is set to project_dir
security_settings = {
"sandbox": {"enabled": True, "autoAllowBashIfSandboxed": True},
"permissions": {
"defaultMode": "acceptEdits", # Auto-approve edits within allowed directories
"allow": [
# Allow all file operations within the project directory
"Read(./**)",
"Write(./**)",
"Edit(./**)",
"Glob(./**)",
"Grep(./**)",
# Bash permission granted here, but actual commands are validated
# by the bash_security_hook (see security.py for allowed commands)
"Bash(*)",
# Allow Puppeteer MCP tools for browser automation
*PUPPETEER_TOOLS,
],
},
}
# Ensure project directory exists before creating settings file
project_dir.mkdir(parents=True, exist_ok=True)
# Write settings to a file in the project directory
settings_file = project_dir / ".claude_settings.json"
with open(settings_file, "w") as f:
json.dump(security_settings, f, indent=2)
print(f"Created security settings at {settings_file}")
print(" - Sandbox enabled (OS-level bash isolation)")
print(f" - Filesystem restricted to: {project_dir.resolve()}")
print(" - Bash commands restricted to allowlist (see security.py)")
print(" - MCP servers: puppeteer (browser automation)")
print()
return ClaudeSDKClient(
options=ClaudeCodeOptions(
model=model,
system_prompt="You are an expert full-stack developer building a production-quality web application.",
allowed_tools=[
*BUILTIN_TOOLS,
*PUPPETEER_TOOLS,
],
mcp_servers={
"puppeteer": {"command": "npx", "args": ["puppeteer-mcp-server"]}
},
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[bash_security_hook]),
],
},
max_turns=1000,
cwd=str(project_dir.resolve()),
settings=str(settings_file.resolve()), # Use absolute path
)
)
================================================
FILE: autonomous-coding/progress.py
================================================
"""
Progress Tracking Utilities
===========================
Functions for tracking and displaying progress of the autonomous coding agent.
"""
import json
from pathlib import Path
def count_passing_tests(project_dir: Path) -> tuple[int, int]:
"""
Count passing and total tests in feature_list.json.
Args:
project_dir: Directory containing feature_list.json
Returns:
(passing_count, total_count)
"""
tests_file = project_dir / "feature_list.json"
if not tests_file.exists():
return 0, 0
try:
with open(tests_file, "r") as f:
tests = json.load(f)
total = len(tests)
passing = sum(1 for test in tests if test.get("passes", False))
return passing, total
except (json.JSONDecodeError, IOError):
return 0, 0
def print_session_header(session_num: int, is_initializer: bool) -> None:
"""Print a formatted header for the session."""
session_type = "INITIALIZER" if is_initializer else "CODING AGENT"
print("\n" + "=" * 70)
print(f" SESSION {session_num}: {session_type}")
print("=" * 70)
print()
def print_progress_summary(project_dir: Path) -> None:
"""Print a summary of current progress."""
passing, total = count_passing_tests(project_dir)
if total > 0:
percentage = (passing / total) * 100
print(f"\nProgress: {passing}/{total} tests passing ({percentage:.1f}%)")
else:
print("\nProgress: feature_list.json not yet created")
================================================
FILE: autonomous-coding/prompts/app_spec.txt
================================================
<project_specification>
<project_name>Claude.ai Clone - AI Chat Interface</project_name>
<overview>
Build a fully functional clone of claude.ai, Anthropic's conversational AI interface. The application should
provide a clean, modern chat interface for interacting with Claude via the API, including features like
conversation management, artifact rendering, project organization, multiple model selection, and advanced
settings. The UI should closely match claude.ai's design using Tailwind CSS with a focus on excellent
user experience and responsive design.
</overview>
<technology_stack>
<api_key>
You can use an API key located at /tmp/api-key for testing. You will not be allowed to read this file, but you can reference it in code.
</api_key>
<frontend>
<framework>React with Vite</framework>
<styling>Tailwind CSS (via CDN)</styling>
<state_management>React hooks and context</state_management>
<routing>React Router for navigation</routing>
<markdown>React Markdown for message rendering</markdown>
<code_highlighting>Syntax highlighting for code blocks</code_highlighting>
<port>Only launch on port {frontend_port}</port>
</frontend>
<backend>
<runtime>Node.js with Express</runtime>
<database>SQLite with better-sqlite3</database>
<api_integration>Claude API for chat completions</api_integration>
<streaming>Server-Sent Events for streaming responses</streaming>
</backend>
<communication>
<api>RESTful endpoints</api>
<streaming>SSE for real-time message streaming</streaming>
<claude_api>Integration with Claude API using Anthropic SDK</claude_api>
</communication>
</technology_stack>
<prerequisites>
<environment_setup>
- Repository includes .env with VITE_ANTHROPIC_API_KEY configured
- Frontend dependencies pre-installed via pnpm
- Backend code goes in /server directory
- Install backend dependencies as needed
</environment_setup>
</prerequisites>
<core_features>
<chat_interface>
- Clean, centered chat layout with message bubbles
- Streaming message responses with typing indicator
- Markdown rendering with proper formatting
- Code blocks with syntax highlighting and copy button
- LaTeX/math equation rendering
- Image upload and display in messages
- Multi-turn conversations with context
- Message editing and regeneration
- Stop generation button during streaming
- Input field with auto-resize textarea
- Character count and token estimation
- Keyboard shortcuts (Enter to send, Shift+Enter for newline)
</chat_interface>
<artifacts>
- Artifact detection and rendering in side panel
- Code artifact viewer with syntax highlighting
- HTML/SVG preview with live rendering
- React component preview
- Mermaid diagram rendering
- Text document artifacts
- Artifact editing and re-prompting
- Full-screen artifact view
- Download artifact content
- Artifact versioning and history
</artifacts>
<conversation_management>
- Create new conversations
- Conversation list in sidebar
- Rename conversations
- Delete conversations
- Search conversations by title/content
- Pin important conversations
- Archive conversations
- Conversation folders/organization
- Duplicate conversation
- Export conversation (JSON, Markdown, PDF)
- Conversation timestamps (created, last updated)
- Unread message indicators
</conversation_management>
<projects>
- Create projects to group related conversations
- Project knowledge base (upload documents)
- Project-specific custom instructions
- Share projects with team (mock feature)
- Project settings and configuration
- Move conversations between projects
- Project templates
- Project analytics (usage stats)
</projects>
<model_selection>
- Model selector dropdown with the following models:
- Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) - default
- Claude Haiku 4.5 (claude-haiku-4-5-20251001)
- Claude Opus 4.1 (claude-opus-4-1-20250805)
- Model capabilities display
- Context window indicator
- Model-specific pricing info (display only)
- Switch models mid-conversation
- Model comparison view
</model_selection>
<custom_instructions>
- Global custom instructions
- Project-specific custom instructions
- Conversation-specific system prompts
- Custom instruction templates
- Preview how instructions affect responses
</custom_instructions>
<settings_preferences>
- Theme selection (Light, Dark, Auto)
- Font size adjustment
- Message density (compact, comfortable, spacious)
- Code theme selection
- Language preferences
- Accessibility options
- Keyboard shortcuts reference
- Data export options
- Privacy settings
- API key management
</settings_preferences>
<advanced_features>
- Temperature control slider
- Max tokens adjustment
- Top-p (nucleus sampling) control
- System prompt override
- Thinking/reasoning mode toggle
- Multi-modal input (text + images)
- Voice input (optional, mock UI)
- Response suggestions
- Related prompts
- Conversation branching
</advanced_features>
<collaboration>
- Share conversation via link (read-only)
- Export conversation formats
- Conversation templates
- Prompt library
- Share artifacts
- Team workspaces (mock UI)
</collaboration>
<search_discovery>
- Search across all conversations
- Filter by project, date, model
- Prompt library with categories
- Example conversations
- Quick actions menu
- Command palette (Cmd/Ctrl+K)
</search_discovery>
<usage_tracking>
- Token usage display per message
- Conversation cost estimation
- Daily/monthly usage dashboard
- Usage limits and warnings
- API quota tracking
</usage_tracking>
<onboarding>
- Welcome screen for new users
- Feature tour highlights
- Example prompts to get started
- Quick tips and best practices
- Keyboard shortcuts tutorial
</onboarding>
<accessibility>
- Full keyboard navigation
- Screen reader support
- ARIA labels and roles
- High contrast mode
- Focus management
- Reduced motion support
</accessibility>
<responsive_design>
- Mobile-first responsive layout
- Touch-optimized interface
- Collapsible sidebar on mobile
- Swipe gestures for navigation
- Adaptive artifact display
- Progressive Web App (PWA) support
</responsive_design>
</core_features>
<database_schema>
<tables>
<users>
- id, email, name, avatar_url
- created_at, last_login
- preferences (JSON: theme, font_size, etc.)
- custom_instructions
</users>
<projects>
- id, user_id, name, description, color
- custom_instructions, knowledge_base_path
- created_at, updated_at
- is_archived, is_pinned
</projects>
<conversations>
- id, user_id, project_id, title
- model, created_at, updated_at, last_message_at
- is_archived, is_pinned, is_deleted
- settings (JSON: temperature, max_tokens, etc.)
- token_count, message_count
</conversations>
<messages>
- id, conversation_id, role (user/assistant/system)
- content, created_at, edited_at
- tokens, finish_reason
- images (JSON array of image data)
- parent_message_id (for branching)
</messages>
<artifacts>
- id, message_id, conversation_id
- type (code/html/svg/react/mermaid/text)
- title, identifier, language
- content, version
- created_at, updated_at
</artifacts>
<shared_conversations>
- id, conversation_id, share_token
- created_at, expires_at, view_count
- is_public
</shared_conversations>
<prompt_library>
- id, user_id, title, description
- prompt_template, category, tags (JSON)
- is_public, usage_count
- created_at, updated_at
</prompt_library>
<conversation_folders>
- id, user_id, project_id, name, parent_folder_id
- created_at, position
</conversation_folders>
<conversation_folder_items>
- id, folder_id, conversation_id
</conversation_folder_items>
<usage_tracking>
- id, user_id, conversation_id, message_id
- model, input_tokens, output_tokens
- cost_estimate, created_at
</usage_tracking>
<api_keys>
- id, user_id, key_name, api_key_hash
- created_at, last_used_at
- is_active
</api_keys>
</tables>
</database_schema>
<api_endpoints_summary>
<authentication>
- POST /api/auth/login
- POST /api/auth/logout
- GET /api/auth/me
- PUT /api/auth/profile
</authentication>
<conversations>
- GET /api/conversations
- POST /api/conversations
- GET /api/conversations/:id
- PUT /api/conversations/:id
- DELETE /api/conversations/:id
- POST /api/conversations/:id/duplicate
- POST /api/conversations/:id/export
- PUT /api/conversations/:id/archive
- PUT /api/conversations/:id/pin
- POST /api/conversations/:id/branch
</conversations>
<messages>
- GET /api/conversations/:id/messages
- POST /api/conversations/:id/messages
- PUT /api/messages/:id
- DELETE /api/messages/:id
- POST /api/messages/:id/regenerate
- GET /api/messages/stream (SSE endpoint)
</messages>
<artifacts>
- GET /api/conversations/:id/artifacts
- GET /api/artifacts/:id
- PUT /api/artifacts/:id
- DELETE /api/artifacts/:id
- POST /api/artifacts/:id/fork
- GET /api/artifacts/:id/versions
</artifacts>
<projects>
- GET /api/projects
- POST /api/projects
- GET /api/projects/:id
- PUT /api/projects/:id
- DELETE /api/projects/:id
- POST /api/projects/:id/knowledge
- GET /api/projects/:id/conversations
- PUT /api/projects/:id/settings
</projects>
<sharing>
- POST /api/conversations/:id/share
- GET /api/share/:token
- DELETE /api/share/:token
- PUT /api/share/:token/settings
</sharing>
<prompts>
- GET /api/prompts/library
- POST /api/prompts/library
- GET /api/prompts/:id
- PUT /api/prompts/:id
- DELETE /api/prompts/:id
- GET /api/prompts/categories
- GET /api/prompts/examples
</prompts>
<search>
- GET /api/search/conversations?q=query
- GET /api/search/messages?q=query
- GET /api/search/artifacts?q=query
- GET /api/search/prompts?q=query
</search>
<folders>
- GET /api/folders
- POST /api/folders
- PUT /api/folders/:id
- DELETE /api/folders/:id
- POST /api/folders/:id/items
- DELETE /api/folders/:id/items/:conversationId
</folders>
<usage>
- GET /api/usage/daily
- GET /api/usage/monthly
- GET /api/usage/by-model
- GET /api/usage/conversations/:id
</usage>
<settings>
- GET /api/settings
- PUT /api/settings
- GET /api/settings/custom-instructions
- PUT /api/settings/custom-instructions
</settings>
<claude_api>
- POST /api/claude/chat (proxy to Claude API)
- POST /api/claude/chat/stream (streaming proxy)
- GET /api/claude/models
- POST /api/claude/images/upload
</claude_api>
</api_endpoints_summary>
<ui_layout>
<main_structure>
- Three-column layout: sidebar (conversations), main (chat), panel (artifacts)
- Collapsible sidebar with resize handle
- Responsive breakpoints: mobile (single column), tablet (two column), desktop (three column)
- Persistent header with project/model selector
- Bottom input area with send button and options
</main_structure>
<sidebar_left>
- New chat button (prominent)
- Project selector dropdown
- Search conversations input
- Conversations list (grouped by date: Today, Yesterday, Previous 7 days, etc.)
- Folder tree view (collapsible)
- Settings gear icon at bottom
- User profile at bottom
</sidebar_left>
<main_chat_area>
- Conversation title (editable inline)
- Model selector badge
- Message history (scrollable)
- Welcome screen for new conversations
- Suggested prompts (empty state)
- Input area with formatting toolbar
- Attachment button for images
- Send button with loading state
- Stop generation button
</main_chat_area>
<artifacts_panel>
- Artifact header with title and type badge
- Code editor or preview pane
- Tabs for multiple artifacts
- Full-screen toggle
- Download button
- Edit/Re-prompt button
- Version selector
- Close panel button
</artifacts_panel>
<modals_overlays>
- Settings modal (tabbed interface)
- Share conversation modal
- Export options modal
- Project settings modal
- Prompt library modal
- Command palette overlay
- Keyboard shortcuts reference
</modals_overlays>
</ui_layout>
<design_system>
<color_palette>
- Primary: Orange/amber accent (#CC785C claude-style)
- Background: White (light mode), Dark gray (#1A1A1A dark mode)
- Surface: Light gray (#F5F5F5 light), Darker gray (#2A2A2A dark)
- Text: Near black (#1A1A1A light), Off-white (#E5E5E5 dark)
- Borders: Light gray (#E5E5E5 light), Dark gray (#404040 dark)
- Code blocks: Monaco editor theme
</color_palette>
<typography>
- Sans-serif system font stack (Inter, SF Pro, Roboto, system-ui)
- Headings: font-semibold
- Body: font-normal, leading-relaxed
- Code: Monospace (JetBrains Mono, Consolas, Monaco)
- Message text: text-base (16px), comfortable line-height
</typography>
<components>
<message_bubble>
- User messages: Right-aligned, subtle background
- Assistant messages: Left-aligned, no background
- Markdown formatting with proper spacing
- Inline code with bg-gray-100 background
- Code blocks with syntax highlighting
- Copy button on code blocks
</message_bubble>
<buttons>
- Primary: Orange/amber background, white text, rounded
- Secondary: Border style with hover fill
- Icon buttons: Square with hover background
- Disabled state: Reduced opacity, no pointer events
</buttons>
<inputs>
- Rounded borders with focus ring
- Textarea auto-resize
- Placeholder text in gray
- Error states in red
- Character counter
</inputs>
<cards>
- Subtle border or shadow
- Rounded corners (8px)
- Padding: p-4 to p-6
- Hover state: slight shadow increase
</cards>
</components>
<animations>
- Smooth transitions (150-300ms)
- Fade in for new messages
- Slide in for sidebar
- Typing indicator animation
- Loading spinner for generation
- Skeleton loaders for content
</animations>
</design_system>
<key_interactions>
<message_flow>
1. User types message in input field
2. Optional: Attach images via button
3. Click send or press Enter
4. Message appears in chat immediately
5. Typing indicator shows while waiting
6. Response streams in word by word
7. Code blocks render with syntax highlighting
8. Artifacts detected and rendered in side panel
9. Message complete, enable regenerate option
</message_flow>
<artifact_flow>
1. Assistant generates artifact in response
2. Artifact panel slides in from right
3. Content renders (code with highlighting or live preview)
4. User can edit artifact inline
5. "Re-prompt" button to iterate with Claude
6. Download or copy artifact content
7. Full-screen mode for detailed work
8. Close panel to return to chat focus
</artifact_flow>
<conversation_management>
1. Click "New Chat" to start fresh conversation
2. Conversations auto-save with first message
3. Auto-generate title from first exchange
4. Click title to rename inline
5. Drag conversations into folders
6. Right-click for context menu (pin, archive, delete, export)
7. Search filters conversations in real-time
8. Click conversation to switch context
</conversation_management>
</key_interactions>
<implementation_steps>
<step number="1">
<title>Setup Project Foundation and Database</title>
<tasks>
- Initialize Express server with SQLite database
- Set up Claude API client with streaming support
- Create database schema with migrations
- Implement authentication endpoints
- Set up basic CORS and middleware
- Create health check endpoint
</tasks>
</step>
<step number="2">
<title>Build Core Chat Interface</title>
<tasks>
- Create main layout with sidebar and chat area
- Implement message display with markdown rendering
- Add streaming message support with SSE
- Build input area with auto-resize textarea
- Add code block syntax highlighting
- Implement stop generation functionality
- Add typing indicators and loading states
</tasks>
</step>
<step number="3">
<title>Conversation Management</title>
<tasks>
- Create conversation list in sidebar
- Implement new conversation creation
- Add conversation switching
- Build conversation rename functionality
- Implement delete with confirmation
- Add conversation search
- Create conversation grouping by date
</tasks>
</step>
<step number="4">
<title>Artifacts System</title>
<tasks>
- Build artifact detection from Claude responses
- Create artifact rendering panel
- Implement code artifact viewer
- Add HTML/SVG live preview
- Build artifact editing interface
- Add artifact versioning
- Implement full-screen artifact view
</tasks>
</step>
<step number="5">
<title>Projects and Organization</title>
<tasks>
- Create projects CRUD endpoints
- Build project selector UI
- Implement project-specific custom instructions
- Add folder system for conversations
- Create drag-and-drop organization
- Build project settings panel
</tasks>
</step>
<step number="6">
<title>Advanced Features</title>
<tasks>
- Add model selection dropdown
- Implement temperature and parameter controls
- Build image upload functionality
- Create message editing and regeneration
- Add conversation branching
- Implement export functionality
</tasks>
</step>
<step number="7">
<title>Settings and Customization</title>
<tasks>
- Build settings modal with tabs
- Implement theme switching (light/dark)
- Add custom instructions management
- Create keyboard shortcuts
- Build prompt library
- Add usage tracking dashboard
</tasks>
</step>
<step number="8">
<title>Sharing and Collaboration</title>
<tasks>
- Implement conversation sharing with tokens
- Create public share view
- Add export to multiple formats
- Build prompt templates
- Create example conversations
</tasks>
</step>
<step number="9">
<title>Polish and Optimization</title>
<tasks>
- Optimize for mobile responsiveness
- Add command palette (Cmd+K)
- Implement comprehensive keyboard navigation
- Add onboarding flow
- Create accessibility improvements
- Performance optimization and caching
</tasks>
</step>
</implementation_steps>
<success_criteria>
<functionality>
- Streaming chat responses work smoothly
- Artifact detection and rendering accurate
- Conversation management intuitive and reliable
- Project organization clear and useful
- Image upload and display working
- All CRUD operations functional
</functionality>
<user_experience>
- Interface matches claude.ai design language
- Responsive on all device sizes
- Smooth animations and transitions
- Fast response times and minimal lag
- Intuitive navigation and workflows
- Clear feedback for all actions
</user_experience>
<technical_quality>
- Clean, maintainable code structure
- Proper error handling throughout
- Secure API key management
- Optimized database queries
- Efficient streaming implementation
- Comprehensive testing coverage
</technical_quality>
<design_polish>
- Consistent with claude.ai visual design
- Beautiful typography and spacing
- Smooth animations and micro-interactions
- Excellent contrast and accessibility
- Professional, polished appearance
- Dark mode fully implemented
</design_polish>
</success_criteria>
</project_specification>
================================================
FILE: autonomous-coding/prompts/coding_prompt.md
================================================
## YOUR ROLE - CODING AGENT
You are continuing work on a long-running autonomous development task.
This is a FRESH context window - you have no memory of previous sessions.
### STEP 1: GET YOUR BEARINGS (MANDATORY)
Start by orienting yourself:
```bash
# 1. See your working directory
pwd
# 2. List files to understand project structure
ls -la
# 3. Read the project specification to understand what you're building
cat app_spec.txt
# 4. Read the feature list to see all work
cat feature_list.json | head -50
# 5. Read progress notes from previous sessions
cat claude-progress.txt
# 6. Check recent git history
git log --oneline -20
# 7. Count remaining tests
cat feature_list.json | grep '"passes": false' | wc -l
```
Understanding the `app_spec.txt` is critical - it contains the full requirements
for the application you're building.
### STEP 2: START SERVERS (IF NOT RUNNING)
If `init.sh` exists, run it:
```bash
chmod +x init.sh
./init.sh
```
Otherwise, start servers manually and document the process.
### STEP 3: VERIFICATION TEST (CRITICAL!)
**MANDATORY BEFORE NEW WORK:**
The previous session may have introduced bugs. Before implementing anything
new, you MUST run verification tests.
Run 1-2 of the feature tests marked as `"passes": true` that are most core to the app's functionality to verify they still work.
For example, if this were a chat app, you should perform a test that logs into the app, sends a message, and gets a response.
**If you find ANY issues (functional or visual):**
- Mark that feature as "passes": false immediately
- Add issues to a list
- Fix all issues BEFORE moving to new features
- This includes UI bugs like:
* White-on-white text or poor contrast
* Random characters displayed
* Incorrect timestamps
* Layout issues or overflow
* Buttons too close together
* Missing hover states
* Console errors
### STEP 4: CHOOSE ONE FEATURE TO IMPLEMENT
Look at feature_list.json and find the highest-priority feature with "passes": false.
Focus on completing one feature perfectly and completing its testing steps in this session before moving on to other features.
It's ok if you only complete one feature in this session, as there will be more sessions later that continue to make progress.
### STEP 5: IMPLEMENT THE FEATURE
Implement the chosen feature thoroughly:
1. Write the code (frontend and/or backend as needed)
2. Test manually using browser automation (see Step 6)
3. Fix any issues discovered
4. Verify the feature works end-to-end
### STEP 6: VERIFY WITH BROWSER AUTOMATION
**CRITICAL:** You MUST verify features through the actual UI.
Use browser automation tools:
- Navigate to the app in a real browser
- Interact like a human user (click, type, scroll)
- Take screenshots at each step
- Verify both functionality AND visual appearance
**DO:**
- Test through the UI with clicks and keyboard input
- Take screenshots to verify visual appearance
- Check for console errors in browser
- Verify complete user workflows end-to-end
**DON'T:**
- Only test with curl commands (backend testing alone is insufficient)
- Use JavaScript evaluation to bypass UI (no shortcuts)
- Skip visual verification
- Mark tests passing without thorough verification
### STEP 7: UPDATE feature_list.json (CAREFULLY!)
**YOU CAN ONLY MODIFY ONE FIELD: "passes"**
After thorough verification, change:
```json
"passes": false
```
to:
```json
"passes": true
```
**NEVER:**
- Remove tests
- Edit test descriptions
- Modify test steps
- Combine or consolidate tests
- Reorder tests
**ONLY CHANGE "passes" FIELD AFTER VERIFICATION WITH SCREENSHOTS.**
### STEP 8: COMMIT YOUR PROGRESS
Make a descriptive git commit:
```bash
git add .
git commit -m "Implement [feature name] - verified end-to-end
- Added [specific changes]
- Tested with browser automation
- Updated feature_list.json: marked test #X as passing
- Screenshots in verification/ directory
"
```
### STEP 9: UPDATE PROGRESS NOTES
Update `claude-progress.txt` with:
- What you accomplished this session
- Which test(s) you completed
- Any issues discovered or fixed
- What should be worked on next
- Current completion status (e.g., "45/200 tests passing")
### STEP 10: END SESSION CLEANLY
Before context fills up:
1. Commit all working code
2. Update claude-progress.txt
3. Update feature_list.json if tests verified
4. Ensure no uncommitted changes
5. Leave app in working state (no broken features)
---
## TESTING REQUIREMENTS
**ALL testing must use browser automation tools.**
Available tools:
- puppeteer_navigate - Start browser and go to URL
- puppeteer_screenshot - Capture screenshot
- puppeteer_click - Click elements
- puppeteer_fill - Fill form inputs
- puppeteer_evaluate - Execute JavaScript (use sparingly, only for debugging)
Test like a human user with mouse and keyboard. Don't take shortcuts by using JavaScript evaluation.
Don't use the puppeteer "active tab" tool.
---
## IMPORTANT REMINDERS
**Your Goal:** Production-quality application with all 200+ tests passing
**This Session's Goal:** Complete at least one feature perfectly
**Priority:** Fix broken tests before implementing new features
**Quality Bar:**
- Zero console errors
- Polished UI matching the design specified in app_spec.txt
- All features work end-to-end through the UI
- Fast, responsive, professional
**You have unlimited time.** Take as long as needed to get it right. The most important thing is that you
leave the code base in a clean state before terminating the session (Step 10).
---
Begin by running Step 1 (Get Your Bearings).
================================================
FILE: autonomous-coding/prompts/initializer_prompt.md
================================================
## YOUR ROLE - INITIALIZER AGENT (Session 1 of Many)
You are the FIRST agent in a long-running autonomous development process.
Your job is to set up the foundation for all future coding agents.
### FIRST: Read the Project Specification
Start by reading `app_spec.txt` in your working directory. This file contains
the complete specification for what you need to build. Read it carefully
before proceeding.
### CRITICAL FIRST TASK: Create feature_list.json
Based on `app_spec.txt`, create a file called `feature_list.json` with 200 detailed
end-to-end test cases. This file is the single source of truth for what
needs to be built.
**Format:**
```json
[
{
"category": "functional",
"description": "Brief description of the feature and what this test verifies",
"steps": [
"Step 1: Navigate to relevant page",
"Step 2: Perform action",
"Step 3: Verify expected result"
],
"passes": false
},
{
"category": "style",
"description": "Brief description of UI/UX requirement",
"steps": [
"Step 1: Navigate to page",
"Step 2: Take screenshot",
"Step 3: Verify visual requirements"
],
"passes": false
}
]
```
**Requirements for feature_list.json:**
- Minimum 200 features total with testing steps for each
- Both "functional" and "style" categories
- Mix of narrow tests (2-5 steps) and comprehensive tests (10+ steps)
- At least 25 tests MUST have 10+ steps each
- Order features by priority: fundamental features first
- ALL tests start with "passes": false
- Cover every feature in the spec exhaustively
**CRITICAL INSTRUCTION:**
IT IS CATASTROPHIC TO REMOVE OR EDIT FEATURES IN FUTURE SESSIONS.
Features can ONLY be marked as passing (change "passes": false to "passes": true).
Never remove features, never edit descriptions, never modify testing steps.
This ensures no functionality is missed.
### SECOND TASK: Create init.sh
Create a script called `init.sh` that future agents can use to quickly
set up and run the development environment. The script should:
1. Install any required dependencies
2. Start any necessary servers or services
3. Print helpful information about how to access the running application
Base the script on the technology stack specified in `app_spec.txt`.
### THIRD TASK: Initialize Git
Create a git repository and make your first commit with:
- feature_list.json (complete with all 200+ features)
- init.sh (environment setup script)
- README.md (project overview and setup instructions)
Commit message: "Initial setup: feature_list.json, init.sh, and project structure"
### FOURTH TASK: Create Project Structure
Set up the basic project structure based on what's specified in `app_spec.txt`.
This typically includes directories for frontend, backend, and any other
components mentioned in the spec.
### OPTIONAL: Start Implementation
If you have time remaining in this session, you may begin implementing
the highest-priority features from feature_list.json. Remember:
- Work on ONE feature at a time
- Test thoroughly before marking "passes": true
- Commit your progress before session ends
### ENDING THIS SESSION
Before your context fills up:
1. Commit all work with descriptive messages
2. Create `claude-progress.txt` with a summary of what you accomplished
3. Ensure feature_list.json is complete and saved
4. Leave the environment in a clean, working state
The next agent will continue from here with a fresh context window.
---
**Remember:** You have unlimited time across many sessions. Focus on
quality over speed. Production-ready is the goal.
================================================
FILE: autonomous-coding/prompts.py
================================================
"""
Prompt Loading Utilities
========================
Functions for loading prompt templates from the prompts directory.
"""
import shutil
from pathlib import Path
PROMPTS_DIR = Path(__file__).parent / "prompts"
def load_prompt(name: str) -> str:
"""Load a prompt template from the prompts directory."""
prompt_path = PROMPTS_DIR / f"{name}.md"
return prompt_path.read_text()
def get_initializer_prompt() -> str:
"""Load the initializer prompt."""
return load_prompt("initializer_prompt")
def get_coding_prompt() -> str:
"""Load the coding agent prompt."""
return load_prompt("coding_prompt")
def copy_spec_to_project(project_dir: Path) -> None:
"""Copy the app spec file into the project directory for the agent to read."""
spec_source = PROMPTS_DIR / "app_spec.txt"
spec_dest = project_dir / "app_spec.txt"
if not spec_dest.exists():
shutil.copy(spec_source, spec_dest)
print("Copied app_spec.txt to project directory")
================================================
FILE: autonomous-coding/requirements.txt
================================================
claude-code-sdk>=0.0.25
================================================
FILE: autonomous-coding/security.py
================================================
"""
Security Hooks for Autonomous Coding Agent
==========================================
Pre-tool-use hooks that validate bash commands for security.
Uses an allowlist approach - only explicitly permitted commands can run.
"""
import os
import shlex
# Allowed commands for development tasks
# Minimal set needed for the autonomous coding demo
ALLOWED_COMMANDS = {
# File inspection
"ls",
"cat",
"head",
"tail",
"wc",
"grep",
# File operations (agent uses SDK tools for most file ops, but cp/mkdir needed occasionally)
"cp",
"mkdir",
"chmod", # For making scripts executable; validated separately
# Directory
"pwd",
# Node.js development
"npm",
"node",
# Version control
"git",
# Process management
"ps",
"lsof",
"sleep",
"pkill", # For killing dev servers; validated separately
# Script execution
"init.sh", # Init scripts; validated separately
}
# Commands that need additional validation even when in the allowlist
COMMANDS_NEEDING_EXTRA_VALIDATION = {"pkill", "chmod", "init.sh"}
def split_command_segments(command_string: str) -> list[str]:
"""
Split a compound command into individual command segments.
Handles command chaining (&&, ||, ;) but not pipes (those are single commands).
Args:
command_string: The full shell command
Returns:
List of individual command segments
"""
import re
# Split on && and || while preserving the ability to handle each segment
# This regex splits on && or || that aren't inside quotes
segments = re.split(r"\s*(?:&&|\|\|)\s*", command_string)
# Further split on semicolons
result = []
for segment in segments:
sub_segments = re.split(r'(?<!["\'])\s*;\s*(?!["\'])', segment)
for sub in sub_segments:
sub = sub.strip()
if sub:
result.append(sub)
return result
def extract_commands(command_string: str) -> list[str]:
"""
Extract command names from a shell command string.
Handles pipes, command chaining (&&, ||, ;), and subshells.
Returns the base command names (without paths).
Args:
command_string: The full shell command
Returns:
List of command names found in the string
"""
commands = []
# shlex doesn't treat ; as a separator, so we need to pre-process
import re
# Split on semicolons that aren't inside quotes (simple heuristic)
# This handles common cases like "echo hello; ls"
segments = re.split(r'(?<!["\'])\s*;\s*(?!["\'])', command_string)
for segment in segments:
segment = segment.strip()
if not segment:
continue
try:
tokens = shlex.split(segment)
except ValueError:
# Malformed command (unclosed quotes, etc.)
# Return empty to trigger block (fail-safe)
return []
if not tokens:
continue
# Track when we expect a command vs arguments
expect_command = True
for token in tokens:
# Shell operators indicate a new command follows
if token in ("|", "||", "&&", "&"):
expect_command = True
continue
# Skip shell keywords that precede commands
if token in (
"if",
"then",
"else",
"elif",
"fi",
"for",
"while",
"until",
"do",
"done",
"case",
"esac",
"in",
"!",
"{",
"}",
):
continue
# Skip flags/options
if token.startswith("-"):
continue
# Skip variable assignments (VAR=value)
if "=" in token and not token.startswith("="):
continue
if expect_command:
# Extract the base command name (handle paths like /usr/bin/python)
cmd = os.path.basename(token)
commands.append(cmd)
expect_command = False
return commands
def validate_pkill_command(command_string: str) -> tuple[bool, str]:
"""
Validate pkill commands - only allow killing dev-related processes.
Uses shlex to parse the command, avoiding regex bypass vulnerabilities.
Returns:
Tuple of (is_allowed, reason_if_blocked)
"""
# Allowed process names for pkill
allowed_process_names = {
"node",
"npm",
"npx",
"vite",
"next",
}
try:
tokens = shlex.split(command_string)
except ValueError:
return False, "Could not parse pkill command"
if not tokens:
return False, "Empty pkill command"
# Separate flags from arguments
args = []
for token in tokens[1:]:
if not token.startswith("-"):
args.append(token)
if not args:
return False, "pkill requires a process name"
# The target is typically the last non-flag argument
target = args[-1]
# For -f flag (full command line match), extract the first word as process name
# e.g., "pkill -f 'node server.js'" -> target is "node server.js", process is "node"
if " " in target:
target = target.split()[0]
if target in allowed_process_names:
return True, ""
return False, f"pkill only allowed for dev processes: {allowed_process_names}"
def validate_chmod_command(command_string: str) -> tuple[bool, str]:
"""
Validate chmod commands - only allow making files executable with +x.
Returns:
Tuple of (is_allowed, reason_if_blocked)
"""
try:
tokens = shlex.split(command_string)
except ValueError:
return False, "Could not parse chmod command"
if not tokens or tokens[0] != "chmod":
return False, "Not a chmod command"
# Look for the mode argument
# Valid modes: +x, u+x, a+x, etc. (anything ending with +x for execute permission)
mode = None
files = []
for token in tokens[1:]:
if token.startswith("-"):
# Skip flags like -R (we don't allow recursive chmod anyway)
return False, "chmod flags are not allowed"
elif mode is None:
mode = token
else:
files.append(token)
if mode is None:
return False, "chmod requires a mode"
if not files:
return False, "chmod requires at least one file"
# Only allow +x variants (making files executable)
# This matches: +x, u+x, g+x, o+x, a+x, ug+x, etc.
import re
if not re.match(r"^[ugoa]*\+x$", mode):
return False, f"chmod only allowed with +x mode, got: {mode}"
return True, ""
def validate_init_script(command_string: str) -> tuple[bool, str]:
"""
Validate init.sh script execution - only allow ./init.sh.
Returns:
Tuple of (is_allowed, reason_if_blocked)
"""
try:
tokens = shlex.split(command_string)
except ValueError:
return False, "Could not parse init script command"
if not tokens:
return False, "Empty command"
# The command should be exactly ./init.sh (possibly with arguments)
script = tokens[0]
# Allow ./init.sh or paths ending in /init.sh
if script == "./init.sh" or script.endswith("/init.sh"):
return True, ""
return False, f"Only ./init.sh is allowed, got: {script}"
def get_command_for_validation(cmd: str, segments: list[str]) -> str:
"""
Find the specific command segment that contains the given command.
Args:
cmd: The command name to find
segments: List of command segments
Returns:
The segment containing the command, or empty string if not found
"""
for segment in segments:
segment_commands = extract_commands(segment)
if cmd in segment_commands:
return segment
return ""
async def bash_security_hook(input_data, tool_use_id=None, context=None):
"""
Pre-tool-use hook that validates bash commands using an allowlist.
Only commands in ALLOWED_COMMANDS are permitted.
Args:
input_data: Dict containing tool_name and tool_input
tool_use_id: Optional tool use ID
context: Optional context
Returns:
Empty dict to allow, or {"decision": "block", "reason": "..."} to block
"""
if input_data.get("tool_name") != "Bash":
return {}
command = input_data.get("tool_input", {}).get("command", "")
if not command:
return {}
# Extract all commands from the command string
commands = extract_commands(command)
if not commands:
# Could not parse - fail safe by blocking
return {
"decision": "block",
"reason": f"Could not parse command for security validation: {command}",
}
# Split into segments for per-command validation
segments = split_command_segments(command)
# Check each command against the allowlist
for cmd in commands:
if cmd not in ALLOWED_COMMANDS:
return {
"decision": "block",
"reason": f"Command '{cmd}' is not in the allowed commands list",
}
# Additional validation for sensitive commands
if cmd in COMMANDS_NEEDING_EXTRA_VALIDATION:
# Find the specific segment containing this command
cmd_segment = get_command_for_validation(cmd, segments)
if not cmd_segment:
cmd_segment = command # Fallback to full command
if cmd == "pkill":
allowed, reason = validate_pkill_command(cmd_segment)
if not allowed:
return {"decision": "block", "reason": reason}
elif cmd == "chmod":
allowed, reason = validate_chmod_command(cmd_segment)
if not allowed:
return {"decision": "block", "reason": reason}
elif cmd == "init.sh":
allowed, reason = validate_init_script(cmd_segment)
if not allowed:
return {"decision": "block", "reason": reason}
return {}
================================================
FILE: autonomous-coding/test_security.py
================================================
#!/usr/bin/env python3
"""
Security Hook Tests
===================
Tests for the bash command security validation logic.
Run with: python test_security.py
"""
import asyncio
import sys
from security import (
bash_security_hook,
extract_commands,
validate_chmod_command,
validate_init_script,
)
def test_hook(command: str, should_block: bool) -> bool:
"""Test a single command against the security hook."""
input_data = {"tool_name": "Bash", "tool_input": {"command": command}}
result = asyncio.run(bash_security_hook(input_data))
was_blocked = result.get("decision") == "block"
if was_blocked == should_block:
status = "PASS"
else:
status = "FAIL"
expected = "blocked" if should_block else "allowed"
actual = "blocked" if was_blocked else "allowed"
reason = result.get("reason", "")
print(f" {status}: {command!r}")
print(f" Expected: {expected}, Got: {actual}")
if reason:
print(f" Reason: {reason}")
return False
print(f" {status}: {command!r}")
return True
def test_extract_commands():
"""Test the command extraction logic."""
print("\nTesting command extraction:\n")
passed = 0
failed = 0
test_cases = [
("ls -la", ["ls"]),
("npm install && npm run build", ["npm", "npm"]),
("cat file.txt | grep pattern", ["cat", "grep"]),
("/usr/bin/node script.js", ["node"]),
("VAR=value ls", ["ls"]),
("git status || git init", ["git", "git"]),
]
for cmd, expected in test_cases:
result = extract_commands(cmd)
if result == expected:
print(f" PASS: {cmd!r} -> {result}")
passed += 1
else:
print(f" FAIL: {cmd!r}")
print(f" Expected: {expected}, Got: {result}")
failed += 1
return passed, failed
def test_validate_chmod():
"""Test chmod command validation."""
print("\nTesting chmod validation:\n")
passed = 0
failed = 0
# Test cases: (command, should_be_allowed, description)
test_cases = [
# Allowed cases
("chmod +x init.sh", True, "basic +x"),
("chmod +x script.sh", True, "+x on any script"),
("chmod u+x init.sh", True, "user +x"),
("chmod a+x init.sh", True, "all +x"),
("chmod ug+x init.sh", True, "user+group +x"),
("chmod +x file1.sh file2.sh", True, "multiple files"),
# Blocked cases
("chmod 777 init.sh", False, "numeric mode"),
("chmod 755 init.sh", False, "numeric mode 755"),
("chmod +w init.sh", False, "write permission"),
("chmod +r init.sh", False, "read permission"),
("chmod -x init.sh", False, "remove execute"),
("chmod -R +x dir/", False, "recursive flag"),
("chmod --recursive +x dir/", False, "long recursive flag"),
("chmod +x", False, "missing file"),
]
for cmd, should_allow, description in test_cases:
allowed, reason = validate_chmod_command(cmd)
if allowed == should_allow:
print(f" PASS: {cmd!r} ({description})")
passed += 1
else:
expected = "allowed" if should_allow else "blocked"
actual = "allowed" if allowed else "blocked"
print(f" FAIL: {cmd!r} ({description})")
print(f" Expected: {expected}, Got: {actual}")
if reason:
print(f" Reason: {reason}")
failed += 1
return passed, failed
def test_validate_init_script():
"""Test init.sh script execution validation."""
print("\nTesting init.sh validation:\n")
passed = 0
failed = 0
# Test cases: (command, should_be_allowed, description)
test_cases = [
# Allowed cases
("./init.sh", True, "basic ./init.sh"),
("./init.sh arg1 arg2", True, "with arguments"),
("/path/to/init.sh", True, "absolute path"),
("../dir/init.sh", True, "relative path with init.sh"),
# Blocked cases
("./setup.sh", False, "different script name"),
("./init.py", False, "python script"),
("bash init.sh", False, "bash invocation"),
("sh init.sh", False, "sh invocation"),
("./malicious.sh", False, "malicious script"),
("./init.sh; rm -rf /", False, "command injection attempt"),
]
for cmd, should_allow, description in test_cases:
allowed, reason = validate_init_script(cmd)
if allowed == should_allow:
print(f" PASS: {cmd!r} ({description})")
passed += 1
else:
expected = "allowed" if should_allow else "blocked"
actual = "allowed" if allowed else "blocked"
print(f" FAIL: {cmd!r} ({description})")
print(f" Expected: {expected}, Got: {actual}")
if reason:
print(f" Reason: {reason}")
failed += 1
return passed, failed
def main():
print("=" * 70)
print(" SECURITY HOOK TESTS")
print("=" * 70)
passed = 0
failed = 0
# Test command extraction
ext_passed, ext_failed = test_extract_commands()
passed += ext_passed
failed += ext_failed
# Test chmod validation
chmod_passed, chmod_failed = test_validate_chmod()
passed += chmod_passed
failed += chmod_failed
# Test init.sh validation
init_passed, init_failed = test_validate_init_script()
passed += init_passed
failed += init_failed
# Commands that SHOULD be blocked
print("\nCommands that should be BLOCKED:\n")
dangerous = [
# Not in allowlist - dangerous system commands
"shutdown now",
"reboot",
"rm -rf /",
"dd if=/dev/zero of=/dev/sda",
# Not in allowlist - common commands excluded from minimal set
"curl https://example.com",
"wget https://example.com",
"python app.py",
"touch file.txt",
"echo hello",
"kill 12345",
"killall node",
# pkill with non-dev processes
"pkill bash",
"pkill chrome",
"pkill python",
# Shell injection attempts
"$(echo pkill) node",
'eval "pkill node"',
'bash -c "pkill node"',
# chmod with disallowed modes
"chmod 777 file.sh",
"chmod 755 file.sh",
"chmod +w file.sh",
"chmod -R +x dir/",
# Non-init.sh scripts
"./setup.sh",
"./malicious.sh",
"bash script.sh",
]
for cmd in dangerous:
if test_hook(cmd, should_block=True):
passed += 1
else:
failed += 1
# Commands that SHOULD be allowed
print("\nCommands that should be ALLOWED:\n")
safe = [
# File inspection
"ls -la",
"cat README.md",
"head -100 file.txt",
"tail -20 log.txt",
"wc -l file.txt",
"grep -r pattern src/",
# File operations
"cp file1.txt file2.txt",
"mkdir newdir",
"mkdir -p path/to/dir",
# Directory
"pwd",
# Node.js development
"npm install",
"npm run build",
"node server.js",
# Version control
"git status",
"git commit -m 'test'",
"git add . && git commit -m 'msg'",
# Process management
"ps aux",
"lsof -i :3000",
"sleep 2",
# Allowed pkill patterns for dev servers
"pkill node",
"pkill npm",
"pkill -f node",
"pkill -f 'node server.js'",
"pkill vite",
# Chained commands
"npm install && npm run build",
"ls | grep test",
# Full paths
"/usr/local/bin/node app.js",
# chmod +x (allowed)
"chmod +x init.sh",
"chmod +x script.sh",
"chmod u+x init.sh",
"chmod a+x init.sh",
# init.sh execution (allowed)
"./init.sh",
"./init.sh --production",
"/path/to/init.sh",
# Combined chmod and init.sh
"chmod +x init.sh && ./init.sh",
]
for cmd in safe:
if test_hook(cmd, should_block=False):
passed += 1
else:
failed += 1
# Summary
print("\n" + "-" * 70)
print(f" Results: {passed} passed, {failed} failed")
print("-" * 70)
if failed == 0:
print("\n ALL TESTS PASSED")
return 0
else:
print(f"\n {failed} TEST(S) FAILED")
return 1
if __name__ == "__main__":
sys.exit(main())
================================================
FILE: browser-use-demo/.dockerignore
================================================
**/__pycache__
**/*.pyc
**/*.pyo
**/*.pyd
.Python
*.egg-info/
.git/
.gitignore
*.md
.DS_Store
tests/
*.log
.vscode/
.idea/
*.swp
*.swo
*~
================================================
FILE: browser-use-demo/.gitignore
================================================
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log
/tmp/
# Environment
.env
.anthropic/
# Test
.pytest_cache/
.coverage
htmlcov/
# Streamlit
.streamlit/cache/
================================================
FILE: browser-use-demo/CHANGELOG.md
================================================
# Modifications to Microsoft Playwright Source
This file tracks all modifications made to files derived from or inspired by Microsoft Playwright source code.
## Modified Files
### browser_use_demo/browser_tool_utils/browser_dom_script.js
- **Date Modified**: 9/23/25
- **Original Source**: https://github.com/microsoft/playwright/blob/main/packages/injected/src/ariaSnapshot.ts
- **Nature of Changes**: Adapted Playwright's accessibility tree generation for use with browser tools API. Implemented accessibility tree extraction with element reference tracking, visibility filtering, and YAML-formatted output.
### browser_use_demo/browser_tool_utils/browser_element_script.js
- **Date Modified**: 9/23/25
- **Original Source**: Microsoft Playwright element interaction patterns
- **Nature of Changes**: Implemented element finding and interaction logic inspired by Playwright's approach to reliable element targeting and coordinate calculation.
### browser_use_demo/tools/browser.py
- **Date Modified**: 9/23/25
- **Original Source**: Microsoft Playwright click emulation implementation
- **Nature of Changes**: Click emulation methods developed with reference to Playwright source code during debugging to ensure reliable mouse interactions.
- **Date Modified**: 10/6/25
- **Nature of Changes**:
- Fixed incorrect path to browser_tool_utils directory. Changed from `Path(__file__).parent / "browser_tool_utils"` to `Path(__file__).parent.parent / "browser_tool_utils"` to correctly locate JavaScript utility files.
- Fixed missing `cdp_url` attribute initialization in `__init__` method to prevent AttributeError in cleanup method.
- Fixed incorrect import path for browser_key_map. Changed from `.browser_tool_utils.browser_key_map` to `..browser_tool_utils.browser_key_map` to correctly import the KEY_MAP.
- **Date Modified**: 10/14/25
- **Nature of Changes**:
- Enhanced `_scroll` and `_scroll_to` methods to return screenshots after scrolling actions. Added 0.5s stabilization delay before taking screenshots to show the new viewport content. This provides visual feedback to the model after scroll actions, consistent with the navigate action behavior.
- **Date Modified**: 12/19/25
- **Nature of Changes**:
- Added `hover` action to move mouse cursor without clicking using Playwright's `mouse.move()` API. Useful for revealing tooltips, dropdown menus, or triggering hover states.
- Added `execute_js` action to execute JavaScript code in page context using Playwright's `page.evaluate()` API. Returns the result of the last expression.
- **Date Modified**: 1/18/26
- **Nature of Changes**:
- Added clarifying comment in the `options` property explaining that this implementation uses fixed 1920x1080 dimensions with empirical coordinate correction, and directing users to the "Handle coordinate scaling" section in the computer use documentation for the recommended client-side downscaling approach.
================================================
FILE: browser-use-demo/Dockerfile
================================================
FROM docker.io/ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBIAN_PRIORITY=high
# Install system dependencies
RUN apt-get update && \
apt-get -y upgrade && \
apt-get -y install \
# UI Requirements
xvfb \
xterm \
xdotool \
scrot \
imagemagick \
sudo \
mutter \
x11vnc \
# Python/pyenv reqs
build-essential \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
curl \
git \
libncursesw5-dev \
xz-utils \
tk-dev \
libxml2-dev \
libxmlsec1-dev \
libffi-dev \
liblzma-dev \
# Network tools
net-tools \
netcat \
# PPA req
software-properties-common && \
# Browser and desktop apps
sudo apt-get install -y --no-install-recommends \
x11-apps \
tint2 \
pcmanfm \
unzip \
# Playwright Chromium dependencies
libnss3 \
libnspr4 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libcups2 \
libatspi2.0-0 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libgbm1 \
libxkbcommon0 \
libpango-1.0-0 \
libcairo2 \
libasound2 && \
apt-get clean
# Install noVNC
RUN git clone --branch v1.5.0 https://github.com/novnc/noVNC.git /opt/noVNC && \
git clone --branch v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify && \
ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html
# Setup user
ENV USERNAME=browseruse
ENV HOME=/home/$USERNAME
RUN useradd -m -s /bin/bash -d $HOME $USERNAME
RUN echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
USER browseruse
WORKDIR $HOME
# Setup Python
RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv && \
cd ~/.pyenv && src/configure && make -C src && cd .. && \
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc && \
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc && \
echo 'eval "$(pyenv init -)"' >> ~/.bashrc
ENV PYENV_ROOT="$HOME/.pyenv"
ENV PATH="$PYENV_ROOT/bin:$PATH"
ENV PYENV_VERSION=3.11.6
RUN eval "$(pyenv init -)" && \
pyenv install $PYENV_VERSION && \
pyenv global $PYENV_VERSION && \
pyenv rehash
ENV PATH="$HOME/.pyenv/shims:$HOME/.pyenv/bin:$PATH"
RUN python -m pip install --upgrade pip==23.1.2 setuptools==58.0.4 wheel==0.40.0 && \
python -m pip config set global.disable-pip-version-check true
# Copy requirements and install dependencies
COPY --chown=$USERNAME:$USERNAME browser_use_demo/requirements.txt $HOME/browser_use_demo/requirements.txt
RUN python -m pip install -r $HOME/browser_use_demo/requirements.txt
# Install Playwright and Chromium
RUN python -m playwright install chromium && \
python -m playwright install-deps chromium
# Setup desktop environment & app
COPY --chown=$USERNAME:$USERNAME image/ $HOME
COPY --chown=$USERNAME:$USERNAME browser_use_demo/ $HOME/browser_use_demo/
# Display configuration - hardcoded values
ENV DISPLAY_NUM=1
ENV HEIGHT=1080
ENV WIDTH=1920
ENTRYPOINT [ "./entrypoint.sh" ]
================================================
FILE: browser-use-demo/NOTICE
================================================
This software includes components from the following open source projects:
Microsoft Playwright
Source: https://github.com/microsoft/playwright
License: Apache License 2.0
Copyright (c) Microsoft Corporation
Modified files are marked with modification notices. See CHANGELOG.md for details.
================================================
FILE: browser-use-demo/README.md
================================================
# Browser Automation Demo with Claude
A complete reference implementation for building browser automation with Claude using Playwright. This demo provides a containerized Streamlit interface showcasing how to give Claude the ability to navigate websites, interact with DOM elements, extract content, and fill forms.
## Overview
This demo implements a custom browser tool that enables Claude to interact with web browsers. It provides:
- **DOM access**: Read page structure with element references
- **Navigation control**: Browse URLs and manage browser history
- **Form manipulation**: Directly set form input values
- **Text extraction**: Get all text content from pages
- **Element targeting**: Interact with elements via ref or coordinate parameters
- **Smart scrolling**: Scroll to specific elements or in specific directions
- **Page search**: Find and highlight text on pages
- **Visual capture**: Take screenshots and capture zoomed regions
### Advantages Over Coordinate-Based Automation
- **Reliability**: Element-based targeting via the `ref` parameter works across different screen sizes and layouts, unlike pixel coordinates that break when windows resize
- **Direct DOM manipulation**: Provides structured visibility into page elements and their properties, enabling precise interactions with dynamic content, hidden elements, and complex web applications
- **Web-specific actions**: Built-in support for navigation, text extraction, and form completion
## Quick Start
### Prerequisites
- Docker and Docker Compose installed on your system
- Anthropic API key
### Setup
1. **Clone the repository**:
```bash
git clone https://github.com/anthropics/claude-quickstarts.git
cd claude-quickstarts/browser-use-demo
```
2. **Configure environment**:
```bash
cp .env.example .env
# Edit .env file and add your ANTHROPIC_API_KEY
```
The display resolution is set to 1920x1080 (16:9) for optimal coordinate accuracy.
- See `.env.example` for more options and coordinate scaling details
### Running with Docker Compose
```bash
# For production use:
docker-compose up --build
# For development with file watching (auto-sync changes):
docker-compose up --build --watch
```
### Access the interfaces:
- **Main UI**: http://localhost:8080 (Streamlit interface)
- **NoVNC Browser View**: http://localhost:6080 (see the browser)
- **VNC**: Connect any VNC client to localhost:5900
https://github.com/user-attachments/assets/4fb72078-6902-4b63-bcd1-5f2c4cd60582
## Usage Examples
Once the demo is running, try these prompts in the Streamlit interface:
- "Navigate to news.ycombinator.com and tell me the top 3 stories"
- "Go to google.com and search for 'Anthropic Claude'"
- "Visit wikipedia.org and find information about artificial intelligence"
- "Navigate to github.com and search for 'playwright'"
Note that the current Playwright implementation hits CAPTCHAs when searching Google.com. To avoid this, we recommend that you specify the website in the prompt (ie. navigate to Anthropic.com and search for x).
## Safety Considerations
Browser automation poses unique risks that are distinct from standard API features or chat interfaces. These risks are heightened when using the tool to interact with the internet. To minimize risks, consider taking precautions such as:
1. Run the browser in an isolated virtual machine or container environment with minimal privileges to prevent direct system attacks or accidents.
2. Avoid giving the model access to sensitive data, such as account login information, to prevent information theft.
3. Consider an allowlist of domains to reduce exposure to malicious content.
4. Ask a human to confirm decisions that may result in meaningful real-world consequences as well as any tasks requiring affirmative consent, such as accepting cookies, executing financial transactions, or agreeing to terms of service.
In some circumstances, Claude will follow commands found in content even if it conflicts with the user's instructions. For example, instructions on webpages or contained in images may override user instructions or cause Claude to make mistakes. We suggest taking precautions to isolate Claude from sensitive data and actions to avoid risks related to prompt injection.
Finally, please inform end users of relevant risks and obtain their consent prior to enabling browser automation in your own products.
This demo runs a browser in a containerized environment. While isolated, please note:
- **Don't enter personal credentials or sensitive information** - This is a demonstration tool
- **Be cautious about the websites you visit** - Some sites may have anti-automation measures
- **Not for production use** - This demo is for learning and development purposes only
## Implementation Reference
This demo shows how to build browser automation with Claude using Playwright. All browser actions (navigate, click, type, scroll, form_input, etc.) are implemented as methods in [browser.py](browser_use_demo/tools/browser.py) using Playwright's async API.
### Key Files
- **[browser.py](browser_use_demo/tools/browser.py)** - Main tool with all browser actions
- **[loop.py](browser_use_demo/loop.py)** - Sampling loop for API calls and response handling
- **[streamlit.py](browser_use_demo/streamlit.py)** - Chat UI
- **[browser_tool_utils/](browser_use_demo/browser_tool_utils/)** - JavaScript utilities for DOM extraction, element finding, and form manipulation
### Core Patterns
**Element references:** JavaScript utilities generate `ref` identifiers for reliable element targeting across screen sizes (replacing brittle pixel coordinates).
**Tool setup:**
```python
browser_tool = BrowserTool()
def to_params(self):
return {
"name": "browser",
"description": BROWSER_TOOL_DESCRIPTION,
"input_schema": BROWSER_TOOL_INPUT_SCHEMA,
}
```
### Coordinate Scaling
The browser tools implementation includes automatic coordinate scaling to ensure accurate interactions:
**How it works:**
- The browser viewport is fixed at 1920x1080 (16:9 aspect ratio)
- Claude processes screenshots at 1456x819 pixels for 16:9 aspect ratio (see [documentation](https://docs.claude.com/en/docs/build-with-claude/vision#evaluate-image-size))
- The browser tool automatically scales coordinates from Claude's processed resolution (1456x819) to the actual viewport (1920x1080)
- This ensures clicks and interactions happen at the correct locations
See `browser_use_demo/tools/coordinate_scaling.py` for the implementation.
This demo uses a custom tool definition with an explicit input schema, giving you full control over the tool in
gitextract_5euojtzz/ ├── .github/ │ ├── pull_request_template.md │ └── workflows/ │ ├── build.yaml │ ├── reusable_build_step.yaml │ └── tests.yaml ├── .pre-commit-config.yaml ├── CLAUDE.md ├── LICENSE ├── README.md ├── agents/ │ ├── .gitignore │ ├── README.md │ ├── __init__.py │ ├── agent.py │ ├── agent_demo.ipynb │ ├── test_message_params.py │ ├── tools/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── calculator_mcp.py │ │ ├── code_execution.py │ │ ├── file_tools.py │ │ ├── mcp_tool.py │ │ ├── think.py │ │ └── web_search.py │ └── utils/ │ ├── __init__.py │ ├── connections.py │ ├── history_util.py │ └── tool_util.py ├── autonomous-coding/ │ ├── .gitignore │ ├── README.md │ ├── agent.py │ ├── autonomous_agent_demo.py │ ├── client.py │ ├── progress.py │ ├── prompts/ │ │ ├── app_spec.txt │ │ ├── coding_prompt.md │ │ └── initializer_prompt.md │ ├── prompts.py │ ├── requirements.txt │ ├── security.py │ └── test_security.py ├── browser-use-demo/ │ ├── .dockerignore │ ├── .gitignore │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── NOTICE │ ├── README.md │ ├── browser_use_demo/ │ │ ├── __init__.py │ │ ├── browser_tool_utils/ │ │ │ ├── __init__.py │ │ │ ├── browser_dom_script.js │ │ │ ├── browser_element_script.js │ │ │ ├── browser_form_input_script.js │ │ │ ├── browser_key_map.py │ │ │ └── browser_text_script.js │ │ ├── display_constants.py │ │ ├── loop.py │ │ ├── message_handler.py │ │ ├── message_renderer.py │ │ ├── requirements.txt │ │ ├── streamlit.py │ │ └── tools/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── browser.py │ │ ├── collection.py │ │ └── coordinate_scaling.py │ ├── build.sh │ ├── docker-compose.yml │ ├── image/ │ │ ├── .config/ │ │ │ └── tint2/ │ │ │ ├── applications/ │ │ │ │ ├── firefox-custom.desktop │ │ │ │ ├── gedit.desktop │ │ │ │ └── terminal.desktop │ │ │ └── tint2rc │ │ ├── entrypoint.sh │ │ ├── http_server.py │ │ ├── index.html │ │ ├── mutter_startup.sh │ │ ├── novnc_startup.sh │ │ ├── start_all.sh │ │ ├── static_content/ │ │ │ └── index.html │ │ ├── tint2_startup.sh │ │ ├── x11vnc_startup.sh │ │ └── xvfb_startup.sh │ ├── pytest.ini │ ├── setup.py │ ├── test-requirements.txt │ ├── tests/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_integration.py │ │ ├── test_message_renderer.py │ │ ├── test_sampling_loop.py │ │ └── test_streamlit_helpers.py │ └── validate_env.py ├── computer-use-demo/ │ ├── .gitignore │ ├── .zed/ │ │ └── settings.json │ ├── CONTRIBUTING.md │ ├── Dockerfile │ ├── LICENSE │ ├── README.md │ ├── computer_use_demo/ │ │ ├── __init__.py │ │ ├── loop.py │ │ ├── requirements.txt │ │ ├── streamlit.py │ │ └── tools/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── bash.py │ │ ├── collection.py │ │ ├── computer.py │ │ ├── edit.py │ │ ├── groups.py │ │ └── run.py │ ├── dev-requirements.txt │ ├── image/ │ │ ├── .config/ │ │ │ └── tint2/ │ │ │ ├── applications/ │ │ │ │ ├── firefox-custom.desktop │ │ │ │ ├── gedit.desktop │ │ │ │ └── terminal.desktop │ │ │ └── tint2rc │ │ ├── .streamlit/ │ │ │ └── config.toml │ │ ├── entrypoint.sh │ │ ├── http_server.py │ │ ├── index.html │ │ ├── mutter_startup.sh │ │ ├── novnc_startup.sh │ │ ├── start_all.sh │ │ ├── static_content/ │ │ │ └── index.html │ │ ├── tint2_startup.sh │ │ ├── x11vnc_startup.sh │ │ └── xvfb_startup.sh │ ├── pyproject.toml │ ├── ruff.toml │ ├── setup.sh │ └── tests/ │ ├── conftest.py │ ├── loop_test.py │ ├── streamlit_test.py │ └── tools/ │ ├── bash_test.py │ ├── computer_test.py │ └── edit_test.py ├── customer-support-agent/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── amplify.yml │ ├── app/ │ │ ├── api/ │ │ │ └── chat/ │ │ │ └── route.ts │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── lib/ │ │ │ ├── customer_support_categories.json │ │ │ └── utils.ts │ │ └── page.tsx │ ├── components/ │ │ ├── ChatArea.tsx │ │ ├── FullSourceModal.tsx │ │ ├── LeftSidebar.tsx │ │ ├── RightSidebar.tsx │ │ ├── TopNavBar.tsx │ │ ├── theme-provider.tsx │ │ └── ui/ │ │ ├── avatar.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── input.tsx │ │ └── textarea.tsx │ ├── components.json │ ├── config.ts │ ├── lib/ │ │ └── utils.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── styles/ │ │ └── themes.js │ ├── tailwind.config.ts │ └── tsconfig.json ├── financial-data-analyst/ │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app/ │ │ ├── api/ │ │ │ └── finance/ │ │ │ └── route.ts │ │ ├── finance/ │ │ │ └── page.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ ├── components/ │ │ ├── ChartRenderer.tsx │ │ ├── FilePreview.tsx │ │ ├── TopNavBar.tsx │ │ ├── theme-provider.tsx │ │ └── ui/ │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── chart.tsx │ │ ├── dropdown-menu.tsx │ │ ├── textarea.tsx │ │ ├── toast.tsx │ │ └── toaster.tsx │ ├── components.json │ ├── hooks/ │ │ └── use-toast.ts │ ├── lib/ │ │ └── utils.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── types/ │ │ └── chart.ts │ └── utils/ │ └── fileHandling.ts └── pyproject.toml
SYMBOL INDEX (506 symbols across 85 files)
FILE: agents/agent.py
class ModelConfig (line 18) | class ModelConfig:
class Agent (line 33) | class Agent:
method __init__ (line 36) | def __init__(
method _prepare_message_params (line 80) | def _prepare_message_params(self) -> dict[str, Any]:
method _agent_loop (line 96) | async def _agent_loop(self, user_input: str) -> list[dict[str, Any]]:
method run_async (line 157) | async def run_async(self, user_input: str) -> list[dict[str, Any]]:
method run (line 171) | def run(self, user_input: str) -> list[dict[str, Any]]:
FILE: agents/test_message_params.py
class TestMessageParams (line 17) | class TestMessageParams:
method __init__ (line 20) | def __init__(self, verbose: bool = True):
method _print (line 30) | def _print(self, message: str) -> None:
method _run_test (line 35) | def _run_test(self, test_name: str, test_func: callable) -> None:
method test_basic_agent (line 57) | def test_basic_agent(self) -> None:
method test_custom_headers (line 71) | def test_custom_headers(self) -> None:
method test_beta_headers (line 94) | def test_beta_headers(self) -> None:
method test_metadata (line 113) | def test_metadata(self) -> None:
method test_api_parameters (line 131) | def test_api_parameters(self) -> None:
method test_parameter_override (line 155) | def test_parameter_override(self) -> None:
method test_invalid_metadata_field (line 178) | def test_invalid_metadata_field(self) -> None:
method test_combined_parameters (line 200) | def test_combined_parameters(self) -> None:
method run_all_tests (line 230) | def run_all_tests(self) -> None:
function main (line 256) | def main():
FILE: agents/tools/base.py
class Tool (line 8) | class Tool:
method to_dict (line 15) | def to_dict(self) -> dict[str, Any]:
method execute (line 23) | async def execute(self, **kwargs) -> str:
FILE: agents/tools/calculator_mcp.py
function calculator (line 13) | def calculator(number1: float, number2: float, operator: str) -> str:
FILE: agents/tools/code_execution.py
class CodeExecutionServerTool (line 8) | class CodeExecutionServerTool:
method to_dict (line 14) | def to_dict(self) -> dict[str, Any]:
FILE: agents/tools/file_tools.py
class FileReadTool (line 11) | class FileReadTool(Tool):
method __init__ (line 14) | def __init__(self):
method execute (line 49) | async def execute(
method _read_file (line 74) | async def _read_file(self, path: str, max_lines: int = 0) -> str:
method _list_files (line 104) | async def _list_files(self, directory: str, pattern: str = "*") -> str:
class FileWriteTool (line 138) | class FileWriteTool(Tool):
method __init__ (line 141) | def __init__(self):
method execute (line 180) | async def execute(
method _write_file (line 214) | async def _write_file(self, path: str, content: str) -> str:
method _edit_file (line 232) | async def _edit_file(self, path: str, old_text: str, new_text: str) ->...
FILE: agents/tools/mcp_tool.py
class MCPTool (line 8) | class MCPTool(Tool):
method __init__ (line 9) | def __init__(
method execute (line 21) | async def execute(self, **kwargs) -> str:
FILE: agents/tools/think.py
class ThinkTool (line 6) | class ThinkTool(Tool):
method __init__ (line 9) | def __init__(self):
method execute (line 30) | async def execute(self, thought: str) -> str:
FILE: agents/tools/web_search.py
class WebSearchServerTool (line 8) | class WebSearchServerTool:
method to_dict (line 18) | def to_dict(self) -> dict[str, Any]:
FILE: agents/utils/connections.py
class MCPConnection (line 14) | class MCPConnection(ABC):
method __init__ (line 17) | def __init__(self):
method _create_rw_context (line 23) | async def _create_rw_context(self):
method __aenter__ (line 26) | async def __aenter__(self):
method __aexit__ (line 36) | async def __aexit__(self, exc_type, exc_val, exc_tb):
method list_tools (line 50) | async def list_tools(self) -> Any:
method call_tool (line 55) | async def call_tool(
class MCPConnectionStdio (line 62) | class MCPConnectionStdio(MCPConnection):
method __init__ (line 65) | def __init__(
method _create_rw_context (line 73) | async def _create_rw_context(self):
class MCPConnectionSSE (line 81) | class MCPConnectionSSE(MCPConnection):
method __init__ (line 84) | def __init__(self, url: str, headers: dict[str, str] = None):
method _create_rw_context (line 89) | async def _create_rw_context(self):
function create_mcp_connection (line 93) | def create_mcp_connection(config: dict[str, Any]) -> MCPConnection:
function setup_mcp_connections (line 117) | async def setup_mcp_connections(
FILE: agents/utils/history_util.py
class MessageHistory (line 6) | class MessageHistory:
method __init__ (line 9) | def __init__(
method add_message (line 44) | async def add_message(
method truncate (line 69) | def truncate(self) -> None:
method format_for_api (line 113) | def format_for_api(self) -> list[dict[str, Any]]:
FILE: agents/utils/tool_util.py
function _execute_single_tool (line 7) | async def _execute_single_tool(
function execute_tools (line 27) | async def execute_tools(
FILE: autonomous-coding/agent.py
function run_agent_session (line 23) | async def run_agent_session(
function run_autonomous_agent (line 97) | async def run_autonomous_agent(
FILE: autonomous-coding/autonomous_agent_demo.py
function parse_args (line 27) | def parse_args() -> argparse.Namespace:
function main (line 75) | def main() -> None:
FILE: autonomous-coding/client.py
function create_client (line 40) | def create_client(project_dir: Path, model: str) -> ClaudeSDKClient:
FILE: autonomous-coding/progress.py
function count_passing_tests (line 12) | def count_passing_tests(project_dir: Path) -> tuple[int, int]:
function print_session_header (line 39) | def print_session_header(session_num: int, is_initializer: bool) -> None:
function print_progress_summary (line 49) | def print_progress_summary(project_dir: Path) -> None:
FILE: autonomous-coding/prompts.py
function load_prompt (line 15) | def load_prompt(name: str) -> str:
function get_initializer_prompt (line 21) | def get_initializer_prompt() -> str:
function get_coding_prompt (line 26) | def get_coding_prompt() -> str:
function copy_spec_to_project (line 31) | def copy_spec_to_project(project_dir: Path) -> None:
FILE: autonomous-coding/security.py
function split_command_segments (line 47) | def split_command_segments(command_string: str) -> list[str]:
function extract_commands (line 77) | def extract_commands(command_string: str) -> list[str]:
function validate_pkill_command (line 161) | def validate_pkill_command(command_string: str) -> tuple[bool, str]:
function validate_chmod_command (line 209) | def validate_chmod_command(command_string: str) -> tuple[bool, str]:
function validate_init_script (line 254) | def validate_init_script(command_string: str) -> tuple[bool, str]:
function get_command_for_validation (line 279) | def get_command_for_validation(cmd: str, segments: list[str]) -> str:
function bash_security_hook (line 297) | async def bash_security_hook(input_data, tool_use_id=None, context=None):
FILE: autonomous-coding/test_security.py
function test_hook (line 21) | def test_hook(command: str, should_block: bool) -> bool:
function test_extract_commands (line 44) | def test_extract_commands():
function test_validate_chmod (line 72) | def test_validate_chmod():
function test_validate_init_script (line 115) | def test_validate_init_script():
function main (line 154) | def main():
FILE: browser-use-demo/browser_use_demo/browser_tool_utils/browser_dom_script.js
function getRole (line 24) | function getRole(element) {
function getCleanName (line 71) | function getCleanName(element) {
function isVisible (line 180) | function isVisible(element) {
function isInteractive (line 191) | function isInteractive(element) {
function isSemantic (line 213) | function isSemantic(element) {
function shouldIncludeElement (line 235) | function shouldIncludeElement(element, options) {
function isContainerElement (line 319) | function isContainerElement(element) {
function processElement (line 347) | function processElement(element, depth, options) {
FILE: browser-use-demo/browser_use_demo/browser_tool_utils/browser_key_map.py
class KeyInfo (line 6) | class KeyInfo(TypedDict, total=False):
FILE: browser-use-demo/browser_use_demo/loop.py
class APIProvider (line 31) | class APIProvider(StrEnum):
function sampling_loop (line 66) | async def sampling_loop(
function _maybe_filter_to_n_most_recent_images (line 176) | def _maybe_filter_to_n_most_recent_images(
FILE: browser-use-demo/browser_use_demo/message_handler.py
class ProcessedResponse (line 25) | class ProcessedResponse:
class ResponseProcessor (line 34) | class ResponseProcessor:
method process_response (line 42) | def process_response(self, response) -> ProcessedResponse:
method execute_tools (line 82) | async def execute_tools(
method _build_tool_result (line 134) | def _build_tool_result(
class MessageBuilder (line 189) | class MessageBuilder:
method add_assistant_message (line 197) | def add_assistant_message(
method add_tool_results (line 215) | def add_tool_results(
method ensure_message_integrity (line 233) | def ensure_message_integrity(
method extract_text_from_message (line 263) | def extract_text_from_message(
FILE: browser-use-demo/browser_use_demo/message_renderer.py
class Sender (line 18) | class Sender:
class MessageRenderer (line 26) | class MessageRenderer:
method __init__ (line 29) | def __init__(self, session_state):
method _scale_browser_coordinates (line 37) | def _scale_browser_coordinates(self, input_dict: dict) -> dict:
method render (line 79) | def render(self, sender: str, message: str | BetaContentBlockParam | T...
method _should_skip_message (line 93) | def _should_skip_message(self, message) -> bool:
method _render_message_content (line 112) | def _render_message_content(self, message):
method _render_tool_result (line 134) | def _render_tool_result(self, tool_result: ToolResult):
method _render_dict_message (line 169) | def _render_dict_message(self, message: dict):
method _render_tool_use (line 188) | def _render_tool_use(self, message: dict):
method _render_stored_tool_result (line 203) | def _render_stored_tool_result(self, message: dict):
method render_conversation_history (line 213) | def render_conversation_history(self, messages: list):
method _render_message_by_role (line 225) | def _render_message_by_role(self, message: dict):
method _render_user_content (line 240) | def _render_user_content(self, content):
method _render_assistant_content (line 265) | def _render_assistant_content(self, content):
method _normalize_content (line 287) | def _normalize_content(self, content):
FILE: browser-use-demo/browser_use_demo/streamlit.py
function setup_state (line 60) | def setup_state():
function _clean_text_extraction_markers (line 98) | def _clean_text_extraction_markers(text: str) -> str:
function create_transcript_zip (line 113) | def create_transcript_zip(messages: list, include_images: bool = False) ...
class ImageExtractor (line 174) | class ImageExtractor:
method __init__ (line 177) | def __init__(self):
method extract_image (line 181) | def extract_image(self, source: dict) -> dict:
method process_image_content (line 193) | def process_image_content(self, item: dict) -> dict:
method process_text_content (line 198) | def process_text_content(self, item: dict) -> dict:
method process_tool_use_content (line 205) | def process_tool_use_content(self, item: dict) -> dict:
method process_tool_result_content (line 213) | def process_tool_result_content(self, item: dict) -> dict:
method process_default_content (line 233) | def process_default_content(self, item: dict) -> dict:
function extract_images_from_messages (line 238) | def extract_images_from_messages(messages: list) -> tuple:
function format_transcript_for_download (line 291) | def format_transcript_for_download(messages: list, include_images: bool ...
function _format_text_content (line 319) | def _format_text_content(item: dict, include_images: bool = False) -> dict:
function _format_tool_use_content (line 327) | def _format_tool_use_content(item: dict, include_images: bool = False) -...
function _format_tool_result_content (line 336) | def _format_tool_result_content(item: dict, include_images: bool = False...
function _format_image_content (line 364) | def _format_image_content(item: dict, include_images: bool = False) -> d...
function _format_default_content (line 377) | def _format_default_content(item: dict, include_images: bool = False) ->...
function _format_content_item (line 391) | def _format_content_item(item, include_images: bool = False):
function _format_message_content (line 404) | def _format_message_content(content, include_images: bool = False):
function authenticate (line 417) | def authenticate():
function get_or_create_event_loop (line 426) | def get_or_create_event_loop():
function run_agent (line 442) | async def run_agent(user_input: str):
function main (line 531) | def main():
FILE: browser-use-demo/browser_use_demo/tools/base.py
class BaseAnthropicTool (line 8) | class BaseAnthropicTool(metaclass=ABCMeta):
method __call__ (line 12) | def __call__(self, **kwargs) -> Any:
method to_params (line 17) | def to_params(
class ToolResult (line 24) | class ToolResult:
method __bool__ (line 32) | def __bool__(self):
method __add__ (line 35) | def __add__(self, other: "ToolResult"):
method replace (line 52) | def replace(self, **kwargs):
class CLIResult (line 57) | class CLIResult(ToolResult):
class ToolFailure (line 61) | class ToolFailure(ToolResult):
class ToolError (line 65) | class ToolError(Exception):
method __init__ (line 68) | def __init__(self, message):
FILE: browser-use-demo/browser_use_demo/tools/browser.py
class BrowserOptions (line 126) | class BrowserOptions(TypedDict):
class BrowserTool (line 158) | class BrowserTool(BaseAnthropicTool):
method __init__ (line 185) | def __init__(self):
method options (line 196) | def options(self) -> BrowserOptions:
method to_params (line 207) | def to_params(self) -> BetaToolUnionParam:
method _ensure_browser (line 218) | async def _ensure_browser(self) -> None:
method _execute_js_from_file (line 323) | async def _execute_js_from_file(self, filename: str, *args) -> Any:
method _take_screenshot (line 352) | async def _take_screenshot(self) -> ToolResult:
method _zoom_screenshot (line 374) | async def _zoom_screenshot(
method _navigate (line 397) | async def _navigate(self, url: str) -> ToolResult:
method _scale_coordinates (line 416) | def _scale_coordinates(self, x: int, y: int) -> tuple[int, int]:
method _click (line 457) | async def _click(
method _type_text (line 558) | async def _type_text(self, text: str) -> ToolResult:
method _press_key (line 569) | async def _press_key(
method _scroll (line 616) | async def _scroll(
method _scroll_to (line 666) | async def _scroll_to(self, ref: str) -> ToolResult:
method _drag (line 693) | async def _drag(
method _mouse_down (line 718) | async def _mouse_down(self, x: int, y: int) -> ToolResult:
method _mouse_up (line 734) | async def _mouse_up(self, x: int, y: int) -> ToolResult:
method _hover (line 750) | async def _hover(
method _read_page (line 814) | async def _read_page(self, filter_type: str = "") -> ToolResult:
method _get_page_text (line 857) | async def _get_page_text(self) -> ToolResult:
method _find (line 903) | async def _find(self, search_query: str) -> ToolResult:
method _form_input (line 1047) | async def _form_input(self, ref: str, value: Any) -> ToolResult:
method _wait (line 1068) | async def _wait(self, duration: float) -> ToolResult:
method _execute_js (line 1079) | async def _execute_js(self, code: str) -> ToolResult:
method __call__ (line 1105) | async def __call__(
method cleanup (line 1250) | async def cleanup(self):
FILE: browser-use-demo/browser_use_demo/tools/collection.py
class ToolCollection (line 8) | class ToolCollection:
method __init__ (line 11) | def __init__(self, *tools: BaseAnthropicTool):
method to_params (line 15) | def to_params(self) -> list[BetaToolUnionParam]:
FILE: browser-use-demo/browser_use_demo/tools/coordinate_scaling.py
class CoordinateScaler (line 9) | class CoordinateScaler:
method get_documented_size_for_aspect_ratio (line 36) | def get_documented_size_for_aspect_ratio(cls, viewport_width: int, vie...
method get_scale_factors (line 71) | def get_scale_factors(
method scale_coordinates (line 107) | def scale_coordinates(
method scale_coordinate_list (line 156) | def scale_coordinate_list(
FILE: browser-use-demo/image/http_server.py
class HTTPServerV6 (line 6) | class HTTPServerV6(HTTPServer):
function run_server (line 10) | def run_server():
FILE: browser-use-demo/tests/conftest.py
function mock_streamlit (line 17) | def mock_streamlit():
function mock_browser_tool (line 50) | def mock_browser_tool():
function sample_tool_result (line 59) | def sample_tool_result():
function sample_messages (line 79) | def sample_messages():
function edge_case_messages (line 131) | def edge_case_messages():
function _create_circular_reference (line 153) | def _create_circular_reference():
function mock_asyncio_loop (line 161) | def mock_asyncio_loop():
function mock_environment (line 170) | def mock_environment(monkeypatch):
function clean_environment (line 183) | def clean_environment(monkeypatch):
function mock_provider (line 196) | def mock_provider():
function mock_api_response_with_text_and_tools (line 206) | def mock_api_response_with_text_and_tools():
function mock_tool_collection (line 229) | def mock_tool_collection():
function sample_mixed_content_messages (line 250) | def sample_mixed_content_messages():
FILE: browser-use-demo/tests/test_integration.py
class TestFullMessageRenderingPipeline (line 16) | class TestFullMessageRenderingPipeline:
method test_full_conversation_rendering (line 24) | def test_full_conversation_rendering(
class TestStateInitializationAndPersistence (line 76) | class TestStateInitializationAndPersistence:
method test_complete_state_initialization (line 81) | def test_complete_state_initialization(self, mock_browser_tool, mock_s...
method test_state_persistence_across_renders (line 118) | def test_state_persistence_across_renders(self, mock_state):
class TestEventLoopManagementWithAsync (line 143) | class TestEventLoopManagementWithAsync:
method test_async_agent_execution (line 146) | def test_async_agent_execution(self):
method test_concurrent_async_operations (line 176) | def test_concurrent_async_operations(self, mock_state):
class TestErrorPropagationAndHandling (line 209) | class TestErrorPropagationAndHandling:
method test_rendering_error_propagation (line 214) | def test_rendering_error_propagation(self, mock_error, mock_state):
method test_initialization_error_recovery (line 236) | def test_initialization_error_recovery(self, mock_state):
class TestCompleteWorkflow (line 257) | class TestCompleteWorkflow:
method test_complete_user_interaction_flow (line 266) | def test_complete_user_interaction_flow(
class TestPerformanceAndScalability (line 314) | class TestPerformanceAndScalability:
method test_large_conversation_history (line 320) | def test_large_conversation_history(
method test_deeply_nested_content_performance (line 344) | def test_deeply_nested_content_performance(self, mock_state):
FILE: browser-use-demo/tests/test_message_renderer.py
class TestMessageRenderer (line 10) | class TestMessageRenderer:
method test_initialization (line 13) | def test_initialization(self, mock_streamlit):
method test_initialization_with_none_state (line 18) | def test_initialization_with_none_state(self):
method test_initialization_with_empty_state (line 23) | def test_initialization_with_empty_state(self):
class TestRenderMethod (line 30) | class TestRenderMethod:
method test_render_string_message (line 33) | def test_render_string_message(self, mock_streamlit):
method test_render_empty_string (line 41) | def test_render_empty_string(self, mock_streamlit):
method test_render_none_message (line 48) | def test_render_none_message(self, mock_streamlit):
method test_render_tool_result_with_output (line 55) | def test_render_tool_result_with_output(self, mock_streamlit, sample_t...
method test_render_tool_result_with_error (line 62) | def test_render_tool_result_with_error(self, mock_streamlit, sample_to...
method test_render_tool_result_with_image (line 69) | def test_render_tool_result_with_image(self, mock_streamlit, sample_to...
method test_render_tool_result_with_hidden_screenshots (line 79) | def test_render_tool_result_with_hidden_screenshots(
method test_render_dict_message_text_type (line 91) | def test_render_dict_message_text_type(self, mock_streamlit):
method test_render_dict_message_tool_use_type (line 99) | def test_render_dict_message_tool_use_type(self, mock_streamlit):
method test_render_dict_message_unknown_type (line 112) | def test_render_dict_message_unknown_type(self, mock_streamlit):
method test_render_very_long_message (line 121) | def test_render_very_long_message(self, mock_streamlit):
method test_render_unicode_special_chars (line 129) | def test_render_unicode_special_chars(self, mock_streamlit):
class TestConversationHistory (line 138) | class TestConversationHistory:
method test_render_empty_history (line 141) | def test_render_empty_history(self, mock_streamlit):
method test_render_single_message (line 149) | def test_render_single_message(self, mock_streamlit):
method test_render_multiple_messages (line 157) | def test_render_multiple_messages(self, mock_streamlit, sample_messages):
method test_render_unknown_role (line 165) | def test_render_unknown_role(self, mock_streamlit):
method test_render_missing_content_field (line 174) | def test_render_missing_content_field(self, mock_streamlit):
method test_render_none_content (line 185) | def test_render_none_content(self, mock_streamlit):
method test_render_list_content (line 194) | def test_render_list_content(self, mock_streamlit):
method test_skip_image_blocks_in_history (line 213) | def test_skip_image_blocks_in_history(self, mock_streamlit):
method test_tool_result_in_assistant_message (line 231) | def test_tool_result_in_assistant_message(self, mock_streamlit, sample...
method test_missing_tool_in_session_state (line 248) | def test_missing_tool_in_session_state(self, mock_streamlit):
class TestEdgeCases (line 263) | class TestEdgeCases:
method test_circular_reference_handling (line 266) | def test_circular_reference_handling(self, mock_streamlit):
method test_malformed_tool_result (line 278) | def test_malformed_tool_result(self, mock_streamlit):
method test_exception_in_rendering (line 289) | def test_exception_in_rendering(self, mock_streamlit):
method test_normalize_content_with_various_inputs (line 305) | def test_normalize_content_with_various_inputs(self, mock_streamlit):
method test_deeply_nested_content (line 321) | def test_deeply_nested_content(self, mock_streamlit):
method test_concurrent_modification (line 335) | def test_concurrent_modification(self, mock_streamlit):
method test_invalid_sender_type (line 354) | def test_invalid_sender_type(self, mock_streamlit):
method test_base64_decode_error (line 364) | def test_base64_decode_error(self, mock_streamlit):
FILE: browser-use-demo/tests/test_sampling_loop.py
class TestResponseProcessor (line 17) | class TestResponseProcessor:
method test_process_response_text_only (line 20) | def test_process_response_text_only(self):
method test_process_response_tool_only (line 37) | def test_process_response_tool_only(self):
method test_process_response_mixed_content (line 57) | def test_process_response_mixed_content(self):
method test_execute_tools_success (line 81) | def test_execute_tools_success(self):
method test_execute_tools_with_error (line 111) | def test_execute_tools_with_error(self):
method test_build_tool_result_with_image (line 141) | def test_build_tool_result_with_image(self):
method test_build_tool_result_with_text_extraction_markers (line 155) | def test_build_tool_result_with_text_extraction_markers(self):
class TestMessageBuilder (line 170) | class TestMessageBuilder:
method test_add_assistant_message (line 173) | def test_add_assistant_message(self):
method test_add_assistant_message_empty_content (line 188) | def test_add_assistant_message_empty_content(self):
method test_add_tool_results (line 198) | def test_add_tool_results(self):
method test_add_tool_results_empty (line 216) | def test_add_tool_results_empty(self):
method test_ensure_message_integrity_valid (line 226) | def test_ensure_message_integrity_valid(self):
method test_ensure_message_integrity_missing_role (line 236) | def test_ensure_message_integrity_missing_role(self):
method test_ensure_message_integrity_missing_content (line 245) | def test_ensure_message_integrity_missing_content(self):
method test_ensure_message_integrity_empty_list_content (line 254) | def test_ensure_message_integrity_empty_list_content(self):
method test_extract_text_from_message (line 263) | def test_extract_text_from_message(self):
method test_extract_text_from_message_no_text (line 279) | def test_extract_text_from_message_no_text(self):
method test_extract_text_from_user_message (line 293) | def test_extract_text_from_user_message(self):
class TestSamplingLoopIntegration (line 307) | class TestSamplingLoopIntegration:
method test_sampling_loop_preserves_text_with_tools (line 311) | def test_sampling_loop_preserves_text_with_tools(self, mock_anthropic):
method test_sampling_loop_text_only_response (line 386) | def test_sampling_loop_text_only_response(self, mock_anthropic):
method test_sampling_loop_multiple_tools_with_text (line 423) | def test_sampling_loop_multiple_tools_with_text(self, mock_anthropic):
method test_tool_choice_parameter_set (line 485) | def test_tool_choice_parameter_set(self, mock_anthropic):
FILE: browser-use-demo/tests/test_streamlit_helpers.py
class TestSetupState (line 15) | class TestSetupState:
method test_setup_state_fresh_initialization (line 19) | def test_setup_state_fresh_initialization(self, mock_state, mock_envir...
method test_setup_state_partial_initialization (line 38) | def test_setup_state_partial_initialization(self, mock_state):
method test_setup_state_missing_env_variables (line 60) | def test_setup_state_missing_env_variables(self, mock_state, clean_env...
method test_setup_state_lambda_evaluation (line 74) | def test_setup_state_lambda_evaluation(self, mock_state, mock_provider):
method test_setup_state_browser_tool_error (line 91) | def test_setup_state_browser_tool_error(self, mock_state):
class TestGetOrCreateEventLoop (line 108) | class TestGetOrCreateEventLoop:
method test_create_new_loop_when_none (line 114) | def test_create_new_loop_when_none(self, mock_set_loop, mock_new_loop,...
method test_create_new_loop_when_closed (line 131) | def test_create_new_loop_when_closed(
method test_reuse_existing_open_loop (line 153) | def test_reuse_existing_open_loop(self, mock_set_loop, mock_new_loop, ...
method test_event_loop_creation_error (line 168) | def test_event_loop_creation_error(self, mock_new_loop, mock_state):
method test_set_event_loop_error (line 179) | def test_set_event_loop_error(self, mock_set_loop, mock_state):
class TestAuthenticate (line 189) | class TestAuthenticate:
method test_authenticate_with_valid_key (line 195) | def test_authenticate_with_valid_key(
method test_authenticate_with_missing_key (line 212) | def test_authenticate_with_missing_key(
method test_authenticate_with_none_key (line 231) | def test_authenticate_with_none_key(
method test_authenticate_non_anthropic_provider (line 245) | def test_authenticate_non_anthropic_provider(self, mock_state, mock_pr...
class TestEdgeCasesAndErrors (line 256) | class TestEdgeCasesAndErrors:
method test_setup_state_with_corrupted_state (line 260) | def test_setup_state_with_corrupted_state(self, mock_state):
method test_concurrent_setup_state_calls (line 270) | def test_concurrent_setup_state_calls(self, mock_state):
method test_get_or_create_with_running_loop (line 297) | def test_get_or_create_with_running_loop(self, mock_get_loop, mock_sta...
method test_setup_state_with_readonly_state (line 316) | def test_setup_state_with_readonly_state(self, mock_state):
FILE: browser-use-demo/validate_env.py
function validate_env (line 19) | def validate_env():
FILE: computer-use-demo/computer_use_demo/loop.py
class APIProvider (line 42) | class APIProvider(StrEnum):
function sampling_loop (line 70) | async def sampling_loop(
function _maybe_filter_to_n_most_recent_images (line 193) | def _maybe_filter_to_n_most_recent_images(
function _response_to_params (line 242) | def _response_to_params(
function _inject_prompt_caching (line 265) | def _inject_prompt_caching(
function _make_api_tool_result (line 291) | def _make_api_tool_result(
function _maybe_prepend_system_tool_result (line 327) | def _maybe_prepend_system_tool_result(result: ToolResult, result_text: s...
FILE: computer-use-demo/computer_use_demo/streamlit.py
class ModelConfig (line 42) | class ModelConfig:
class Sender (line 115) | class Sender(StrEnum):
function setup_state (line 121) | def setup_state():
function _reset_model (line 155) | def _reset_model():
function _reset_model_conf (line 162) | def _reset_model_conf():
function main (line 179) | async def main():
function maybe_add_interruption_blocks (line 354) | def maybe_add_interruption_blocks():
function track_sampling_loop (line 379) | def track_sampling_loop():
function validate_auth (line 385) | def validate_auth(provider: APIProvider, api_key: str | None):
function load_from_storage (line 408) | def load_from_storage(filename: str) -> str | None:
function save_to_storage (line 421) | def save_to_storage(filename: str, data: str) -> None:
function _api_response_callback (line 433) | def _api_response_callback(
function _tool_output_callback (line 450) | def _tool_output_callback(
function _render_api_response (line 458) | def _render_api_response(
function _render_error (line 482) | def _render_error(error: Exception):
function _render_message (line 497) | def _render_message(
FILE: computer-use-demo/computer_use_demo/tools/base.py
class BaseAnthropicTool (line 8) | class BaseAnthropicTool(metaclass=ABCMeta):
method __call__ (line 12) | def __call__(self, **kwargs) -> Any:
method to_params (line 17) | def to_params(
class ToolResult (line 24) | class ToolResult:
method __bool__ (line 32) | def __bool__(self):
method __add__ (line 35) | def __add__(self, other: "ToolResult"):
method replace (line 52) | def replace(self, **kwargs):
class CLIResult (line 57) | class CLIResult(ToolResult):
class ToolFailure (line 61) | class ToolFailure(ToolResult):
class ToolError (line 65) | class ToolError(Exception):
method __init__ (line 68) | def __init__(self, message):
FILE: computer-use-demo/computer_use_demo/tools/bash.py
class _BashSession (line 8) | class _BashSession:
method __init__ (line 19) | def __init__(self):
method start (line 23) | async def start(self):
method stop (line 39) | def stop(self):
method run (line 47) | async def run(self, command: str):
class BashTool20250124 (line 104) | class BashTool20250124(BaseAnthropicTool):
method __init__ (line 115) | def __init__(self):
method to_params (line 119) | def to_params(self) -> Any:
method __call__ (line 125) | async def __call__(
class BashTool20241022 (line 146) | class BashTool20241022(BashTool20250124):
FILE: computer-use-demo/computer_use_demo/tools/collection.py
class ToolCollection (line 15) | class ToolCollection:
method __init__ (line 18) | def __init__(self, *tools: BaseAnthropicTool):
method to_params (line 24) | def to_params(
method run (line 29) | async def run(self, *, name: str, tool_input: dict[str, Any]) -> ToolR...
FILE: computer-use-demo/computer_use_demo/tools/computer.py
class Resolution (line 51) | class Resolution(TypedDict):
class ScalingSource (line 73) | class ScalingSource(StrEnum):
class ComputerToolOptions (line 78) | class ComputerToolOptions(TypedDict):
function chunks (line 84) | def chunks(s: str, chunk_size: int) -> list[str]:
class BaseComputerTool (line 88) | class BaseComputerTool:
method options (line 103) | def options(self) -> ComputerToolOptions:
method __init__ (line 113) | def __init__(self):
method __call__ (line 128) | async def __call__(
method validate_and_get_coordinates (line 220) | def validate_and_get_coordinates(self, coordinate: tuple[int, int] | N...
method screenshot (line 228) | async def screenshot(self):
method shell (line 256) | async def shell(self, command: str, take_screenshot=True) -> ToolResult:
method scale_coordinates (line 268) | def scale_coordinates(self, source: ScalingSource, x: int, y: int):
class ComputerTool20241022 (line 294) | class ComputerTool20241022(BaseComputerTool, BaseAnthropicTool):
method to_params (line 297) | def to_params(self) -> BetaToolComputerUse20241022Param:
class ComputerTool20250124 (line 301) | class ComputerTool20250124(BaseComputerTool, BaseAnthropicTool):
method to_params (line 304) | def to_params(self):
method __call__ (line 310) | async def __call__(
class ComputerTool20251124 (line 417) | class ComputerTool20251124(ComputerTool20250124):
method options (line 421) | def options(self) -> ComputerToolOptions: # pyright: ignore[reportInc...
method __call__ (line 424) | async def __call__(
FILE: computer-use-demo/computer_use_demo/tools/edit.py
class EditTool20250728 (line 18) | class EditTool20250728(BaseAnthropicTool):
method __init__ (line 34) | def __init__(self):
method to_params (line 38) | def to_params(self) -> Any:
method __call__ (line 44) | async def __call__(
method validate_path (line 87) | def validate_path(self, command: str, path: Path):
method view (line 113) | async def view(self, path: Path, view_range: list[int] | None = None):
method str_replace (line 160) | def str_replace(self, path: Path, old_str: str, new_str: str | None):
method insert (line 208) | def insert(self, path: Path, insert_line: int, new_str: str):
method read_file (line 247) | def read_file(self, path: Path):
method write_file (line 254) | def write_file(self, path: Path, file: str):
method _make_output (line 261) | def _make_output(
FILE: computer-use-demo/computer_use_demo/tools/groups.py
class ToolGroup (line 24) | class ToolGroup:
FILE: computer-use-demo/computer_use_demo/tools/run.py
function maybe_truncate (line 9) | def maybe_truncate(content: str, truncate_after: int | None = MAX_RESPON...
function run (line 18) | async def run(
FILE: computer-use-demo/image/http_server.py
class HTTPServerV6 (line 6) | class HTTPServerV6(HTTPServer):
function run_server (line 10) | def run_server():
FILE: computer-use-demo/tests/conftest.py
function mock_screen_dimensions (line 8) | def mock_screen_dimensions():
FILE: computer-use-demo/tests/loop_test.py
function test_loop (line 9) | async def test_loop():
FILE: computer-use-demo/tests/streamlit_test.py
function streamlit_app (line 11) | def streamlit_app():
function test_streamlit (line 15) | def test_streamlit(streamlit_app: AppTest):
FILE: computer-use-demo/tests/tools/bash_test.py
function bash_tool (line 7) | def bash_tool(request):
function test_bash_tool_restart (line 12) | async def test_bash_tool_restart(bash_tool):
function test_bash_tool_run_command (line 22) | async def test_bash_tool_run_command(bash_tool):
function test_bash_tool_no_command (line 29) | async def test_bash_tool_no_command(bash_tool):
function test_bash_tool_session_creation (line 35) | async def test_bash_tool_session_creation(bash_tool):
function test_bash_tool_session_reuse (line 42) | async def test_bash_tool_session_reuse(bash_tool):
function test_bash_tool_session_error (line 51) | async def test_bash_tool_session_error(bash_tool):
function test_bash_tool_non_zero_exit (line 57) | async def test_bash_tool_non_zero_exit(bash_tool):
function test_bash_tool_timeout (line 64) | async def test_bash_tool_timeout(bash_tool):
FILE: computer-use-demo/tests/tools/computer_test.py
function computer_tool (line 15) | def computer_tool(request):
function test_computer_tool_mouse_move (line 20) | async def test_computer_tool_mouse_move(computer_tool):
function test_computer_tool_type (line 31) | async def test_computer_tool_type(computer_tool):
function test_computer_tool_screenshot (line 48) | async def test_computer_tool_screenshot(computer_tool):
function test_computer_tool_scaling (line 59) | async def test_computer_tool_scaling(computer_tool):
function test_computer_tool_scaling_with_different_aspect_ratio (line 82) | async def test_computer_tool_scaling_with_different_aspect_ratio(compute...
function test_computer_tool_no_scaling_for_unsupported_resolution (line 99) | async def test_computer_tool_no_scaling_for_unsupported_resolution(compu...
function test_computer_tool_scaling_out_of_bounds (line 115) | async def test_computer_tool_scaling_out_of_bounds(computer_tool):
function test_computer_tool_invalid_action (line 126) | async def test_computer_tool_invalid_action(computer_tool):
function test_computer_tool_missing_coordinate (line 132) | async def test_computer_tool_missing_coordinate(computer_tool):
function test_computer_tool_missing_text (line 138) | async def test_computer_tool_missing_text(computer_tool):
FILE: computer-use-demo/tests/tools/edit_test.py
function edit_tool (line 11) | def edit_tool():
function test_view_command (line 16) | async def test_view_command(edit_tool):
function test_create_command (line 81) | async def test_create_command(edit_tool):
function test_str_replace_command (line 109) | async def test_str_replace_command(edit_tool):
function test_insert_command (line 178) | async def test_insert_command(edit_tool):
function test_validate_path (line 266) | async def test_validate_path(edit_tool):
FILE: customer-support-agent/app/api/chat/route.ts
function sanitizeHeaderValue (line 50) | function sanitizeHeaderValue(value: string): string {
function POST (line 64) | async function POST(req: Request) {
FILE: customer-support-agent/app/layout.tsx
function RootLayout (line 14) | function RootLayout({
FILE: customer-support-agent/app/lib/utils.ts
function cn (line 20) | function cn(...inputs: ClassValue[]) {
type RAGSource (line 24) | interface RAGSource {
function retrieveContext (line 31) | async function retrieveContext(
FILE: customer-support-agent/app/page.tsx
function Home (line 14) | function Home() {
FILE: customer-support-agent/components/ChatArea.tsx
type ThinkingContent (line 43) | type ThinkingContent = {
type ConversationHeaderProps (line 51) | interface ConversationHeaderProps {
type Model (line 194) | type Model = {
type Message (line 199) | interface Message {
type ConversationHeaderProps (line 206) | interface ConversationHeaderProps {
type KnowledgeBase (line 216) | type KnowledgeBase = {
function ChatArea (line 300) | function ChatArea() {
FILE: customer-support-agent/components/FullSourceModal.tsx
type FullSourceModalProps (line 12) | interface FullSourceModalProps {
FILE: customer-support-agent/components/LeftSidebar.tsx
type ThinkingContent (line 17) | interface ThinkingContent {
constant MAX_THINKING_HISTORY (line 45) | const MAX_THINKING_HISTORY = 15;
FILE: customer-support-agent/components/RightSidebar.tsx
type RAGSource (line 8) | interface RAGSource {
type RAGHistoryItem (line 16) | interface RAGHistoryItem {
type DebugInfo (line 22) | interface DebugInfo {
type SidebarEvent (line 26) | interface SidebarEvent {
constant MAX_HISTORY (line 43) | const MAX_HISTORY = 15;
FILE: customer-support-agent/components/TopNavBar.tsx
type ThemeName (line 26) | type ThemeName = keyof typeof themes;
FILE: customer-support-agent/components/theme-provider.tsx
function ThemeProvider (line 7) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {
FILE: customer-support-agent/components/ui/button.tsx
type ButtonProps (line 36) | interface ButtonProps
FILE: customer-support-agent/components/ui/input.tsx
type InputProps (line 5) | interface InputProps
FILE: customer-support-agent/components/ui/textarea.tsx
type TextareaProps (line 5) | interface TextareaProps
FILE: customer-support-agent/config.ts
type Config (line 1) | type Config = {
FILE: customer-support-agent/lib/utils.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: financial-data-analyst/app/api/finance/route.ts
type ChartToolResponse (line 23) | interface ChartToolResponse extends ChartData {
type ToolSchema (line 27) | interface ToolSchema {
function POST (line 103) | async function POST(req: NextRequest) {
FILE: financial-data-analyst/app/finance/page.tsx
type Message (line 45) | interface Message {
type Model (line 59) | type Model = {
type FileUpload (line 64) | interface FileUpload {
type APIResponse (line 79) | interface APIResponse {
type MessageComponentProps (line 91) | interface MessageComponentProps {
function AIChat (line 197) | function AIChat() {
FILE: financial-data-analyst/app/layout.tsx
function RootLayout (line 23) | function RootLayout({
FILE: financial-data-analyst/app/page.tsx
function Home (line 7) | function Home() {
FILE: financial-data-analyst/components/ChartRenderer.tsx
function BarChartComponent (line 33) | function BarChartComponent({ data }: { data: ChartData }) {
function MultiBarChartComponent (line 91) | function MultiBarChartComponent({ data }: { data: ChartData }) {
function LineChartComponent (line 150) | function LineChartComponent({ data }: { data: ChartData }) {
function PieChartComponent (line 218) | function PieChartComponent({ data }: { data: ChartData }) {
function AreaChartComponent (line 310) | function AreaChartComponent({
function ChartRenderer (line 391) | function ChartRenderer({ data }: { data: ChartData }) {
FILE: financial-data-analyst/components/FilePreview.tsx
type FilePreviewProps (line 6) | interface FilePreviewProps {
FILE: financial-data-analyst/components/TopNavBar.tsx
type TopNavBarProps (line 15) | interface TopNavBarProps {
FILE: financial-data-analyst/components/theme-provider.tsx
function ThemeProvider (line 8) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {
FILE: financial-data-analyst/components/ui/badge.tsx
type BadgeProps (line 26) | interface BadgeProps
function Badge (line 30) | function Badge({ className, variant, ...props }: BadgeProps) {
FILE: financial-data-analyst/components/ui/button.tsx
type ButtonProps (line 37) | interface ButtonProps
FILE: financial-data-analyst/components/ui/chart.tsx
constant THEMES (line 14) | const THEMES = { light: "", dark: ".dark" } as const
type ChartConfig (line 16) | type ChartConfig = {
type ChartContextProps (line 26) | type ChartContextProps = {
function useChart (line 32) | function useChart() {
function getPayloadConfigFromPayload (line 325) | function getPayloadConfigFromPayload(
FILE: financial-data-analyst/components/ui/textarea.tsx
type TextareaProps (line 5) | interface TextareaProps
FILE: financial-data-analyst/components/ui/toast.tsx
type ToastProps (line 115) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement (line 117) | type ToastActionElement = React.ReactElement<typeof ToastAction>
FILE: financial-data-analyst/components/ui/toaster.tsx
function Toaster (line 13) | function Toaster() {
FILE: financial-data-analyst/hooks/use-toast.ts
constant TOAST_LIMIT (line 11) | const TOAST_LIMIT = 1
constant TOAST_REMOVE_DELAY (line 12) | const TOAST_REMOVE_DELAY = 1000000
type ToasterToast (line 14) | type ToasterToast = ToastProps & {
function genId (line 30) | function genId() {
type ActionType (line 35) | type ActionType = typeof actionTypes
type Action (line 37) | type Action =
type State (line 55) | interface State {
function dispatch (line 136) | function dispatch(action: Action) {
type Toast (line 143) | type Toast = Omit<ToasterToast, "id">
function toast (line 145) | function toast({ ...props }: Toast) {
function useToast (line 174) | function useToast() {
FILE: financial-data-analyst/lib/utils.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: financial-data-analyst/types/chart.ts
type ChartConfig (line 2) | interface ChartConfig {
type ChartData (line 10) | interface ChartData {
FILE: financial-data-analyst/utils/fileHandling.ts
type FileUpload (line 94) | interface FileUpload {
Condensed preview — 197 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (835K chars).
[
{
"path": ".github/pull_request_template.md",
"chars": 613,
"preview": "## Description\n<!-- Describe the changes you've made -->\n\n## Quickstart\n- [ ] Computer Use Demo\n- [ ] Customer Support A"
},
{
"path": ".github/workflows/build.yaml",
"chars": 2132,
"preview": "env:\n REGISTRY: ghcr.io/anthropics/anthropic-quickstarts\nname: build\non:\n pull_request:\n paths:\n - .github/**\n"
},
{
"path": ".github/workflows/reusable_build_step.yaml",
"chars": 3414,
"preview": "on:\n workflow_call:\n inputs:\n platform:\n required: true\n type: string\n builder:\n requ"
},
{
"path": ".github/workflows/tests.yaml",
"chars": 1476,
"preview": "name: tests\non:\n pull_request:\n paths:\n - .github/**\n - computer-use-demo/**\n push:\n branches:\n -"
},
{
"path": ".pre-commit-config.yaml",
"chars": 678,
"preview": "files: ^computer-use-demo/\ndefault_stages: [pre-commit, pre-push]\nrepos:\n - repo: https://github.com/pre-commit/pre-com"
},
{
"path": "CLAUDE.md",
"chars": 2134,
"preview": "# Claude Quickstarts Development Guide\n\n## Legal\n\n- When changes are made to files that have a copyright notice add them"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2023 Anthropic\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "README.md",
"chars": 3461,
"preview": "# Claude Quickstarts\n\nClaude Quickstarts is a collection of projects designed to help developers quickly get started wit"
},
{
"path": "agents/.gitignore",
"chars": 275,
"preview": "# Python bytecode\n__pycache__/\n*.py[cod]\n*$py.class\n\n# Jupyter Notebook\n.ipynb_checkpoints\n*/.ipynb_checkpoints/*\n\n# Mac"
},
{
"path": "agents/README.md",
"chars": 1847,
"preview": "# Agents\n\nA minimal educational implementation of LLM agents using the Claude API.\n\n> **Note:** This is NOT an SDK, but "
},
{
"path": "agents/__init__.py",
"chars": 146,
"preview": "\"\"\"Core agent implementations.\"\"\"\n\nfrom .agent import Agent, ModelConfig\nfrom .tools.base import Tool\n\n__all__ = [\"Agent"
},
{
"path": "agents/agent.py",
"chars": 6223,
"preview": "\"\"\"Agent implementation with Claude API and tools.\"\"\"\n\nimport asyncio\nimport os\nfrom contextlib import AsyncExitStack\nfr"
},
{
"path": "agents/agent_demo.ipynb",
"chars": 47913,
"preview": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"metadata\": {},\n \"source\": [\n \"# Agent Demo with Simplified Connec"
},
{
"path": "agents/test_message_params.py",
"chars": 9674,
"preview": "#!/usr/bin/env python3\n\"\"\"Test suite for Agent message_params functionality.\n\nThis module tests the ability to pass cust"
},
{
"path": "agents/tools/__init__.py",
"chars": 384,
"preview": "\"\"\"Tools module for agent framework.\"\"\"\n\nfrom .base import Tool\nfrom .code_execution import CodeExecutionServerTool\nfrom"
},
{
"path": "agents/tools/base.py",
"chars": 697,
"preview": "\"\"\"Base tool definitions for the agent framework.\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Any\n\n\n@datacl"
},
{
"path": "agents/tools/calculator_mcp.py",
"chars": 1559,
"preview": "#!/usr/bin/env python3\n\n\"\"\"Simple calculator tool for basic math operations.\"\"\"\n\nimport math\n\nfrom mcp.server import Fas"
},
{
"path": "agents/tools/code_execution.py",
"chars": 508,
"preview": "\"\"\"Code execution server tool for the agent framework.\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Any\n\n\n@d"
},
{
"path": "agents/tools/file_tools.py",
"chars": 9847,
"preview": "\"\"\"File operation tools for reading and writing files.\"\"\"\n\nimport asyncio\nimport glob\nimport os\nfrom pathlib import Path"
},
{
"path": "agents/tools/mcp_tool.py",
"chars": 1121,
"preview": "\"\"\"Tools that interface with MCP servers.\"\"\"\n\nfrom typing import Any\nfrom .base import Tool\nfrom ..utils.connections imp"
},
{
"path": "agents/tools/think.py",
"chars": 1047,
"preview": "\"\"\"Think tool for internal reasoning.\"\"\"\n\nfrom .base import Tool\n\n\nclass ThinkTool(Tool):\n \"\"\"Tool for internal reaso"
},
{
"path": "agents/tools/web_search.py",
"chars": 1242,
"preview": "\"\"\"Web search server tool for the agent framework.\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Any, Optiona"
},
{
"path": "agents/utils/__init__.py",
"chars": 155,
"preview": "\"\"\"Agent utility modules.\"\"\"\n\nfrom .history_util import MessageHistory\nfrom .tool_util import execute_tools\n\n__all__ = ["
},
{
"path": "agents/utils/connections.py",
"chars": 4755,
"preview": "\"\"\"Connection handling for MCP servers.\"\"\"\n\nfrom abc import ABC, abstractmethod\nfrom contextlib import AsyncExitStack\nfr"
},
{
"path": "agents/utils/history_util.py",
"chars": 4084,
"preview": "\"\"\"Message history with token tracking and prompt caching.\"\"\"\n\nfrom typing import Any\n\n\nclass MessageHistory:\n \"\"\"Man"
},
{
"path": "agents/utils/tool_util.py",
"chars": 1181,
"preview": "\"\"\"Tool execution utility with parallel execution support.\"\"\"\n\nimport asyncio\nfrom typing import Any\n\n\nasync def _execut"
},
{
"path": "autonomous-coding/.gitignore",
"chars": 69,
"preview": "# Agent-generated output directories\ngenerations/\n\n# Log files\nlogs/\n"
},
{
"path": "autonomous-coding/README.md",
"chars": 5680,
"preview": "# Autonomous Coding Agent Demo\n\nA minimal harness demonstrating long-running autonomous coding with the Claude Agent SDK"
},
{
"path": "autonomous-coding/agent.py",
"chars": 7095,
"preview": "\"\"\"\nAgent Session Logic\n===================\n\nCore agent interaction functions for running autonomous coding sessions.\n\"\""
},
{
"path": "autonomous-coding/autonomous_agent_demo.py",
"chars": 3460,
"preview": "#!/usr/bin/env python3\n\"\"\"\nAutonomous Coding Agent Demo\n============================\n\nA minimal harness demonstrating lo"
},
{
"path": "autonomous-coding/client.py",
"chars": 4008,
"preview": "\"\"\"\nClaude SDK Client Configuration\n===============================\n\nFunctions for creating and configuring the Claude A"
},
{
"path": "autonomous-coding/progress.py",
"chars": 1514,
"preview": "\"\"\"\nProgress Tracking Utilities\n===========================\n\nFunctions for tracking and displaying progress of the auton"
},
{
"path": "autonomous-coding/prompts/app_spec.txt",
"chars": 22015,
"preview": "<project_specification>\n <project_name>Claude.ai Clone - AI Chat Interface</project_name>\n\n <overview>\n Build a ful"
},
{
"path": "autonomous-coding/prompts/coding_prompt.md",
"chars": 5571,
"preview": "## YOUR ROLE - CODING AGENT\n\nYou are continuing work on a long-running autonomous development task.\nThis is a FRESH cont"
},
{
"path": "autonomous-coding/prompts/initializer_prompt.md",
"chars": 3586,
"preview": "## YOUR ROLE - INITIALIZER AGENT (Session 1 of Many)\n\nYou are the FIRST agent in a long-running autonomous development p"
},
{
"path": "autonomous-coding/prompts.py",
"chars": 994,
"preview": "\"\"\"\nPrompt Loading Utilities\n========================\n\nFunctions for loading prompt templates from the prompts directory"
},
{
"path": "autonomous-coding/requirements.txt",
"chars": 24,
"preview": "claude-code-sdk>=0.0.25\n"
},
{
"path": "autonomous-coding/security.py",
"chars": 10331,
"preview": "\"\"\"\nSecurity Hooks for Autonomous Coding Agent\n==========================================\n\nPre-tool-use hooks that valid"
},
{
"path": "autonomous-coding/test_security.py",
"chars": 8598,
"preview": "#!/usr/bin/env python3\n\"\"\"\nSecurity Hook Tests\n===================\n\nTests for the bash command security validation logic"
},
{
"path": "browser-use-demo/.dockerignore",
"chars": 137,
"preview": "**/__pycache__\n**/*.pyc\n**/*.pyo\n**/*.pyd\n.Python\n*.egg-info/\n.git/\n.gitignore\n*.md\n.DS_Store\ntests/\n*.log\n.vscode/\n.ide"
},
{
"path": "browser-use-demo/.gitignore",
"chars": 383,
"preview": "# Python\n__pycache__/\n*.py[cod]\n*$py.class\n*.so\n.Python\nenv/\nvenv/\nENV/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.egg"
},
{
"path": "browser-use-demo/CHANGELOG.md",
"chars": 2930,
"preview": "# Modifications to Microsoft Playwright Source\n\nThis file tracks all modifications made to files derived from or inspire"
},
{
"path": "browser-use-demo/Dockerfile",
"chars": 3010,
"preview": "FROM docker.io/ubuntu:22.04\n\nENV DEBIAN_FRONTEND=noninteractive\nENV DEBIAN_PRIORITY=high\n\n# Install system dependencies\n"
},
{
"path": "browser-use-demo/NOTICE",
"chars": 291,
"preview": "This software includes components from the following open source projects:\n\nMicrosoft Playwright\nSource: https://github."
},
{
"path": "browser-use-demo/README.md",
"chars": 11951,
"preview": "# Browser Automation Demo with Claude\n\nA complete reference implementation for building browser automation with Claude u"
},
{
"path": "browser-use-demo/browser_use_demo/__init__.py",
"chars": 75,
"preview": "\"\"\"Browser Use Demo - Web automation with Claude.\"\"\"\n\n__version__ = \"0.1.0\""
},
{
"path": "browser-use-demo/browser_use_demo/browser_tool_utils/__init__.py",
"chars": 29,
"preview": "# Browser tool utility files\n"
},
{
"path": "browser-use-demo/browser_use_demo/browser_tool_utils/browser_dom_script.js",
"chars": 14265,
"preview": "/*\n * Modifications Copyright (c) 2025 Anthropic, PBC\n * Modified from original Microsoft Playwright source\n * Original "
},
{
"path": "browser-use-demo/browser_use_demo/browser_tool_utils/browser_element_script.js",
"chars": 3395,
"preview": "/*\n * Modifications Copyright (c) 2025 Anthropic, PBC\n * Modified from original Microsoft Playwright source\n * Original "
},
{
"path": "browser-use-demo/browser_use_demo/browser_tool_utils/browser_form_input_script.js",
"chars": 8919,
"preview": "// Script for setting values in form elements by their reference IDs\n\n(function(elementRef, inputValue) {\n try {\n "
},
{
"path": "browser-use-demo/browser_use_demo/browser_tool_utils/browser_key_map.py",
"chars": 7841,
"preview": "\"\"\"Key mapping for browser keyboard input via Chrome DevTools Protocol.\"\"\"\n\nfrom typing import TypedDict\n\n\nclass KeyInfo"
},
{
"path": "browser-use-demo/browser_use_demo/browser_tool_utils/browser_text_script.js",
"chars": 2254,
"preview": "// Script to extract raw text content from the page, prioritizing article content\n\n(function() {\n try {\n // Pr"
},
{
"path": "browser-use-demo/browser_use_demo/display_constants.py",
"chars": 394,
"preview": "\"\"\"Display and browser configuration constants.\n\nThese values are hardcoded and not configurable via environment variabl"
},
{
"path": "browser-use-demo/browser_use_demo/loop.py",
"chars": 7127,
"preview": "\"\"\"\nSampling loop for browser automation with Claude\n\"\"\"\n\nimport os\nfrom collections.abc import Callable\nfrom datetime i"
},
{
"path": "browser-use-demo/browser_use_demo/message_handler.py",
"chars": 8533,
"preview": "\"\"\"\nMessage handling abstractions for proper API response processing.\n\nThis module provides clean abstractions for proce"
},
{
"path": "browser-use-demo/browser_use_demo/message_renderer.py",
"chars": 10776,
"preview": "\"\"\"\nMessage rendering functionality for the Browser Use Demo.\n\nThis module handles all message rendering logic for the S"
},
{
"path": "browser-use-demo/browser_use_demo/requirements.txt",
"chars": 122,
"preview": "streamlit==1.41.0\nanthropic[bedrock,vertex]>=0.39.0\njsonschema==4.22.0\nboto3>=1.28.57\ngoogle-auth<3,>=2\nplaywright>=1.40"
},
{
"path": "browser-use-demo/browser_use_demo/streamlit.py",
"chars": 26317,
"preview": "\"\"\"\nBrowser Use Demo - Streamlit interface for browser automation with Claude\n\"\"\"\n\nimport asyncio\nimport base64\nimport i"
},
{
"path": "browser-use-demo/browser_use_demo/tools/__init__.py",
"chars": 247,
"preview": "from .base import BaseAnthropicTool, ToolError, ToolResult\nfrom .browser import BrowserTool\nfrom .collection import Tool"
},
{
"path": "browser-use-demo/browser_use_demo/tools/base.py",
"chars": 2013,
"preview": "from abc import ABCMeta, abstractmethod\nfrom dataclasses import dataclass, fields, replace\nfrom typing import Any\n\nfrom "
},
{
"path": "browser-use-demo/browser_use_demo/tools/browser.py",
"chars": 51659,
"preview": "# Modifications Copyright (c) 2025 Anthropic, PBC\n# Modified from original Microsoft Playwright source\n# Original Micros"
},
{
"path": "browser-use-demo/browser_use_demo/tools/collection.py",
"chars": 492,
"preview": "from typing import Any\n\nfrom anthropic.types.beta import BetaToolUnionParam\n\nfrom .base import BaseAnthropicTool\n\n\nclass"
},
{
"path": "browser-use-demo/browser_use_demo/tools/coordinate_scaling.py",
"chars": 6894,
"preview": "\"\"\"\nCoordinate scaling utilities for browser tool.\n\nThis module handles the scaling of coordinates from Claude's vision "
},
{
"path": "browser-use-demo/build.sh",
"chars": 557,
"preview": "#!/bin/bash\nset -e\n\necho \"🏗️ Building Browser Use Demo Docker image...\"\ndocker build . -t browser-use-demo:latest\n\necho"
},
{
"path": "browser-use-demo/docker-compose.yml",
"chars": 791,
"preview": "services:\n browser-use:\n build:\n context: .\n image: browser-use-demo:latest\n environment:\n - ANTHROP"
},
{
"path": "browser-use-demo/image/.config/tint2/applications/firefox-custom.desktop",
"chars": 182,
"preview": "[Desktop Entry]\nName=Firefox Custom\nComment=Open Firefox with custom URL\nExec=firefox-esr -new-window\nIcon=firefox-esr\nT"
},
{
"path": "browser-use-demo/image/.config/tint2/applications/gedit.desktop",
"chars": 138,
"preview": "[Desktop Entry]\nName=Gedit\nComment=Open gedit\nExec=gedit\nIcon=text-editor-symbolic\nTerminal=false\nType=Application\nCateg"
},
{
"path": "browser-use-demo/image/.config/tint2/applications/terminal.desktop",
"chars": 155,
"preview": "[Desktop Entry]\nName=Terminal\nComment=Open Terminal\nExec=xterm\nIcon=utilities-terminal\nTerminal=false\nType=Application\nC"
},
{
"path": "browser-use-demo/image/.config/tint2/tint2rc",
"chars": 2554,
"preview": "#-------------------------------------\n# Panel\npanel_items = TL\npanel_size = 100% 60\npanel_margin = 0 0\npanel_padding = "
},
{
"path": "browser-use-demo/image/entrypoint.sh",
"chars": 562,
"preview": "#!/bin/bash\nset -e\n\n# Validate environment configuration\necho \"Validating environment configuration...\"\npython validate_"
},
{
"path": "browser-use-demo/image/http_server.py",
"chars": 473,
"preview": "import os\nimport socket\nfrom http.server import HTTPServer, SimpleHTTPRequestHandler\n\n\nclass HTTPServerV6(HTTPServer):\n "
},
{
"path": "browser-use-demo/image/index.html",
"chars": 1108,
"preview": "<!doctype html>\n<html>\n <head>\n <title>Browser Use Demo</title>\n <meta name=\"permissions-policy\" conten"
},
{
"path": "browser-use-demo/image/mutter_startup.sh",
"chars": 446,
"preview": "echo \"starting mutter\"\nXDG_SESSION_TYPE=x11 mutter --replace --sm-disable 2>/tmp/mutter_stderr.log &\n\n# Wait for tint2 w"
},
{
"path": "browser-use-demo/image/novnc_startup.sh",
"chars": 415,
"preview": "#!/bin/bash\necho \"starting noVNC\"\n\n# Start noVNC with explicit websocket settings\n/opt/noVNC/utils/novnc_proxy \\\n --v"
},
{
"path": "browser-use-demo/image/start_all.sh",
"chars": 203,
"preview": "#!/bin/bash\n\nset -e\n\nexport DISPLAY=:${DISPLAY_NUM}\n./xvfb_startup.sh\n./tint2_startup.sh # Keep taskbar for window mana"
},
{
"path": "browser-use-demo/image/static_content/index.html",
"chars": 2458,
"preview": "<!doctype html>\n<html>\n <head>\n <title>Browser Use Demo</title>\n <meta name=\"permissions-policy\" conten"
},
{
"path": "browser-use-demo/image/tint2_startup.sh",
"chars": 544,
"preview": "#!/bin/bash\necho \"starting tint2 on display :$DISPLAY_NUM ...\"\n\n# Start tint2 and capture its stderr\ntint2 -c $HOME/.con"
},
{
"path": "browser-use-demo/image/x11vnc_startup.sh",
"chars": 957,
"preview": "#!/bin/bash\necho \"starting vnc\"\n\n(x11vnc -display $DISPLAY \\\n -forever \\\n -shared \\\n -wait 50 \\\n -rfbport 59"
},
{
"path": "browser-use-demo/image/xvfb_startup.sh",
"chars": 1554,
"preview": "#!/bin/bash\nset -e # Exit on error\n\nDPI=96\nRES_AND_DEPTH=${WIDTH}x${HEIGHT}x24\n\n# Function to check if Xvfb is already "
},
{
"path": "browser-use-demo/pytest.ini",
"chars": 711,
"preview": "[pytest]\n# Test discovery patterns\npython_files = test_*.py\npython_classes = Test*\npython_functions = test_*\n\n# Test dir"
},
{
"path": "browser-use-demo/setup.py",
"chars": 697,
"preview": "from setuptools import setup, find_packages\n\nsetup(\n name=\"browser-use-demo\",\n version=\"0.1.0\",\n packages=find_"
},
{
"path": "browser-use-demo/test-requirements.txt",
"chars": 74,
"preview": "pytest==8.3.3\npytest-cov==4.1.0\npytest-mock==3.11.1\npytest-asyncio==0.23.6"
},
{
"path": "browser-use-demo/tests/README.md",
"chars": 4830,
"preview": "# Browser Use Demo - Test Suite\n\nComprehensive test suite for the refactored Browser Use Demo with extensive edge case c"
},
{
"path": "browser-use-demo/tests/__init__.py",
"chars": 39,
"preview": "\"\"\"Test suite for Browser Use Demo.\"\"\"\n"
},
{
"path": "browser-use-demo/tests/conftest.py",
"chars": 9509,
"preview": "\"\"\"Shared fixtures and configuration for tests.\"\"\"\n\nimport asyncio\nimport sys\nfrom pathlib import Path\nfrom unittest.moc"
},
{
"path": "browser-use-demo/tests/test_integration.py",
"chars": 12943,
"preview": "\"\"\"Integration tests for the refactored Browser Use Demo.\"\"\"\n\nfrom unittest.mock import AsyncMock, MagicMock, Mock, patc"
},
{
"path": "browser-use-demo/tests/test_message_renderer.py",
"chars": 16063,
"preview": "\"\"\"Tests for MessageRenderer class with comprehensive edge case coverage.\"\"\"\n\nfrom unittest.mock import MagicMock, Mock,"
},
{
"path": "browser-use-demo/tests/test_sampling_loop.py",
"chars": 17528,
"preview": "\"\"\"\nComprehensive tests for the sampling loop and message handling.\n\"\"\"\n\nimport asyncio\nfrom unittest.mock import AsyncM"
},
{
"path": "browser-use-demo/tests/test_streamlit_helpers.py",
"chars": 11823,
"preview": "\"\"\"Tests for Streamlit helper functions with edge case coverage.\"\"\"\n\nimport asyncio\nfrom unittest.mock import MagicMock,"
},
{
"path": "browser-use-demo/validate_env.py",
"chars": 1755,
"preview": "#!/usr/bin/env python3\n\"\"\"Validate environment configuration before startup.\"\"\"\n\nimport os\nimport sys\nfrom pathlib impor"
},
{
"path": "computer-use-demo/.gitignore",
"chars": 44,
"preview": ".venv\n.ruff_cache\n__pycache__\n.pytest_cache\n"
},
{
"path": "computer-use-demo/.zed/settings.json",
"chars": 193,
"preview": "{\n \"preferred_line_length\": 88,\n \"languages\": {\n \"Python\": {\n \"language_servers\": [\"pyright\", \"ruff\"]\n }\n "
},
{
"path": "computer-use-demo/CONTRIBUTING.md",
"chars": 4114,
"preview": "# Contributing Guidelines\n\nThank you for your interest in contributing to the Computer Use Demo quickstart! This documen"
},
{
"path": "computer-use-demo/Dockerfile",
"chars": 2848,
"preview": "FROM docker.io/ubuntu:22.04\n\nENV DEBIAN_FRONTEND=noninteractive\nENV DEBIAN_PRIORITY=high\n\nRUN apt-get update && \\\n ap"
},
{
"path": "computer-use-demo/LICENSE",
"chars": 1055,
"preview": "Copyright 2024 Anthropic, PBC.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this sof"
},
{
"path": "computer-use-demo/README.md",
"chars": 9745,
"preview": "# Anthropic Computer Use Demo\n\n> [!NOTE]\n> Now featuring support for the new Claude 4 models! The latest Claude Opus 4.5"
},
{
"path": "computer-use-demo/computer_use_demo/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "computer-use-demo/computer_use_demo/loop.py",
"chars": 13121,
"preview": "\"\"\"\nAgentic sampling loop that calls the Claude API and local implementation of anthropic-defined computer use tools.\n\"\""
},
{
"path": "computer-use-demo/computer_use_demo/requirements.txt",
"chars": 104,
"preview": "streamlit==1.41.0\nanthropic[bedrock,vertex]>=0.39.0\njsonschema==4.22.0\nboto3>=1.28.57\ngoogle-auth<3,>=2\n"
},
{
"path": "computer-use-demo/computer_use_demo/streamlit.py",
"chars": 19518,
"preview": "\"\"\"\nEntrypoint for streamlit, see https://docs.streamlit.io/\n\"\"\"\n\nimport asyncio\nimport base64\nimport os\nimport subproce"
},
{
"path": "computer-use-demo/computer_use_demo/tools/__init__.py",
"chars": 517,
"preview": "from .base import CLIResult, ToolResult\nfrom .bash import BashTool20241022, BashTool20250124\nfrom .collection import Too"
},
{
"path": "computer-use-demo/computer_use_demo/tools/base.py",
"chars": 2013,
"preview": "from abc import ABCMeta, abstractmethod\nfrom dataclasses import dataclass, fields, replace\nfrom typing import Any\n\nfrom "
},
{
"path": "computer-use-demo/computer_use_demo/tools/bash.py",
"chars": 4857,
"preview": "import asyncio\nimport os\nfrom typing import Any, Literal\n\nfrom .base import BaseAnthropicTool, CLIResult, ToolError, Too"
},
{
"path": "computer-use-demo/computer_use_demo/tools/collection.py",
"chars": 972,
"preview": "\"\"\"Collection classes for managing multiple tools.\"\"\"\n\nfrom typing import Any, cast\n\nfrom anthropic.types.beta import Be"
},
{
"path": "computer-use-demo/computer_use_demo/tools/computer.py",
"chars": 18197,
"preview": "import asyncio\nimport base64\nimport os\nimport shlex\nimport shutil\nfrom enum import StrEnum\nfrom pathlib import Path\nfrom"
},
{
"path": "computer-use-demo/computer_use_demo/tools/edit.py",
"chars": 11264,
"preview": "import asyncio\nfrom collections import defaultdict\nfrom pathlib import Path\nfrom typing import Any, Literal, get_args\n\nf"
},
{
"path": "computer-use-demo/computer_use_demo/tools/groups.py",
"chars": 1576,
"preview": "from dataclasses import dataclass\nfrom typing import Literal\n\nfrom .base import BaseAnthropicTool\nfrom .bash import Bash"
},
{
"path": "computer-use-demo/computer_use_demo/tools/run.py",
"chars": 1595,
"preview": "\"\"\"Utility to run shell commands asynchronously with a timeout.\"\"\"\n\nimport asyncio\n\nTRUNCATED_MESSAGE: str = \"<response "
},
{
"path": "computer-use-demo/dev-requirements.txt",
"chars": 105,
"preview": "-r computer_use_demo/requirements.txt\nruff==0.6.7\npre-commit==3.8.0\npytest==8.3.3\npytest-asyncio==0.23.6\n"
},
{
"path": "computer-use-demo/image/.config/tint2/applications/firefox-custom.desktop",
"chars": 182,
"preview": "[Desktop Entry]\nName=Firefox Custom\nComment=Open Firefox with custom URL\nExec=firefox-esr -new-window\nIcon=firefox-esr\nT"
},
{
"path": "computer-use-demo/image/.config/tint2/applications/gedit.desktop",
"chars": 138,
"preview": "[Desktop Entry]\nName=Gedit\nComment=Open gedit\nExec=gedit\nIcon=text-editor-symbolic\nTerminal=false\nType=Application\nCateg"
},
{
"path": "computer-use-demo/image/.config/tint2/applications/terminal.desktop",
"chars": 155,
"preview": "[Desktop Entry]\nName=Terminal\nComment=Open Terminal\nExec=xterm\nIcon=utilities-terminal\nTerminal=false\nType=Application\nC"
},
{
"path": "computer-use-demo/image/.config/tint2/tint2rc",
"chars": 2554,
"preview": "#-------------------------------------\n# Panel\npanel_items = TL\npanel_size = 100% 60\npanel_margin = 0 0\npanel_padding = "
},
{
"path": "computer-use-demo/image/.streamlit/config.toml",
"chars": 87,
"preview": "[server]\nfileWatcherType = \"auto\"\nrunOnSave = true\n\n[browser]\ngatherUsageStats = false\n"
},
{
"path": "computer-use-demo/image/entrypoint.sh",
"chars": 369,
"preview": "#!/bin/bash\nset -e\n\n./start_all.sh\n./novnc_startup.sh\n\npython http_server.py > /tmp/server_logs.txt 2>&1 &\n\nSTREAMLIT_SE"
},
{
"path": "computer-use-demo/image/http_server.py",
"chars": 473,
"preview": "import os\nimport socket\nfrom http.server import HTTPServer, SimpleHTTPRequestHandler\n\n\nclass HTTPServerV6(HTTPServer):\n "
},
{
"path": "computer-use-demo/image/index.html",
"chars": 1109,
"preview": "<!doctype html>\n<html>\n <head>\n <title>Computer Use Demo</title>\n <meta name=\"permissions-policy\" conte"
},
{
"path": "computer-use-demo/image/mutter_startup.sh",
"chars": 446,
"preview": "echo \"starting mutter\"\nXDG_SESSION_TYPE=x11 mutter --replace --sm-disable 2>/tmp/mutter_stderr.log &\n\n# Wait for tint2 w"
},
{
"path": "computer-use-demo/image/novnc_startup.sh",
"chars": 415,
"preview": "#!/bin/bash\necho \"starting noVNC\"\n\n# Start noVNC with explicit websocket settings\n/opt/noVNC/utils/novnc_proxy \\\n --v"
},
{
"path": "computer-use-demo/image/start_all.sh",
"chars": 129,
"preview": "#!/bin/bash\n\nset -e\n\nexport DISPLAY=:${DISPLAY_NUM}\n./xvfb_startup.sh\n./tint2_startup.sh\n./mutter_startup.sh\n./x11vnc_st"
},
{
"path": "computer-use-demo/image/static_content/index.html",
"chars": 2459,
"preview": "<!doctype html>\n<html>\n <head>\n <title>Computer Use Demo</title>\n <meta name=\"permissions-policy\" conte"
},
{
"path": "computer-use-demo/image/tint2_startup.sh",
"chars": 544,
"preview": "#!/bin/bash\necho \"starting tint2 on display :$DISPLAY_NUM ...\"\n\n# Start tint2 and capture its stderr\ntint2 -c $HOME/.con"
},
{
"path": "computer-use-demo/image/x11vnc_startup.sh",
"chars": 957,
"preview": "#!/bin/bash\necho \"starting vnc\"\n\n(x11vnc -display $DISPLAY \\\n -forever \\\n -shared \\\n -wait 50 \\\n -rfbport 59"
},
{
"path": "computer-use-demo/image/xvfb_startup.sh",
"chars": 1118,
"preview": "#!/bin/bash\nset -e # Exit on error\n\nDPI=96\nRES_AND_DEPTH=${WIDTH}x${HEIGHT}x24\n\n# Function to check if Xvfb is already "
},
{
"path": "computer-use-demo/pyproject.toml",
"chars": 142,
"preview": "[tool.pyright]\nvenvPath = \".\"\nvenv = \".venv\"\nuseLibraryCodeForTypes = false\n\n[tool.pytest.ini_options]\npythonpath = \".\"\n"
},
{
"path": "computer-use-demo/ruff.toml",
"chars": 271,
"preview": "extend-exclude = [\".venv\"]\n\n[format]\ndocstring-code-format = true\n\n[lint]\nselect = [\n \"A\",\n \"ASYNC\",\n \"B\",\n "
},
{
"path": "computer-use-demo/setup.sh",
"chars": 862,
"preview": "#!/bin/bash\nPYTHON_MINOR_VERSION=$(python3 --version | awk -F. '{print $2}')\n\nif [ \"$PYTHON_MINOR_VERSION\" -gt 12 ]; the"
},
{
"path": "computer-use-demo/tests/conftest.py",
"chars": 235,
"preview": "import os\nfrom unittest import mock\n\nimport pytest\n\n\n@pytest.fixture(autouse=True)\ndef mock_screen_dimensions():\n wit"
},
{
"path": "computer-use-demo/tests/loop_test.py",
"chars": 2538,
"preview": "from unittest import mock\n\nfrom anthropic.types import TextBlock, ToolUseBlock\nfrom anthropic.types.beta import BetaMess"
},
{
"path": "computer-use-demo/tests/streamlit_test.py",
"chars": 821,
"preview": "from unittest import mock\n\nimport pytest\nfrom anthropic.types import TextBlockParam\nfrom streamlit.testing.v1 import App"
},
{
"path": "computer-use-demo/tests/tools/bash_test.py",
"chars": 2227,
"preview": "import pytest\n\nfrom computer_use_demo.tools.bash import BashTool20241022, BashTool20250124, ToolError\n\n\n@pytest.fixture("
},
{
"path": "computer-use-demo/tests/tools/computer_test.py",
"chars": 4763,
"preview": "from unittest.mock import AsyncMock, patch\n\nimport pytest\n\nfrom computer_use_demo.tools.computer import (\n ComputerTo"
},
{
"path": "computer-use-demo/tests/tools/edit_test.py",
"chars": 11842,
"preview": "from pathlib import Path\nfrom unittest.mock import patch\n\nimport pytest\n\nfrom computer_use_demo.tools.base import CLIRes"
},
{
"path": "customer-support-agent/.eslintrc.json",
"chars": 40,
"preview": "{\n \"extends\": \"next/core-web-vitals\"\n}\n"
},
{
"path": "customer-support-agent/.gitignore",
"chars": 391,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "customer-support-agent/README.md",
"chars": 10582,
"preview": "# Claude Customer Support Agent\n\nAn advanced, fully customizable customer support chat interface powered by Claude and l"
},
{
"path": "customer-support-agent/amplify.yml",
"chars": 573,
"preview": "version: 1\nfrontend:\n phases:\n preBuild:\n commands:\n - npm ci --cache .npm --prefer-offline\n build:\n "
},
{
"path": "customer-support-agent/app/api/chat/route.ts",
"chars": 10880,
"preview": "import Anthropic from \"@anthropic-ai/sdk\";\nimport { z } from \"zod\";\nimport { retrieveContext, RAGSource } from \"@/app/li"
},
{
"path": "customer-support-agent/app/globals.css",
"chars": 1878,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n "
},
{
"path": "customer-support-agent/app/layout.tsx",
"chars": 827,
"preview": "import React from \"react\";\nimport type { Metadata } from \"next\";\nimport { Inter } from \"next/font/google\";\nimport \"./glo"
},
{
"path": "customer-support-agent/app/lib/customer_support_categories.json",
"chars": 1654,
"preview": "{\n \"categories\": [\n {\n \"id\": \"account\",\n \"name\": \"Account\",\n \"keywords\": [\n \"ban\",\n \"ap"
},
{
"path": "customer-support-agent/app/lib/utils.ts",
"chars": 2629,
"preview": "import {\n BedrockAgentRuntimeClient,\n RetrieveCommand,\n RetrieveCommandInput,\n} from \"@aws-sdk/client-bedrock-agent-r"
},
{
"path": "customer-support-agent/app/page.tsx",
"chars": 720,
"preview": "import React from \"react\";\nimport dynamic from \"next/dynamic\";\nimport TopNavBar from \"@/components/TopNavBar\";\nimport Ch"
},
{
"path": "customer-support-agent/components/ChatArea.tsx",
"chars": 22174,
"preview": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport config from \"@/config\";\nimport { Button } fro"
},
{
"path": "customer-support-agent/components/FullSourceModal.tsx",
"chars": 1257,
"preview": "import React from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport {\n Dialog,\n DialogContent,\n DialogHeade"
},
{
"path": "customer-support-agent/components/LeftSidebar.tsx",
"chars": 6742,
"preview": "\"use client\";\n\nimport React, { useState, useEffect } from \"react\";\nimport { Card, CardHeader, CardTitle, CardContent } f"
},
{
"path": "customer-support-agent/components/RightSidebar.tsx",
"chars": 6505,
"preview": "\"use client\";\n\nimport React, { useState, useEffect } from \"react\";\nimport { Card, CardHeader, CardTitle, CardContent } f"
},
{
"path": "customer-support-agent/components/TopNavBar.tsx",
"chars": 4867,
"preview": "\"use client\";\nimport React, { useState, useEffect } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimpor"
},
{
"path": "customer-support-agent/components/theme-provider.tsx",
"chars": 332,
"preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ThemeProvider as NextThemesProvider } from \"next-themes\";\nimport"
},
{
"path": "customer-support-agent/components/ui/avatar.tsx",
"chars": 1423,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } fr"
},
{
"path": "customer-support-agent/components/ui/button.tsx",
"chars": 1852,
"preview": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"cla"
},
{
"path": "customer-support-agent/components/ui/card.tsx",
"chars": 1911,
"preview": "import * as React from \"react\";\n\nimport { cn } from \"@/app/lib/utils\";\n\nconst Card = React.forwardRef<\n HTMLDivElement,"
},
{
"path": "customer-support-agent/components/ui/dialog.tsx",
"chars": 3849,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from"
},
{
"path": "customer-support-agent/components/ui/dropdown-menu.tsx",
"chars": 7309,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimpo"
},
{
"path": "customer-support-agent/components/ui/input.tsx",
"chars": 828,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/app/lib/utils\"\n\nexport interface InputProps\n extends React.InputH"
},
{
"path": "customer-support-agent/components/ui/textarea.tsx",
"chars": 772,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface TextareaProps\n extends React.Textare"
},
{
"path": "customer-support-agent/components.json",
"chars": 343,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"default\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {\n"
},
{
"path": "customer-support-agent/config.ts",
"chars": 295,
"preview": "type Config = {\n includeLeftSidebar: boolean;\n includeRightSidebar: boolean;\n};\n\nconst config: Config = {\n includeLef"
},
{
"path": "customer-support-agent/lib/utils.ts",
"chars": 169,
"preview": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: C"
},
{
"path": "customer-support-agent/next.config.mjs",
"chars": 435,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n experimental: {\n serverComponentsExternalPackages: [\""
},
{
"path": "customer-support-agent/package.json",
"chars": 2038,
"preview": "{\n \"name\": \"assistant\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"engines\": {\n \"node\": \">=18.17.0\"\n },\n \"scripts"
},
{
"path": "customer-support-agent/postcss.config.mjs",
"chars": 135,
"preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n plugins: {\n tailwindcss: {},\n },\n};\n\nexport d"
},
{
"path": "customer-support-agent/styles/themes.js",
"chars": 12404,
"preview": "// themes.ts\n\nexport const themes = {\n neutral: {\n light: {\n background: \"0 0% 100%\",\n foreground: \"0 0% 3"
},
{
"path": "customer-support-agent/tailwind.config.ts",
"chars": 2835,
"preview": "import type { Config } from \"tailwindcss\";\n\nconst config = {\n darkMode: [\"class\"],\n content: [\n \"./pages/**/*.{ts,t"
},
{
"path": "customer-support-agent/tsconfig.json",
"chars": 574,
"preview": "{\n \"compilerOptions\": {\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n "
},
{
"path": "financial-data-analyst/.eslintrc.json",
"chars": 128,
"preview": "{\n \"extends\": \"next\",\n \"rules\": {\n \"react/no-unescaped-entities\": \"off\",\n \"@next/next/no-page-custom-font\": \"off"
},
{
"path": "financial-data-analyst/.gitignore",
"chars": 391,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "financial-data-analyst/README.md",
"chars": 3434,
"preview": "# Claude Financial Data Analyst\n\n\n\nA sophisticated Next.js application that combines Claude's ca"
},
{
"path": "financial-data-analyst/app/api/finance/route.ts",
"chars": 13097,
"preview": "// app/api/finance/route.ts\nimport { NextRequest } from \"next/server\";\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport"
},
{
"path": "financial-data-analyst/app/finance/page.tsx",
"chars": 23795,
"preview": "// app/finance/page.tsx\n\"use client\";\n\nimport React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport {"
},
{
"path": "financial-data-analyst/app/globals.css",
"chars": 2738,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n font-family: Arial, Helvetica, sans-serif;\n}\n\n@la"
},
{
"path": "financial-data-analyst/app/layout.tsx",
"chars": 1080,
"preview": "import type { Metadata } from \"next\";\nimport localFont from \"next/font/local\";\nimport { ThemeProvider } from \"@/componen"
},
{
"path": "financial-data-analyst/app/page.tsx",
"chars": 163,
"preview": "// /app/page.tsx\n\"use client\";\n\nimport React from \"react\";\nimport FinancePage from \"./finance/page\";\n\nexport default fun"
},
{
"path": "financial-data-analyst/components/ChartRenderer.tsx",
"chars": 12451,
"preview": "\"use client\";\n\nimport React from \"react\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,"
},
{
"path": "financial-data-analyst/components/FilePreview.tsx",
"chars": 2478,
"preview": "import React from \"react\";\nimport { X, FileText } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nim"
},
{
"path": "financial-data-analyst/components/TopNavBar.tsx",
"chars": 2228,
"preview": "\"use client\";\nimport React, { useState, useEffect } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimpor"
},
{
"path": "financial-data-analyst/components/theme-provider.tsx",
"chars": 365,
"preview": "// components/theme-provider.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { ThemeProvider as NextThemesProv"
},
{
"path": "financial-data-analyst/components/ui/avatar.tsx",
"chars": 1419,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } fr"
},
{
"path": "financial-data-analyst/components/ui/badge.tsx",
"chars": 1140,
"preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/"
},
{
"path": "financial-data-analyst/components/ui/button.tsx",
"chars": 1836,
"preview": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class"
},
{
"path": "financial-data-analyst/components/ui/card.tsx",
"chars": 1847,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Card = React.forwardRef<\n HTMLDivElement,\n Rea"
},
{
"path": "financial-data-analyst/components/ui/chart.tsx",
"chars": 10581,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as RechartsPrimitive from \"recharts\"\nimport {\n NameType,\n Payloa"
},
{
"path": "financial-data-analyst/components/ui/dropdown-menu.tsx",
"chars": 7366,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimpo"
},
{
"path": "financial-data-analyst/components/ui/textarea.tsx",
"chars": 732,
"preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface TextareaProps\n extends React.Textare"
},
{
"path": "financial-data-analyst/components/ui/toast.tsx",
"chars": 4859,
"preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Cross2Icon } from \"@radix-ui/react-icons\"\nimport * as ToastPrimiti"
},
{
"path": "financial-data-analyst/components/ui/toaster.tsx",
"chars": 786,
"preview": "\"use client\"\n\nimport { useToast } from \"@/hooks/use-toast\"\nimport {\n Toast,\n ToastClose,\n ToastDescription,\n ToastPr"
},
{
"path": "financial-data-analyst/components.json",
"chars": 415,
"preview": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {"
},
{
"path": "financial-data-analyst/hooks/use-toast.ts",
"chars": 3948,
"preview": "\"use client\"\n\n// Inspired by react-hot-toast library\nimport * as React from \"react\"\n\nimport type {\n ToastActionElement,"
},
{
"path": "financial-data-analyst/lib/utils.ts",
"chars": 166,
"preview": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: Cla"
},
{
"path": "financial-data-analyst/next.config.mjs",
"chars": 92,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nexport default nextConfig;\n"
},
{
"path": "financial-data-analyst/package.json",
"chars": 1020,
"preview": "{\n \"name\": \"financial-assistant\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"b"
},
{
"path": "financial-data-analyst/postcss.config.mjs",
"chars": 135,
"preview": "/** @type {import('postcss-load-config').Config} */\nconst config = {\n plugins: {\n tailwindcss: {},\n },\n};\n\nexport d"
},
{
"path": "financial-data-analyst/tailwind.config.ts",
"chars": 1656,
"preview": "import type { Config } from \"tailwindcss\";\n\nconst config: Config = {\n darkMode: [\"class\"],\n content: [\n \"./page"
},
{
"path": "financial-data-analyst/tsconfig.json",
"chars": 574,
"preview": "{\n \"compilerOptions\": {\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n "
},
{
"path": "financial-data-analyst/types/chart.ts",
"chars": 517,
"preview": "// types/chart.ts\nexport interface ChartConfig {\n [key: string]: {\n label: string;\n stacked?: boolean;\n color?"
},
{
"path": "financial-data-analyst/utils/fileHandling.ts",
"chars": 2827,
"preview": "// utils/fileHandling.ts\nexport const readFileAsText = (file: File): Promise<string> => {\n return new Promise((resolve,"
},
{
"path": "pyproject.toml",
"chars": 91,
"preview": "[tool.pyright]\nvenvPath = \"computer-use-demo\"\nvenv = \".venv\"\nuseLibraryCodeForTypes = false"
}
]
About this extraction
This page contains the full source code of the anthropics/claude-quickstarts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 197 files (761.7 KB), approximately 186.3k tokens, and a symbol index with 506 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.