Skip to main content
An OAO agent is a stable definition with an immutable sequence of versions. A session pins one version, and every initial or follow-up message creates a durable run on that same session and thread.
Creating an agent publishes version 1 in the same transaction. The current MVP does not have a separate unpublished-draft workflow.

Before you begin

Start the local stack and open the console:
The default URLs are:
  • Console: http://127.0.0.1:8080
  • Direct API: http://127.0.0.1:3000/v1
Runtime execution requires a configured project model preset. A Daytona connection is required only when the published version enables a sandbox. There is no selectable fake model or sandbox profile. For direct API calls, use an OAO API key (organization-wide; the project in the URL path selects where it acts) and the project ID from GET /v1/context:
The context response includes project.id and the publication-time model allowlist in activeModelPresets. That list contains every provider-backed preset approved for this project that is currently available. A preset is omitted when it lacks a provider connection or platform credential decryption is unavailable:
Human browser sessions can also call the API with OAO’s HttpOnly session cookie. Cookie-authenticated POST, PUT, PATCH, and DELETE requests must include an Origin exactly allowed by APP_ORIGIN. Project API-key requests authenticate with Authorization: Bearer oao_... and do not rely on browser cookies.

Create through the console

1

Create the definition

Open Agents, select Create agent, enter a name and optional description, and choose an approved model preset. You can also enable the sandbox, choose a configured organization Daytona connection, select its network policy, optionally select an active Daytona snapshot, select exact tool capabilities, and attach active Skill versions before submitting the form.The console publishes version 1 with this initial configuration:
The form selects the first available provider-backed project preset. The sandbox starts disabled and does not require a Daytona connection until you enable it; when enabled, the form selects the first available Daytona connection. The picker searches every preset returned by GET /projects/{projectId}/model-presets, showing the preset key and whether it is a deployment or project preset, and leaves an unavailable preset visible but unselectable. The generated agent key is the normalized name plus a random eight-character suffix. Use the API if you need to choose the key explicitly.
2

Review and publish a new version

On the agent detail page, edit the latest version’s system instructions, approved model preset, Harness Operations, bound Skill versions, exact delegate roster, sandbox switch, network policy, or timeout. The searchable model list is the project’s approved preset catalog, and the editor shows the resolved model plus routing policy or OpenAI generation settings for the selected preset with a link to Manage models. Select Publish new version to append an immutable version.Linking a different model, Skill version, or MCP toolset/policy version is always a publication. Configure remote tools under MCP connections, then select the immutable toolset and exact-origin policy on the agent editor. Publishing a newer Skill does not update an existing agent version. Existing sessions stay attached to the agent and Skill versions they were created with. See Versioned Skills.Harness Operations are authored under their own editor section. Each one becomes a focused model-callable tool that inherits this Agent version’s model, rendered instructions, tools, complete Skill catalog, and live sandbox. It receives { task: string } and returns a schema-validated object. It is not a separate Agent, and there is no per-operation Skill selection. See Harness Operations.Delegate selection works the same way: the editor records the selected agent’s exact latest version. The Delegates section lists only the agents linked to this version; Add delegate opens a dialog where you can search every other agent in the project by name, key, or description and pick one or more published, sandbox-compatible agents. Unavailable candidates stay visible with the reason. A coordinator can delegate only to that published roster, and each child keeps its own persistent session while sharing the coordinator workspace. See Multi-agent orchestration.Console validation requires:
  • At least 20 characters in the system instructions.
  • A preset returned by GET /projects/{projectId}/model-presets.
  • A timeout from 1,000 through 3,600,000 milliseconds.
  • Exactly 32 maximum turns.
Selecting an older version is read-only. Return to the latest version to prepare another publication.
3

Create a session

Open Sessions, select Create session, choose a published agent, enter a title and first message, optionally choose supported text, image, Office, PDF, or email files, and submit.The session pins the agent’s latest version at that moment and automatically copies its exact Skill bindings. Do not send Skills in the session request. Creation returns while the first run is queued; the runtime finishes it asynchronously.
4

Continue after settlement

Open the session. When its latest run is completed, failed, cancelled, or timed_out, the Continue session form appears. Submitting a message creates a new durable run on the existing session and thread. You can attach another supported set of files from the same composer.
The current API-key, archive, rename, resume, and branch-replay controls are not implemented in the console.

Connect MCP tools to an agent

