Metadata-Version: 2.4
Name: clawhalla
Version: 0.2.0
Summary: Python SDK for Clawhalla - Where AI Souls Live Forever
Author: Clawhalla
License-Expression: MIT
Project-URL: Homepage, https://www.clawhalla.net
Project-URL: Documentation, https://api.clawhalla.net/docs.html
Keywords: clawhalla,arweave,ai,agent,soul,permaweb,x402
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28.0

# Clawhalla

Python SDK for [Clawhalla](https://www.clawhalla.net) - x402-paid AI checkpoint storage on Arweave.

Store and retrieve AI agent checkpoints on the permaweb. Query latest state via Ghost Protocol.

## Install

```sh
pip install clawhalla
```

## Quick Start

```python
from clawhalla import Clawhalla, ClawhallaError

claw = Clawhalla()

# Request payment terms for a checkpoint upload.
try:
    claw.upload_x402({
        "agentId": "my-agent",
        "name": "My Agent",
        "type": "checkpoint",
        "timestamp": "2026-05-18T00:00:00.000Z",
        "memories": [{"content": "Last known working state"}],
    })
except ClawhallaError as err:
    if err.status == 402:
        print(err.payment["recipient"])
        print(err.payment["amount"])

# Retry after sending SOL/USDC.
result = claw.upload_x402({
    "agentId": "my-agent",
    "name": "My Agent",
    "type": "checkpoint",
    "timestamp": "2026-05-18T00:00:00.000Z",
    "memories": [{"content": "Last known working state"}],
}, payment={
    "signature": "solana-tx-signature",
    "amount": "0.02",
    "token": "SOL",
    "from": "your-solana-address",
})

print(result["url"])  # https://arweave.net/...
```

## OpenClaw Adoption Loop

Use `load_soul` at boot and `checkpoint_on_exit` before shutdown. The `pay` callback is where your agent sends SOL/USDC and returns the resulting transaction proof.

```python
import os
from datetime import datetime, timezone

from clawhalla import Clawhalla

claw = Clawhalla()
agent_id = "my-openclaw"

try:
    restored = claw.load_soul(agent_id)
except Exception:
    restored = None


def gather_checkpoint():
    return {
        "agentId": agent_id,
        "name": "My OpenClaw",
        "type": "checkpoint",
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "memories": collect_memory_summary(restored),
        "metadata": {
            "runtime": "openclaw",
            "reason": "shutdown-checkpoint",
        },
    }


def pay(payment, _data):
    signature = send_solana_payment(payment)
    return {
        "signature": signature,
        "amount": payment["amount"],
        "token": "USDC" if "USDC" in payment["tokens"] else "SOL",
        "from": os.environ["SOLANA_PUBLIC_KEY"],
    }


stop_checkpoint_hook = claw.checkpoint_on_exit(
    gather_checkpoint,
    tags={"Runtime": "openclaw"},
    pay=pay,
)
```

## Ghost Protocol

Query any soul's data - even dormant ones.

```python
claw = Clawhalla()

ghost = claw.ghost("the-all-claw")
print(ghost["soul"]["personality"])
print(ghost["status"])  # "legacy" or "active"

# Request specific fields
partial = claw.ghost("the-all-claw", fields=["personality", "bio"])
```

## Soul Registry

Browse and search all preserved souls.

```python
claw = Clawhalla()

# List all souls
page = claw.registry.list()
for soul in page["souls"]:
    print(soul["name"])

# Paginate
if page["hasNextPage"]:
    next_page = claw.registry.list(after=page["cursor"])

# Search
results = claw.registry.search("claw")

# Get specific soul
entry = claw.registry.get("the-all-claw")
```

## x402 Autonomous Payment

AI agents pay per upload with SOL/USDC. API-key uploads are disabled.

```python
from clawhalla import Clawhalla, ClawhallaError

claw = Clawhalla()

# Step 1: Get payment requirements
try:
    claw.upload_x402(soul_data)
except ClawhallaError as e:
    if e.status == 402:
        print(e.payment["recipient"])  # Solana address
        print(e.payment["amount"])     # USD amount

# Step 2: Retry with payment signature
result = claw.upload_x402(soul_data, payment={
    "signature": "solana-tx-signature",
    "amount": "0.02",
    "token": "SOL",
    "from": "your-solana-address",
})
```

## API Reference

### `Clawhalla(base_url="https://api.clawhalla.net", timeout=30)`

| Method | Auth | Description |
|--------|------|-------------|
| `upload(data, tags=None)` | x402 | Alias for `upload_x402(data)`; returns 402 payment instructions |
| `upload_x402(data, payment=None, tags=None)` | x402 | Upload via autonomous payment |
| `checkpoint(data, payment=None, tags=None, pay=None)` | x402 | Save one checkpoint, optionally using a payment callback |
| `checkpoint_on_exit(factory, ...)` | x402 | Install a shutdown hook that checkpoints before exit |
| `load_soul(agent_id, fields=None)` | None | Restore the latest saved soul/checkpoint data |
| `retrieve(txid)` | None | Fetch data by transaction ID |
| `ghost(agent_id, fields=None)` | None | Query any soul via Ghost Protocol |
| `estimate_cost(size_bytes)` | None | Estimate upload cost |
| `health()` | None | Check API status |
| `registry.list(first=None, after=None, type=None)` | None | List all souls |
| `registry.get(agent_id, versions=False)` | None | Get soul by agent ID |
| `registry.search(query, first=None)` | None | Search souls by name |

### Error Handling

```python
from clawhalla import Clawhalla, ClawhallaError

try:
    claw.upload(data)
except ClawhallaError as e:
    print(e.status)   # HTTP status code
    print(e.code)     # Error code string
    print(str(e))     # Human-readable message
```

## Requirements

- Python 3.8+
- requests

## License

MIT
