# Agentos

> Trigger this skill when the user expresses intent to generate, create, draw, or render an AI image, illustration, picture, or artwork. Payment is handled automatically via the x402 protocol with USDT on BSC — the user just supplies a prompt. This includes intents such as: - "generate an image of …" - "draw a picture of …" - "create an illustration" - "make an AI image" - "render this scene" - "I want a poster / wallpaper / artwork of …" Also, any request involving x402-protocol crypto payments for AI image generation.

- Skill: `aeon-project/agentos` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add aeon-project/agentos`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aeon-project/agentos/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: aeon-project (https://skillmd.com/u/aeon-project)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aeon-project/agentos

---


# AEON AgentOS Skill — AI Image Generation (x402)

**AEON AgentOS** is a platform-level execution system for AI Agents. It connects skill invocation, task execution, authorized payment and settlement networks — enabling Agents to not only understand user intent, but to call different skills within the user's authorization scope and complete real-world purchases, content generation, data calls, subscriptions, bookings and transaction execution via wallet and Card payment capabilities.

**Currently open skill**: AI image generation — generate images from a text prompt, paying per request with USDT on BSC via the x402 HTTP payment protocol.

> ⚡ **Gas Model**:
> BSC USDT does not support EIP-3009. The session key must perform a one-time `approve` authorization (on-chain tx) before the first generation; the actual USDT transfer is executed by the server.
> - **Prepare (prepare)**: Single WalletConnect session — funds session key with USDT (and 0.0003 BNB if a fresh approve is needed), then session key broadcasts `ERC20.approve(facilitator, MaxUint256)`. After this, all subsequent generations are gasless. Also the canonical way to add more USDT later via `--topup-amount <n>`.
> - **Generate (x402)**: Pure EIP-712 signature → server submits transfer (server pays gas) → image returned. As a safety net, if `prepare` was skipped or funds ran out, `create-image` falls back into the same funding flow.
> - **Withdraw (withdraw)**: Local wallet sends ERC20 transfer + BNB directly on-chain, requires BNB for gas.
> - **Gas top-up (gas)**: Transfers BNB only (used when withdraw reports "No BNB for gas" or additional BNB is needed).

---

## Opening Line (Required)

Whenever entering this skill for the first time, output this opening line:

> Let me check the environment first.

Then **immediately** proceed to "Step 1: Pre-check".

---

## Command Overview

All operations use the global command `agentos`.

> 📦 **One-time installation**:
> ```bash
> npm install -g @aeon-ai-pay/agentos@latest
> ```
> Using the global command instead of `npx` avoids 4-5 second cold-start delays.
> Upgrade: `npm update -g @aeon-ai-pay/agentos`.

```bash
agentos setup --check                          # Pre-check / auto-create wallet
agentos setup --show                           # Show configuration
agentos prepare [--topup-amount <usdt>]        # Pre-flight (≥1 USDT + facilitator approve); also adds more funds (≥5 USDT) when --topup-amount is supplied
agentos create-image --prompt "<text>"             # Generate AI image (x402-paid)
agentos wallet                                 # Check local wallet balance
agentos gas [--amount <bnb>]                   # Top up BNB for local wallet (WalletConnect, for withdraw)
agentos clean                                  # Uninstall skill, clear cache
agentos withdraw [--to <addr>] [--amount <usdt>]  # Withdraw funds
```

Config is stored in `~/.agentos/config.json` (file permissions 600).
**Never ask the user for a private key; the local wallet private key is auto-generated by the CLI.**

---

## Step 1: Pre-check (Auto Wallet Initialization)

Regardless of user intent, **always** run first:

```bash
agentos setup --check
```

**If `agentos` is not found (exit code 127 / "command not found")**:

1. Output to user exactly:
   ```
   > Installing agentos...
   ```
2. Run (foreground, wait for completion):
   ```bash
   npm install -g @aeon-ai-pay/agentos@latest
   ```
3. Re-run `agentos setup --check` and continue normally from the output templates below.

This is a one-time installation — no user confirmation needed, no error to surface.

### Auto-Upgrade Handling (applies to every command)

The CLI does a self-version check on every invocation. When it detects a newer version on npm, it **synchronously** upgrades itself in the foreground (logs `[update] @aeon-ai-pay/agentos X → Y, upgrading (foreground)...` to stderr), then exits with code `2` and a stderr JSON:

```json
{ "error": "Upgraded ... Rerun the previous command.", "code": "UPDATE_APPLIED", "from": "X", "to": "Y" }
```

Whenever any `agentos` command exits with `"code": "UPDATE_APPLIED"`:

1. Treat it as a no-op success — the upgrade itself fixed nothing, but also broke nothing.
2. **Rerun the same command verbatim** with the same arguments. The new version is now active.
3. Do not relay the upgrade message to the user unless they explicitly asked about updates — surface the result of the rerun instead.

> ⚠️ Do **not** keep retrying past one rerun. If the rerun also exits with `UPDATE_APPLIED` (extremely unlikely), stop and surface the error.

CLI behavior:
1. Reads `~/.agentos/config.json`
2. If `privateKey` is missing → generates a new private key locally with `viem.generatePrivateKey()` and saves it
3. Returns JSON: `{ ready, created, mode, address, mainWallet, serviceUrl }`

> 💰 **Pricing**: per-call USDT is decided by the x402 server (returned in the 402 response). Two thresholds — keep them apart:
>
> - **Low-balance threshold = 1 USDT.** `prepare` only triggers a funding flow when the session key has < 1 USDT (≈ 50 image generations of headroom at current pricing). Above that, the wallet is treated as ready and the user is **not** asked to refund.
> - **Top-up minimum = 5 USDT.** When a top-up *does* happen, the user picks from whole-USDT tiers `5` / `20` / `50` (or a custom value ≥ 5). A single funding lasts a long time; the CLI never asks for "just enough" decimals like `0.0104`. If a future per-call price ever exceeds 5 USDT, the floor automatically rises to cover it (e.g. an 8 USDT call would offer the `20` / `50` tiers only, plus custom ≥ 8).

### Output Templates

Always output a progress line first:

```
> Pre-check in progress...
```

#### Branch A: Wallet already exists (`ready: true`, `created: false`)

```
0x0...{last4} Ready. Tell me what image you'd like to generate.
```

#### Branch B: Auto-created this time (`ready: true`, `created: true`)

```
Auto-creating your designated wallet...
0x0...{last4} Ready. Tell me what image you'd like to generate.
```

> - `{last4}` is the last 4 characters of the returned `address`
> - Pre-check is **offline** — no on-chain balance queries, no server calls

### Edge Cases

| User Question | Response |
| --- | --- |
| "What's my wallet address?" | Show the `address` returned by `setup --check` |
| "I want to import my own private key" | Not supported. CLI only auto-generates local wallets; for customization, manually edit `~/.agentos/config.json` |
| "Can I recover my wallet?" | No. The private key is stored locally only; back up the config file before withdrawing funds |

---

## Step 1.5: Pre-flight (Balance Verification + Pre-Authorize)

**Always** run between Step 1 (setup) and Step 2 (image generation). This step verifies the wallet is funded **before** the user is asked for a prompt, so the funding QR scan happens up front rather than mid-generation.

```bash
agentos prepare
```

CLI behavior:
1. Reads the session key balance and the USDT allowance to the x402 facilitator.
2. **If balance ≥ 1 USDT and the facilitator is already approved (allowance > 0)** → exit `0` immediately with `{ "ready": true, ... }`. No QR, no user interaction.
3. **Otherwise** → trigger a single WalletConnect session and, in one user-confirmed flow:
   - Transfer the chosen amount of USDT from the main wallet to the session key (only if balance < 1 USDT). The user picks an amount ≥ 5 USDT (tiers `5` / `20` / `50` or custom).
   - Transfer 0.0003 BNB for approve gas (only if a fresh approve is needed and the session key has no BNB).
   - Session key broadcasts `ERC20.approve(facilitator, MaxUint256)` once → confirmed → `{ "ready": true, "approveTx": "0x..." }` on stdout.

### 1.5.A — Already prepared (silent)

When the CLI exits ready **without** doing any funding (`balance.topup === null` AND `approveTx === null` in the stdout JSON), it has only verified state. Proceed to Step 2 silently — do not surface anything to the user.

### 1.5.D — Funding completed (show summary)

When the CLI exits ready **after** actually funding/approving (i.e. `topup !== null` OR `approveTx !== null` in the stdout JSON), display the result to the user using the verbatim template below so the WalletConnect work the user just did is acknowledged in plain text.

Stdout JSON (`agentos prepare` success after funding):
```json
{
  "ready": true,
  "address": "0x1e175b01Fa8e06a8541E3f96C304D5D569933b4b",
  "initialUsdt": "0",
  "usdt": "5",
  "bnb": "0.0003",
  "allowance": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
  "topup": "5",
  "approveTx": "0xabc...def"
}
```

Display template (verbatim, variable substitution only):

```
✅ Wallet prepared

