Config-as-Code Pipelines
Define multi-agent workflows in .caged.yaml — with DAG-based execution, crash recovery, cost gates, and human approval checkpoints.
Why Agent Pipelines?
Unlike traditional CI/CD (shell commands running builds), agent pipelines let AI agents do the work:
| Traditional CI | Agent Pipeline |
|---|---|
npm run build |
Agent writes the code |
npm test |
Agent writes + runs tests |
| Human creates PR | Agent creates the PR |
| Human reviews | Agent reviews, human approves |
| Fixed commands | Dynamic prompts with context |
Define these workflows alongside your code — version controlled, reproducible, portable.
Agent Stage Type
The type: agent stage is the core building block. It spawns a sandbox, runs an AI agent inside, and captures the session:
stages:
- name: implement-feature
type: agent
agent: claude-code # or: aider, codex, custom
prompt: |
Implement the following feature:
$FEATURE_SPEC
Requirements:
- Write production-quality code
- Add unit tests with >80% coverage
- Commit changes with conventional commits
template: node-20
timeout: 30m
budget: 15.00
The agent runs autonomously until it completes, hits the budget, or times out.
Real-World Example: Feature Implementation
An AI agent implements a feature, another agent reviews it, human approves, then it deploys:
# .caged.yaml — Agent-driven feature implementation
template: node-20
budget: 50.00
network_mode: allowlist
allowed_hosts:
- api.anthropic.com
- api.openai.com
- github.com
- registry.npmjs.org
secrets:
- ANTHROPIC_API_KEY
- GITHUB_TOKEN
pipelines:
- name: agent-feature
description: AI agents implement features with human oversight
stages:
# 1. Agent implements the feature
- name: implement
type: agent
agent: claude-code
prompt: |
Implement the following feature:
$FEATURE_SPEC
Requirements:
- Write production-quality code
- Add unit tests with >80% coverage
- Update documentation
- Commit changes with conventional commits
template: node-20
timeout: 30m
budget: 15.00
# 2. Different agent reviews the implementation
- name: code-review
type: agent
agent: aider
prompt: |
Review the changes made in the previous step:
- Check for bugs, security issues, performance problems
- Verify tests are comprehensive
- Suggest improvements
Output a structured review with PASS/FAIL and findings.
depends_on: [implement]
timeout: 10m
budget: 5.00
# 3. Run automated tests
- name: test
type: command
command: npm test && npm run lint
depends_on: [code-review]
# 4. Gates before human review
- name: quality-gate
type: gate
depends_on: [test]
gate:
trust_above: 70
cost_below: 25.00
# 5. Human reviews agent's work
- name: human-approval
type: await_approval
depends_on: [quality-gate]
approval:
message: |
Agent implemented: $FEATURE_SPEC
Review the diff and agent's work before merging.
channels: [slack, dashboard]
sla_timeout: 24h
# 6. Agent addresses feedback if any
- name: address-feedback
type: agent
agent: claude-code
prompt: |
Address the reviewer's feedback:
$APPROVAL_COMMENTS
depends_on: [human-approval]
condition:
if: approval.has_comments
timeout: 15m
# 7. Merge and deploy
- name: merge
type: command
command: git push origin HEAD:main
depends_on: [address-feedback]
This pipeline:
- Agent implements — Claude Code writes code, tests, docs
- Agent reviews — Different agent (Aider) provides independent review
- Tests run — Automated validation
- Gates check — Trust score and cost limits
- Human approves — Review the agent's work
- Agent fixes — Address any feedback
- Merge — Ship it
More Real-World Pipelines
Bug Fix Pipeline
Agent investigates and fixes bugs from issue tracker:
pipelines:
- name: bug-fix
description: Agent investigates and fixes reported bugs
stages:
- name: investigate
type: agent
agent: claude-code
prompt: |
Investigate this bug report:
$BUG_REPORT
1. Reproduce the issue
2. Find the root cause
3. Document your findings
timeout: 15m
budget: 5.00
- name: fix
type: agent
agent: claude-code
prompt: |
Fix the bug you investigated.
- Write a failing test first
- Implement the fix
- Verify all tests pass
- Commit with "fix: <description>"
depends_on: [investigate]
timeout: 20m
budget: 10.00
- name: verify
type: command
command: npm test
depends_on: [fix]
- name: approve
type: await_approval
depends_on: [verify]
approval:
message: "Bug fix ready for review. See diff."
sla_timeout: 4h
Dependency Upgrade Pipeline
Agent upgrades dependencies and fixes breaking changes:
pipelines:
- name: dep-upgrade
description: Agent upgrades dependencies safely
stages:
- name: audit
type: command
command: npm audit --json > audit.json
- name: upgrade
type: agent
agent: claude-code
prompt: |
Upgrade all dependencies with known vulnerabilities.
Read audit.json for the list.
For each upgrade:
1. Update package.json
2. Run npm install
3. Fix any breaking changes
4. Ensure tests pass
5. Commit each upgrade separately
depends_on: [audit]
timeout: 45m
budget: 20.00
- name: test
type: command
command: npm test
depends_on: [upgrade]
- name: approve
type: await_approval
depends_on: [test]
approval:
message: "Dependency upgrades complete. Review changes."
Code Migration Pipeline
Agent migrates codebase (e.g., JS → TypeScript):
pipelines:
- name: migrate-to-typescript
description: Agent converts JavaScript to TypeScript
stages:
- name: setup
type: command
command: |
npm install typescript @types/node --save-dev
npx tsc --init
- name: migrate-batch
type: agent
agent: claude-code
prompt: |
Convert the next batch of JavaScript files to TypeScript.
Rules:
- Convert 5-10 files per batch
- Add proper type annotations
- Fix any type errors
- Ensure tests still pass
- Track progress in MIGRATION.md
depends_on: [setup]
timeout: 30m
budget: 15.00
- name: typecheck
type: command
command: npx tsc --noEmit
depends_on: [migrate-batch]
- name: review-batch
type: await_approval
depends_on: [typecheck]
approval:
message: "Migration batch complete. Review before continuing."
sla_timeout: 2h
- name: continue-or-done
type: gate
depends_on: [review-batch]
gate:
# Custom expression: continue if JS files remain
if: "count(glob('**/*.js')) > 0"
Parallel Agent Execution
Run multiple agents in parallel when they don't depend on each other:
pipelines:
- name: multi-agent-review
stages:
- name: implement
type: agent
agent: claude-code
prompt: "Implement feature: $FEATURE_SPEC"
timeout: 30m
# These three run in parallel after implement
- name: security-review
type: agent
agent: claude-code
prompt: "Security review: Check for vulnerabilities, injection risks, auth issues."
depends_on: [implement]
timeout: 10m
budget: 3.00
- name: perf-review
type: agent
agent: aider
prompt: "Performance review: Check for N+1 queries, memory leaks, bottlenecks."
depends_on: [implement]
timeout: 10m
budget: 3.00
- name: test-coverage
type: agent
agent: codex
prompt: "Add missing test cases to achieve 90% coverage."
depends_on: [implement]
timeout: 15m
budget: 5.00
# Waits for all three reviews
- name: human-review
type: await_approval
depends_on: [security-review, perf-review, test-coverage]
approval:
message: "Three agents reviewed. Check their findings."
DAG visualization:
implement
/ | \
security perf coverage
\ | /
human-review
Gate Stages
Add guardrails before expensive operations:
stages:
- name: implement
type: agent
agent: claude-code
prompt: "Build the feature"
budget: 20.00
- name: cost-gate
type: gate
depends_on: [implement]
gate:
cost_below: 15.00 # Fail if agent spent too much
- name: trust-gate
type: gate
depends_on: [cost-gate]
gate:
trust_above: 75 # Fail if trust score dropped
- name: deploy
type: command
command: npm run deploy
depends_on: [trust-gate]
Gates check instantly:
cost_below— fails if run cost exceeds threshold (USD)trust_above— fails if agent trust score is below threshold (0-100)
Pipeline Defaults
Set defaults that apply to all stages:
pipelines:
- name: agent-pipeline
defaults:
template: node-20
timeout: 20m
budget: 10.00
retry:
max_attempts: 2
backoff: 30s
stages:
- name: task-a
type: agent
agent: claude-code
prompt: "Do task A"
# Inherits: template, timeout, budget, retry
- name: task-b
type: agent
agent: claude-code
prompt: "Do task B"
budget: 25.00 # Override default
depends_on: [task-a]
Conditional Execution
Run stages only when conditions are met:
stages:
- name: implement
type: agent
agent: claude-code
prompt: "Implement the feature"
- name: notify-success
type: command
command: |
curl -X POST $SLACK_WEBHOOK \
-d '{"text":"✅ Agent completed task successfully!"}'
depends_on: [implement]
condition:
on_success: true
- name: escalate-failure
type: command
command: |
curl -X POST $PAGERDUTY_WEBHOOK \
-d '{"message":"Agent failed. Human intervention needed."}'
depends_on: [implement]
condition:
on_failure: true
Triggering Pipelines
Once defined in .caged.yaml, trigger pipelines via CLI or API:
# List pipelines
caged pipeline list
# Start a feature implementation
caged pipeline run agent-feature \
--env FEATURE_SPEC="Add dark mode toggle to settings page"
# Start a bug fix
caged pipeline run bug-fix \
--env BUG_REPORT="Users report 500 error on /api/users endpoint"
# Check run status
caged pipeline runs agent-feature
Or via API:
curl -X POST https://api.caged.dev/v1/pipelines/agent-feature/runs \
-H "Authorization: Bearer caged_sk_..." \
-d '{
"env": {
"FEATURE_SPEC": "Add dark mode toggle to settings page"
}
}'
Failure Handling
Control what happens when an agent stage fails:
stages:
- name: primary-agent
type: agent
agent: claude-code
prompt: "Implement the feature"
on_failure: continue # Don't fail pipeline
- name: fallback-agent
type: agent
agent: aider # Try different agent
prompt: "Complete the task the previous agent couldn't finish"
depends_on: [primary-agent]
condition:
on_failure: true # Only run if primary failed
Multiple Pipelines
Define different pipelines for different workflows:
pipelines:
- name: feature
description: Agent implements new features
stages:
- name: implement
type: agent
agent: claude-code
prompt: "$FEATURE_SPEC"
# ...
- name: bugfix
description: Agent investigates and fixes bugs
stages:
- name: investigate
type: agent
agent: claude-code
prompt: "$BUG_REPORT"
# ...
- name: refactor
description: Agent refactors code
stages:
- name: analyze
type: agent
agent: claude-code
prompt: "Analyze $TARGET_PATH for refactoring opportunities"
# ...
Shared State Store
Pipelines include a built-in key/value store for sharing data between stages. Instead of passing artifacts via git branches or external storage, stages can read and write to a shared state store scoped to the run.
Writing State from Stages
Agent stages can write to state using the caged state command available in the sandbox:
stages:
- name: analyze
type: agent
agent: claude-code
prompt: |
Analyze the codebase and identify files that need refactoring.
When done, save your findings:
caged state set analysis_results '{"files": [...], "priority": "high"}'
- name: refactor
type: agent
agent: claude-code
prompt: |
Read the analysis results and refactor the identified files.
caged state get analysis_results
depends_on: [analyze]
State Types
State entries support different types for structured data:
| Type | Description | Example Use |
|---|---|---|
string |
Plain text | Status messages, IDs |
json |
JSON object | Analysis results, configs |
file |
File reference | Generated artifacts |
patch |
Git diff | Code changes for review |
artifact |
Binary data | Build outputs |
# Saving different types via CLI in stage commands
stages:
- name: generate
type: command
command: |
# String
caged state set status "completed"
# JSON
caged state set config '{"debug": true}' --type json
# File reference
caged state set build_artifact './dist/app.js' --type file
# Git patch
git diff HEAD~1 | caged state set changes - --type patch
State TTL and Limits
State entries have automatic expiration and size limits:
- Max key length: 256 characters
- Max value size: 1 MB per entry
- Max entries per run: 100
- Max total size per run: 10 MB
- Default TTL: 7 days
- Max TTL: 30 days
Set custom TTL when writing:
# Expires in 1 hour
caged state set temp_data '{"key": "value"}' --ttl 3600
Accessing State via CLI
# List all state for a run
caged pipeline state list <pipeline-id> <run-id>
# Get a specific entry
caged pipeline state get <pipeline-id> <run-id> analysis_results
# Set a value
caged pipeline state set <pipeline-id> <run-id> review_status '"approved"'
# Set from file
caged pipeline state set <pipeline-id> <run-id> config -f config.json
# Delete an entry
caged pipeline state delete <pipeline-id> <run-id> temp_data
Accessing State via API
# List state
curl https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state \
-H "Authorization: Bearer caged_sk_..."
# Get single entry
curl https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state/analysis_results \
-H "Authorization: Bearer caged_sk_..."
# Set entry
curl -X PUT https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state/my_key \
-H "Authorization: Bearer caged_sk_..." \
-d '{
"value": {"result": "success"},
"type": "json",
"ttl_seconds": 86400,
"created_by": "api"
}'
# Delete entry
curl -X DELETE https://api.caged.dev/v1/pipelines/{id}/runs/{runId}/state/temp_key \
-H "Authorization: Bearer caged_sk_..."
Multi-Agent Handoff with State
State is essential for multi-agent pipelines where one agent's output becomes another's input:
pipelines:
- name: ai-code-review
stages:
- name: implement
type: agent
agent: claude-code
prompt: |
Implement: $FEATURE_SPEC
When complete, save a summary:
caged state set implementation '{"files_changed": [...], "approach": "..."}'
budget: 15.00
- name: security-review
type: agent
agent: claude-code
prompt: |
Review for security issues.
First, get implementation context:
caged state get implementation
Save your findings:
caged state set security_review '{"issues": [...], "severity": "..."}'
depends_on: [implement]
budget: 5.00
- name: compile-report
type: command
command: |
# Combine all reviews into final report
impl=$(caged state get implementation)
security=$(caged state get security_review)
echo "{\"implementation\": $impl, \"security\": $security}" > report.json
caged state set final_report -f report.json
depends_on: [security-review]
- name: human-approval
type: await_approval
depends_on: [compile-report]
approval:
message: "Review complete. See state entry: final_report"
State keeps agents loosely coupled — they communicate through shared state rather than direct dependencies.
Full Example
A complete .caged.yaml with sandbox config and pipelines:
# .caged.yaml
template: node-20
resources:
cpu: 4
memory: 4096
disk: 20
timeout: 3600
budget: 25.00
network_mode: allowlist
allowed_hosts:
- api.openai.com
- registry.npmjs.org
- github.com
secrets:
- OPENAI_API_KEY
- GITHUB_TOKEN
policy:
template: soc2
require_approval:
- "rm -rf *"
- "drop table"
pipelines:
- name: agent-ci-cd
description: AI agent builds, tests, and deploys with human oversight
defaults:
template: node-20
timeout: 15m
retry:
max_attempts: 2
backoff: 30s
stages:
- name: clone
type: command
command: git clone $REPO_URL repo && cd repo
- name: install
type: command
command: cd repo && npm ci
depends_on: [clone]
timeout: 10m
- name: build
type: command
command: cd repo && npm run build
depends_on: [install]
- name: test
type: command
command: cd repo && npm test
depends_on: [build]
retry:
max_attempts: 3
backoff: 10s
- name: lint
type: command
command: cd repo && npm run lint
depends_on: [build]
on_failure: continue
- name: security-scan
type: command
command: cd repo && npm audit --audit-level=high
depends_on: [build]
on_failure: continue
- name: cost-gate
type: gate
depends_on: [test, lint, security-scan]
gate:
cost_below: 10.00
- name: trust-gate
type: gate
depends_on: [cost-gate]
gate:
trust_above: 75
- name: approve-deploy
type: await_approval
depends_on: [trust-gate]
approval:
message: "All checks passed. Deploy to production?"
channels: [slack, dashboard]
sla_timeout: 4h
auto_decision: reject
- name: deploy
type: command
command: cd repo && npm run deploy:prod
depends_on: [approve-deploy]
env:
DEPLOY_ENV: production
- name: notify-success
type: command
command: |
curl -X POST $SLACK_WEBHOOK \
-d '{"text":"✅ Deployment successful!"}'
depends_on: [deploy]
condition:
on_success: true
- name: notify-failure
type: command
command: |
curl -X POST $SLACK_WEBHOOK \
-d '{"text":"❌ Pipeline failed. Check dashboard."}'
depends_on: [deploy]
condition:
on_failure: true
Triggering Pipelines
Once defined in .caged.yaml, pipelines are synced to Caged when you create a sandbox:
# Create sandbox from repo with pipelines
caged create --repo https://github.com/myorg/myapp
# List available pipelines
caged pipeline list
# Start a pipeline run
caged pipeline run agent-ci-cd
Or via API:
# Pipelines are auto-synced on sandbox creation
# Start a run
curl -X POST https://api.caged.dev/v1/pipelines/agent-ci-cd/runs \
-H "Authorization: Bearer caged_sk_..." \
-d '{"env": {"REPO_URL": "https://github.com/myorg/myapp"}}'
Next Steps
- Pipeline State Store Guide — detailed patterns for shared state
- Pipelines Guide — core concepts and best practices
- Multi-Agent Collaboration — multiple agents on the same codebase
- Pipelines API Reference — full endpoint documentation
- Approvals Guide — configuring HITL approval channels