> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yutori.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Building agents with n2

> Step-by-step guide to build a Python agent loop that lets Navigator n2 control a Daytona sandbox

[Navigator n2](/reference/n2) is a computer-use model that looks at desktop screenshots and predicts tool calls — mouse and keyboard actions, or bash commands — to be executed in desktop environments.
[Daytona](https://www.daytona.io) provides sandboxes with full desktop environments where the predicted actions can be executed.

This guide walks you through how to set up a simple agent loop that wires the two together to set up a complete computer-use agent stack.

<Note>
  To try n2 without building anything, open the [Playground](https://platform.yutori.com/navigator/playground). To drive your own Mac, install [Yutori MCP](https://github.com/yutori-ai/yutori-mcp) — it ships a local loop already wired up.
</Note>

```text theme={null}
          ┌──────────────────────────────────────────────────┐
          │                    Agent Loop                    │
          │               (your code, Python)                │
          └──────────────────────────────────────────────────┘
                   │   ▲                         │   ▲
 task + screenshot │   │ next action     execute │   │ screenshot
                   ▼   │                         ▼   │
          ┌────────────────────┐        ┌────────────────────┐
          │    Navigator n2    │        │  Daytona Sandbox   │
          │    (Yutori API)    │        │  Linux desktop +   │
          │                    │        │  Computer Use API  │
          └────────────────────┘        └────────────────────┘
                 decides                   perceives + acts
```

## Components

| Component           | Role                                                                               | Your part                                                                                                              |
| ------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **Navigator n2**    | Reads the screenshot and returns the next tool call(s)                             | A Yutori API key                                                                                                       |
| **Daytona sandbox** | Runs those actions on a real Linux desktop                                         | A Daytona API key                                                                                                      |
| **Agent loop**      | Sends the task and screenshot, executes the calls, sends the results back, repeats | [`navigator_n2_daytona.py`](https://github.com/yutori-ai/yutori-sdk-python/blob/main/examples/navigator_n2_daytona.py) |

The loop is `N2ComputerAgent` from the [Python SDK](https://github.com/yutori-ai/yutori-sdk-python): it owns the turns, the message history, the screenshot window, and the coordinate math. Everything specific to Daytona lives in one adapter class, `DaytonaComputer`. Both are in [`examples/navigator_n2_daytona.py`](https://github.com/yutori-ai/yutori-sdk-python/blob/main/examples/navigator_n2_daytona.py), along with a `main` that creates the sandbox, runs the agent, and deletes the sandbox.

n2 typically answers with one `computer_batch` call, which chains several actions against a single screenshot. `N2ComputerAgent` runs whatever comes back and returns a fresh screenshot to the model — plus the command output for `bash`.

## Prerequisites

* **Python 3.10+.**
* A **Yutori API key** — see [Authentication](/authentication).
* A **Daytona API key** from the [Daytona dashboard](https://app.daytona.io).

```bash theme={null}
pip install 'yutori>=0.9.2' daytona

yutori auth login            # or: export YUTORI_API_KEY=...
export DAYTONA_API_KEY=...
```

## Run it

Download the script from the SDK repository and run it with a task:

```bash theme={null}
curl -fsSLO https://raw.githubusercontent.com/yutori-ai/yutori-sdk-python/main/examples/navigator_n2_daytona.py
python navigator_n2_daytona.py "Write 'hello from n2' to /tmp/demo.txt, then open a terminal and cat the file"
```

Each turn prints the model's own text and the call it made:

```text theme={null}
ACTION bash: {"command": "echo 'hello from n2' > /tmp/demo.txt"}
The file is written. Now I'll launch the Terminal Emulator.
ACTION computer_batch: {"actions": [{"name": "left_click", "arguments": {"coordinates": [56, 11]}}]}
```

The loop ends when n2 returns a message with no tool calls. The sandbox is deleted in a `finally` block, so it does not outlive the run even when the run fails.

### Adapting it with a coding agent

Point your coding agent to the script, and describe what needs to change for your setup.

```text theme={null}
Start from https://github.com/yutori-ai/yutori-sdk-python/blob/main/examples/navigator_n2_daytona.py
— it is a working Navigator n2 + Daytona agent loop. Copy it in and adapt it for <what I'm building>.

Install: pip install 'yutori>=0.9.2' daytona
Keys:    YUTORI_API_KEY and DAYTONA_API_KEY in the environment.

Leave alone unless I ask: the pinned tool_set, the typing chunker, the scroll conversion,
the bash working-directory sentinel, and deleting the sandbox in a finally block.
Each is there for a reason documented at https://docs.yutori.com/reference/n2-daytona.

Change: <the snapshot, the step cap, how the result gets back to my code>.
```

## Adapter details

`DaytonaComputer` is the class `N2ComputerAgent` calls to observe and act. Most methods pass through to Daytona unchanged. The exceptions are below:

* **Sandbox lifecycle** — wait for `computer_use.start()` to finish before interacting with the sandbox, and always delete it in a `finally` block to stop billing.
* **Coordinates** — n2 predicts them in a [normalized 1000×1000 space](/reference/n2#coordinate-system). `N2ComputerAgent` rescales them to the desktop's native pixel size before calling the adapter, so the adapter does not do any conversions.
* **Typing** — `keyboard.type` fails on control characters and silently truncates long strings, so send `\n` and `\t` as key presses and chunk the rest.
* **Scrolling** — `N2ComputerAgent` passes a pixel distance, one notch of the model's `amount` being a tenth of the screen; Daytona wants wheel notches, so the adapter converts back.
* **`bash` results** — the command's output, with the exit code appended on failure. `N2ComputerAgent` caps each result at 8,000 characters so a large `cat` cannot push the request over the [10 MB limit](/reference/n2#screenshots).

<Note>
  `bash` promises a working directory that persists across calls, but every `exec` is a fresh process, so a `cd` would be forgotten by the next command. The adapter honors the contract: each command prints its final `$PWD` on a sentinel line, and the next command starts there through `exec`'s `cwd`. `run_in_background` detaches the command with `nohup` and returns its pid and output file.
</Note>

## Knowing when to stop

Without a limit, a loop will keep going on a task it cannot finish. The `StepLimit` callback in the script caps the number of model turns: `N2ComputerAgent` calls its `on_run_continue` before each turn and stops when it returns `False`.

Execution errors are not fatal to the run. A batch stops at the first action that fails, and the tool result tells the model which action failed along with a screenshot of the resulting state; a `bash` call that cannot run (a timeout, for example) comes back as an `[ERROR] bash failed: ...` result, and a nonzero exit code is simply part of the output. In every case the model sees what happened and routes around it.

For anything unattended on a machine that is not disposable, add an `action_confirmation_callback`. `N2ComputerAgent` calls it before every action except `screenshot`, `wait`, `mouse_move`, and `scroll`, and skips the call if it returns `False`. The sandbox here is disposable, so the script does not confirm anything. On a harness driving a real machine, keep shell confirmation mandatory.

## Going further

* **Pick a snapshot with the apps you need.** `daytonaio/sandbox:0.6.0` is a bare XFCE desktop at 1024×768. Build your own to give the model a browser, an editor, or a dataset.
* **Watch the run.** `(await sandbox.get_preview_link(6080)).url` is a noVNC view of the desktop, which is the fastest way to see what a failing run is doing.
* **Send the whole history.** `N2ComputerAgent` keeps every turn and strips only the images outside the [two newest image-bearing messages](/reference/n2#message-history), which is what the server would drop anyway. Trimming messages yourself risks the model repeating work it has already done.

## Related

<Card title="Navigator n2" icon="desktop" href="/reference/n2">
  The model reference — tools, actions, coordinates, and request fields.
</Card>
