@capsule-run/sdk
v0.6.5
Published
Capsule JavaScript SDK - run AI agent tasks in secure WASM sandboxes
Maintainers
Readme
capsule
A secure, durable runtime for agentic workflows
Overview
Capsule is a runtime for coordinating AI agent tasks in isolated environments. It is designed to handle untrusted code execution, long-running workflows, large-scale processing, or even multi-agent systems.
Each task runs inside its own WebAssembly sandbox, providing:
- Isolated execution: Each task runs isolated from your host system
- Resource limits: Set CPU, memory, and timeout limits per task
- Automatic retries: Handle failures without manual intervention
- Lifecycle tracking: Monitor which tasks are running, completed, or failed
This enables safe task-level execution of untrusted code within AI agent systems.
Installation
npm install -g @capsule-run/cli
npm install @capsule-run/sdkGetting started
Create hello.ts:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (): string => {
return "Hello from Capsule!";
});Run it:
capsule run hello.tsUse
--verboseto display real-time task execution details.
Production
Running source code directly (like .ts) evaluates and compiles your file at runtime. While great for development, this compilation step adds a few seconds of latency. For use cases where sub-second latency is critical, you should build your tasks ahead of time.
# Generates an optimized hello.wasm file
capsule build hello.ts --export
# Execute the compiled artifact directly
capsule exec hello.wasm[!NOTE] Or from your existing code:
import { run } from '@capsule-run/sdk/runner'; const result = await run({ file: './hello.wasm', // or `hello.ts` args: [] }); console.log(`Task completed: ${result.result}`);See Integrate Into an Existing Project for details.
Executing a .wasm file bypasses the compiler completely, reducing initialization time to milliseconds while using a natively optimized (.cwasm) format behind the scenes.
Integrate Into an Existing Project
The run() function lets you execute tasks programmatically from your application code, no CLI needed.
You need
@capsule-run/cliin your dependencies to use therunnerfunctions in TypeScript.
import { run } from '@capsule-run/sdk/runner';
const result = await run({
file: './capsule.ts', // or `capsule.wasm`
args: ['code to execute']
});Create capsule.ts:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (code: string): string => {
return eval(code);
});How It Works
Simply use a wrapper function to define your tasks:
import { task } from "@capsule-run/sdk";
export const analyzeData = task({
name: "analyze_data",
compute: "MEDIUM",
ram: "512MB",
timeout: "30s",
maxRetries: 1
}, (dataset: number[]): object => {
// Your code runs safely in a Wasm sandbox
return { processed: dataset.length, status: "complete" };
});
export const main = task({
name: "main",
compute: "HIGH"
}, () => {
return analyzeData([1, 2, 3, 4, 5]);
});The runtime requires a task named
"main"as the entry point.
When you run capsule run main.ts, your code is compiled into a WebAssembly module and executed in a dedicated sandbox.
Response Format
Every task returns a structured JSON envelope containing both the result and execution metadata:
{
"success": true,
"result": { "processed": 5, "status": "complete" },
"error": null,
"execution": {
"task_name": "data_processor",
"duration_ms": 1523,
"retries": 0,
"fuel_consumed": 45000
}
}Response fields:
success— Boolean indicating whether the task completed successfullyresult— The actual return value from your task (json, string, null on failure etc.)error— Error details if the task failed ({ error_type: string, message: string })execution— Performance metrics:task_name— Name of the executed taskduration_ms— Execution time in millisecondsretries— Number of retry attempts that occurredfuel_consumed— CPU resources used (see Compute Levels)
Documentation
Task Configuration Options
| Parameter | Description | Type | Default | Example |
|-----------|-------------|------|---------|---------|
| name | Task identifier | string | required | "process_data" |
| compute | CPU level: "LOW", "MEDIUM", "HIGH" | string | "MEDIUM" | "HIGH" |
| ram | Memory limit | string | unlimited | "512MB", "2GB" |
| timeout | Maximum execution time | string or number | unlimited | "30s", "5m" |
| maxRetries | Retry attempts on failure | number | 0 | 3 |
| allowedFiles | Folders accessible in the sandbox | string[] | [] | ["./data", "./output"] |
| allowedHosts | Domains accessible in the sandbox | string[] | ["*"] | ["api.openai.com", "*.anthropic.com"] |
| envVariables | Environment variables accessible in the sandbox | string[] | [] | ["API_KEY"] |
Compute Levels
- LOW: Minimal allocation for lightweight tasks
- MEDIUM: Balanced resources for typical workloads
- HIGH: Maximum fuel for compute-intensive operations
- CUSTOM: Specify exact fuel value (e.g.,
compute="1000000")
Project Configuration (Optional)
Create a capsule.toml file in your project root to set default options:
[workflow]
name = "My AI Workflow"
version = "1.0.0"
entrypoint = "src/main.ts" # Run `capsule run` without specifying a file
[tasks]
default_compute = "MEDIUM"
default_ram = "256MB"
default_timeout = "30s"Task-level options always override these defaults.
File Access
Tasks can read and write files within directories specified in allowedFiles. Any attempt to access files outside these directories is not possible.
Common Node.js built-ins are available. Use the standard fs module:
import { task } from "@capsule-run/sdk";
import fs from "fs/promises";
export const restrictedWriter = task({
name: "restricted_writer",
allowedFiles: ["./output"]
}, async () => {
await fs.writeFile("./output/result.txt", "result");
});
export const main = task({ name: "main", allowedFiles: ["./data"] }, async () => {
await restrictedWriter();
return await fs.readFile("./data/input.txt", "utf8");
});Network Access
Tasks can make HTTP requests to domains specified in allowedHosts. By default, all outbound requests are allowed ([\"*\"]). Restrict access by providing a whitelist of domains.
Wildcards are supported:
*.example.commatches all subdomains ofexample.com.
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
allowedHosts: ["api.openai.com", "*.anthropic.com"]
}, async () => {
const response = await fetch("https://api.openai.com/v1/models");
return response.json();
});Environment Variables
Tasks can access environment variables to read configuration, API keys, or other runtime settings. Use the standard process.env:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
envVariables: ["API_KEY"]
}, () => {
const apiKey = process.env.API_KEY;
return { apiKeySet: apiKey !== undefined };
});Compatibility
✅ Supported:
- npm packages and ES modules
- Common Node.js built-ins. If you have any trouble with a built-in, do not hesitate to open an issue.
⚠️ Not supported (inside the sandbox):
- Native Node.js addons (C++ bindings)
These limitations only apply to the task file executed in the sandbox. Your host code using
run()runs in a normal Node.js environment with no restrictions. (see Integrate Into an Existing Project)
