Skip to main content
Cloudflare Workers Compatibility: Inline code (config.code) is not supported in Cloudflare Workers due to security restrictions. Workers blocks new Function() and eval() by design. Use config.script to reference bundled script files instead.
Use it for transformations, validations, calculations, and custom logic that doesn’t require AI or external services.

Basic Usage

Create a script file in your scripts/ directory:
// scripts/calculate-profit.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function calculateProfit(context: AgentExecutionContext) {
  const { revenue, costs } = context.input as { revenue: number; costs: number }
  const profit = revenue - costs
  return { profit, margin: (profit / revenue) * 100 }
}
Reference it in your ensemble:
agents:
  - name: transform
    operation: code
    config:
      script: scripts/calculate-profit
    input:
      revenue: $input.revenue
      costs: $input.costs

Configuration

config:
  script: string  # Path to bundled TypeScript/JavaScript script
The script receives the full AgentExecutionContext:

Script File Format

Scripts must export a default function:
// scripts/my-script.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function myScript(context: AgentExecutionContext) {
  const input = context.input as MyInputType

  // Your logic here

  return { result: 'success' }
}

Async Scripts

Scripts can be async for operations that need to wait:
// scripts/process-with-storage.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function processWithStorage(context: AgentExecutionContext) {
  const { env, input } = context

  // Access KV storage
  const cached = await env.KV?.get('my-key')
  if (cached) {
    return JSON.parse(cached)
  }

  const result = processData(input)
  await env.KV?.put('my-key', JSON.stringify(result))

  return result
}

URI Formats

Both formats are supported for referencing scripts:
FormatExample
Shorthandscripts/transforms/csv
Full URIscript://transforms/csv
With versionscripts/transforms/[email protected]

Common Use Cases

1. Data Transformation

// scripts/transforms/format-output.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

interface FormatInput {
  firstName: string
  lastName: string
  email: string
  timestamp: string
}

export default function formatOutput(context: AgentExecutionContext) {
  const input = context.input as FormatInput

  return {
    fullName: `${input.firstName} ${input.lastName}`,
    email: input.email.toLowerCase(),
    displayDate: new Date(input.timestamp).toLocaleDateString()
  }
}
agents:
  - name: format-output
    operation: code
    config:
      script: scripts/transforms/format-output
    input:
      firstName: $input.firstName
      lastName: $input.lastName
      email: $input.email
      timestamp: $input.timestamp

2. Calculations

// scripts/finance/calculate-roi.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

interface RoiInput {
  initialInvestment: number
  currentValue: number
  years: number
}

export default function calculateRoi(context: AgentExecutionContext) {
  const { initialInvestment, currentValue, years } = context.input as RoiInput

  const profit = currentValue - initialInvestment
  const roi = (profit / initialInvestment) * 100
  const annualizedROI = Math.pow(currentValue / initialInvestment, 1 / years) - 1

  return {
    profit,
    roi: parseFloat(roi.toFixed(2)),
    annualizedROI: parseFloat((annualizedROI * 100).toFixed(2))
  }
}

3. Validation

// scripts/validators/validate-order.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

interface OrderInput {
  email?: string
  amount?: number
  items?: unknown[]
}

export default function validateOrder(context: AgentExecutionContext) {
  const input = context.input as OrderInput
  const errors: string[] = []

  // Validate email
  if (!input.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.email)) {
    errors.push('Invalid email address')
  }

  // Validate amount
  if (!input.amount || input.amount <= 0) {
    errors.push('Amount must be positive')
  }

  // Validate items
  if (!input.items || input.items.length === 0) {
    errors.push('Order must contain at least one item')
  }

  return {
    valid: errors.length === 0,
    errors
  }
}

4. Array Operations

// scripts/utils/analyze-array.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function analyzeArray(context: AgentExecutionContext) {
  const { numbers } = context.input as { numbers: number[] }

  const sum = numbers.reduce((acc, n) => acc + n, 0)
  const avg = sum / numbers.length
  const sorted = [...numbers].sort((a, b) => a - b)
  const median = sorted[Math.floor(sorted.length / 2)]
  const min = Math.min(...numbers)
  const max = Math.max(...numbers)

  return {
    sum,
    average: avg,
    median,
    min,
    max,
    count: numbers.length
  }
}

