Daita agents / guide
Configure model routes, outer run budgets, tool-surface bounds, workspace admission, and the single-writer state root.
Persistent configuration is intentionally small:
from daita import AgentConfig, LoopLimits
config = AgentConfig(
limits=LoopLimits(
max_steps=24,
max_total_tokens=100_000,
max_wall_time_seconds=300.0,
max_estimated_cost_usd=None,
)
)AgentConfig contains an optional ModelRoute and LoopLimits. Explicit runtime arguments to Agent.create() or Agent.open() can override limits for that composition without rewriting a persisted model route.
| Field | Default | Purpose |
|---|---|---|
max_steps | 24 | Maximum model/tool progression steps |
max_total_tokens | 100000 | Aggregate provider-neutral token ceiling |
max_wall_time_seconds | 300.0 | Outer wall-clock deadline |
max_estimated_cost_usd | None | Optional fail-closed estimated-cost ceiling |
These are outer boundaries. They do not create checkpoints, resumable runs, automatic whole-run retries, or a verifier pass.
from decimal import Decimal
from pathlib import Path
from daita import Agent, LocalWorkspace, LoopLimits
agent = await Agent.open(
"atlas",
workspace=LocalWorkspace(Path("/absolute/path/project")),
limits=LoopLimits(
max_steps=12,
max_total_tokens=40_000,
max_wall_time_seconds=90,
max_estimated_cost_usd=Decimal("0.50"),
),
)LoopLimits also owns execution bounds such as tool calls per response and run, frozen run-catalog size, pinned and loaded definitions, toolbox search/load results, individual tool-result size and depth, parallel reads, context evidence bytes, and side-effect recovery time.
Defaults include 16 tool calls per model response, 64 per run, 512 frozen catalog entries, 32 pinned tools, 16 loaded on-demand tools, eight parallel reads, four parallel reads per source, and 256 KiB per normalized tool result. Constructor validation preserves the relationships among these bounds.
Change advanced limits only when the surrounding provider and application budgets have been reviewed. Larger values do not grant capabilities, expand source permissions, or bypass per-capability bounds.
configure_model() validates and persists one route for the next open:
route = await agent.configure_model(
provider="gemini",
model="gemini-3.6-flash",
api_key=api_key,
)Custom OpenAI-compatible endpoints require base_url. Unknown model identities also require hard token limits:
route = await agent.configure_model(
provider="acme",
model="internal-chat",
base_url="https://models.example.com/v1",
api_key=api_key,
context_window_tokens=128_000,
max_output_tokens=8_192,
)Local API composition requires an explicit LocalWorkspace. The CLI admits --workspace or chooses a safe default for interactive use. Workspace and state roots must not overlap.
By default, Daita owns application state beneath ~/.daita. Pass an absolute root to isolate environments:
workspace = LocalWorkspace(Path("/srv/my-app/workspace"))
agent = await Agent.create(
"atlas",
workspace=workspace,
root="/srv/my-app/daita-state",
)One agent home admits one process writer. A foreground application and resident host must hand off the lock; they cannot share it concurrently.
Hosted composition uses hosted=True and deliberately admits no local workspace or local artifact-delivery surface.
The first production state format has not yet been frozen. Unreleased development homes use one current physical schema and record shape; a state-shape change may require recreating the development agent home. Daita does not add compatibility decoders or bridges for unreleased formats.
Once the first production baseline is frozen, later durable changes use the existing checksummed copy-and-swap migration engine under the agent-home writer lock. Legacy pre-1.0 framework state is a different product family and is not migrated in place.