# Guide for AI agents

Learn how an AI coding agent should read these docs, get a key, handle errors, and check
its work.

## Read the docs as text

- [/llms.txt](https://yellowandblack.dev/llms.txt) lists every page, with a one-line summary each.
- [/llms-full.txt](https://yellowandblack.dev/llms-full.txt) holds every page in one file.
- Any page is markdown when you add `.md` to its address, for example
  [/docs/quickstart.md](https://yellowandblack.dev/docs/quickstart.md). Asking for `text/markdown` in the `Accept`
  header works too.
- In a terminal, `yb docs` lists the pages and `yb docs <page>` prints one, for example
  `yb docs robot-contracts`. No key is needed.
- [/openapi.json](https://yellowandblack.dev/openapi.json) describes every HTTP route.

## Get a key

A person must do two things in the dashboard first:

1. Create an account. Verify its email before adding credit; the free sandbox doesn't
   need it. Accept the service terms too, if the dashboard asks.
2. Create an API key in the **API keys** tab.

Then give the agent the key as `YB_API_KEY`.

> **Note:** Never put the key in code, in a command-line argument, in a committed file,
> or in a chat.

## Start safely

- Start with `transport-demo` and `max_spend_usd="0"`. It's a free sandbox, not a model:
  it returns placeholder actions, and it can't spend money.
- Write `max_spend_usd` as a string, such as `"5"`, never as a float.
- On a real robot, open sessions with `transport="owned"`, so a stalled network can't
  freeze the control loop.
- Open sessions with `with client.session(...) as policy:`, so they always close. An open
  session holds a place on the model, and its reserved credit, until it closes or times
  out.
- If your code may retry `client.session(...)` after a crash, pass the same
  `idempotency_key` each time. The same key never opens a second session.
- Never send generated test data to a real model: its answers would be meaningless. Use
  recorded data, or the robot itself.

## Handle errors

Every error has a `code`, such as `capacity`. The Python SDK and `yb` also give a `fix`, a
`docs_url`, and a `retry` value. Plain HTTP responses give only `code` and `message`,
sometimes with a true or false `retry`; see [HTTP API](https://yellowandblack.dev/docs/http-api.md#errors).

Use `retry` to decide what to do next:

| `retry` | What the agent should do |
|---|---|
| `retry` | Wait, then repeat the same call. Wait longer after each failure, and give up after a few tries. For a `PlatformError`, stop at once if `error.retryable` is `False`. |
| `fix` | Do what the `fix` text says first. The same call again fails the same way. |
| `next_request` | Only this request failed, and the session is still open. Send the next observation. |
| `new_session` | The session has ended. Open a new one if the task still needs it. |
| `none` | A normal ending. Nothing to do. |

For example:

```python
from robot_inference_client import InferenceError, PlatformError

captured = policy.capture_time_ns()  # read when the cameras take the pictures
try:
    result = policy.infer(observation, captured_ns=captured)
except InferenceError as error:
    if error.retry != "next_request":
        raise  # the session ended; error.fix says what to do
    # the answer was late or replaced: skip it and send the next observation
except PlatformError as error:
    print(error.code, error.fix, error.docs_url)
    raise
```

`yb errors <code>` explains any code without going online. `yb` exits with status 75 for
a temporary error, 1 for other errors, and 2 for a wrong command line.

## Check the integration

1. `yb models --ready` lists the offered model. If it is on demand and idle, the
   session waits for startup; otherwise check that capacity is available. Listing
   alone does not start a model.
2. Each observation matches the model's format on
   [Robot data formats](https://yellowandblack.dev/docs/robot-contracts.md): the exact names, `uint8` images of shape
   (224, 224, 3), the right number of state values, and a `prompt` equal to the session's
   task.
3. `captured_ns` comes from `policy.capture_time_ns()`, read when the cameras take the
   pictures.
4. The loop treats errors with `retry == "next_request"` as skipped steps, not as crashes.
5. Every session closes on every path, including when an exception is raised.
6. After closing, `policy.closure` isn't `None`, `policy.closure["close_confirmed"]`
   is `True`, and `policy.closure["charged_usd"]` is what you expect.