5. Conditional Logic

// scripts/business/determine-action.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

interface ActionInput {
  amount: number
  riskScore: number
}

export default function determineAction(context: AgentExecutionContext) {
  const { amount, riskScore } = context.input as ActionInput

  if (amount > 1000) {
    return { action: 'manual_review', reason: 'high_value' }
  } else if (riskScore > 0.8) {
    return { action: 'flag', reason: 'high_risk' }
  } else {
    return { action: 'approve', reason: 'normal' }
  }
}

6. Date/Time Operations

// scripts/utils/process-dates.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function processDates(context: AgentExecutionContext) {
  const { timestamp } = context.input as { timestamp: string }
  const date = new Date(timestamp)

  return {
    iso: date.toISOString(),
    local: date.toLocaleString(),
    date: date.toLocaleDateString(),
    time: date.toLocaleTimeString(),
    unix: Math.floor(date.getTime() / 1000),
    dayOfWeek: date.toLocaleDateString('en-US', { weekday: 'long' }),
    month: date.toLocaleDateString('en-US', { month: 'long' }),
    year: date.getFullYear()
  }
}

7. Location-Aware Logic

Use the location context for geo-aware business logic:
// scripts/geo/localized-pricing.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function localizedPricing(context: AgentExecutionContext) {
  const { basePrice } = context.input as { basePrice: number }
  const { location } = context

  // Apply regional pricing
  const regionalMultipliers: Record<string, number> = {
    US: 1.0,
    GB: 0.85,
    EU: 0.90,
    IN: 0.50,
  }

  const region = location?.isIn(['EU']) ? 'EU' : location?.country
  const multiplier = regionalMultipliers[region ?? ''] ?? 1.0

  return {
    price: basePrice * multiplier,
    currency: location?.isIn(['GB']) ? 'GBP' : location?.isIn(['EU']) ? 'EUR' : 'USD',
    country: location?.country,
    requiresConsent: location?.requiresConsent('analytics')
  }
}

8. Security & Compliance Checks

Use the edge context for security decisions:
// scripts/security/traffic-filter.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function trafficFilter(context: AgentExecutionContext) {
  const { edge, location, logger } = context

  let riskScore = 0
  const flags: string[] = []

  // Cloud provider traffic often indicates automation
  if (edge?.isFromCloudProvider()) {
    riskScore += 30
    flags.push(`cloud_provider:${edge.getCloudProvider()}`)
  }

  // VPN usage
  if (edge?.isFromVPN()) {
    riskScore += 10
    flags.push('vpn_detected')
  }

  // Outdated TLS
  if (!edge?.isModernTLS()) {
    riskScore += 20
    flags.push('legacy_tls')
  }

  logger?.info('Traffic analysis', { riskScore, flags, asn: edge?.asn })

  return {
    allowed: riskScore < 50,
    riskScore,
    flags,
    datacenter: edge?.coloName
  }
}

Using Previous Agent Outputs

Reference outputs from previous agents via the input mapping:
agents:
  - name: fetch-data
    operation: http
    config:
      url: https://api.example.com/data

  - name: process-results
    operation: code
    config:
      script: scripts/process-api-data
    input:
      apiData: $fetch-data.output.data
// scripts/process-api-data.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function processApiData(context: AgentExecutionContext) {
  const { apiData } = context.input as { apiData: Array<{ id: string; value: number }> }

  const processed = apiData.map(item => ({
    id: item.id,
    value: item.value * 2
  }))

  return { processed }
}

Accessing Environment

Scripts have access to Cloudflare bindings via context.env:
// scripts/with-env.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function withEnv(context: AgentExecutionContext) {
  const { env, input } = context

  // Access KV storage
  const cached = await env.KV?.get(`cache:${input.key}`)
  if (cached) {
    return JSON.parse(cached)
  }

  // Access D1 database
  const result = await env.DB?.prepare('SELECT * FROM users WHERE id = ?')
    .bind(input.userId)
    .first()

  // Access environment variables
  const apiKey = env.API_KEY

  return { user: result, apiKey: apiKey ? 'present' : 'missing' }
}

Error Handling

Throw Errors

