@capsuleer/core
v1.1.1
Published
Core SDK for capsuleer — defineCapsule, buntime, and tracing
Maintainers
Readme
@capsuleer/core
The core runtime library for Capsuleer. Provides Capsule() for spawning sandboxed Bun subprocesses and defineModule() for authoring capability modules. Everything else in the Capsuleer ecosystem is built on top of these two primitives.
Capsule(input)
Spawn a capsule — a long-running Bun subprocess with a defined capability surface. Accepts commands over stdin, executes TypeScript or shell, streams events back over stdout.
import { Capsule } from "@capsuleer/core"
const capsule = await Capsule({
name: "my-agent",
env: {
GITHUB_TOKEN: process.env.GITHUB_TOKEN!,
},
policy: {
fs: {
read: true,
write: { deny: ["/etc/**", "~/.ssh/**"] },
delete: "escalate",
},
shell: false,
resources: {
maxCommandTimeMs: 15_000,
maxFileReadBytes: 10_000_000,
},
},
})
// Listen to all events from the capsule
capsule.onEvent(e => console.log(e))
// Send a TypeScript command
await capsule.command({
type: "ts",
code: `
const files = await fs.find("**/*.ts", { cwd: "/project" })
return { count: files.length }
`
})
// Shut down when done
await capsule.shutdown()Input
| Field | Type | Description |
|-------|------|-------------|
| name | string | Session name — used in trace events |
| env | Record<string, string> | Environment variables forwarded to the subprocess |
| policy | CapsulePolicy | Access control rules — filesystem, shell, network, resource limits |
| entrypoint | string? | Absolute path to the environment index.ts (required inside bundlers) |
| boot | () => Promise<void> | Optional setup hook |
| shutdown | () => Promise<void> | Optional teardown hook |
Events
The capsule emits JSONL events over stdout:
| Event | Description |
|-------|-------------|
| module:manifest | Emitted on boot — type declarations for each loaded module |
| start | Command execution begun |
| stdin | Command echoed back |
| stdout | A console.log() from agent code |
| exit | Command completed — ok: true, result: <value> |
| error | Command failed — ok: false, error: <message> |
| policy:denied | Operation blocked by policy rule |
| policy:escalation | Operation paused — awaiting policy:response |
| resource:exceeded | A resource limit was hit |
Commands
// Execute TypeScript
capsule.command({ type: "ts", code: "return fs.read('/file.ts')" })
// Execute a shell command (subject to shell policy)
capsule.command({ type: "shell", code: "git status" })
// Respond to a policy escalation
capsule.command({ type: "policy:response", id: "esc-abc123", allow: true })defineModule(input)
Define a Capsuleer module. Returns a module descriptor that the environment's module loader registers as a global in the capsule's execution scope.
import { defineModule } from "@capsuleer/core"
const stripe = {
/**
* Retrieve a Stripe customer by ID.
* @param customerId - Stripe customer ID (cus_xxx)
*/
async getCustomer(customerId: string) {
const res = await fetch(`https://api.stripe.com/v1/customers/${customerId}`, {
headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}` },
})
return res.json()
},
}
export default defineModule({
name: "stripe",
version: "1.0.0",
description: "Stripe billing API",
api: stripe,
})After installation, agent code calls stripe.getCustomer("cus_abc123") directly — no imports, no setup.
Input
| Field | Type | Description |
|-------|------|-------------|
| name | string | Global namespace — must be unique across loaded modules |
| version | string? | Semver |
| description | string? | Shown in registry, injected into LLM context |
| api | object | All methods become available as name.method() |
| globals | object? | Individual functions promoted to top-level globals |
JSDoc comments on api methods are extracted into the type manifest and surfaced to the LLM — write them.
Policy
The CapsulePolicy type controls what the subprocess is allowed to do:
type CapsulePolicy = {
fs?: {
read?: PolicyRule
write?: PolicyRule
delete?: PolicyRule
find?: PolicyRule
grep?: PolicyRule
list?: PolicyRule
mkdir?: PolicyRule
}
shell?: PolicyRule
network?: { fetch?: PolicyRule }
resources?: {
maxMemoryMb?: number
maxCommandTimeMs?: number
maxFileReadBytes?: number
maxFileWriteBytes?: number
maxNetworkRequests?: number
}
}
// PolicyRule:
type PolicyRule =
| true // always allow
| false // always deny
| "escalate" // pause and ask
| { allow?: string[]; deny?: string[] } // glob-basedSee the policy docs for the full reference.
