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:- Console:
http://127.0.0.1:8080 - Direct API:
http://127.0.0.1:3000/v1
GET /v1/context:
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:
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,000through3,600,000milliseconds. - Exactly
32maximum turns.
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.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.
Through the API
Read the latest complete agent config, append onemcpBindings 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.
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/jsonwhen it has a JSON body.- A non-empty
Idempotency-Keyof at most 200 characters. - A principal scoped to the path’s project with the required authorization scope.
* 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:201 Created. Its response has this exact field shape; UUIDs,
hash-dependent IDs, and timestamps below are illustrative values:
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.201 Created with the newly inserted immutable version:
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
enumvalues and primitiveconst. title,description, and up to eight JSONexampleson every schema node.minLength,maxLength, and thedate,date-time,email,time,uri, anduuidstring formats.minimum,maximum,exclusiveMinimum,exclusiveMaximum, andmultipleOfnumeric constraints.minItemsandmaxItemsarray bounds.- Open objects, closed objects with
additionalProperties: false, and typed maps whoseadditionalPropertiesis another supported schema.
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.
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.
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
Setsandbox.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
SendagentId 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.
201 Created without waiting for the model:
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-levelstatus is completed, failed, cancelled,
or timed_out:
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, anddebug.workspaceBackups.
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:
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
runningstate 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.
Through the API
Useful read endpoints are: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:
id, the product event kind in event, and a
public product-event object in data. Reconnect with the most recent ID:
?once=true to read the currently
available page and close the connection.
Resolve caller tools correctly
For acaller tool, claim the work, retain the returned positive-string
fence, and submit a versioned result envelope. The value must match the
published outputSchema:
{ "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:
Errors and cancellation
Errors use one public envelope: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:
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, sendDELETE /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
agentIdor by one of itsagentVersionIds. - 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
1through256. New agents default to32to preserve existing behavior;128is 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_exceededand a safe explanation. Raising the limit does not extend the timeout. - The console caps the execution budget
timeoutMsat 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.
restrictedsandbox 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.