// scripts/divide.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function divide(context: AgentExecutionContext) {
  const { numerator, denominator } = context.input as { numerator: number; denominator: number }

  if (denominator === 0) {
    throw new Error('Division by zero')
  }

  return { result: numerator / denominator }
}

Return Error State

// scripts/safe-operation.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function safeOperation(context: AgentExecutionContext) {
  try {
    const result = performRiskyOperation(context.input)
    return { success: true, result }
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error',
      fallback: getDefaultValue()
    }
  }
}

Directory Organization

Organize your scripts by domain:
scripts/
├── transforms/
│   ├── csv.ts
│   ├── json.ts
│   └── normalize.ts
├── validators/
│   ├── email.ts
│   └── order.ts
├── finance/
│   ├── calculate-roi.ts
│   └── compound-interest.ts
├── utils/
│   ├── format-date.ts
│   └── parse-json.ts
└── auth/
    ├── verify-token.ts
    └── hash-password.ts
Reference them with their path:
agents:
  - name: validate
    operation: code
    config:
      script: scripts/validators/email

Best Practices

1. Keep Scripts Focused
// Good: Single responsibility
export default function calculateTotal(context: AgentExecutionContext) {
  const items = context.input as Array<{ price: number }>
  return { total: items.reduce((sum, item) => sum + item.price, 0) }
}

// Bad: Multiple responsibilities in one script
export default function doEverything(context: AgentExecutionContext) {
  // Validate, transform, calculate, format... 200 lines
}
2. Type Your Inputs
// Good: Clear types
interface OrderInput {
  items: Array<{ price: number; quantity: number }>
  discount?: number
}

export default function processOrder(context: AgentExecutionContext) {
  const input = context.input as OrderInput
  // TypeScript helps catch errors
}
3. Handle Nulls
// Good: Check for null/undefined
export default function processItems(context: AgentExecutionContext) {
  const items = (context.input as { items?: unknown[] }).items || []
  return { count: items.length }
}

// Bad: Assume data exists
export default function processItems(context: AgentExecutionContext) {
  return { count: (context.input as any).items.length } // May throw
}
4. Return Objects
// Good: Return object
export default function calculate(context: AgentExecutionContext) {
  return { result: value }
}

// Bad: Return primitive (less flexible)
export default function calculate(context: AgentExecutionContext) {
  return value
}
5. Don’t Mutate Input
// Good: Create new array
export default function sortItems(context: AgentExecutionContext) {
  const items = [...(context.input as { items: number[] }).items]
  items.sort()
  return { items }
}

// Bad: Mutate original
export default function sortItems(context: AgentExecutionContext) {
  const items = (context.input as { items: number[] }).items
  items.sort() // Mutates original
  return { items }
}

Migration from Inline Code

If you have existing ensembles with inline code, migrate them to script files:

Before (Not Supported in Workers)

# ❌ This won't work in Cloudflare Workers
agents:
  - name: transform
    operation: code
    config:
      code: |
        const { revenue, costs } = input;
        return { profit: revenue - costs };
// scripts/calculate-profit.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function calculateProfit(context: AgentExecutionContext) {
  const { revenue, costs } = context.input as { revenue: number; costs: number }
  return { profit: revenue - costs }
}
# ✅ Works everywhere
agents:
  - name: transform
    operation: code
    config:
      script: scripts/calculate-profit
    input:
      revenue: $input.revenue
      costs: $input.costs

Key Differences

Inline CodeScript Files
input variablecontext.input property
Direct returnreturn from function
No typesFull TypeScript support
No env accesscontext.env for bindings
Dynamic evaluationBuild-time bundling

Testing

Test your scripts with full TypeScript support:
// scripts/__tests__/calculate-profit.test.ts
import calculateProfit from '../calculate-profit'

describe('calculateProfit', () => {
  it('calculates profit correctly', () => {
    const mockContext = {
      input: { revenue: 1000, costs: 600 },
      env: {},
    } as any

    const result = calculateProfit(mockContext)

    expect(result.profit).toBe(400)
  })
})

When to Use Custom Agents Instead

Use full custom agents (with agents/ directory) when you need:
  1. Multiple related operations - Group related functionality
  2. Complex state management - Shared state across operations
  3. External dependencies - npm packages
  4. Database access - Complex queries
  5. Reusable across projects - Publish as agent package
See Creating Agents for more.

Next Steps