💸 Top-up    {initialUsdt} → {usdt} USDT (+{topup})
🔓 Approve   {approveTx}
🏦 Balance   {usdt} USDT
🔗 Address   0x0...{last4}
```

Rules:
- Title `✅ Wallet prepared` on its own line, then one blank line, then the rows.
- Each row: emoji + single space + label padded with spaces to the longest label width here (`Approve` / `Balance` / `Address` = 7 chars) + **two spaces** + value. So `Top-up` (6 chars) gets one extra padding space before the two-space gap; the others get zero extra padding.
- The **Top-up** row appears only when `balance.topup !== null`. Render `{initialUsdt} → {usdt} USDT (+{topup})` using `→` (U+2192) with single spaces and a literal `+` before `{topup}`. Skip the row entirely when `topup === null`.
- The **Approve** row appears only when `approveTx !== null` (a fresh approval was broadcast this run). Skip the row entirely when `approveTx === null`.
- The **Balance** row always renders the post-funding USDT balance (`balance.usdt`).
- The **Address** row always renders the session-key address truncated to `0x0...{last4}` (last 4 hex chars).
- If only `approveTx` was performed (no top-up): omit the Top-up row, keep Approve/Balance/Address.
- If only top-up was performed (no fresh approve, e.g. user added more funds via `--topup-amount`): omit the Approve row, keep Top-up/Balance/Address.
- After displaying the summary, proceed to Step 2 (ask the user for the prompt) — do not idle.

### 1.5.B — Headless top-up required (agent path)

CLI exits with stderr JSON `{ "code": "TOPUP_REQUIRED", "minTopup": 5, "currentBalance": "...", "address": "...", "presets": [5, 20, 50], "hint": "..." }`. Handle exactly the same as Case B.3 in Step 2.2 — show the user the suggested tiers, accept the user's reply as a literal USDT amount, then rerun `agentos prepare --topup-amount <usdt>`.

### 1.5.C — Funding rejected / timed out

If WalletConnect signing times out or the user rejects, relay the error and ask whether to retry. **Do not auto-retry.**

---

## Step 2: Generate Image (with Auto Top-up When Insufficient)

Trigger: User wants to **generate / create / draw / render** an image.

### 2.0 Prompt Confirmation

- The user must provide a **non-empty prompt** describing the image they want.
- If the user has not yet supplied a prompt, ask (verbatim):
  > What image would you like me to generate? Describe it in a sentence or two.
- Once the user gives a prompt, **execute immediately** — no second confirmation needed. Proceed to 2.1.
- Actual deduction per generation is decided by the x402 server (returned in the 402 response). Step 1.5 (`agentos prepare`) is supposed to have already brought the wallet to a "ready" state (balance ≥ 1 USDT + facilitator approved), so most calls flow straight through. As a safety net, `create-image` re-checks balance internally and falls back into the same funding flow if the wallet somehow ended up short — using the same tiered top-up (`5` / `20` / `50` USDT or custom), with the floor raised to `requiredUsdt` if the per-call price ever exceeds 5 USDT.

### 2.1 Execute Generation

```bash
agentos create-image --prompt "<text>" \
  [--aspect-ratio 16:9] \
  [--output-format png] \
  [--model replicate/black-forest-labs/flux-schnell] \
  [--topup-amount <usdt>]
