Recursive AI

Instances: format v1

All onellm docs

An instance is one run of a definition and everything it has said and done. It is a YAML file named <instanceId>.yaml. The rules for unknown keys, null values, empty lists and byte-for-byte saving in spec/definitions.md apply here too.

Layout

An instance file is an agent definition (every key of spec/definitions.md, as they were when the run started) plus these keys:

KeyTypeMeaning
instanceIdstring1 to 128 characters of letters, digits, - and _. It is the file name.
agentDefinitionstringPath or URI of the definition the run started from.
requestedModelstringThe model as first requested, before alias resolution. model holds the resolved name.
maxImageSizeintegerThe longest image edge in pixels the instance sends. Only ever tightened.
statestringThe lifecycle state, below.
callerInstanceIdstringThe instance that last messaged this one.
spawnerInstanceIdstringThe instance that started this one. Set once.
callerToolCallIdstringThe tool call of the caller that started this instance.
delegationPromptstringThe prompt a calling agent gave when it started this instance.
runStartedAtstringRFC 3339 time the current run started.
metadata.renderedSystemPromptstringThe system prompt as rendered for the latest turn: what the model saw.
pendingApprovalslistTool calls waiting for a decision, below.
compactionslistRecords of context compaction, below.
messageslistThe conversation, in the message format (spec/messages.md).

OneLLM writes the keys in this order, with messages last, so that a long conversation can be appended to without disturbing the header. A reader must accept any order.

Keys other tools put in an instance file (a bound subscription, a wait record, context watchers, ...) are unknown keys and are kept. metadata keys other than renderedSystemPrompt are kept.

States

state is a string. The values OneLLM uses:

ValueMeaning
runningA run is in progress.
waitingThe agent finished its turn and waits for input.
waiting_for_approvalA tool call needs a person's decision; see pendingApprovals.
stoppedThe run ended. A new message continues the instance.
errorThe run failed.

Any other string loads and saves unchanged. A runner that finds a state it does not know treats the instance as stopped.

pendingApprovals

Each entry: toolCallId, tool, messageId (the message that holds the call, when it has been written), inputs, reason and requestedAt (RFC 3339). Approvals are saved with the instance, so an approval or denial still works after a restart. Unknown keys are kept.

A decision is written into the same entry as two more keys: decision (approved or denied) and, for a denial, reason for the person's note. The entry stays until the run that follows has answered the call. While a turn waits for approval, its assistant message is saved with the held calls unanswered; the other calls of the turn already have their results. When every entry is decided, the run executes the approved calls, answers the denied ones with an error result that carries the reason, and replaces the saved message with the finished turn.

compactions

Each entry records one context compaction: strategy (clearToolResults, compact or providerNative), at, replacedMessageIds, summaryMessageId and cost (a decimal in USD, when the compaction made a model call). Two unknown keys are written: atMessageId (the model call whose context triggered it, so a strategy runs once per call) and replacedCost (a decimal as a string: what the removed messages and their tool calls cost, so the instance's total does not shrink when they go).

Keys the agent loop adds

Written as unknown keys, so other readers keep them:

KeyMeaning
error{type, message} of the failure that put the instance in error. A run that reaches maxIterations ends in error with type MaxIterations and the message max iterations (N) reached (maxIterations defaults to 200).
cancelledtrue when the latest run was cancelled. The instance is stopped and keeps the partial turn: finished tool calls have their results, interrupted ones an error result saying they were cancelled.
capabilitiesThe grants the run started with, so a resumed instance is bounded by them: readRoots, writeRoots, workingDirectory, shell {enabled, approval, commands?} and network {anyPublic, hosts}; and tools, the tool names a sub-agent started from a file was limited to. A reader that ignores it loses nothing but the narrowing. The runtime intersects it with its own grants and never widens from it. It is written with capabilitiesRecorded: true. A file with the marker but no well-formed capabilities entry is not run (it gets no grants); a file with neither, written before grants were recorded, runs with the resuming runtime's grants.
askUser{toolCallId, messageId} of an ask_user call that has no answer yet. The instance is waiting; the next message from the person or host (source api or user) becomes that call's result instead of a separate user message; other messages are added behind it.

Fork and duplicate

A fork copies an instance under a new id, optionally only up to and including one message. It starts stopped with no pending approvals and no runStartedAt, and records where it came from in an unknown key forkedFrom (instanceId, messageId). A duplicate is a fork of the whole conversation.

Assets

Assets of an instance live beside it, in a directory <instanceId>.assets/, in a file store. Messages refer to them by asset (storage, key, hash, content type, size, name).

Persistence layout and cost

A save writes a temporary file in the same directory, flushes it to disk and renames it over the instance, so a reader sees the old file or the new one. A lock file <instanceId>.lock, held open without sharing for the life of a run, keeps a second runner out, across processes; the operating system drops it if the holder dies.

Messages are immutable once appended. A save re-serializes the header and any message it has not written before, and reuses the text of the others, so adding a turn to a long instance costs the new turn plus the write of the file.