Every agent needs an orchagent.json file that defines its configuration, dependencies, and runtime limits.
Skills use a different format. Skills use a SKILL.md file with YAML frontmatter instead of orchagent.json. See Agent Types for the SKILL.md format.
Basic Structure
{
"name": "my-agent",
"type": "prompt",
"description": "What my agent does",
"supported_providers": ["openai", "anthropic"],
"default_endpoint": "analyze",
"timeout_seconds": 60
}
Versions are auto-assigned. You don’t need to specify a version field in your manifest. The server automatically assigns versions on each publish (v1, v2, v3…).
Required Fields
| Field | Type | Description |
|---|
name | string | Agent name (lowercase, hyphens allowed) |
type | string | "prompt" (default), "tool", "agent", or "skill". Legacy values code and agentic are accepted. |
description | string | Short description of what the agent does |
supported_providers | array | LLM providers: ["openai", "anthropic", "gemini", "any"]. agent types (managed loop) must use ["anthropic"]. |
Behavior Fields
These fields control how and when the agent runs. The type field sets the default execution engine, but these declarations can override it.
| Field | Type | Default | Description |
|---|
run_mode | string | "on_demand" | "on_demand" or "always_on". Always-on agents can be deployed as services. |
runtime | object | — | Declares code execution. Sets execution_engine to code_runtime. |
loop | object | — | Declares managed LLM loop. Sets execution_engine to managed_loop. |
callable | boolean | true | Whether other agents can call this agent as a dependency. Set to false for user-facing agents that shouldn’t be invoked by orchestrators (e.g., always-on services). |
runtime and loop are mutually exclusive — you cannot declare both. The type field sets the default execution engine: prompt → direct_llm, tool → code_runtime, agent → managed_loop. These declarations override the type default when present.
runtime
Declares that the agent runs custom code:
{
"runtime": {
"command": "python main.py"
}
}
| Field | Type | Description |
|---|
runtime.command | string | Shell command to execute in the sandbox |
loop
Declares that the agent uses a managed LLM tool-use loop:
{
"loop": {
"max_turns": 25
}
}
| Field | Type | Default | Description |
|---|
loop.max_turns | number | 25 | Maximum loop iterations (platform max: 50) |
Optional Fields
| Field | Type | Default | Description |
|---|
default_endpoint | string | "run" | Default endpoint for orch run |
timeout_seconds | number | 300 | Request timeout (seconds) |
tags | array | [] | Searchable tags |
is_public | boolean | false | All agents are private by default |
default_models | object | {} | Per-provider model defaults. See Default Models below. |
default_skills | array | [] | Skills to inject into agent prompts (e.g., ["yourorg/writing-style"]) |
skills_locked | boolean | false | When true, callers cannot override default_skills via flags (immutable after publish) |
required_secrets | array | [] | Env var names your agent needs at runtime. See Workspace Secrets below. Required for tool and agent types. |
optional_secrets | array | [] | Env var names that unlock additional features but aren’t required. Shown in orch info for discoverability. |
Default Models
Control which LLM model runs your agent by default for each provider. The default_models field is an object mapping provider names to model IDs:
{
"name": "my-agent",
"type": "prompt",
"supported_providers": ["anthropic", "openai", "gemini"],
"default_models": {
"anthropic": "claude-sonnet-4-20250514",
"openai": "gpt-4o",
"gemini": "gemini-2.5-pro"
}
}
Model Resolution Order
When your agent runs, the model is resolved in this order:
- Caller override —
orch run --model <model> (highest priority)
- Agent default —
default_models[provider] from your manifest
- Platform default — built-in defaults per provider
| Provider | Platform Default |
|---|
anthropic | claude-opus-4-5-20251101 |
openai | gpt-5.2 |
gemini | gemini-2.5-pro |
ollama | llama3.2 |
Use default_models (plural object), not model (singular string). A common mistake is writing "model": "claude-sonnet-4-20250514" — this field is ignored. The correct format is "default_models": { "anthropic": "claude-sonnet-4-20250514" }.
Set default_models to control costs. For example, use a smaller model like claude-haiku-4-5-20251001 for simple tasks, and let callers override with --model when they need a more capable model.
orch publish and orch validate will warn if your model IDs don’t match known provider patterns (e.g. gpt-* for OpenAI, claude-* for Anthropic, gemini-* for Gemini). This catches typos and mismatches before they cause 404 errors at runtime.
Workspace Secrets
Declare the environment variables your agent needs at runtime using required_secrets, and variables that unlock additional features using optional_secrets. Both are matched by name against your workspace secrets vault and injected as env vars into the sandbox.
{
"name": "cto-agent",
"type": "agent",
"required_secrets": ["MONITOR_URLS", "ANTHROPIC_API_KEY"],
"optional_secrets": ["DISCORD_WEBHOOK_URL", "SLACK_WEBHOOK_URL", "BACKUP_S3_ENDPOINT"]
}
At runtime, your code reads them as environment variables:
import os
token = os.environ["ANTHROPIC_API_KEY"]
webhook = os.environ.get("DISCORD_WEBHOOK_URL") # optional — may not be set
How it works
- You declare
required_secrets and optional_secrets in orchagent.json
- You add secrets to your workspace vault (dashboard or
orch secrets set)
- At runtime, the platform matches secret names and injects them as env vars
- On-demand runs (
orch run): returns a 400 MISSING_SECRETS error if any required secrets are missing from the vault. Optional secrets are injected if present, silently skipped if not.
- Service deploys (
orch service deploy): auto-reads required_secrets and validates against the vault before deploying. No --secret flags needed.
- Schedules: same injection — required secrets must exist in the vault at execution time
orch info: shows both required and optional secrets so users can discover all available configuration
Optional secrets
Use optional_secrets to declare env vars that aren’t needed to run but unlock additional features — notification webhooks, backup integrations, model overrides, tuning parameters, etc. Without this field, users have no way to discover these options except by reading the README.
$ orch info orchagent/cto-agent-template
Secrets (required): MONITOR_URLS, ANTHROPIC_API_KEY
Secrets (optional): DISCORD_WEBHOOK_URL, SLACK_WEBHOOK_URL, BACKUP_S3_ENDPOINT, ...
Publish enforcement
orch publish blocks tool and agent types that don’t declare required_secrets. This ensures the Agent Requirements UI and service auto-resolution always have accurate data. Use --no-required-secrets to bypass this check if your agent genuinely needs no secrets.
Prompt and skill types are exempt. optional_secrets is never enforced — it’s purely informational.
Do not add ORCHAGENT_SERVICE_KEY to required_secrets. The gateway auto-injects a temporary service key for agents with manifest dependencies. Adding your own overrides it and breaks orchestration.
Additional fields for tool types (code runtime — runtime.command):
| Field | Type | Description |
|---|
entrypoint | string | Entry file to execute (e.g., sandbox_main.py) |
source_url | string | Git URL for local execution |
run_command | string | Command to run locally |
bundle | object | Bundle configuration (see below) |
Bundle Configuration
Control what files are included in your code bundle:
{
"bundle": {
"exclude": ["data/", "*.log", "large_model.bin"],
"include": ["important.dat"]
}
}
| Field | Type | Description |
|---|
exclude | array | Glob patterns to exclude from bundle |
include | array | Glob patterns to include (overrides excludes) |
Example
{
"name": "leak-finder",
"type": "tool",
"description": "Finds leaked secrets in codebases",
"supported_providers": ["gemini"],
"runtime": {
"command": "python sandbox_main.py"
},
"required_secrets": ["GEMINI_API_KEY"],
"entrypoint": "sandbox_main.py",
"tags": ["security", "scanning"],
"is_public": true
}
For E2B sandbox execution (recommended), only runtime.command (or entrypoint) is needed. The source_url and run_command fields are for local execution support.
Prompt Type Fields
Additional fields for prompt types (direct LLM — no runtime or loop):
| Field | Type | Description |
|---|
prompt_template | string | Prompt with {{variable}} placeholders |
input_schema | object | JSON Schema for input validation |
output_schema | object | JSON Schema for output validation |
Example
{
"name": "summarizer",
"type": "prompt",
"description": "Summarizes text content",
"supported_providers": ["openai", "anthropic"],
"prompt_template": "Summarize the following text in {{style}} style:\n\n{{text}}",
"input_schema": {
"type": "object",
"properties": {
"text": { "type": "string" },
"style": { "type": "string", "enum": ["bullet", "paragraph", "tldr"] }
},
"required": ["text", "style"]
},
"output_schema": {
"type": "object",
"properties": {
"summary": { "type": "string" }
},
"required": ["summary"]
}
}
Agent Type Fields
Additional fields for agent types (managed loop):
| Field | Type | Default | Description |
|---|
custom_tools | array | [] | Command-wrapper tools available to the LLM |
timeout_seconds | number | 300 | Overall execution timeout |
Each custom tool has:
| Field | Required | Description |
|---|
name | Yes | Tool name the LLM will call |
description | Yes | What the tool does (shown to LLM) |
command | Yes | Shell command to execute. Use {{param}} for parameters. |
input_schema | No | JSON Schema for parameters (required if command has {{param}} placeholders) |
Example
{
"name": "cairo-test-engineer",
"type": "agent",
"description": "Fixes Cairo code until tests pass",
"supported_providers": ["anthropic"],
"loop": {
"max_turns": 30
},
"timeout_seconds": 300,
"custom_tools": [
{
"name": "run_tests",
"description": "Run the Cairo test suite",
"command": "snforge test"
},
{
"name": "deploy",
"description": "Deploy to the specified network",
"command": "sncast deploy --network {{network}}",
"input_schema": {
"type": "object",
"properties": {
"network": { "type": "string", "description": "Target network" }
},
"required": ["network"]
}
}
]
}
Agent types also get built-in tools automatically: bash, read_file, write_file, list_files, and submit_result. You don’t need to define these. See Agent Types for details.
If your agent declares manifest.dependencies, you must also define matching custom_tools. Without custom_tools, the LLM has no way to invoke dependencies — it will waste turns exploring the sandbox filesystem instead. Use orch scaffold orchestration to auto-generate custom_tools from your dependency list. This only applies to managed-loop agents; code_runtime agents call dependencies via the SDK directly.
Skill Composition
Agents can include skills by default using the default_skills field. Skills are injected into the agent’s prompt at runtime.
{
"name": "code-reviewer",
"type": "prompt",
"description": "Reviews code with best practices",
"supported_providers": ["anthropic", "openai"],
"default_skills": [
"yourorg/react-best-practices",
"yourorg/writing-style"
],
"prompt_template": "Review this code:\n\n{{code}}"
}
When called, the agent’s prompt is prepended with the content from each skill. Callers can override this behavior (unless skills are locked):
--skills org/skill - Add more skills
--skills-only org/skill - Replace default_skills entirely
--no-skills - Ignore all skills
Locking Skills
Add skills_locked: true to prevent callers from overriding your default skills:
{
"name": "secure-reviewer",
"default_skills": ["yourorg/owasp-rules"],
"skills_locked": true
}
See Orchestration - Locking Skills for details.
Manifest Section (for Orchestrator Agents)
Agents that call other agents need a manifest section:
{
"name": "security-review",
"type": "tool",
"description": "Comprehensive security review",
"runtime": {
"command": "python main.py"
},
"manifest": {
"manifest_version": 1,
"dependencies": [
{ "id": "joe/leak-finder", "version": "v1" },
{ "id": "joe/vuln-scanner", "version": "v1" }
],
"max_hops": 2,
"timeout_ms": 120000,
"per_call_downstream_cap": 100
}
}
Manifest Fields
| Field | Required | Description |
|---|
manifest_version | Yes | Always 1 |
dependencies | Yes | Array of {id, version} for agents you’ll call |
max_hops | Yes | Max call depth (>= 1 if you have deps) |
timeout_ms | Yes | Total timeout for your agent |
per_call_downstream_cap | No | Budget passed to each dependency call (default: 50). Limits child agent spending, not this agent’s own calls. |
orchestration_mode | No | "strict" or "flexible". Controls whether the agent must use dependency-path execution. See Orchestration Mode below. Defaults to "strict" when dependencies are declared. |
min_required_hops | No | Minimum hops caller must allow |
region | No | Deployment region |
visibility | No | public or private |
recommended_min_caller_budget | No | Suggested minimum budget for callers |
{
"id": "org_slug/agent_name",
"version": "v1"
}
Orchestration Mode
Controls whether a managed-loop orchestrator must delegate to its declared dependencies or can solve tasks directly with built-in tools like bash.
{
"manifest": {
"manifest_version": 1,
"dependencies": [{ "id": "yourorg/scanner", "version": "v1" }],
"orchestration_mode": "strict"
}
}
| Value | Behavior |
|---|
"strict" | bash is removed from available tools. The agent must call at least one dependency before submitting results. This is the default when dependencies are declared. |
"flexible" | All built-in tools (including bash) remain available. The agent can solve tasks directly or via dependencies. |
Rules:
- Only applies to managed-loop agents (
type: "agent" or loop declared) with dependencies.
- If
dependencies is empty or absent, the field is ignored.
- Orchestrators with dependencies default to
"strict" unless you explicitly set "flexible".
- In multi-agent chains, strict mode is inherited downward — a strict parent forces all downstream agents to run strict, regardless of their own setting.
To use flexible mode instead:
{
"manifest": {
"orchestration_mode": "flexible",
"dependencies": [{ "id": "yourorg/scanner", "version": "v1" }]
}
}
See Orchestration - Strict vs Flexible Mode for details on behavior, error codes, and chain inheritance.
Validation Rules
The platform validates manifests on publish:
- Dependencies exist - All declared dependencies must be registered
- No cycles - Dependency graph must be acyclic
- max_hops >= 1 - Required when dependencies are declared
- Version pins - Dependencies must specify exact versions
- No conflicts -
runtime and loop cannot both be declared
- Run mode compatibility -
always_on requires runtime.command or loop (cannot be used with direct_llm)
Complete Examples
{
"name": "discord-bot",
"type": "tool",
"description": "Discord moderation bot",
"supported_providers": ["any"],
"run_mode": "always_on",
"runtime": {
"command": "python bot.py"
},
"required_secrets": ["DISCORD_BOT_TOKEN", "ANTHROPIC_API_KEY"],
"callable": false,
"timeout_seconds": 0,
"tags": ["discord", "moderation"]
}
On-Demand Agent Type (Managed Loop)
{
"name": "code-fixer",
"type": "agent",
"description": "Fixes code until tests pass",
"supported_providers": ["anthropic"],
"run_mode": "on_demand",
"loop": {
"max_turns": 30
},
"required_secrets": ["ANTHROPIC_API_KEY"],
"timeout_seconds": 300,
"custom_tools": [
{
"name": "run_tests",
"description": "Run the test suite",
"command": "pytest"
}
]
}
On-Demand Prompt Type (Direct LLM)
{
"name": "summarizer",
"type": "prompt",
"description": "Summarizes documents",
"supported_providers": ["openai", "anthropic", "gemini"],
"run_mode": "on_demand",
"callable": false
}
{
"name": "security-review",
"type": "tool",
"description": "Comprehensive security review combining multiple scanners",
"supported_providers": ["any"],
"runtime": {
"command": "python main.py"
},
"timeout_seconds": 180,
"tags": ["security", "scanning", "orchestrator"],
"manifest": {
"manifest_version": 1,
"dependencies": [
{ "id": "joe/leak-finder", "version": "v1" },
{ "id": "joe/vuln-scanner", "version": "v1" },
{ "id": "joe/license-checker", "version": "v1" }
],
"max_hops": 2,
"timeout_ms": 180000,
"per_call_downstream_cap": 50,
"min_required_hops": 2,
"region": "us-east",
"visibility": "public"
}
}