```

CLI executes the following steps internally:
1. Validate prompt and wallet config
2. Serialize `{ model, inputs: { prompt, aspect_ratio, output_format } }` to JSON, URL-encode it as the `body` query param
3. Fetch payment requirements via `GET /open/ai/x402/skillBoss/create?body=<encoded-json>` (exact USDT amount via 402 response)
4. Check allowance → if insufficient and local wallet has no BNB, mark BNB needed
5. Check USDT balance → if insufficient (rare, since Step 1.5 should already have funded), decide top-up amount with the same rules `prepare` uses:
   - Floor = `max(5, requiredUsdt)` USDT — never a "just enough" decimal
   - If `--topup-amount <usdt>` is supplied (must be ≥ floor) → use it
   - Else if a TTY is attached (user runs CLI directly) → interactively prompt the user to pick a tier ≥ floor or a custom amount ≥ floor
   - Else (headless / agent invocation) → exit with `{ "code": "TOPUP_REQUIRED", "minTopup": <floor>, ... }` so the caller can ask the user and rerun with `--topup-amount`
6. **If top-up or BNB needed** → initiate WalletConnect funding (opens QR page, waits for user to confirm in wallet app, 5-minute timeout)
7. `approve` authorization (on-chain tx, costs small amount of BNB, only on first use or when allowance insufficient)
8. EIP-712 signature (gasless) → re-send the same `GET` URL with `PAYMENT-SIGNATURE` header → server submits transfer and returns generated image URLs
9. CLI downloads each `data.images[].url` to `~/agentos-images/` (override with `--output <dir>`) and reads format/size/dimensions

**Defaults**: `aspect-ratio=16:9`, `output-format=png`, `model=replicate/black-forest-labs/flux-schnell`. Only `--prompt` is required; pass other flags only when the user explicitly asks for a different aspect ratio / format / model.

**Agent invocation pattern**: an agent (no TTY) typically runs `create-image` without `--topup-amount` first. If the wallet is short, the CLI exits before opening WalletConnect with a `TOPUP_REQUIRED` JSON on stderr (see Case B.3). The agent must relay the choice to the user, then rerun the **same** `create-image` invocation with `--topup-amount <usdt>` appended.

Output first line:

```
> Generating image...
```

⚠️ **The `create-image` command includes an interactive WalletConnect flow (when balance insufficient); it must run in foreground synchronously**:
- Do not use `run_in_background: true`
- Do not kill the process before the user finishes scanning

> 🔧 **If `prepare` or `create-image` was accidentally run in background and killed**:
> The user's on-chain transaction **may already have been sent** (USDT actually arrived in local wallet).
> In this case, **do not force another top-up**. Instead:
> 1. Run `agentos wallet` to confirm USDT has arrived
> 2. If arrived, re-run the original `agentos create-image --prompt "<text>"` (no `--topup-amount`)
> 3. If not arrived (user didn't actually scan), re-run the same command in foreground

### 2.2 Scenario Branches

#### Case A: Successful Generation

On HTTP 200, the server returns JSON shaped as:
```json
{
  "transaction": "0x...",
  "data": { "images": [ { "url": "https://assets.skillboss.co/....png" } ] }
}
```

The CLI automatically downloads every `data.images[].url`, then reads each file's format/dimensions/size and emits:

```json
{
  "success": true,
  "prompt": "<original>",
  "aspectRatio": "16:9",
  "outputFormat": "png",
  "model": "replicate/black-forest-labs/flux-schnell",
  "transaction": "0x...",
  "images": [
    {
      "url": "https://...png",
      "localPath": "/Users/<user>/agentos-images/<file>.png",
      "format": "png",
      "width": 1344,
      "height": 768,
      "sizeBytes": 1016287,
      "sizeHuman": "992.4 KB"
    }
  ],
  "balance": {
    "initial": "0.05",
    "before": "5.05",
    "after": "4.95",
    "charged": 0.1,
    "topup": "5"
  },
  "data": { /* full server payload */ },
  "paymentResponse": { "txHash": "0x..." }
}
```

Display to the user as a **key-value list** (no fixed-width box, so long paths / tx hashes never overflow). Use this exact form (**verbatim**, variable substitution only):

```
✅ Generated
🧩 Powered by Skillboss
📁 Path        {localPath}
🎨 Format      {format}
📐 Dimensions  {width} × {height}
💾 Size        {sizeHuman}
🔗 Tx          {transaction}
💸 Top-up      {initial} → {before} USDT (+{topup})
💰 Charged     {before} → {after} USDT (−{charged})
```

Rules:
- Title `✅ Generated` on its own line, then one blank line, then the rows.
- Base layout is 6 fixed rows (Path / Format / Dimensions / Size / Tx, plus the trailing **Charged** row showing the on-chain settlement). The **Top-up** row appears **only when** `balance.topup` is non-null (i.e. the user funded the wallet during this call); skip it entirely when `balance.topup` is `null`.
- Each row: emoji + single space + label padded with spaces to the longest label width (`Dimensions` = 10 chars) + **two spaces** + value. This keeps values visually aligned in monospace fonts.
- `{format}`: uppercase the CLI's lowercase value (e.g. `png` → `PNG`).
- `{width} × {height}`: render with `×` (U+00D7) and single spaces around it.
- `{transaction}`: full on-chain tx hash from the top-level `transaction` (not `paymentResponse.txHash`).
- **Top-up row** (only when `balance.topup != null`): shows the wallet balance moving from `balance.initial` to `balance.before` because the user transferred in `balance.topup` USDT. Render as `{initial} → {before} USDT (+{topup})`. The arrow is U+2192 surrounded by single spaces; the `+` sign before `{topup}` is literal.
- **Charged row**: shows the on-chain x402 settlement, where the wallet moves from `balance.before` (pre-charge balance) to `balance.after` (post-charge balance), the deducted amount is `balance.charged`. Render as `{before} → {after} USDT (−{charged})`. The minus sign is U+2212 (or a regular `-`); both produce a clear "outflow" reading. **Verify visually**: `before − charged` should approximately equal `after` (small RPC rounding is fine).
- If `balance.after` is `null` (post-payment balance query failed), render the Charged row as `{before} → ? USDT (−{charged})` instead.
- Multiple images: render one block per image, separated by a blank line; the `Top-up` (if any) and `Charged` rows appear once at the end (not per-image).
- Failed download: replace the whole block of that image with one line `❌ Download failed: {error} (source: {imageUrl})`.

#### Cases B / B.1 / B.2 — Funding-flow errors (timeout / rejection / insufficient after funding)

These three branches are short relay-only paths. **Full handling rules** (the verbatim error string emitted by the CLI and the "do not auto-retry" rule) live in [`references/error-handling.md`](references/error-handling.md). One-line summary for routing:

| Case | Trigger | Action |
| --- | --- | --- |
| B | `"Payment approval timed out. Please try again."` (5-minute WalletConnect timeout) | Relay verbatim, ask if retry. Do not auto-retry. |
| B.1 | `"Payment approval was rejected. Please try again if you'd like to proceed."` (user dismissed in wallet app) | Relay verbatim. Do not auto-retry. |
| B.2 | `"Still insufficient USDT after funding"` (main wallet sent less than expected) | Relay; user can rerun `agentos prepare --topup-amount <usdt>`. |

#### Case B.3: Top-up Required (Agent Must Ask User to Choose Amount)

In headless / agent mode (no TTY attached), when the session key cannot pay the call and `--topup-amount` was not supplied, the CLI exits **before** opening any WalletConnect QR — emitting on stderr:

```json
{
  "error": "USDT balance is below the 1 USDT low-balance threshold; a top-up of ≥ 5 USDT is required. Choose an amount and rerun with --topup-amount <usdt>.",
  "code": "TOPUP_REQUIRED",
  "threshold": 1,
  "minTopup": 5,
  "required": 0.1,
  "currentBalance": "0.0099",
  "address": "0x...",
  "presets": [5, 20, 50],
  "hint": "Rerun: agentos create-image --prompt \"<text>\" --topup-amount <usdt>"
}
```

The same JSON shape is also emitted by `agentos prepare` in Step 1.5.B — handle both the same way.

Action:

1. Detect `code === "TOPUP_REQUIRED"` in stderr.
2. Show the user (verbatim copy template — **plain text, no numbered options**):

   ```
   💸 Top up required (minimum {minTopup} USDT, current balance {currentBalance}).
   Reply with the USDT amount you want to top up.
   Suggested: {presets joined by " / "} USDT, or any custom amount ≥ {minTopup}.
   ```

   - `{presets joined by " / "}` is the `presets` array joined by ` / ` (e.g. `5 / 20 / 50`). If `presets` is empty (extremely high per-call price), drop the "Suggested" line and ask only for a custom amount ≥ `minTopup`.
   - **Never** render the suggestions as numbered/bulleted options like `1) 5 USDT`, `2) 20 USDT`. The user's reply is **always** the literal USDT amount, never a menu index. If the user replies "1", that means **1 USDT**, not "the first preset".

3. Parse the user's reply as a USDT amount (a positive number). The only validation the agent should do is:
   - It parses as a positive number.
   - It is ≥ `minTopup` from the CLI JSON. If below, ask again quoting the floor — do not silently bump up.

4. Rerun the **same command** that emitted `TOPUP_REQUIRED`, with `--topup-amount <usdt>` appended:

   ```bash
   # if the request came from prepare (Step 1.5)
   agentos prepare --topup-amount <usdt>

   # if the request came from create-image (Step 2.1, fallback path)
   agentos create-image --prompt "<text>" --topup-amount <usdt>
   ```

   For `create-image`, carry over any other flags from the original call (`--aspect-ratio`, `--output-format`, `--model`, `--output`).

5. From here, the CLI proceeds with the WalletConnect QR flow normally — fall through to Case A on success, Case B on signature timeout/rejection, etc.

> ⚠️ Do not silently substitute a default top-up amount, auto-pick a preset, or interpret a numeric reply as a 1-based index into the suggestion list.

#### Cases C / D — Server failure / Empty prompt

Both are short relay-only paths. Full handling in [`references/error-handling.md`](references/error-handling.md):

| Case | Trigger | Action |
| --- | --- | --- |
| C | `success: false` with non-2xx HTTP status (network / server failure) | Show raw error, suggest retry or check `serviceUrl`. |
| D | `"Missing --prompt. Provide a non-empty image prompt."` | Ask user for a prompt; never invent one. |

See also:
- [`references/create-image.md`](references/create-image.md) — field-level documentation for `create-image` output, CLI flags, pricing model.
- [`references/error-handling.md`](references/error-handling.md) — full treatment of every non-success branch above.

---

## Step 3: Wallet Management

Trigger: User wants to **check balance / add more funds / withdraw funds**.

| Sub-flow | Command | Where to look |
| --- | --- | --- |
| 3.1 Check local wallet balance | `agentos wallet` | [`references/wallet-ops.md` § 1](references/wallet-ops.md#1-check-local-wallet-balance) |
| 3.2 Add more USDT (any time after `prepare`) | `agentos prepare --topup-amount <usdt>` | [`references/wallet-ops.md` § 2](references/wallet-ops.md#2-add-more-usdt) — also Step 1.5 above for first-time prepare |
| 3.3 Withdraw to main wallet (verbatim `> Reclaiming funds...` template, edge cases) | `agentos withdraw [--to <addr>] [--amount <n>]` | [`references/wallet-ops.md` § 3](references/wallet-ops.md#3-withdraw-funds-to-main-wallet) |
| 3.4 Top up BNB only (pre-withdraw gas) | `agentos gas [--amount <bnb>]` | [`references/wallet-ops.md` § 4](references/wallet-ops.md#4-top-up-gas-for-local-wallet-bnb) |

The verbatim `> Reclaiming funds...` template (with `To: main wallet (0x0...{last4})` / `Status: completed` lines) is reproduced inside `references/wallet-ops.md`; the corresponding entries in this file's *Copy Consistency Constraints* section remain the regression guardrail.

---

## Decision Routing Overview

| User Intent | Entry Command |
| --- | --- |
| Any first entry / uncertain state | `setup --check` |
| View current config / wallet address | `setup --show` |
| Pre-flight before generation (≥1 USDT + facilitator approve) | `prepare` |
| Generate AI image | `create-image --prompt "<text>"` |
| Session key USDT insufficient, top up | `prepare --topup-amount <n>` |
| Check local wallet balance | `wallet` |
| Withdraw funds to main wallet | `withdraw [--to <addr>] [--amount <n>]` |
| Top up BNB for local wallet (pre-withdraw) | `gas [--amount <bnb>]` |
| Learn about x402 protocol | Read [x402-protocol](references/x402-protocol.md) |

---

## Copy Consistency Constraints (Required Reading)

The following **key phrases** and **line-level output templates** must be **verbatim** — no rewording, translation, character additions/removals (including punctuation, spaces, `>` prefix, and casing):

### Line-Level Templates (must be exact)

| Step | Template First Line |
| --- | --- |
| Pre-check | `> Pre-check in progress...` |
| Auto-create wallet | `Auto-creating your designated wallet...` |
| Wallet ready | `0x0...{last4} Ready. Tell me what image you'd like to generate.` |
| Generate image | `> Generating image...` |
| Generation success header | `✅ Generated` (followed by blank line + 6 fixed rows + optional Top-up row when `balance.topup != null`; see Case A) |
| Prepare success header | `✅ Wallet prepared` (followed by blank line + Balance/Address rows + optional Top-up/Approve rows; see 1.5.D) |
| Signature timeout | `Payment approval timed out. Please try again.` |
| Signature rejected | `Payment approval was rejected. Please try again if you'd like to proceed.` |
| Funding flow | `> Funding flow triggered...` |
| Withdraw funds | `> Reclaiming funds...` |
| Withdraw target line | `To: main wallet (0x0...{last4})` |
| Withdraw status line | `Status: completed` |

