batonjs
v0.0.52
Published
A lightweight workflow engine that orchestrates AI agents via pluggable SDK backends
Maintainers
Readme
BatonJS
A TypeScript-based workflow script runner for coding agents.
Why BatonJS?
A TypeScript-based workflow script runner for coding agents. Write plain async/await scripts with a small set of injected globals — the engine handles concurrency, retries, budget tracking, and SDK orchestration.
SDK-agnostic — Pluggable adapters for Anthropic Claude, Tencent CodeBuddy, OpenAI Codex, and Reasonix. Swap providers without rewriting workflow logic.
Zero boilerplate —
agent(),parallel(),pipeline(),phase(),log(), andbudgetare all you need. No class inheritance, decorators, or DSL.Automatic concurrency & throttling —
parallel([...])handles hundreds of items with automatic semaphore management.Built-in budget tracking — Every
agent()call deducts from a shared pool; loops exit automatically when budget is exhausted.Retry, structured output, sub-workflows — Exponential backoff for transient failures, JSON Schema validation for typed results, and nested workflow composition — all out of the box.
Quick Start
Prerequisites: Node.js ≥ 18
Install:
npm install batonjsOr run directly without installing:
npx -y batonjs ./workflow.jsWrite a workflow script:
// workflow.js
export const meta = {
name: 'my-first-workflow',
phases: [{ title: 'Analyze' }, { title: 'Summarize' }],
}
// Phase 1: Run two agents in parallel
phase('Analyze')
const [security, perf] = await parallel([
() => agent('Check this code for security issues', { label: 'security' }),
() => agent('Check this code for performance bottlenecks', { label: 'perf' }),
])
// Phase 2: Summarize findings
phase('Summarize')
const summary = await agent(
`Summarize these findings:\n${JSON.stringify({ security, perf })}`,
{ label: 'summarize' },
)
log('Done!')
return { summary }Run it:
batonjs ./workflow.jsSwitch to a different SDK backend:
batonjs --sdk codex ./workflow.js
batonjs --sdk codebuddy ./workflow.js
batonjs --sdk reasonix ./workflow.jsCLI Reference
batonjs [options] <script>| Flag | Description | Default |
|------|-------------|---------|
| --args <json> | Pass arguments into the script as the args global | — |
| --budget <usd> | Max spend in USD | unlimited |
| --concurrency <n> | Max concurrent agents | 2 |
| --cwd <dir> | Working directory for agents | . |
| --sdk <name> | claude, codebuddy, codex, or reasonix | claude |
| --timeout <minutes> | Per-agent timeout in minutes | 5 |
| -h, --help | Show help | — |
Script API
Workflow scripts receive these injected globals:
| Global | Description |
|--------|-------------|
| agent(prompt, opts?) | Run an AI agent. Returns the result or null on failure |
| parallel(thunks) | Run an array of async thunks concurrently; waits for all |
| pipeline(items, ...stages) | Stream each item through stages independently |
| phase(title) | Mark the current phase (for progress display) |
| log(message) | Emit a log message |
| budget | { total, spent(), remaining() } — real-time budget info |
| args | Custom arguments passed via --args |
| workflow(ref, args?) | Run a nested sub-workflow (one level max) |
agent(prompt, opts?)
// Free-text response
const result = await agent('Explain this error')
// Structured output with JSON Schema
const issues = await agent('Find bugs in this code', {
schema: {
type: 'object',
properties: { bugs: { type: 'array', items: { type: 'string' } } },
required: ['bugs'],
},
})
// issues.bugs — validated array of stringsOptions:
| Option | Description |
|--------|-------------|
| label | Display name for progress output |
| phase | Assign to a phase group |
| schema | JSON Schema for structured output |
| model | Override model for this agent |
Examples
The examples/ directory contains 31 ready-to-run workflows, organized by level:
| # | Pattern | Description |
|---|---------|-------------|
| 01 | agent() | Hello World — one agent, one result |
| 06 | schema | Structured output with JSON Schema validation |
| 07–08 | parallel / pipeline | Concurrency primitives |
| 09 | pipeline vs parallel | When to use which |
| 11 | Multi-stage pipeline | Chain stages per item |
| 12 | Loop-until-dry | Keep discovering until exhausted |
| 13 | Loop-until-budget | Scale depth to budget |
| 15 | Adversarial verify | N skeptics try to refute a claim |
| 16 | Judge panel | Diverse approaches scored and synthesized |
| 21 | Code review | Multi-dimension review with verification |
| 31 | README generator | Five-dimension README generation |
Run any example:
batonjs ./examples/01-hello-world.js
batonjs --sdk codex ./examples/06-structured-output.js
batonjs --budget 2.0 ./examples/21-code-review.jsProgrammatic API
import { Engine } from 'batonjs'
const engine = new Engine({
scriptPath: './workflow.js',
sdk: 'claude', // or 'codebuddy', 'codex', or 'reasonix'
maxBudgetUsd: 2.0,
maxConcurrency: 5,
})
engine.on((event) => {
if (event.kind === 'log') console.log(event.message)
})
const result = await engine.run()
if (result.ok) {
console.log('Result:', result.value.result)
console.log('Cost: $' + result.value.totalCostUsd.toFixed(4))
}API Reference
BatonJS exposes a compact public surface — engine, result helpers, SDK adapters, and a rich set of types. Everything below is re-exported from the entry point.
Engine
The workflow runtime that loads a script, injects globals, and orchestrates execution.
| Export | Description |
|--------|-------------|
| Engine | Load a script and run it inside a sandboxed workflow environment. |
| EngineOptions | Configuration for new Engine(): scriptPath, args, maxBudgetUsd, maxConcurrency, cwd, permissionMode, sdk, agentTimeoutMs, signal. |
| EngineRunResult | Return type of engine.run() — always a Result<EngineResult, Error>. |
| EngineResult | Successful run payload: { success, result, totalCostUsd, durationMs, meta }. |
import { Engine } from "batonjs";
const engine = new Engine({ scriptPath: "./my-workflow.ts", maxBudgetUsd: 5.0 });
const outcome = await engine.run(); // Result<EngineResult, Error>→ Quick Start · Events
Result Helpers
Predictable, allocation-friendly error handling without try/catch.
| Export | Description |
|--------|-------------|
| ok | Create a successful Result<T, E> value. |
| err | Create a failed Result<T, E> error. |
| Result | Discriminated union: { ok: true; value: T } \| { ok: false; error: E }. |
import { ok, err } from "batonjs";
const found = ok(42); // Result<number, never>
const failed = err("missing"); // Result<never, string>SDK Adapters
Pluggable backends that translate BatonJS calls into provider-specific SDK operations.
| Export | Description |
|--------|-------------|
| createSdkProvider | Factory — returns an SdkProvider for a given backend name. |
| SdkName | Union: 'claude' \| 'codebuddy' \| 'codex' \| 'reasonix'. |
| SdkProvider | Contract: { query(options): SdkQueryHandle }. |
| SdkQueryOptions | Options for a query call: permissionMode, abortController, outputFormat, model, cwd, maxBudgetUsd, effort. |
| SdkQueryHandle | Async-iterable handle with interrupt() and return() cleanup. |
| SdkResultMessage | Normalised result message, discriminated on subtype: 'success' vs error variants. |
| EffortLevel | Unified reasoning effort: 'medium' \| 'high' \| 'xhigh'. |
import { createSdkProvider } from "batonjs";
const sdk = createSdkProvider("claude");
const handle = sdk.query({ model: "claude-sonnet-4-20250514", permissionMode: "auto" });Events
Synchronous, type-safe event bus for observing engine lifecycle.
| Export | Description |
|--------|-------------|
| EngineEventBus | Create a bus, register handlers, and emit typed events. |
| EngineEvent | Discriminated union of all lifecycle events (workflow_start, workflow_end, phase, log, agent_start, agent_end, agent_error, budget_update, …). |
| EngineEventHandler | Handler signature: (event: EngineEvent) => void. |
import { EngineEventBus } from "batonjs";
const bus = new EngineEventBus();
bus.on("agent_start", (e) => console.log(`→ ${e.label}`));Script Globals
The runtime contract injected into every workflow script.
| Export | Description |
|--------|-------------|
| ScriptGlobals | Full set: agent, parallel, pipeline, phase, log, budget, args, workflow. |
| ScriptMeta | Shape of export const meta = { name, description, phases }. |
| AgentOpts | Options for agent(): label, phase, schema, model, maxRetries, effort. |
| BudgetHandle | Script-facing budget: { total, spent(), remaining() }. |
| WorkflowRef | Reference to another workflow: string or { scriptPath }. |
// Inside a workflow script — globals are injected by the Engine
const result = await agent("Summarise the article", { schema: SummarySchema });
const all = await parallel([() => agent("A"), () => agent("B")]);→ Script Globals types · Examples
Concurrency & Budget
Utilities for controlling parallelism and spending.
| Export | Description |
|--------|-------------|
| Semaphore | Counting semaphore — acquire() / release() to cap concurrency. |
| BudgetTracker | Track cumulative USD spend — record(), tryAcquire(), adjust(). |
import { Semaphore, BudgetTracker } from "batonjs";
const sem = new Semaphore(4);
const budget = new BudgetTracker(10.0); // $10 USD capSDK Backends
| Backend | --sdk value | Package |
|---------|--------------|---------|
| Anthropic Claude | claude (default) | @anthropic-ai/claude-agent-sdk |
| Tencent CodeBuddy | codebuddy | @tencent-ai/agent-sdk |
| OpenAI Codex | codex | @openai/codex-sdk |
| Reasonix | reasonix | CLI binary (reasonix run) |
Documentation & Resources
Guides
- Getting Started — Installation, quick start, and basic usage of the BatonJS workflow engine
- Workflow Tutorial — Step-by-step tutorial for AI assistants to learn BatonJS concepts, API patterns, and best practices (6 chapters)
- Examples — 31 workflow examples from hello-world to full quality gate, covering pipeline, parallel, adversarial verify, judge panel, and more
API Reference
- API Entry Point — Public API: Engine class, Result type, SDK provider, event bus, concurrency primitives, budget tracker
- CLI Reference — Command-line interface options: --args, --budget, --concurrency, --cwd, --sdk, --timeout
- Script Globals — Workflow script globals: agent(), parallel(), pipeline(), phase(), log(), budget, args, workflow()
- Engine Events — Discriminated union event types emitted during workflow execution for observability
- SDK Adapters — Pluggable SDK backends: Anthropic Claude Agent SDK, Tencent Codebuddy Agent SDK, OpenAI Codex SDK, Reasonix
Development
- TypeScript Philosophy — Project coding conventions: zero any, discriminated unions, Result pattern, never exhaustiveness checks
- Contributing — Development commands (dev, build, test, lint, check) and project setup with husky + lint-staged
Project
- Changelog — Release history and version changes
- GitHub Repository — Source code, issues, and pull requests
Contributing
Contributions are welcome! To get started:
- Bug reports — Open an issue with a minimal reproduction.
- Feature requests — Open an issue with a clear use case.
- Pull requests — Fork the repo, create a branch, and submit a PR. Please ensure
npm run checkpasses before submitting.
License
This project is licensed under the MIT license.
