> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ensemble.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# code Operation

> Execute custom JavaScript/TypeScript for data transformation, business logic, and calculations. Fast, free, fully under your control.

<Warning>
  **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.
</Warning>

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:

```typescript theme={null}
// 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:

```yaml theme={null}
agents:
  - name: transform
    operation: code
    config:
      script: scripts/calculate-profit
    input:
      revenue: $input.revenue
      costs: $input.costs
```

## Configuration

```yaml theme={null}
config:
  script: string  # Path to bundled TypeScript/JavaScript script
```

The script receives the full `AgentExecutionContext`:

* `context.input` - The input data passed to the agent
* `context.env` - Environment bindings (KV, D1, etc.)
* `context.logger` - Structured logging
* `context.request` - HTTP request (if triggered via HTTP)
* `context.cache` - Caching utilities
* `context.location` - [Geographic and compliance data](/conductor/core-concepts/location-context)
* `context.edge` - [Network and protocol info](/conductor/core-concepts/edge-context)
* `context.telemetry` - [Emit business metrics](/conductor/operations/telemetry)

## Script File Format

Scripts must export a default function:

```typescript theme={null}
// 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:

```typescript theme={null}
// 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:

| Format       | Example                         |
| ------------ | ------------------------------- |
| Shorthand    | `scripts/transforms/csv`        |
| Full URI     | `script://transforms/csv`       |
| With version | `scripts/transforms/csv@v1.0.0` |

## Common Use Cases

### 1. Data Transformation

```typescript theme={null}
// 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()
  }
}
```

```yaml theme={null}
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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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](/conductor/core-concepts/location-context) for geo-aware business logic:

```typescript theme={null}
// 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](/conductor/core-concepts/edge-context) for security decisions:

```typescript theme={null}
// 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:

```yaml theme={null}
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
```

```typescript theme={null}
// 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`:

```typescript theme={null}
// 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

```typescript theme={null}
// 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

```typescript theme={null}
// 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:

```yaml theme={null}
agents:
  - name: validate
    operation: code
    config:
      script: scripts/validators/email
```

## Best Practices

**1. Keep Scripts Focused**

```typescript theme={null}
// 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**

```typescript theme={null}
// 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**

```typescript theme={null}
// 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**

```typescript theme={null}
// 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**

```typescript theme={null}
// 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)

```yaml theme={null}
# ❌ This won't work in Cloudflare Workers
agents:
  - name: transform
    operation: code
    config:
      code: |
        const { revenue, costs } = input;
        return { profit: revenue - costs };
```

### After (Recommended)

```typescript theme={null}
// 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 }
}
```

```yaml theme={null}
# ✅ Works everywhere
agents:
  - name: transform
    operation: code
    config:
      script: scripts/calculate-profit
    input:
      revenue: $input.revenue
      costs: $input.costs
```

### Key Differences

| Inline Code        | Script Files               |
| ------------------ | -------------------------- |
| `input` variable   | `context.input` property   |
| Direct `return`    | `return` from function     |
| No types           | Full TypeScript support    |
| No `env` access    | `context.env` for bindings |
| Dynamic evaluation | Build-time bundling        |

## Testing

Test your scripts with full TypeScript support:

```typescript theme={null}
// 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](/conductor/building/creating-agents) for more.

## Next Steps

<CardGroup cols={2}>
  <Card title="think Operation" icon="brain" href="/conductor/operations/think">
    AI reasoning
  </Card>

  <Card title="storage Operation" icon="database" href="/conductor/operations/storage">
    Data persistence
  </Card>

  <Card title="Script Components" icon="code" href="/conductor/components/scripts">
    Versioned scripts
  </Card>

  <Card title="Playbooks" icon="layer-group" href="/conductor/playbooks/rag-pipeline">
    Common patterns
  </Card>
</CardGroup>