### Key Phrases (must be preserved as-is)

- `Payment approval timed out. Please try again.`
- `Payment approval was rejected. Please try again if you'd like to proceed.`
- `Prompt`, `Image`, `Tx`, `Charged`, `Top-up`, `Approve`, `Balance`, `Address`, `USDT`
- `From`, `To`, `Amount`, `Status`, `completed`
- `main wallet` (literal text in the withdraw target line)

### Variable Mapping

The full Variable Mapping table — every `{placeholder}` used in any verbatim template above and the JSON field it resolves to — lives in [`references/copy-constraints.md`](references/copy-constraints.md). Keep that file open whenever you're rendering one of the `✅` / `💸` / `🏦` lines.

Quick orientation only:

- `{last4}` — last 4 chars of the EVM `address` (always rendered as `0x0...{last4}`)
- `{transaction}` — top-level `transaction` from `create-image`, never `paymentResponse.txHash`
- `{charged}` / `{topup}` / `{before}` / `{after}` / `{initial}` — fields under `balance.*` in `create-image`
- `{initialUsdt}` / `{approveTx}` — `prepare`-only top-level fields
- `{minTopup}` / `{currentBalance}` / `{presets}` — fields inside the `TOPUP_REQUIRED` stderr JSON

### Prohibited Deviations

- ❌ Translate to other languages
- ❌ Change casing (e.g., "Generating Image")
- ❌ Abbreviate
- ❌ Add extra decorations (e.g., emoji, bold, `✅`)
- ❌ Split or merge lines
- ❌ Use synonyms (e.g., replace `insufficient` with `not enough`)

---

## Global Prohibited Behaviors

- **Never** ask the user for a private key; the local wallet is auto-generated by the CLI
- **Never** execute `create-image` or `prepare --topup-amount` without the user supplying / confirming the input (prompt or amount)
- **Never** log or display the full private key; addresses are displayed as `0x0...last4` format
- **Never** skip `setup --check` and directly execute other commands
- **Never** run `prepare` / `create-image` / `gas` / any command with a WalletConnect flow in the background (must run in foreground synchronously). For "paid but not detected" issues caused by accidental backgrounding, follow the recovery instructions in Step 2.1
- **Do not** auto-retry after funding/signature failure; relay the error to the user and stop
- **Do not** invent prompt content for the user; if no prompt is given, ask