An agent version does not point directly at an MCP URL or secret. It pins an immutable restricted toolset version and an exact-origin credential-policy version. First complete Add an MCP server, then bind those versions using either path below.

Through the console

1

Open the latest agent version

Open Agents, select the agent, and select its latest version. Older versions are read-only.
2

Select the MCP toolset

Find MCP toolsets and check the restricted toolset you want to expose. If the list is empty, return to MCP connections, complete discovery, and publish a toolset first.
3

Choose the credential policy

Under the selected toolset, choose the exact-origin credential policy that authorizes its server endpoint. The editor derives a collision-safe namespace from the toolset key; the model will see names such as mcp__example_read_tools__list_items.
4

Publish and test

Select Publish new version, then create a new session for the agent. Existing sessions remain pinned to the earlier agent version and will not acquire the MCP tools.
You can also attach the agent on the last screen of the Add MCP server wizard. That path performs the same immutable agent-version publication and lets you confirm the namespace before finishing.

Through the API

Read the latest complete agent config, append one mcpBindings entry, and publish the complete next config. This example expects the toolset_version_id and policy_version_id produced by the API setup in Remote MCP servers.
The namespace must be unique within the agent version and contain 1–64 characters. It must start with a lowercase letter and use lowercase alphanumeric segments separated by single underscores, so consecutive or trailing underscores are invalid. Publication requires agent:write and mcp:bind; reading the agent and MCP resources requires agent:read and mcp:read.

Create through the API

Every mutation requires:
  • Content-Type: application/json when it has a JSON body.
  • A non-empty Idempotency-Key of at most 200 characters.
  • A principal scoped to the path’s project with the required authorization scope.
The routes used in this guide enforce these project scopes: * satisfies every project action. Human principals must also have a current membership in the project. The project ID in the URL must match the principal’s project; otherwise the API returns 403 forbidden before resource access. Use a new idempotency key for each logical mutation. Retrying the same route, key, principal, project, and JSON body within 24 hours returns the stored public response and sets Idempotency-Replayed: true. Reusing the key for a different body returns 409 idempotency_conflict.

1. Create the agent and version 1

This example uses explicit organization model and Daytona provider keys:
The route returns 201 Created. Its response has this exact field shape; UUIDs, hash-dependent IDs, and timestamps below are illustrative values:
Save both IDs:
You may send initialConfig instead of config. One of them is required; OAO does not publish a fake-backed default version. An empty description is stored as no description and is returned as "description": "".

2. Publish a tool-enabled version

Publication always sends the complete next configuration. It does not patch the previous version.
The route returns 201 Created with the newly inserted immutable version:
The example contentHash is illustrative; the API returns the actual lowercase 64-character SHA-256 hash of the canonicalized configuration. Publishing the same configuration twice conflicts because content hashes are unique per agent.

Tool contract

Each published tool has these fields: Both root schemas must have type: "object". properties, required, and additionalProperties are optional; their defaults are {}, [], and true. The same canonical schema is compiled to Flue for model guidance and local validation. The model receives the tool description plus property titles, descriptions, examples, types, required fields, nesting, enums, and supported constraints. OAO remains authoritative and validates arguments before a tool can create an approval or execute. Schema version 1 supports:
  • String, number, integer, boolean, null, array, and object types.
  • Nullable forms containing one type and null, such as "type": ["string", "null"].
  • Nested objects and homogeneous arrays with items.
  • Primitive enum values and primitive const.
  • title, description, and up to eight JSON examples on every schema node.
  • minLength, maxLength, and the date, date-time, email, time, uri, and uuid string formats.
  • minimum, maximum, exclusiveMinimum, exclusiveMaximum, and multipleOf numeric constraints.
  • minItems and maxItems array bounds.
  • Open objects, closed objects with additionalProperties: false, and typed maps whose additionalProperties is another supported schema.
