# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.

## Project Overview

ConnectOnion is a Python framework for creating AI agents with automatic activity logging, interactive debugging, and multi-agent collaboration. Philosophy: **"Keep simple things simple, make complicated things possible"** - simple 2-line agent creation, but production-ready with trust verification, event system, and plugin architecture.

## Architecture

### Core Components

- **Agent** (`connectonion/agent.py:29`): Main orchestrator with LLM integration, tool execution, event system, and trust verification
- **LLM** (`connectonion/llm.py:11`): Unified abstraction supporting OpenAI, Anthropic, Gemini, and managed keys via factory pattern
- **Tool Executor** (`connectonion/tool_executor.py:24`): Executes tools with xray context injection, timing, error handling, and trace recording
- **Tool Factory** (`connectonion/tool_factory.py`): Converts Python functions to OpenAI-compatible tool schemas automatically
- **Logger** (`connectonion/logger.py`): Unified logging facade (terminal + plain text + YAML sessions) with `quiet` and `log` parameters
- **Console** (`connectonion/console.py`): Low-level terminal output with Rich formatting (used internally by Logger)
- **Events** (`connectonion/events.py:11`): Lifecycle hooks (after_user_input, before_llm, after_llm, before_each_tool, before_tools, after_each_tool, after_tools, on_error, on_complete, on_stop_signal)
- **Trust System** (`connectonion/network/trust/`): Three-level verification (open/careful/strict) with custom policy support
- **XRay Debug** (`connectonion/xray.py:32`): Runtime context injection for interactive debugging with `@xray` decorator

### Key Design Patterns

#### Tool System
- **Function-based (recommended)**: Regular Python functions auto-convert to tools via type hints and docstrings
- **Class-based (legacy)**: Inherit from `Tool` base class with explicit schemas
- Auto-conversion: `create_tool_from_function()` inspects signatures and generates OpenAI schemas

#### Agent Execution Loop (`connectonion/agent.py:input()`)
1. Initialize/extend session with user input
2. Fire `after_user_input` event
3. Loop (max_iterations times):
   - Fire `before_llm` event
   - Call LLM with messages and tool schemas
   - Fire `after_llm` event
   - If tool_calls: execute via `tool_executor.execute_and_record_tools()`
   - Fire `before_tools` event ONCE before ALL tools execute
   - Fire `before_each_tool` and `after_each_tool` events per individual tool
   - Fire `after_tools` event ONCE after ALL tools complete (safe for adding messages)
   - Add results to messages, continue
4. Return final response or iteration limit message

