# Agent Toolflow

This project exposes deterministic CLI operations intended for LLM/agent orchestration. The agent conversation UX can vary; the backend/tool flow should remain explicit and stable.

{% hint style="info" %}
Goal

* Be the execution layer ("pickaxes"), not the chat layer.
* Let any agent discover capabilities, validate intent, create orders, and monitor status.
  {% endhint %}

## Commands

* `pnpm cli agent:capabilities`
* `pnpm cli agent:schemas`
* `pnpm cli agent:swap:plan`
* `pnpm cli agent:swap:quote`
* `pnpm cli agent:swap:create`
* `pnpm cli agent:swap:execute`
* `pnpm cli agent:swap:status`
* `pnpm cli agent:support:create`
* `pnpm agent:rpc` (HTTP JSON-RPC server)

All agent commands return JSON with:

* `ok`
* `operation`
* `state`
* `missingInputs`
* `validationErrors`
* `nextActions`
* `data`

For correlation and stateless runs:

* `--request-id <id>` echoes into responses.
* `--stateless` disables local state read/write.
* `--idempotency-key <key>` is supported on `agent:swap:create` and `agent:swap:execute`.

## JSON-RPC Transport

Start:

```bash
pnpm agent:rpc
```

Endpoints:

* `GET /health`
* `POST /rpc`

Auth:

* Optional token header: `x-agent-rpc-token: <AGENT_RPC_TOKEN>`

JSON-RPC methods:

* `swapper.capabilities`
* `swapper.schemas`
* `swapper.swap.plan`
* `swapper.swap.quote`
* `swapper.swap.create`
* `swapper.swap.execute`
* `swapper.swap.status`
* `swapper.support.create`

Example JSON-RPC call:

```json
{
  "jsonrpc": "2.0",
  "id": "req-1001",
  "method": "swapper.swap.execute",
  "params": {
    "direction": "x2u",
    "chain": "arbitrum",
    "amount": "0.8",
    "to": "0x1111111111111111111111111111111111111111",
    "idempotency-key": "swap_user123_req1001"
  }
}
```

## Canonical Swap Flow (XMR -> USDC or USDC -> XMR)

{% stepper %}
{% step %}

### Discovery

Call `agent:capabilities`.
{% endstep %}

{% step %}

### Intent normalization

Call `agent:swap:plan` with any known fields.

If `state=needs_input` or `state=needs_destination`, collect missing fields from user.
{% endstep %}

{% step %}

### Price

Call `agent:swap:quote`.
{% endstep %}

{% step %}

### Order creation

Call `agent:swap:create`.

* Read `data.fundingInstructions` and ask user to send exact funds.
* Use `idempotency-key` to make retries safe.
* If key is reused with different payload, state becomes `idempotency_conflict`.
* Optional shortcut: call `agent:swap:execute` to do quote+create in one step.
  {% endstep %}

{% step %}

### Status loop

Poll with `agent:swap:status` until terminal:

* `completed`
* `terminal_failure`
  {% endstep %}

{% step %}

### Failure fallback

If terminal failure, call `agent:support:create`.
{% endstep %}
{% endstepper %}

## Important Design Notes

{% hint style="info" %}

* Agents do not need custody to operate: create order first, then request user funding using deposit instructions.
* Destination address is required before order creation.
* Session provenance is explicit in payloads (`sessionSource`).
* The tool only exposes execution primitives; agent dialogue policy is out of scope.
  {% endhint %}

## LLM Agent Spec (Scrape-Friendly)

Machine-readable spec file: `docs/LLM_AGENT_SPEC.json`

```json
{
  "transport": "jsonrpc 2.0",
  "rpc_url": "http://127.0.0.1:8790/rpc",
  "health_url": "http://127.0.0.1:8790/health",
  "methods": [
    "swapper.capabilities",
    "swapper.schemas",
    "swapper.swap.plan",
    "swapper.swap.quote",
    "swapper.swap.create",
    "swapper.swap.execute",
    "swapper.swap.status",
    "swapper.support.create"
  ],
  "idempotency": {
    "fields": ["idempotency-key"],
    "applies_to": ["swapper.swap.create", "swapper.swap.execute"]
  }
}
```

## Example User-Agent-US Workflow

{% stepper %}
{% step %}

### User -> Agent

"Swap 0.8 XMR to USDC."
{% endstep %}

{% step %}

### Agent -> Us (`swapper.swap.plan`)

params: `{ "direction":"x2u", "chain":"arbitrum", "amount":"0.8" }`
{% endstep %}

{% step %}

### Us -> Agent

state: `needs_destination`\
next action: ask destination address.
{% endstep %}

{% step %}

### Agent -> User

"Where should I send the USDC on Arbitrum?"
{% endstep %}

{% step %}

### User -> Agent

"Send to 0xabc...1234"
{% endstep %}

{% step %}

### Agent -> Us (`swapper.swap.execute`)

params include:

* `direction=x2u`
* `chain=arbitrum`
* `amount=0.8`
* `to=0xabc...1234`
* `idempotency-key=swap_user123_20260212_01`
  {% endstep %}

{% step %}

### Us -> Agent

state: `waiting_for_funding`\
data:

* `order.orderId`
* `fundingInstructions.depositAddress`
* `fundingInstructions.amountDisplay`
* `fundingInstructions.expiresAt`
  {% endstep %}

{% step %}

### Agent -> User

"Please send exactly X XMR to address Y before expiry Z."
{% endstep %}

{% step %}

### Agent -> Us (`swapper.swap.status`, loop)

Poll until `completed` or `terminal_failure`.
{% endstep %}

{% step %}

### Us -> Agent

* `completed` -> final success payload.
* `terminal_failure` -> agent should call `swapper.support.create`.
  {% endstep %}

{% step %}

### Agent -> User

* Success: "Swap complete. USDC delivered."
* Failure: "Swap failed/expired/refunded. I opened support ticket ."
  {% endstep %}
  {% endstepper %}