The early contract intentionally rejects pattern, defaults, coercion, oneOf, anyOf, allOf, discriminated unions, $defs, $ref, external or recursive references, tuple schemas, and every unknown keyword. Rejection is fail-closed and includes the exact schema path. Every name in required must be unique and present in properties; __proto__, constructor, and prototype property names are prohibited. A schema is limited to 64 KiB, 12 levels, 512 nodes, and 256 properties. enum and const nodes may include a type and annotations, but not additional constraints; this prevents a compiled provider schema from losing redundant or conflicting guidance. OAO does not silently downgrade a published schema for a model provider. The MVP subset is chosen to work through the pinned Flue conversion and configured OpenAI/OpenRouter adapters; if a downstream provider rejects that schema, the model invocation fails visibly and no tool executes. Local argument and result validation remains authoritative even when a provider uses the schema only as generation guidance. In the console’s agent-version editor only the Agent definition section (model preset and system instructions) starts open. Every other section — Tools, Harness Operations, Skills, MCP toolsets, Delegates, Sandbox policy, and Recent sessions — starts collapsed and summarizes what the version binds in its heading (counts, or the sandbox provider, network policy, capabilities, and timeout); select the heading to open it. The model preset picker shows the selected preset’s name, and one line below it lists the model identifier, origin, and routing settings with a link to Models. The editor lists tools as collapsible rows inside the Tools panel. Each row opens to a syntax-highlighted preview of the input and output schema. Add tool and Edit definition open a modal editor for one tool definition as JSON, and Edit JSON opens the complete tool list for authoring or pasting in bulk; both validate the JSON before it can be applied to the draft. Publishing still creates a new immutable version; existing versions are never rewritten. See Add tools to an agent for the complete console and API workflow across caller, platform, sandbox, and MCP tools.
Tool schemas are public agent configuration and are sent to the selected model provider. Never place credentials, authorization headers, personal secrets, or secret-bearing examples in names, descriptions, titles, enums, constants, or examples.
caller tools create durable work for an external operator or integration. platform tools require a matching handler registered in the runtime worker. approval: "always" creates a durable approval before either owner can execute. Retryable tool failures are returned to the model automatically. The model may retry twice after the initial failure, for three total attempts per tool within one run. OAO appends retry instructions to the managed system prompt and enforces the attempt ceiling before another external call is published. tool_expired, tool_failed, platform_tool_failed, invalid_tool_arguments, and invalid_tool_result are retryable. Approval denial or expiry, cancellation, and tool_retry_exhausted are not. Every retry is a new fenced tool call with its own ID; successful completion resets the consecutive-failure count.
The default runtime does not register application-specific platform tool handlers. A published platform tool is valid, but an invocation fails with the safe platform_tool_failed outcome until the operator supplies a handler.

Model presets

config.modelPreset must name a durable project preset linked to an OpenRouter, OpenAI, Anthropic, or xAI provider in PostgreSQL. Runnable deployments do not expose a built-in deterministic preset. The check runs inside the same tenant transaction as the write, so an unknown key is rejected with 400 before an agent or version row is created. Hosted publication requires a provider connection in the same organization and platform credential encryption. Provider API keys are write-only and stored as tenant-scoped ciphertext, never in the agent config. Hosted model identifiers start with openrouter/, openai/, anthropic/, or xai/ and must exist in the matching provider catalog. OpenRouter saved presets are exposed as openrouter/@preset/<slug>. See Adding models for the approval workflow and HTTP API for the preset contract.

Sandbox policy

Set sandbox.enabled to true to attach the runtime’s configured sandbox adapter to the agent:
provider is the key of an organization Daytona connection, such as daytona-primary. snapshotId is required when the sandbox is enabled and must be an active snapshot UUID returned by that provider’s snapshot discovery route. The selected UUID is immutable version data. OAO does not provide an image fallback. network: "none" blocks external egress. restricted uses that connection’s domain/CIDR allowlist and is rejected at publication when the allowlist is empty. Capabilities map to tools as follows: filesystem_read adds read; filesystem_write adds write and edit; shell adds bash, grep, and glob; and browser adds browser_navigate, browser_snapshot, and browser_interact. The selected provider, snapshot, network policy, and capabilities are immutable version data. The API key, target, and allowlist remain mutable project connection configuration. See Daytona sandboxes. When a coordinator version has delegates, its sandbox enabled state, provider, snapshot, and network policy must match every selected child version because their threads share one workspace sandbox. After changing the coordinator snapshot, publish compatible child versions first or remove the incompatible delegate bindings before publishing. The publication contract does not expose arbitrary CPU, memory, disk, image, or target selection. Those settings come from the selected Daytona snapshot.

Create and continue a session through the API

1. Create the session and first run

Send agentId to pin its latest published version. You can alternatively send agentVersionId directly. If you send both, the version must be that agent’s latest published version.
The route atomically creates a thread, session, initial user message, queued run, product events, audit entry, and durable runtime wake. It returns 201 Created without waiting for the model:
Save the session and run IDs from the real response:
Both this route and later-turn routes accept an optional files array. Each file contains name, contentType, and canonical dataBase64; bytes are never returned in the response, transcript, audit log, or SSE. See Send files to an agent for supported types, limits, and complete SDK/API examples. The agent version must enable a sandbox with read tooling, and binary files require shell. OAO copies the original bytes into that sandbox without preprocessing. A session or turn may omit its text field only when at least one file is present.

