Metadata-Version: 2.4
Name: mini-harness
Version: 1.0.1
Summary: A minimal Agent Harness core — ReAct loop + tool registry + permission model
Project-URL: Repository, https://github.com/JIANGXIADADAO/mini-harness
License-Expression: MIT
Requires-Python: >=3.10
Requires-Dist: httpx>=0.28.0
Description-Content-Type: text/markdown

# Mini Harness

> A minimal Agent Harness core — the smallest complete implementation of an agent runtime.
> **1001 lines of Python. 49 tests. DeepSeek-first.**

ReAct agent loop, streaming tool-call delta concatenation, 7-tool registry,
3-tier permission model, checkpoint/resume, step-level observability.

## Demo

See [demos/](demos/) — screenshots and HTML terminal replay covering all 5 features.

## What is this?

The "brain + body" of an AI agent — the runtime shell that turns a raw LLM
into a tool-using, permission-gated, controllable agent.

**Does**:
- ReAct agent loop with streaming (think → act → observe → repeat)
- 7 tools: read, write, edit, grep, glob, bash, fetch
- Tool call delta concatenation (streamed arguments assembled by index)
- 3-tier permission model (read / yn / auto) + hard-wall
- Step trace: timing, token count, step counter per turn
- Interrupt/Resume: Ctrl+C mid-run → rollback to checkpoint → continue/discard
- Write verification: auto-read after write to prevent fake success

**Does NOT**:
- Knowledge management, multi-agent, web dashboards, skill systems

## Architecture

```
User Input
    │
    ▼
┌──────────────────────────────────────┐
│           Agent (loop.py)            │
│                                      │
│  save_checkpoint()                   │
│  async for delta in chat_stream():   │
│    yield text_delta  ← streaming     │
│    accumulate tool_call deltas       │
│  assemble response                   │
│  for each tool_call:                 │
│    permission gate ──────────────┐   │
│    execute handler (async)       │   │
│    result → messages ────────────┘   │
│  yield summary (steps/duration/tok)  │
└──────────────────────────────────────┘
    │            │               │
    ▼            ▼               ▼
┌─────────┐ ┌──────────┐ ┌──────────────┐
│ llm.py  │ │ tools.py │ │permissions.py│
│DeepSeek │ │ 7 tools  │ │ read/yn/auto │
│streaming│ │ unified  │ │ + hard-wall  │
│+ tokens │ │  async   │ │              │
└─────────┘ └──────────┘ └──────────────┘
```

## Install

```bash
pip install mini-harness
# or dev:
pip install -e .
```

## Quick Start

### CLI

```bash
mini-harness
```

First run prompts for your DeepSeek API key (saved to `~/.mini-harness/config.toml`).

```
> 列出当前目录的 Python 文件
  [1/15] glob · 0.8s · 45t
  [2/15] text · 0.3s · 89t
  — 2 steps · 1.1s · 134t
main.py
cli.py
```

Ctrl+C mid-run → `[C]ontinue [D]iscard [Q]uit`.

### API

```python
import asyncio
from harness import Agent, DeepSeekClient, Mode

async def main():
    client = DeepSeekClient(api_key="sk-...")
    agent = Agent(client, system_prompt="You are a helpful assistant.",
                  mode=Mode.AUTO)

    async for event in agent.run("List Python files in this project"):
        if event["type"] == "text_delta":
            print(event["text"], end="", flush=True)
        elif event["type"] == "tool_call":
            print(f"\n  [{event['step']}/{event['max_steps']}] {event['tool']}")
        elif event["type"] == "summary":
            print(f"\n  — {event['steps']} steps · "
                  f"{event['duration_ms']/1000:.1f}s · {event['tokens']}t")

asyncio.run(main())
```

## Event Reference

| Event | Fields | When |
|---|---|---|
| `text_delta` | `text` | Per-chunk during streaming |
| `tool_call` | `tool`, `params`, `step`, `max_steps`, `duration_ms`, `tokens` | Before tool execution |
| `tool_result` | `tool`, `success`, `output`, `step` | After tool execution |
| `step_end` | `step`, `kind`, `duration_ms`, `tokens` | Text-only responses |
| `summary` | `steps`, `duration_ms`, `tokens` | End of turn |
| `interrupted` | `step`, `message` | Ctrl+C caught |
| `error` | `message` | API failure, max steps |

## Permission Modes

| Mode | Read | Write | Bash | Hard-wall bypass? |
|------|:----:|:-----:|:----:|:-----------------:|
| **Read** | ✅ | ❌ | ❌ | No |
| **YN** | ✅ | Prompt | Prompt | No |
| **Auto** | ✅ | ✅ | ✅ | No |

**Hard-wall** (permanently blocked): `rm -rf`, `sudo`, `git push --force`,
paths: `raw/`, `agents/`, `archive/`, `/etc/`, `/proc/`, `/sys/`.

## Project Structure

```
mini-harness/
├── harness/
│   ├── __init__.py        # Public API
│   ├── cli.py             # REPL with interrupt dialog
│   ├── loop.py            # Agent Loop + streaming + checkpoint
│   ├── llm.py             # DeepSeek API (streaming + usage)
│   ├── tools.py           # 7 tools + registry + async handlers
│   └── permissions.py     # 3-tier + hard-wall
├── tests/                 # 49 tests
├── README.md
├── CHANGELOG.md
└── pyproject.toml
```

## License

MIT
