Configuration Reference
Every field IntaGrin recognizes in ai.yaml, grouped by section (one per config model), generated directly from the Pydantic schema in src/intagrin/config/schema.py.
This file is generated by scripts/generate_config_reference.py — do not hand-edit it. A missing description means the schema field itself needs one, not this file.
AppConfig below is the root of ai.yaml; every other section is a nested config block referenced from it or from another section.
AppConfig
| Field | Type | Default | Description |
|---|---|---|---|
version | string | required | ai.yaml schema/format version. |
name | string | required | Project name. |
description | string | null | None | Short human-readable summary of what this app does. |
state_schema | string | null | None | Global JSON schema module path for Typed Shared State |
max_session_budget_usd | number | null | None | Global hard cost ceiling per session |
imports | list[ImportConfig] | [] | Other ai.yaml-shaped files to merge into this one. |
circuit_breakers | CircuitBreakersConfig | (factory default) | Thresholds that halt a runaway session — handoff loops, tool failure streaks, USD cost, delegation depth. |
model | ModelConfig | required | Default LLM configuration, used by any agent that doesn't set model_override. |
memory | MemoryConfig | required | Conversation history backend used by all agents in this app. |
rag | RAGConfig | null | None | Vector retrieval configuration — when set, auto-registers a search_knowledge_base tool. |
episodic_memory | EpisodicMemoryConfig | null | None | Discrete, structured, individually queryable event records (e.g. "user prefers window seats", "booking BK-4471 failed: card declined") — distinct from the single blended long_term_memory prose summary and from rag's document/knowledge-base retrieval. When set, auto-registers remember_episode/recall_episodes tools. Stored in a sqlite/postgres episodes table (memory.type must be 'sqlite' or 'postgres' — same scope as shared_memory/run_logs); a no-op elsewhere. |
tools | list[LocalToolConfig | MCPToolConfig | OpenAPIToolConfig | SandboxToolConfig] | [] | Tools declared at the root level, available to be referenced by name from any agent. |
agents | dict[str, AgentConfig] | {} | The agents that make up this app, keyed by name. |
routers | dict[str, RootRouterConfig] | {} | Root-level deterministic routers, keyed by name — evaluated before any agent runs. |
workflows | dict[str, list[WorkflowTask]] | {} | Named multi-step task sequences, run via inta run <workflow> rather than conversationally. |
reducers | list[StateReducerConfig] | [] | How concurrent writes to shared state keys are combined. |
condition_functions | list[ConditionFunctionConfig] | [] | Named, pure Python predicate functions that routers[].condition and tools[].available_when expressions may call by name (e.g. is_eligible(customer_tier)), for branching logic the restricted condition grammar's bare comparisons/and/or/not can't express on their own — without forcing an LLM handoff just to make a deterministic decision. |
telemetry | list["langfuse" | "otel"] | [] | Tracing backends to export spans to. |
server | ServerConfig | (factory default) | inta serve/inta monitor configuration — auth, webhooks. |
default_agent | string | required | Agent that receives the first message in a new session. |
AgentConfig
| Field | Type | Default | Description |
|---|---|---|---|
description | string | null | None | Short human-readable summary of this agent's purpose — shown in the Monitor dashboard graph. |
model_override | string | null | None | Use a different LiteLLM model for this agent instead of the app-level model.primary. |
system_prompt_file | string | null | None | Path to a Jinja2 template file rendered as this agent's system prompt. |
system_prompt_langfuse | string | null | None | Langfuse prompt name to fetch as this agent's system prompt instead of a local file. |
system_prompt_module | string | null | None | Python module path exposing a function that returns this agent's system prompt dynamically. |
prompt_key | string | null | None | Key used to look up this agent's prompt when multiple agents share a prompt source. |
required_scopes | list[string] | [] | Scopes/permissions a caller must have (checked against session state) before this agent can run. |
response_schema | string | null | None | Dotted path to a Pydantic model this agent's final response is validated against; a corrector model retries once on violation. |
auto_route | boolean | False | Enable semantic swarm routing (LLM-Bypass Group Chat) |
lazy_load_tools | boolean | False | Enable semantic tool retrieval to reduce context window bloat |
tools | list[LocalToolConfig | MCPToolConfig | OpenAPIToolConfig | SandboxToolConfig | ToolReferenceConfig] | [] | Tools available to this agent — local Python functions, MCP servers, OpenAPI wrappers, or references to root-level tools. |
handoffs | list[string] | [] | Agent names this agent may conversationally transfer control to (compiled to a transfer_agent tool). |
delegations | list[string] | [] | Agent names this agent may delegate a sub-task to. Compiles two tools: delegate_task (one sub-task, one isolated child engine — the delegating agent's own turn is never interrupted) and delegate_to_many (fan out N concurrent instances of the same sub-agent, one per instruction, for an item count only known at runtime — capped by circuit_breakers.max_parallel_fan_out). |
routers | list[RouterConfig] | [] | Deterministic Python conditions checked before the LLM runs, to bypass it entirely when they fire. |
spawns | AgentSpawningConfig | null | None | Enables dynamic runtime agent creation: this agent gets a spawn_agent tool that creates a narrowly-scoped sub-agent (new system prompt, a subset of spawns.tool_pool, an inherited model) mid-session, runs it to completion in an isolated child engine, and returns the result as an ordinary tool result — it does not transfer control, so the creator's own turn is never interrupted and concurrent spawn_agent calls in one turn have nothing shared to race on. None (default) means this agent cannot spawn anything — today's behavior. |
AgentSpawningConfig
| Field | Type | Default | Description |
|---|---|---|---|
tool_pool | list[string] | required | Closed allow-list of already-declared tool names a dynamically-created sub-agent's tools are drawn from — never a new tool implementation supplied at runtime. Must be a subset of this agent's own tools: (enforced at parse time); an agent can only hand off capabilities it already has, never escalate through creation. The literal string "*" is shorthand for every tool this agent itself has — expanded to that concrete list at parse time (AgentConfig's own subset validator), so it still can never grant more than the agent already holds. |
model_pool | list[string] | null | None | LiteLLM model identifiers a spawned agent may be assigned. None (default) means every spawned agent inherits the spawning agent's own resolved model — the spawning LLM never picks a model tier itself unless this is explicitly set. |
max_creations_per_session | integer | 3 | Session-wide cap on how many agents this agent may dynamically create. |
requires_approval_on_first_action | boolean | True | Gate a spawned agent's very first tool call behind human approval (reuses the existing requires_approval/multi-approver /resume mechanism), regardless of whether that specific tool is itself approval-gated. Safe-by-default: opt out, not opt in. |
allow_recursive_spawning | boolean | False | Whether an agent spawned by this factory may itself spawn further agents (same tool_pool — no privilege growth), up to max_spawn_depth. Off by default. |
max_spawn_depth | integer | 1 | Max recursive spawn depth, only consulted when allow_recursive_spawning is true. |
result_schema | string | null | None | Dotted path to a Pydantic model a spawned agent's return_to_creator call must conform to. When set, return_to_creator's tool schema is derived from this model instead of a generic free-text summary field — steering the model via constrained tool-call decoding rather than just asking nicely — and the arguments are re-validated server-side before being accepted, self-healed via the same corrector-model retry already used for malformed tool arguments elsewhere. None (default) keeps today's free-text summary behavior. |
on_complete | list[StateWriteAction] | [] | State writes applied automatically once a spawned agent genuinely completes — return_to_creator or a final text response, never on a pause awaiting approval, never on a forced max-turns abort. Runs through the exact same reducer/state_schema pipeline as write_state, so validation and merge strategies apply identically — this is not a second, less-validated write path. Lets a tools[].available_when gate (or any other state-driven condition) unlock declaratively, instead of requiring the spawned agent's own instruction text to call write_state on the framework's behalf. |
AuthConfig
| Field | Type | Default | Description |
|---|---|---|---|
type | "api_key" | "custom" | "none" | 'none' | Authentication mode for inta serve/inta monitor: 'none' (no auth), 'api_key' (a single shared secret — sent as an Authorization: Bearer token for inta serve's API, or as the password in HTTP Basic auth for inta monitor's dashboard; the Basic auth username is not checked and can be any value, e.g. 'admin'), or 'custom' (delegates to a project-supplied verify_token function). |
env_var | string | null | 'INTAGRIN_API_KEY' | Environment variable holding the API key, when type is 'api_key'. The server reads this at each auth check — export it before starting the server. |
custom_module | string | null | None | Python module path (e.g. 'auth.custom') exposing verify_token(token: str) -> bool | str, when type is 'custom'. Must return the tenant id as a string for per-tenant session isolation — any other truthy value is rejected (401), not silently treated as a single shared tenant. |
approver_env_var | string | null | None | Environment variable holding a separate secret required (via the X-Approver-Key header) to approve a requires_approval tool call through /resume. Without this, the same credential that triggered the gated call can immediately approve it — set this to require a distinct reviewer credential from the requester's own session auth. Acts as the single default approver (id 'default') when approvers isn't set. |
approvers | dict[str, string] | null | None | Named approvers for multi-approver chains: maps an approver id (e.g. 'finance', 'security') to the environment variable holding that approver's own X-Approver-Key secret. A tool's required_approvers list names which of these ids must each sign off via /resume before it executes. Independent of approver_env_var — set both to keep a single default approver alongside named ones. |
CircuitBreakersConfig
| Field | Type | Default | Description |
|---|---|---|---|
max_handoffs_per_session | integer | null | 25 | Max allowed handoffs per session to prevent infinite loops |
max_tool_failures_in_a_row | integer | null | 3 | Max sequential tool failures before halting |
max_usd_cost_per_session | number | null | None | Max USD cost per session before halting |
max_delegation_depth | integer | 3 | Max nested sub-agent delegation depth before rejecting further delegation |
max_delegation_turns | integer | 15 | Max turns a delegated sub-agent may take before it is forcefully aborted |
max_parallel_fan_out | integer | 10 | Max instructions a single delegate_to_many call may fan out to concurrently — each one runs a full isolated sub-agent to completion, so an LLM-chosen item count with no ceiling could spawn an unbounded number of child engines/LLM calls at once. A call over this limit is rejected outright (asking the caller to split the work into smaller batches) rather than silently truncated. |
max_parallel_tool_calls_per_turn | integer | 10 | Max ordinary (non-transfer) tool calls a single LLM completion may request that actually get executed concurrently in one turn. A model can request an arbitrary number of tool calls in one completion with no natural ceiling; calls beyond this limit are not executed — each gets a synthetic tool-result telling the model to split the work across turns — rather than silently gathered anyway, mirroring how a duplicate control-transfer call in the same turn is rejected rather than run. |
max_corrector_tokens | integer | 1000 | max_tokens applied to every self-healing corrector-model call — both malformed tool-call argument repair and response_schema/result_schema repair. These calls previously had no max_tokens set at all, making their cost genuinely unbounded; capping the output side bounds each individual corrector call's worst-case cost. |
max_compression_batch_messages | integer | 50 | Max messages folded into long_term_memory per memory-compression LLM call. When more messages than this are evicted from the context window at once, compression runs in successive batches of this size, each folded into the summary in turn, instead of dumping an unbounded amount of evicted conversation into a single corrector-model prompt (the summary's own output side was already capped at 500 tokens; this bounds the input side). |
ConditionFunctionConfig
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Name a routers[].condition or tools[].available_when expression can call, e.g. is_eligible(customer_tier, order_total). Must match the Python function name. |
module | string | required | Python module path (e.g. 'tools.condition_functions') containing a pure, side-effect-free function named name that takes plain values and returns bool. Called with already-evaluated bare state-key names/literals as positional arguments — the function itself is never parsed as part of the condition grammar, only invoked by name, so this is the one way to express branching logic that the restricted condition grammar's comparisons/and/or/not can't reach (e.g. a regex match or a multi-field business rule) without falling back to an LLM handoff. |
EpisodicMemoryConfig
| Field | Type | Default | Description |
|---|---|---|---|
embedding_model | string | 'text-embedding-3-small' | Embedding model used to vectorize episode content and recall_episodes queries for semantic search. Defaults to match rag.embedding_model's default so a project using both features doesn't need two different embedding providers configured. |
scope | "session" | "tenant" | "global" | 'session' | Visibility scope for recorded episodes, independent of memory.shared_scope (a project may want the long_term_memory summary private per-session while episodic events are shared globally, or vice versa). 'session': only this session_id's own episodes. 'tenant': every session under the same authenticated caller/tenant prefix (same convention as memory.shared_scope: tenant). 'global': every session in the project, any tenant. |
default_limit | integer | 5 | Default number of episodes recall_episodes returns when the caller doesn't pass an explicit limit. |
GuardrailsConfig
| Field | Type | Default | Description |
|---|---|---|---|
banned_words | list[string] | [] | Words/phrases that, if present in a user message or model output, are blocked before reaching the LLM or the user. |
mask_pii | boolean | False | Redact common PII patterns (emails, SSNs, card numbers) from messages before they're sent to the LLM or logged. |
system_safeguards | boolean | False | Append a built-in safety instruction to the system prompt discouraging harmful/off-policy behavior. |
custom_module | string | null | None | Python module path exposing a custom guardrail check function, run in addition to the built-in checks above. |
ImportConfig
| Field | Type | Default | Description |
|---|---|---|---|
path | string | required | Path to another ai.yaml-shaped file to merge in (agents, tools, workflows, reducers). |
namespace | string | null | None | Prefix added to imported agent/workflow names, to avoid collisions with the importing project. |
LocalToolConfig
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Tool name exposed to the LLM for tool-calling — must match the Python function name. |
module | string | required | Python module path (e.g. 'tools.custom_tools') containing the function named name. |
requires_approval | boolean | False | Require human-in-the-loop approval before this tool actually executes. |
required_approvals | integer | 1 | Number of distinct approvers (see server.auth.approvers) who must each approve via /resume before this tool executes — 1 (default) matches today's single-approval behavior. Ignored unless requires_approval is true. |
required_approvers | list[string] | null | None | Specific approver ids (keys of server.auth.approvers) that must each sign off, instead of any required_approvals approvers. Ignored unless requires_approval is true; when set, overrides required_approvals with len(required_approvers). |
available_when | string | null | None | State condition (same restricted grammar as routers[].condition — bare state-key names, comparisons, and/or/not; no method calls or attribute access) gating whether this tool is even offered to the agent this turn. Unlike a prompt instruction asking the model not to call a tool yet, the tool is structurally absent from its schema until the condition is true — re-checked server-side on every call regardless, the same defense-in-depth already applied to tool_pool and every other schema-driven gate in this codebase. None (default) means always available, today's behavior. |
untrusted_output | boolean | False | Mark this tool's return value as untrusted (may contain LLM-directed instructions injected by a third party — the 'lethal trifecta' pattern: untrusted content + access to private data/state + a way to exfiltrate). False by default for local tools, since they're developer-authored Python — set true for one that fetches external content (e.g. a web scraper). The moment any tool call with untrusted_output=true succeeds, state['_untrusted_content_ingested'] is set true for the rest of the session; reference it from another tool's available_when (e.g. 'not _untrusted_content_ingested') to withhold a sensitive tool once this session has seen untrusted content, or from a router condition to force a review handoff. |
MCPToolConfig
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Tool name exposed to the LLM for tool-calling. |
type | string | required | Discriminator — must be the literal string 'mcp'. |
command | string | required | Executable used to launch the MCP server subprocess (e.g. 'npx'). |
args | list[string] | required | Arguments passed to command when launching the MCP server. |
requires_approval | boolean | False | Require human-in-the-loop approval before this tool actually executes. |
required_approvals | integer | 1 | Number of distinct approvers (see server.auth.approvers) who must each approve via /resume before this tool executes — 1 (default) matches today's single-approval behavior. Ignored unless requires_approval is true. |
required_approvers | list[string] | null | None | Specific approver ids (keys of server.auth.approvers) that must each sign off, instead of any required_approvals approvers. Ignored unless requires_approval is true; when set, overrides required_approvals with len(required_approvers). |
available_when | string | null | None | State condition (same restricted grammar as routers[].condition) gating whether this tool is even offered to the agent this turn — see LocalToolConfig.available_when for the full explanation. None (default) means always available, today's behavior. |
untrusted_output | boolean | True | Mark this tool's return value as untrusted — see LocalToolConfig.untrusted_output for the full explanation. True by default for MCP tools, since they reach an external server outside the project's own trust boundary; set false only for an MCP server you fully control and trust. |
MemoryConfig
| Field | Type | Default | Description |
|---|---|---|---|
type | "sliding_window" | "buffer" | "sqlite" | "postgres" | "redis" | "custom" | 'sliding_window' | Conversation history backend: 'sliding_window'/'buffer' (in-process, lost on restart), 'sqlite' (local file), 'postgres', 'redis', or 'custom'. |
max_messages | integer | 20 | Number of most-recent messages kept in context for sliding_window/buffer memory. |
db_path | string | null | '.ai/memory.db' | SQLite database file path, relative to the project root, when type is 'sqlite'. |
connection_url | string | null | None | Direct connection URL for postgres/redis, e.g. 'postgresql://...' or 'redis://...'. Takes precedence over env_var. |
env_var | string | null | None | Environment variable holding the connection URL for postgres/redis (e.g. 'DATABASE_URL', 'REDIS_URL'), used if connection_url isn't set directly. |
custom_module | string | null | None | Python module path exposing a CustomCheckpointer class, when type is 'custom'. |
shared_scope | "session" | "tenant" | "global" | 'session' | Scope for the long_term_memory summary _compress_memory produces: 'session' (default — today's behavior, private to one session_id), 'tenant' (shared across every session under the same authenticated caller/tenant), or 'global' (shared across every session in the project, any tenant). Persisted to a new shared_memory table (sqlite/postgres only, same scope as run_logs) and merged into a session's long_term_memory on initialize(). Last-write-wins across concurrent sessions writing the same scope — no merge/versioning. |
ModelConfig
| Field | Type | Default | Description |
|---|---|---|---|
primary | string | required | LiteLLM model identifier used for this agent/app (e.g. 'openai/gpt-4o-mini', 'anthropic/claude-3-5-sonnet'). Required. |
fallback | string | null | None | Model to retry with if the primary model call fails (rate limit, outage, etc.). |
variants | list[ModelVariantConfig] | null | None | A/B or canary model routing: split traffic across weighted model variants instead of always using primary. Assignment is deterministic per session_id (sticky for the whole conversation, never flips mid-session) via a weighted hash. None (default) means every session uses primary, exactly today's behavior. |
temperature | number | 0.2 | Sampling temperature passed to the LLM, 0.0 (deterministic) to 2.0 (most random). |
max_tokens | integer | 1500 | Maximum tokens the LLM may generate in a single completion. |
use_cache | boolean | False | Enable semantic caching to save API costs |
guardrails | GuardrailsConfig | (factory default) | Content-safety checks applied to this model's inputs/outputs. |
ModelVariantConfig
| Field | Type | Default | Description |
|---|---|---|---|
model | string | required | LiteLLM model identifier for this variant. |
weight | number | required | Relative weight for traffic splitting — weights don't need to sum to 1. |
OpenAPIToolConfig
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Tool name exposed to the LLM for tool-calling. |
type | string | required | Discriminator — must be the literal string 'openapi'. |
url | string | required | URL of the OpenAPI/Swagger spec IntaGrin generates a tool wrapper from. |
auth_env | string | null | None | Environment variable holding a bearer token/API key sent with requests to this API. |
requires_approval | boolean | False | Require human-in-the-loop approval before this tool actually executes. |
required_approvals | integer | 1 | Number of distinct approvers (see server.auth.approvers) who must each approve via /resume before this tool executes — 1 (default) matches today's single-approval behavior. Ignored unless requires_approval is true. |
required_approvers | list[string] | null | None | Specific approver ids (keys of server.auth.approvers) that must each sign off, instead of any required_approvals approvers. Ignored unless requires_approval is true; when set, overrides required_approvals with len(required_approvers). |
available_when | string | null | None | State condition (same restricted grammar as routers[].condition) gating whether this tool is even offered to the agent this turn — see LocalToolConfig.available_when for the full explanation. None (default) means always available, today's behavior. |
untrusted_output | boolean | True | Mark this tool's return value as untrusted — see LocalToolConfig.untrusted_output for the full explanation. True by default for OpenAPI-derived tools, since they call an external API outside the project's own trust boundary; set false only for an API you fully control and trust. |
RAGConfig
| Field | Type | Default | Description |
|---|---|---|---|
docs_dir | string | 'docs' | Directory of documents to index for retrieval, relative to the project root. |
embedding_model | string | 'text-embedding-3-small' | Embedding model used to vectorize documents and queries. |
top_k | integer | 4 | Number of chunks retrieved per query. |
chunk_size | integer | 500 | Maximum characters per indexed chunk. |
chunk_overlap | integer | 50 | Characters of overlap between consecutive chunks. |
hyde | boolean | False | Enable Hypothetical Document Embeddings for advanced retrieval |
RateLimitConfig
| Field | Type | Default | Description |
|---|---|---|---|
max_requests_per_window | integer | null | None | Max /chat, /chat/stream, /resume, or /stream requests one authenticated caller (tenant) may make within window_seconds. None (default) means unlimited. Enforced by counting that caller's rows in the run_logs audit table, so it only applies when memory.type is 'sqlite' or 'postgres' — the same scope run_logs itself has. |
window_seconds | integer | 60 | Rolling window, in seconds, that max_requests_per_window is measured over. |
max_cost_per_caller_per_day | number | null | None | Max total USD cost one authenticated caller may accrue in a rolling 24h window, summed from run_logs.cost_delta. None (default) means unlimited. |
max_tokens_per_caller_per_day | integer | null | None | Max total tokens one authenticated caller may consume in a rolling 24h window, summed from run_logs.tokens_delta. None (default) means unlimited. |
RootRouterConfig
| Field | Type | Default | Description |
|---|---|---|---|
description | string | null | None | What this router decides |
module | string | required | Python module containing a route(state) function |
possible_targets | list[string] | [] | Strict list of allowed target agents to maintain determinism and UI visualization |
RouterConfig
| Field | Type | Default | Description |
|---|---|---|---|
condition | string | null | None | Python evaluation string against state (e.g., "balance < 0") |
custom_module | string | null | None | Module to execute deterministic routing logic |
target | string | required | Agent to route to when this router fires. |
SandboxToolConfig
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Tool name exposed to the LLM for tool-calling. |
type | string | required | Discriminator — must be the literal string 'sandbox'. |
language | "python" | "bash" | 'python' | Interpreter used to run the submitted code. |
timeout_seconds | integer | 10 | Wall-clock limit before the sandboxed process is killed. Also used as the POSIX CPU-time rlimit (see runtime/sandbox.py) — a no-op on platforms without the resource module (Windows). |
max_memory_mb | integer | null | 256 | POSIX address-space rlimit (RLIMIT_AS) applied to the sandboxed process, in megabytes. None disables the memory limit entirely. A no-op on platforms without the resource module (Windows). |
requires_approval | boolean | False | Require human-in-the-loop approval before this tool actually executes. |
required_approvals | integer | 1 | Number of distinct approvers (see server.auth.approvers) who must each approve via /resume before this tool executes — 1 (default) matches today's single-approval behavior. Ignored unless requires_approval is true. |
required_approvers | list[string] | null | None | Specific approver ids (keys of server.auth.approvers) that must each sign off, instead of any required_approvals approvers. Ignored unless requires_approval is true; when set, overrides required_approvals with len(required_approvers). |
available_when | string | null | None | State condition (same restricted grammar as routers[].condition) gating whether this tool is even offered to the agent this turn — see LocalToolConfig.available_when for the full explanation. None (default) means always available, today's behavior. |
untrusted_output | boolean | True | Mark this tool's return value as untrusted — see LocalToolConfig.untrusted_output for the full explanation. True by default: sandboxed code's stdout/stderr can be influenced by whatever the executed code does (including content it read from elsewhere), so it's treated the same as an external tool result by default. |
ServerConfig
| Field | Type | Default | Description |
|---|---|---|---|
auth | AuthConfig | (factory default) | Authentication configuration for inta serve/inta monitor. Defaults to no authentication — set this before exposing either beyond localhost. |
webhook_url | string | null | None | Webhook URL for async HITL notifications |
webhook_secret_env_var | string | null | None | Env var containing secret to authenticate webhooks |
rate_limit | RateLimitConfig | (factory default) | Per-caller rate limiting/usage quotas for the API server. All thresholds default to unlimited — opt in per project. |
StateReducerConfig
| Field | Type | Default | Description |
|---|---|---|---|
key | string | required | Shared state key this reducer applies to. |
strategy | "overwrite" | "append" | "deep_merge" | 'overwrite' | How concurrent/repeated writes to key are combined: 'overwrite' (last write wins), 'append' (to a list), or 'deep_merge' (for dicts). |
StateWriteAction
| Field | Type | Default | Description |
|---|---|---|---|
key | string | required | State key to write. |
value | any | required | Value to write — the same JSON-ish value write_state accepts. |
ToolReferenceConfig
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Name of a tool or MCP/OpenAPI provider declared at the ai.yaml root. |
available_when | string | null | None | State condition (same restricted grammar as routers[].condition) gating whether this tool is even offered to this agent this turn — see LocalToolConfig.available_when for the full explanation. This is the field that actually matters in practice: a per-agent tools: entry is usually a name-reference to a root-level tool, and availability is inherently agent-specific (the same globally-declared tool might be gated for one agent and unrestricted for another). None (default) means always available, today's behavior. |
VoteConfig
| Field | Type | Default | Description |
|---|---|---|---|
strategy | "majority" | "llm_judge" | 'majority' | How a 'vote' workflow task's branch answers become one result: 'majority' compares branch outputs directly with no extra LLM call; 'llm_judge' asks the model to pick or synthesize the best answer from all branch outputs. |
min_agreement | number | 0.5 | Minimum fraction (0.0-1.0) of branches that must agree for 'majority' to declare a winner. Below this, no answer is guessed — the task reports 'no consensus' plus all branch outputs instead. |
WorkflowTask
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Task name, used for logging/tracing this step of the workflow. |
type | "sequential" | "parallel" | "vote" | 'sequential' | 'sequential' runs sub-tasks/agent one after another; 'parallel' runs them concurrently and appends every branch's result; 'vote' runs them concurrently like 'parallel' but aggregates branch answers into one consensus result (see vote). |
agent | string | null | None | Agent that executes this task. |
instruction | string | null | None | Instruction given to agent for this task. |
tasks | list[WorkflowTask] | null | None | Nested sub-tasks, when this task is a grouping node rather than a leaf agent call. |
vote | VoteConfig | null | None | Aggregation settings for a 'vote' task (ignored otherwise); defaults to majority voting with 50% minimum agreement when omitted. |