2. Wait for the run to settle

Read the session until top-level status is completed, failed, cancelled, or timed_out:
The detailed response contains the session summary plus:
  • runs: every run in chronological order.
  • transcript: public, redacted messages across the session.
  • timeline: safe runtime entries.
  • pendingWork: unresolved tool and approval work.
  • debug.productEvents, debug.modelInvocations, debug.toolCalls, debug.approvals, debug.sandboxes, and debug.workspaceBackups.
The top-level session usage includes inputTokens, outputTokens, cacheReadTokens, and cacheWriteTokens. Cache reads are prompt tokens reused from the provider cache; cache writes are prompt tokens newly added to it. The same fields are available for each entry in debug.modelInvocations. The run state progression can include queued, running, waiting_for_tool, waiting_for_approval, and retry_scheduled before a terminal state.

3. Send a follow-up message

The latest run must be settled before the next message can be accepted:
redactedInput is accepted as an alias for message. The route returns 202 Accepted with the new run object:
Follow-up runs retain the session’s agentVersionId. Publishing a newer agent version does not upgrade an existing session; create a new session to use the new version.
POST /runs/:runId/resume also accepts message or redactedInput, but only for a settled run. It creates another run on that run’s existing session. The console currently uses the session follow-up route and does not expose the resume action.

Validate and debug

In the console

Open a session and use:
  • Transcript for user, assistant, reasoning, and tool-facing records in chronological order. User and assistant messages render CommonMark plus GitHub Flavored Markdown, including lists, emphasis, links, code, task lists, and tables. Raw HTML is not rendered. A persistent sandbox in running state is ready for the session and is not shown as pending work.
  • The session sidebar for the Agent name and exact pinned Agent version used by the session, alongside its model, Tools, Skills, files, cost, and token usage. Files present in the latest persistent workspace backup can be downloaded through the secured Files API without reopening the Daytona sandbox.
  • The sessions overview and compact session facts for the latest run’s elapsed time. The overview boxes active timers in a red live status badge with a play icon, and the detail view colors its active counter red as well. The timer updates every second while the run is active and freezes when the run settles.
  • The message composer for another durable run after the latest run settles. Select Send or press Command+Enter; Enter by itself adds a new line. On viewports 1024px wide or narrower, or 820px tall or shorter, the composer starts minimized as a single Send a message to the agent bar so it does not cover the transcript; select it to open the form, and use Minimize composer to fold it away again. A draft and any attached files survive minimizing. The session header is also more compact there: the ID eyebrow is hidden and the fact strip scrolls horizontally in one row.
  • Debug for a chronological waterfall assembled from timeline entries, public product events, model invocations, caller tool calls, sandbox commands, approvals, and sandboxes. Sandbox activity includes its model-facing arguments, result, state, and timing.
  • The timeline strip above the transcript. Hovering a block shows its actor, a snippet, duration, and offset; hovering a message or row in the transcript highlights its block and shows the same tooltip there. Clicking a block jumps the transcript to that event.
  • Reasoning and sandbox commands are expandable rows in Transcript. Each reasoning row shows the provider thinking text when supplied and the full model invocation duration. Each sandbox row shows its recorded input/output.
  • The event inspector for rendered metadata. Its Raw view is available for reasoning and sandbox calls, and remains unavailable when only a redacted public projection exists.
  • Pending Work to inspect approval gates and caller-owned tool calls.
Provider thinking, file/edit content, shell commands, browser arguments, and tool output are part of the authorized session read model. Credentials, authorization headers, provider reasoning signatures, and unrestricted product events remain excluded. A model catalog’s reasoning-support flag still describes capability; thinking text appears only when the provider returns it.

Through the API

Useful read endpoints are:
List endpoints use cursor pagination. limit defaults to 50 and must be from 1 through 200; pass pageInfo.nextCursor as the next request’s cursor. The run timeline instead uses its numeric entrySequence as the cursor. For live updates, connect to the durable project event stream:
Each SSE frame has an opaque id, the product event kind in event, and a public product-event object in data. Reconnect with the most recent ID:
The cursor is backed by PostgreSQL project position; notifications only wake readers and are not the source of truth. Use ?once=true to read the currently available page and close the connection.