#### Multi-LLM Provider Architecture (`connectonion/llm.py`)
- Factory pattern: `create_llm(model, api_key)` routes to provider classes
- OpenAI format as lingua franca (all providers convert to/from)
- Structured output: Each provider uses native API (OpenAI's `parse()`, Anthropic's forced tool calling, Gemini's `response_schema`)
- Tool calling: Unified `ToolCall` dataclass format across all providers

#### Event System & Plugins (`connectonion/events.py`)
- Wrapper functions tag handlers with `_event_type` attribute
- Plugins are lists of event handlers bundled together
- Handlers receive `agent` instance, can modify `current_session`
- Built-in plugins: reflection, ReAct, image_result_formatter

#### Trust Verification (`connectonion/network/trust/`)
- Three levels: "open" (dev), "careful" (staging), "strict" (prod)
- Custom policies: markdown files or inline text describing verification rules
- Custom agents: Pass your own Agent instance with verification tools
- Environment-based defaults: `CONNECTONION_ENV` sets trust level automatically
- Module structure: `factory.py` (creation), `prompts.py` (level prompts), `tools.py` (verification tools), `trust_agent.py` (TrustAgent class)
- Onboard methods: `invite_code` (verify against configured codes), `payment` (verify via oo-api credit transfer)
- Payment verification: `TrustAgent.verify_payment()` calls oo-api `/api/v1/onboard/verify` to check for recent transfers

#### XRay Debugging (`connectonion/xray.py`)
- `@xray` decorator injects context: `xray.agent`, `xray.task`, `xray.messages`, `xray.iteration`
- `xray.trace()` displays formatted execution history
- `inject_xray_context()` in `tool_executor.py:24` provides runtime context
- Enables interactive debugging with `agent.auto_debug()`

## Development Commands

### Installation
```bash
pip install -r requirements.txt
pip install -e .  # Development mode
```

### Testing
```bash
# Run all tests except real API calls (default)
python -m pytest

# Run specific test categories
python -m pytest tests/unit           # Unit tests (fast, mocked)
python -m pytest tests/integration    # Integration tests (no external APIs)
python -m pytest tests/cli            # CLI tests
python -m pytest tests/e2e            # End-to-end example agent

# Run real API tests (requires keys)
python -m pytest tests/real_api -m real_api

# Run with coverage
python -m pytest --cov=connectonion --cov-report=term-missing

# Run single test file
python -m pytest tests/unit/test_agent.py
python -m pytest tests/unit/test_agent.py::test_specific_function
```

### CLI Commands
```bash
# Create new agent project
co create my-agent                    # The co-ai template (default)
co create my-agent --template custom --description "..."   # AI writes agent.py

# Available templates: co-ai (default), custom
# One template on purpose: it is the same agent `co ai` runs, and you
# specialise it with skills in .co/skills/ rather than a different skeleton.

# Initialize global credentials, or explicitly select a project
co init                               # Global ~/.co/keys.env only
co init ./                            # Project config, .env, and docs
co init ./ --template co-ai            # Add full template

# Authentication (for managed keys)
co auth login                         # Interactive login
co auth status                        # Check auth status
co auth logout                        # Logout

# OAuth integrations (for email/calendar tools)
co auth google                        # Connect Google (Gmail, Calendar)
co auth microsoft                     # Connect Microsoft (Outlook, Calendar)

# Browser automation
co browser                            # Launch browser agent

# Diagnostics
co doctor                             # Check installation
co status                             # Show project status
```

### Building & Publishing
```bash
# Validate the exact candidate locally (see VERSIONING.md)
python -m build
python -m twine check dist/connectonion-X.Y.Z.tar.gz dist/connectonion-X.Y.Z-py3-none-any.whl

# Normal publication is tag-driven and uses PyPI Trusted Publishing.
# Merge the reviewed version commit, tag that immutable commit, push the tag,
# then wait for the pinned release workflow. Never publish from a workstation.
git fetch origin
git tag -a vX.Y.Z <reviewed-merge-commit> -m "Release vX.Y.Z"
git push origin vX.Y.Z
gh run list --workflow release.yml --limit 1
```

## Project Structure

```
connectonion/
├── connectonion/
│   ├── __init__.py                 # Main exports
│   ├── agent.py                    # Agent class with event system
│   ├── llm.py                      # Multi-provider LLM abstraction
│   ├── tool_executor.py            # Tool execution with xray
│   ├── tool_factory.py             # Function → tool conversion
│   ├── logger.py                   # Unified logging facade (terminal + file + YAML sessions)
│   ├── console.py                  # Low-level terminal output with Rich
│   ├── events.py                   # Event system
│   ├── network/
│   │   ├── trust/                  # Trust verification system
│   │   │   ├── factory.py          # Trust agent creation
│   │   │   ├── prompts.py          # Trust level prompts
│   │   │   └── tools.py            # Verification tools
│   ├── xray.py                     # XRay debugging
│   ├── decorators.py               # @replay, @xray_replay
│   ├── llm_do.py                   # One-shot LLM function
│   ├── prompts.py                  # Prompt loading utilities
│   ├── connect.py                  # Multi-agent networking
│   ├── relay.py                    # Agent relay server
│   ├── announce.py                 # Service announcement
│   ├── address.py                  # Agent addressing
│   ├── auto_debug_exception.py     # Exception debugging
│   ├── cli/
│   │   ├── main.py                 # CLI entry point
│   │   ├── commands/               # CLI command implementations
│   │   └── templates/              # Agent templates
│   │       ├── minimal/
│   │       ├── coder/
│   │       ├── browser/
│   │       └── web-research/
│   ├── useful_tools/               # Built-in tools
│   │   ├── send_email.py
│   │   └── get_emails.py
│   ├── useful_plugins/             # Built-in plugins
│   │   ├── reflection.py
│   │   ├── react.py
│   │   └── image_result_formatter.py
│   ├── debug_agent/                # Interactive debugger
│   ├── debug_explainer/            # Debug explanation agent
│   └── execution_analyzer/         # Execution analysis
├── tests/
│   ├── unit/                       # Fast, isolated tests
│   ├── integration/                # Multi-component tests
│   ├── real_api/                   # Tests requiring API keys
│   ├── cli/                        # CLI command tests
│   └── e2e/                        # End-to-end examples
├── docs/                           # Markdown documentation
├── wiki/                           # GitHub Wiki (nested repo)
├── docs-site/                      # Next.js docs site (nested repo, private)
├── examples/                       # Example agents
├── prompts/                        # System prompt templates
├── setup.py                        # Package configuration
├── pytest.ini                      # Test configuration
└── requirements.txt                # Dependencies
```

## Key Implementation Details

### Agent Session Management (`connectonion/agent.py`)
- `current_session`: Runtime-only context with `messages`, `trace`, `turn`, `iteration`
- Session persists across turns for multi-turn conversations
- `tools`: ToolRegistry with O(1) lookup via `.get()` or attribute access (`agent.tools.tool_name`)
- Class instances accessible via `agent.tools.instance_name` (e.g., `agent.tools.gmail`)
- Default model: `co/o4-mini` (managed keys via OpenOnion proxy)

### LLM Provider Routing (`connectonion/llm.py:create_llm()`)
- Model prefix determines provider:
  - `gpt-*` → OpenAI
  - `Codex-*` or `anthropic.*` → Anthropic
  - `gemini-*` or `models/gemini-*` → Google
  - `co/*` → OpenOnion managed keys (OpenAI proxy)
- API keys from environment or parameter
- Structured output via provider-native APIs

### Tool Execution Flow (`connectonion/tool_executor.py`)
1. Add assistant message with tool_calls to session
2. For each tool:
   - `inject_xray_context()` provides runtime context
   - If tool has `_needs_agent` flag, inject `agent` into tool args (for IO access)
   - Execute tool function with arguments
   - Record timing and result in trace
   - Clear xray context
   - Add tool result message
3. Fire `before_each_tool` and `after_each_tool` events per tool, then `after_tools` once after all
4. Handle errors: capture in trace, return to LLM for retry

### Agent Injection for Tools
Tools that need access to `agent.io` (for frontend communication) declare `agent` as a parameter:
- `tool_factory` detects `agent` in signature → skips it from LLM schema, sets `_needs_agent=True`
- `tool_executor` checks `_needs_agent` flag → injects `agent` at call time
- Used by: `ask_user`, `DiffWriter.write()` (approval flow)
- The LLM never sees the `agent` parameter

### Trust Verification (`connectonion/network/trust/`)
- Environment defaults: `CONNECTONION_ENV=development` → trust="open"
- Custom policies loaded from markdown files or inline strings
- Trust agent created lazily when trust parameter provided
- Prevents infinite recursion: trust agents don't have their own trust agents
- Files: `factory.py` (create_trust_agent), `fast_rules.py` (parse_policy, evaluate_request), `tools.py` (is_whitelisted, is_blocked, promote_to_contact)
- Policy files: `prompts/trust/{open,careful,strict}.md` with YAML frontmatter for fast rules

### XRay Context Injection (`connectonion/xray.py`)
- Stores context in `builtins.xray` global object
- Thread-safe via thread-local storage
- Tools access: `xray.agent`, `xray.task`, `xray.messages`, `xray.iteration`, `xray.previous_tools`
- `xray.trace()` displays Rich-formatted execution history

### Logging System (`connectonion/logger.py`)
- **Logger class**: Unified facade for terminal output + plain text + YAML sessions
- **Console output**: Rich-formatted terminal output (unless `quiet=True`)
- **Plain text logs**: `.co/logs/{agent_name}.log` (automatic audit trail)
- **YAML sessions**: `.co/evals/{agent_name}_{timestamp}.yaml` (for eval/replay)
- **Parameters**:
  - `quiet=True`: Suppress console output, keep session logging
  - `log=False`: Disable all logging (console still shows)
  - `log="path/to/file.log"`: Custom log file path
- Environment variable: `CONNECTONION_LOG` (highest priority)
- Session format: Per-turn tracking with input, model, duration_ms, tokens, cost, tools_called, result, messages (JSON)
- Use cases:
  - Development (default): `Agent("name")` - everything on
  - Eval/testing: `Agent("name", quiet=True)` - sessions only
  - Benchmarking: `Agent("name", log=False)` - nothing

## Test Organization

### Folder Structure (see `tests/TEST_ORGANIZATION.md`)
- `tests/unit/` - One test file per source file, mocked dependencies, fast (<1s)
- `tests/integration/` - Multi-component tests, no external APIs, medium speed
- `tests/real_api/` - Actual API calls, requires keys, slow (5-30s), costs money
- `tests/cli/` - CLI command tests, file system operations
- `tests/e2e/` - Complete example agent (living documentation)

### Running Tests
```bash
# Fast feedback loop (unit + integration only)
pytest -m "not real_api"

# Test specific module
pytest tests/unit/test_agent.py

# Real API tests (set API keys first)
export OPENAI_API_KEY=sk-...
pytest tests/real_api/test_real_openai.py -m real_api

# All tests except real API (default, for CI)
pytest
```

### Test Markers
- `@pytest.mark.unit` - Unit tests
- `@pytest.mark.integration` - Integration tests
- `@pytest.mark.real_api` - Requires API keys
- `@pytest.mark.cli` - CLI tests
- `@pytest.mark.slow` - Takes >10 seconds
- `@pytest.mark.network` - Requires network/relay

## Documentation Architecture

### Three-Layer Strategy
1. **GitHub Wiki** (public, SEO-focused): Nested repo at `wiki/`, targets search keywords
2. **Docs Website** (private during dev): Nested repo at `docs-site/`, Next.js site at https://docs.connectonion.com
3. **Main Repo Docs** (public): Markdown files in `docs/` folder

### Nested Repository Pattern
Both wiki and docs-site are separate Git repositories inside the main repo:

```bash
# Wiki editing (public)
cd wiki/
git add .
git commit -m "Update tutorial"
git push origin master

# Docs site editing (private)
cd docs-site/
git add .
git commit -m "Update agent page"
git push origin main

# Main repo (ignores both)
cd connectonion/
git add .
git commit -m "Add feature"
git push
```

**Important:** Both `wiki/` and `docs-site/` are in `.gitignore` - they are independent repos.

### Documentation Guidelines
- Start with minimal examples, progressively add complexity
- Show real working code with actual output
- One concept per page (progressive disclosure)
- Mobile-friendly, scannable structure
- Command blocks use `CommandBlock` component (no $ in copy text)
- Each page has "copy all as markdown" button
- Meaningful feature-train launches, phase promotions, stable releases, and
  material architecture or workflow decisions need a public Design Journal
  entry. Draft it with the change, record alternatives and tradeoffs, and
  publish it only after any claimed package and GitHub Release are visible.
- Maintenance-only patches need release notes, not a thin duplicate blog post,
  unless they contain a reusable design lesson.

## Common Development Tasks

### Adding a New Tool
1. Write function with type hints and docstring
2. Pass to Agent: `agent = Agent("name", tools=[my_tool])`
3. Tool auto-converts via `create_tool_from_function()`

### Adding a New Event Handler
1. Import wrapper: `from connectonion import after_tools`
2. Define handler: `def my_handler(agent): ...`
3. Register: `agent = Agent("name", on_events=[after_tools(my_handler)])`

### Creating a Plugin
1. Define event handlers
2. Bundle in list: `my_plugin = [after_tools(handler1), before_llm(handler2)]`
3. Use: `agent = Agent("name", plugins=[my_plugin])`

### Adding a CLI Command
1. Create command in `connectonion/cli/commands/`
2. Import in `connectonion/cli/main.py`
3. Add to CLI group with `@cli.command()`

### Adding a CLI Template
1. Create folder in `connectonion/cli/templates/{template-name}/`
2. Add `agent.py`, `.env.example`, prompt files
3. Update `setup.py` package_data to include template files
4. Test: `co create test-project --template {template-name}`

### Adding LLM Provider Support
1. Implement class inheriting from `LLM` in `connectonion/llm.py`
2. Implement `complete()` and `structured_complete()` methods
3. Add routing logic to `create_llm()` factory function
4. Add tests in `tests/real_api/test_real_{provider}.py`

## Version Numbering Strategy

**Current candidate:** 1.7.0a2 (Preview). **Stable:** 1.6.4.

Use SemVer with PEP 440 preview suffixes. Patch numbers do not roll over, and a
whole-number release is earned by completed end-to-end evidence rather than by
a counter. See `VERSIONING.md` for the authoritative rules and checklist. A
release updates `_version.py`, `pyproject.toml`, `VERSIONING.md`, `uv.lock`, and
the matching docs-site channel, then publishes only from the reviewed exact tag.

## Philosophy & Principles

### Core Philosophy
**"Keep simple things simple, make complicated things possible"**
- Simple: 2-line agent creation (`Agent("name").input("query")`)
- Complicated: Trust verification, multi-agent networking, custom LLM providers, plugin system

### Design Principles
1. **Function as Primitive**: Everything is a function (agents, tools, trust, events)
2. **The 100-Line Test**: If a feature needs >100 lines, it's too complex
3. **Behavior Over Identity**: Trust earned through action, not authority
4. **Simplicity Enables Robustness**: Complex systems are fragile

### Code Quality Standards
- **Single responsibility** per function/class
- **Avoid premature abstractions** - wait until you need it 3 times
- **No clever tricks** - choose the boring solution
- **Explicit over implicit** - clear data flow and dependencies
- **Test behavior, not implementation**

### Error Handling Philosophy
- **Fail fast** with descriptive messages
- **Include context** for debugging
- **Let errors bubble** to agent for retry
- **Never silently swallow exceptions**
- **No try-except-pass** unless explicitly required

### Custom Exceptions

**InsufficientCreditsError** (`connectonion/core/exceptions.py`):
- Raised by `OpenOnionLLM` when account has insufficient ConnectOnion credits
- Transforms API 402 errors into beautiful, actionable error messages
- Provides typed attributes: `balance`, `required`, `shortfall`, `address`, `public_key`
- Example:
  ```python
  try:
      agent = Agent("my_agent", model="co/gemini-3.7-flash")
      response = agent.input("Hello")
  except InsufficientCreditsError as e:
      print(f"Need ${e.shortfall:.4f} more credits")
      print(f"Account: {e.address}")
      # Join Discord or run 'co status' to add credits
  ```

### Process Guidelines
1. **Planning**: Break complex work into 3-5 stages in IMPLEMENTATION_PLAN.md
2. **Implementation**: Test first (red) → Minimal code (green) → Refactor → Commit
3. **When Stuck**: Max 3 attempts, then document failures and try different approach
4. **Definition of Done**: Tests pass, follows conventions, no TODOs, documentation updated
5. **Independent experience review**: After each update round, release or trial,
   use a separate reviewer with a marketing/UI technology-founder perspective.
   Inspect rendered page structure, content and design on desktop and mobile,
   including value clarity, evidence, next actions, navigation and interaction
   states. Record prioritized issues, user impact, evidence and recheck criteria;
   fix significant issues in scope and inspect the result again. Name the pages
   and states actually reviewed; sample coverage is not an all-page pass. An AI
   role review must not be described as a real human founder's review.

## Important Reminders

### NEVER
- Use `--no-verify` to bypass commit hooks
- Disable tests instead of fixing them
- Commit code that doesn't compile
- Add try-except-pass without explicit requirement
- Use utils.py or helper.py (keep helpers with features)

### ALWAYS
- Commit working code incrementally
- Let programs crash to see errors (avoid try-except)
- Keep functions under 30 lines when possible
- Use existing test patterns from codebase
- Remind user to update documentation for user-facing features:
  - GitHub Wiki: `cd wiki/ && git commit && git push`
  - Docs Website: `cd docs-site/ && git commit && git push`
  - Main docs: `docs/` folder

### Code Organization Preferences
- Avoid `utils.py` - keep helper functions with their features
- Default model for agents: `co/o4-mini` (production), `o4-mini` (testing)
- No Co-Authored-By lines in commit messages (no Codex, Happy, or other brand attribution)
- Function-based tools over class-based tools
- Events/plugins over subclassing Agent

## Community & Support

- **Documentation**: https://docs.connectonion.com
- **Discord**: https://discord.gg/4xfD9k8AUF
- **GitHub**: https://github.com/openonion/connectonion
- **PyPI**: https://pypi.org/project/connectonion/
