Complete reference for ensemble, agent, and component YAML files.
Ensemble Schema
ensemble: string # Required: Ensemble name
description: string # Optional: Description
apiExecutable: boolean # Optional: Control Execute API access (default: true)
trigger: # Optional: Trigger configuration
- type: http | webhook | mcp | email | queue | cron | build | cli
# See Trigger Types section below for type-specific fields
inputs: # Optional: Input definitions
input_name:
type: string | number | boolean | array | object
required: boolean
default: any
description: string
state: # Optional: Ensemble state
schema:
field_name: type
agents: # Required: Agent list
- name: string # Required: Agent name
agent: string # Agent reference (OR operation)
operation: string # Operation type (OR agent)
inputs: # Agent inputs
key: value
config: # Operation config
key: value
condition: string # Optional: Execution condition
cache: # Optional: Cache config
ttl: number
key: string
retry: # Optional: Retry config
maxAttempts: number
backoff: exponential | linear
initialDelay: number
maxDelay: number
retryOn: [number]
timeout: number # Optional: Timeout (ms)
state: # Optional: State access
use: [string]
set:
field: value
output: # Optional: Output definition
status: number # HTTP status code
headers: # Response headers
key: value
format: # Response format config
type: json | text | html | xml | csv | markdown | yaml | ics | rss | atom
extract: string # Field to extract from body
body: # Response body
key: value
Agent Schema
agent: string # Required: Agent name
description: string # Optional: Description
apiExecutable: boolean # Optional: Control Execute API access (default: true)
inputs: # Optional: Input definitions
input_name:
type: string | number | boolean | array | object
required: boolean
default: any
description: string
state: # Optional: Agent state
schema:
field_name: type
operations: # Required: Operation list
- name: string # Required: Operation name
operation: string # Required: Operation type
config: # Operation-specific config
key: value
condition: string # Optional: Execution condition
cache: # Optional: Cache config
ttl: number
key: string
retry: # Optional: Retry config
maxAttempts: number
backoff: exponential | linear
timeout: number # Optional: Timeout (ms)
state: # Optional: State access
use: [string]
set:
field: value
outputs: # Optional: Output definition
output_name: expression
cache: # Optional: Agent-level cache
ttl: number
key: string
retry: # Optional: Agent-level retry
maxAttempts: number
backoff: exponential | linear
Component Schema
component: string # Required: Component name
version: string # Required: Semantic version
description: string # Optional: Description
type: prompt | template | schema | config
content: string | object # Component content
Trigger Types
Ensembles can be invoked via multiple trigger mechanisms. Each trigger type has specific configuration fields.
HTTP Trigger
Expose ensembles as HTTP endpoints with support for single or multiple paths.
trigger:
- type: http
path: /api/users/:id # Single path
methods: [GET, POST]
auth:
type: bearer
secret: ${env.API_TOKEN}
rateLimit:
requests: 100
window: 60
cors:
origin: "https://myapp.com"
public: true # Optional: bypass auth
Multi-Path HTTP
Define multiple paths with different methods in a single trigger:
trigger:
- type: http
paths:
- path: /api/v1/users
methods: [GET, POST]
- path: /api/v1/users/:id
methods: [GET, PUT, DELETE]
- path: /api/v1/users/:id/profile
methods: [GET, PATCH]
auth:
type: bearer
public: false
Webhook Trigger
Handle incoming webhooks from external services.
trigger:
- type: webhook
path: /webhooks/github
methods: [POST]
auth:
type: signature
secret: ${env.WEBHOOK_SECRET}
async: true # Optional: process asynchronously
public: false
MCP Trigger
Expose ensemble as an MCP (Model Context Protocol) tool for AI agents.
trigger:
- type: mcp
auth:
type: api_key
public: false
Email Trigger
Route emails to ensembles for processing.
Queue Trigger
Process messages from queues.
trigger:
- type: queue
queue: user-events
batch_size: 10
max_retries: 3
Cron Trigger
Schedule ensemble execution at specific times.
trigger:
- type: cron
cron: "0 8 * * *" # 8 AM daily
timezone: "America/New_York"
enabled: true
input: # Optional: static input
report_type: "daily"
Build Trigger (NEW)
Execute ensemble at build time for static generation.
trigger:
- type: build
enabled: true
output: ./dist/docs # Optional: output directory
input: # Optional: build-time input
action: generate-openapi
metadata: # Optional: metadata
version: 1.0.0
Use Cases:
- Generate OpenAPI documentation
- Pre-render static pages
- Build search indexes
- Generate sitemap files
CLI Usage:
ensemble conductor build # Run all build triggers
CLI Trigger (NEW)
Create custom CLI commands for development and operations.
trigger:
- type: cli
command: generate-docs
description: Generate documentation
options:
- name: format
type: string
default: yaml
description: Output format
- name: output
type: string
required: true
description: Output file path
- name: verbose
type: boolean
default: false
description: Verbose output
Access Options in Flow:
flow:
- agent: docs-generator
input:
format: ${trigger.options.format}
output: ${trigger.options.output}
verbose: ${trigger.options.verbose}
CLI Usage:
ensemble conductor run generate-docs --output ./docs --format markdown --verbose
Option Types:
string - Text value
number - Numeric value
boolean - True/false flag
array - Multiple values
Expression Syntax
Variable Access
${input.field} # Input variable
${env.VARIABLE} # Environment variable
${state.field} # State variable
${agent-name.output} # Agent output
${agent-name.output.nested.field} # Nested output
${[email protected]} # Component reference
${trigger.options.format} # CLI trigger option
Array Indexing
${input.items[0]} # First array element
${input.items[2]} # Third element (0-indexed)
${input.users[0].name} # Property of first element
${data.results[1].meta.id} # Deep nested access
Nullish Coalescing (??)
Returns the first value that is not null or undefined:
${input.name ?? "default"} # Use "default" if null/undefined
${input.query.name ?? input.body.name ?? "Guest"} # Chain fallbacks
# Preserves falsy values (unlike ||)
${input.count ?? 0} # Returns 0 if count is 0, not the fallback
${input.value ?? ""} # Returns "" if value is ""
Falsy Coalescing (||)
Returns the first truthy value (catches "", 0, false, null, undefined):
${input.name || "default"} # Use "default" if empty/falsy
${input.count || 10} # Use 10 if count is 0
${input.enabled || true} # Use true if enabled is false
${input.a || input.b || "fallback"} # Chain multiple values
Ternary Conditionals (?:)
${input.enabled ? "yes" : "no"} # Returns "yes" if truthy
${input.count ? input.count : 1} # Returns count or 1
${input.premium ? 100 : 10} # Numeric conditional
${input.type ? input.type : "default"} # Path in branches
Boolean Negation (!)
${!input.disabled} # true if disabled is falsy
${!input.hidden} # true if hidden is false/undefined
${!agent.executed} # true if agent didn't run
Conditionals
condition: ${input.value > 10}
condition: ${agent.success}
condition: ${agent.failed}
condition: ${!agent.executed}
condition: ${input.type === 'premium'}
condition: ${input.age >= 18 && input.verified}
Filter Chains
${input.text | uppercase} # Apply uppercase filter
${input.text | split(" ") | first} # Chain multiple filters
${input.items | length} # Array/string length
${input.value | default("none")} # Default if empty
Functions
${Date.now()} # Current timestamp
${JSON.stringify(object)} # JSON encode
${JSON.parse(string)} # JSON decode
${Math.round(number)} # Math functions
${array.length} # Array length
${array.map(item => item.id)} # Array map
${array.filter(item => item.active)} # Array filter
${array.reduce((sum, item) => sum + item.value, 0)} # Array reduce
${string.split(',')} # String split
${string.toUpperCase()} # String uppercase
Operator Precedence
When combining operators, they’re evaluated in this order:
- Ternary (
?:) - evaluated first
- Nullish coalescing (
??) - null/undefined only
- Falsy coalescing (
||) - all falsy values
- Filters (
|) - transformation chains
# Complex example
${input.enabled ? input.premium ?? "basic" : "disabled"}
# If enabled is truthy: returns premium or "basic"
# If enabled is falsy: returns "disabled"
Operation-Specific Config
think
config:
provider: openai | anthropic | cloudflare | groq
model: string
prompt: string
messages: array
temperature: number
maxTokens: number
schema: string | object # JSON Schema reference or inline schema
image: string # For vision models
stream: boolean
code
config:
code: string # JavaScript code
storage
# KV
config:
type: kv
action: get | put | delete
key: string
value: any # For put
ttl: number # For put
# D1
config:
type: d1
query: string
params: array
# R2
config:
type: r2
action: get | put | delete
key: string
value: any # For put
# Vectorize
config:
type: vectorize
action: search | embed | delete
query: string # For search
text: string | array # For embed
topK: number # For search
namespace: string
filter: object
http
config:
url: string
method: GET | POST | PUT | PATCH | DELETE
headers: object
body: any
timeout: number
config:
tool: string
params: object
email
config:
to: string | array
from: string
subject: string
body: string
html: string
sms
config:
to: string
from: string
body: string
html
config:
template: string
data: object
html: string # Or raw HTML
pdf
config:
html: string
filename: string
format: A4 | Letter
margin: object
page
config:
component: string
props: object
renderMode: ssr | csr | hybrid
Cache Configuration
cache:
ttl: number # Time to live (seconds)
key: string # Cache key (supports expressions)
Example:
cache:
ttl: 3600
key: user-${input.user_id}
Retry Configuration
retry:
maxAttempts: number # Max retry attempts (default: 3)
backoff: exponential | linear # Backoff strategy (default: exponential)
initialDelay: number # Initial delay (ms, default: 1000)
maxDelay: number # Max delay (ms, default: 30000)
retryOn: [number] # HTTP status codes to retry (default: [500, 502, 503, 504])
State Configuration
state:
schema: # State schema
field1: type
field2: type
use: [field1, field2] # Read these fields
set: # Write these fields
field1: ${expression}
field2: ${expression}
Condition Expressions
# Simple comparisons
condition: ${value > 10}
condition: ${value === 'active'}
condition: ${value !== null}
# Boolean operations
condition: ${value1 && value2}
condition: ${value1 || value2}
condition: ${!value}
# Agent execution status
condition: ${agent.success} # Agent succeeded
condition: ${agent.failed} # Agent failed
condition: ${agent.executed} # Agent ran
condition: ${!agent.executed} # Agent didn't run
# Complex conditions
condition: ${input.amount > 1000 && input.verified}
condition: ${user.role === 'admin' || user.role === 'moderator'}
Output Definitions
# Simple output
output:
result: ${agent.output}
# Multiple outputs
output:
data: ${process.output}
metadata:
timestamp: ${Date.now()}
version: "1.0"
# Computed outputs
output:
total: ${items.reduce((sum, item) => sum + item.price, 0)}
count: ${items.length}
The format field in the output block controls response serialization and Content-Type headers. This is the recommended approach for non-JSON responses.
| Type | Content-Type | Description |
|---|
json | application/json | JSON serialization (default) |
text | text/plain | Plain text |
html | text/html | HTML content |
xml | application/xml | XML content |
csv | text/csv | CSV serialization from arrays |
markdown | text/markdown | Markdown content |
yaml | application/x-yaml | YAML serialization |
ics | text/calendar | iCalendar format |
rss | application/rss+xml | RSS feed |
atom | application/atom+xml | Atom feed |
output:
status: 200
format: text
body:
content: "Hello, World!"
Use extract to specify which field from the body should be serialized:
output:
status: 200
format:
type: csv
extract: users
body:
users: ${fetch-users.output}
metadata: ${meta.output}
CSV Export:
output:
status: 200
headers:
Content-Disposition: attachment; filename="data.csv"
format:
type: csv
extract: records
body:
records: ${query.output}
YAML Config:
output:
status: 200
format:
type: yaml
extract: config
body:
config: ${build-config.output}
iCalendar Event:
output:
status: 200
headers:
Content-Disposition: attachment; filename="event.ics"
format: ics
body:
calendar: ${generate-ics.output}
RSS Feed:
output:
status: 200
format: rss
body:
feed: ${build-feed.output}
API Execution Configuration
Control which agents and ensembles can be executed via the Execute API (/api/v1/execute/agent/* and /api/v1/execute/ensemble/*).
Project-Level Configuration
In conductor.config.ts:
const config: ConductorConfig = {
api: {
execution: {
agents: {
// When true, agents must have apiExecutable: true to be API executable
requireExplicit: false
},
ensembles: {
// When true, ensembles must have apiExecutable: true to be API executable
requireExplicit: false
}
}
}
};
Agent/Ensemble-Level Configuration
Use apiExecutable to control individual agent or ensemble access:
# Explicitly allow API execution
agent: my-public-agent
apiExecutable: true
# Explicitly deny API execution
ensemble: internal-workflow
apiExecutable: false
Behavior Matrix
requireExplicit | apiExecutable | Result |
|---|
false (default) | undefined | ✅ Allowed |
false | true | ✅ Allowed |
false | false | ❌ Denied |
true | undefined | ❌ Denied |
true | true | ✅ Allowed |
true | false | ❌ Denied |
When requireExplicit: true, agents/ensembles must explicitly opt-in to API execution. This is recommended for production environments where you want fine-grained control over which workflows are externally accessible.
Examples
Complete Ensemble
ensemble: user-onboarding
description: Onboard new users
inputs:
email:
type: string
required: true
name:
type: string
required: true
state:
schema:
onboarding_step: number
agents:
- name: validate-email
operation: code
config:
script: scripts/validate-email-address
input:
email: ${input.email}
- name: create-account
condition: ${validate-email.output.valid}
operation: storage
config:
type: d1
query: INSERT INTO users (email, name) VALUES (?, ?)
params:
- ${input.email}
- ${input.name}
// scripts/validate-email-address.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'
export default function validateEmailAddress(context: AgentExecutionContext) {
const { email } = context.input
const regex = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/
return { valid: regex.test(email) }
}
- name: send-welcome
condition: ${create-account.success}
operation: email
config:
to: ${input.email}
subject: "Welcome!"
body: "Welcome ${input.name}!"
output:
user_id: ${create-account.output.id}
success: ${send-welcome.success}
Next Steps