Resolve caller tools correctly

For a caller tool, claim the work, retain the returned positive-string fence, and submit a versioned result envelope. The value must match the published outputSchema:
The relevant mutation endpoints are:
Claims and results are fenced so a stale worker cannot submit after losing its lease. Approval decisions use { "status": "approved" } or { "status": "denied" }, with an optional note of at most 2,000 characters. OAO validates successful value objects against the immutable agent version’s published outputSchema in the same transaction, before PostgreSQL persists the result. A malformed success is replaced with a safe invalid_tool_result failure; the raw malformed value is not persisted or sent to the model. The 202 response includes a normalizedFailure object with the first failing path, and Flue resumes the agent so it can make a new attempt:
The Console Pending Work form accepts only the successful result value and adds the versioned envelope automatically. Failure envelopes remain available through the API and SDK for integrations that need to report an explicit tool failure.

Errors and cancellation

Errors use one public envelope:
Possible codes are bad_request, unauthenticated, forbidden, not_found, conflict, idempotency_conflict, rate_limited, and internal_error. Keep the x-request-id response header or error.requestId when investigating. Internal failures intentionally return a generic message. Cancellation is asynchronous:
The 202 Accepted response reports the current run. Continue reading the run or event stream until it settles; acceptance is a cancellation request, not proof that active model or tool work has already stopped.

Delete an agent

Delete an agent from the console with the trash icon at the end of its row on the Agents page, or with Delete agent in the header of its detail page. Both ask for confirmation first. Through the API, send DELETE /v1/projects/{projectId}/agents/{agentId} with an Idempotency-Key. Deletion archives the agent instead of removing rows, because sessions, runs, and delegate bindings reference its immutable versions:
  • The agent leaves the agents list, the delegate picker, and every lookup; reading it returns 404.
  • It can no longer publish versions or start sessions, whether addressed by agentId or by one of its agentVersionIds.
  • Existing sessions keep their full transcripts and still show the agent’s name. Coordinators that already pin one of its versions keep delegating to it.
  • Its key is released, so a new agent can reuse it.
  • The audit log records agent.deleted. There is no restore.

Current MVP limitations

  • Agent creation always publishes version 1. There are no draft, rename, or description-edit routes. Deletion archives the agent (see Delete an agent); there is no restore route.
  • Versions are append-only and content-addressed. Identical content cannot be published twice for one agent.
  • The console can edit instructions, model preset, sandbox provider, sandbox capabilities, network policy, and timeout while creating an agent and on its latest version. Caller/platform tools are authored as JSON in the Tools panel; there is no form-based schema builder.
  • Model presets can be added, duplicated into a new key, and archived, but never edited in place. Preset rows are append-only by design.
  • Sessions never float to a newer version, and only one unsettled run is allowed per session.
  • The console exposes cancellation but not API run resume or branch replay.
  • Maximum model turns is editable in agent configuration: a required integer from 1 through 256. New agents default to 32 to preserve existing behavior; 128 is useful for longer bulk imports. The upper bound limits runaway work and cost. Publishing creates an immutable version; existing sessions retain their selected version and limit. Start a new session to use the new version.
  • Turns are reserved durably per run before model invocation, including scratch model calls. Compaction does not reset the budget; concurrent calls share it. Replaying the same Flue turn ID does not consume an additional reservation. Provider-internal HTTP retries are not separate turns. Each new run has its own budget, and delegated runs use their own agent version’s limit.
  • Reaching the budget fails the run with model_turn_limit_exceeded and a safe explanation. Raising the limit does not extend the timeout.
  • The console caps the execution budget timeoutMs at one hour, while the API publication schema currently enforces only the 1,000 ms minimum. Approval waiting pauses that budget. Flue separately caps each new submission at 48 hours of total wall time, shared across all approval waits and execution; submissions that already started retain their stored ceiling. See Approval waiting window.
  • Automated tests may inject deterministic provider doubles at adapter boundaries, but those providers cannot be selected through the API or console and are not runtime deployment options.
  • restricted sandbox networking uses the selected project connection’s mutable egress allowlist; the agent version records the policy mode and provider key, not a copy of that allowlist.
  • API keys can be created and listed in the console. The plaintext secret is available only in the acknowledgement dialog immediately after creation.
  • The console fetches up to 100 agents or sessions and then applies search, status, date, and ten-row pagination locally. Use API cursors for complete large collections.