Skip to main content
Navigator n2 is a computer-use model that looks at desktop screenshots and predicts tool calls — mouse and keyboard actions, bash commands, or file edits — to be executed in desktop environments. Daytona 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.
To try n2 without building anything, open the Playground. To drive your own Mac, install Yutori MCP — it ships a local loop already wired up.

Components

The loop is N2ComputerAgent from the Python SDK: 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, 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. It can also call bash, or one of the file tools (read, write, edit) that the example serves through the SDK’s ShellFileToolsMixin. N2ComputerAgent runs whatever comes back and replies with a fresh screenshot for the batch, or the tool’s own output otherwise.

Prerequisites

  • uv — the script declares its dependencies inline (PEP 723), so uv run picks a Python 3.10+ interpreter and installs them into an isolated environment. Nothing to pin by hand.
  • A Yutori API key — see Authentication.
  • A Daytona API key from the Daytona dashboard.
The two are not interchangeable when both are present: the SDK resolves the key as explicit argument, then YUTORI_API_KEY, then the stored login. A stale YUTORI_API_KEY left in your shell silently shadows yutori auth login and the run fails with 401.

Run it

Run the script straight from the SDK repository with a task — uv fetches it, resolves its inline dependencies, and runs it:
Each turn prints the model’s own text and the call it made:
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. Two flags are worth knowing: --max-steps N raises the turn budget from its default of 50, and --record saves a screen recording of the desktop to n2-daytona-run.mp4 when the run ends, which is how you review a run after the sandbox is gone.

Adapting it with a coding agent

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

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. N2ComputerAgent rescales them to the desktop’s native pixel size before calling the adapter, so the adapter does not do any conversions.
  • Typingkeyboard.type fails on control characters and silently truncates long strings, so send \n and \t as key presses and chunk the rest.
  • Scrolling — Daytona wants wheel notches. The adapter declares a model_action parameter, so the loop hands it the model’s own call, whose direction and amount are already notches. The pixel distance the loop passes otherwise — one notch of amount being a tenth of the screen — is converted back as the fallback.
  • bash results — the command’s output, with an Exit code N header prepended on failure. The SDK’s format_shell_output truncates each result at 30,000 characters, and N2ComputerAgent holds a 256 KiB backstop above that, so a large cat cannot push the request over the 10 MB limit.
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.

Knowing when to stop

Without a limit, a loop will keep going on a task it cannot finish. N2ComputerAgent’s max_steps caps the number of model turns; the script defaults to 50 and exposes it as --max-steps. When that budget is spent, agent.stopped_by reads "max_steps", and the script spends one last completion asking the model to summarize how far it got before exiting non-zero. For a stop condition of your own, a callback’s on_run_continue runs before each turn and ends the run 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 the adapter cannot run at all comes back as an [ERROR] bash failed: ... result, and a nonzero exit code is simply part of the output. A timeout is deliberately not an error: the adapter reports it as an ordinary Command timed out after 120s result, because an expiry is something the model should react to rather than a failure of the harness. 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, which is what the server would drop anyway. Trimming messages yourself risks the model repeating work it has already done. A run long enough to approach the context window compacts instead of truncating — compactor="auto" is the default, and each pass fires the on_compaction callback the script prints from.

Navigator n2

The model reference — tools, actions, coordinates, and request fields.