Behind every chat, DataMind Oda runs a live worker called an agent. The agent drives the chat's loop: it reads the session that records the conversation, builds each model request, runs tool calls and writes results back as they settle. This page describes the runtime itself: how agents are created, resumed and disposed, the states they move through, the operations that drive a conversation, and the options and limits that shape a run. The record an agent works on is described on Sessions and the transcript.
get), list the live agents (list), list the roots (roots), and check whether one agent belongs to another (isOwnedBy).agent "<id>" is already registered.disposed as its cause.Agents can also be declared in the profile the runtime loads, and declared agents start by themselves when it loads. Each declares:
| Field | What it sets |
|---|---|
id (required) | The agent's label, and the base of its session id. |
provider and model | The model route; both are needed before a request can be built. |
reasoningEffort | Optional; a whole number of at least 1. |
maxTokens | Optional token cap; a positive safe integer. |
cwd | The folder the agent works in; an absolute path. |
sessionId | Pins the session id. |
resumeSessionId | Resumes a stored session by id; cannot be combined with sessionId. |
sessionId or resumeSessionId mints a new session id on every startup, of the form <id>-session-<uuid>, so a restart never collides with an earlier run.sessionId and resumeSessionId are mutually exclusive.agents "<first>" and "<second>" use duplicate exact session identity "<id>". An empty id is refused.agent maxTokens must be a positive safe integer.config-driven resume of "<id>" failed instead of failing the whole start.A handle drives the conversation through a small set of operations. They differ in where what they carry goes and whether the agent wakes for it.
send hands the agent a message; it is the general delivery call that the named forms are built on.user for a person's stop, parent for a parent agent, hook with a reason string, disposed when the agent is torn down), and the first cause to arrive decides. A plain cancel also clears input that is still waiting; a cancel asked to keep the inbox leaves the waiting input for a later wake. With nothing running, a cancel changes nothing.idle. A maintenance run asked for while work is active is refused: agent "<id>" already has active work. Two maintenance runs never overlap.How these appear in the app is on Prompts, queue and steering; what a stop leaves behind is on Stopping Oda and queued messages.
idle or running.idle, so an idle status can still mean housekeeping is in flight.running and a single change back to idle.idle.agent "<id>" has no provider/ model: set AgentOptions. provider and AgentOptions. model or supply both via the agent/ request waterfall.reasoningEffort is optional and a whole number of at least 1; maxTokens is an optional cap and must be a positive safe integer.maxParallelToolCalls, ten by default, or 1 to run calls one at a time): the update keeps the same loop and the agents already composed, and nothing restarts. A value the loop cannot take is refused, and the previous value stays in force. The declared agent list is not part of this; it is read at startup. What the cap does is on Tools and how they are offered.no agent factory registered.it is not a sandbox or an authority boundary.A chat is what you see: its history, its name and its place in the sidebar. Under it sit a session, the stored record, and an agent, the live worker that acts on it. This page is about the worker; the record is described on Sessions and the transcript.
The agent's loop stops, the agent unregisters, the session is removed and the scope unwinds, taking everything registered under the agent with it. Only the holder of the handle can dispose it, and a turn that was running is closed as aborted with disposed as its cause.
No. A second agent cannot join under an id that is in use (agent "<id>" is already registered), and two declared agents cannot resolve to the same exact session identity (agents "<first>" and "<second>" use duplicate exact session identity "<id>").
It reached a model request with no route: no provider and model were set for it, and none were supplied while the request was built. The refusal says what to set: agent "<id>" has no provider/.
Without sessionId or resumeSessionId, every startup mints a fresh id of the form <id>-session-<uuid>. sessionId pins one identity for the agent instead, and resumeSessionId resumes a stored session by id; the two cannot be combined (sessionId and resumeSessionId are mutually exclusive).
Two: idle and running. Maintenance presents as idle, and a change is reported only when it happens: the same status is never reported twice in a row.
Yes. A running loop accepts a live update, such as the parallel tool-call cap, and keeps the same loop and the agents already composed. A value it cannot take is refused, and the previous value stays in force. The declared agent list is the exception: it is read at startup.
A steer joins the turn that is running and is taken at its next step, so the turn continues. An inject stages context without opening a turn: the agent stays idle, and the context goes with a later request.
The failure is logged and contained: it cannot stop the loop or starve the other listeners. A registration that fails is rolled back, so nothing half-applied stays behind.
No. There is no built-in turn budget: the loop keeps going until the work ends, is stopped, or fails.
No. Tools and prompt sections registered through one agent belong to that agent and are removed with it. Scopes pass definitions down, with the nearest one winning, and events up; a chain cannot loop, and a scope is not a security boundary.