https://github.com/b4fun/smol-workflows
https://github.com/b4fun/smol-workflows
Last synced: 20 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/b4fun/smol-workflows
- Owner: b4fun
- Created: 2026-05-31T06:40:57.000Z (28 days ago)
- Default Branch: main
- Last Pushed: 2026-06-03T06:44:47.000Z (25 days ago)
- Last Synced: 2026-06-03T08:27:05.236Z (25 days ago)
- Language: Rust
- Size: 322 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# smol-workflows
Minimal agentic workflow runtime for orchestrating your agents at scale.
`smol-workflows` runs ES-module workflow scripts inside a self-contained Rust/QuickJS engine. Workflows use injected workflow primitives such as `agent`, `parallel`, `pipeline`, `workflow`, `budget`, `phase`, and `log`; agent calls are checkpointed in SQLite so interrupted runs can be resumed without re-running completed provider steps.
## Getting Started
### Installing
Download the latest release into the current directory:
```sh
# Linux x86_64
curl -fsSL https://github.com/b4fun/smol-workflows/releases/latest/download/smol-wf-linux-x86_64.tar.gz | tar -xz
# macOS Apple Silicon
curl -fsSL https://github.com/b4fun/smol-workflows/releases/latest/download/smol-wf-macos-aarch64.tar.gz | tar -xz
```
### Running your first workflow
Here is a workflow that uses `agent`, `parallel`, and `pipeline` to diagnose Kubernetes pod status. This example is also available at [`examples/pod-diagnostics.mjs`](examples/pod-diagnostics.mjs):
```js
export const meta = {
name: 'pod-diagnostics',
description: 'Diagnose Kubernetes pod status with agent, parallel, and pipeline primitives',
phases: [
{ title: 'Discover', detail: 'Find the target pods', model: 'github-copilot/gpt-5.4-mini' },
{ title: 'Inspect', detail: 'Gather metrics and logs for each pod', model: 'github-copilot/gpt-5.4-mini' },
{ title: 'Summarize', detail: 'Generate diagnostic guidance', model: 'github-copilot/gpt-5.5' },
],
}
const target = typeof args.target === 'string'
? args.target
: 'pods that look unhealthy in the current Kubernetes context'
const POD_LIST_SCHEMA = {
type: 'object',
properties: {
pods: {
type: 'array',
items: {
type: 'object',
properties: {
namespace: { type: 'string' },
name: { type: 'string' },
reason: { type: 'string' },
},
required: ['namespace', 'name', 'reason'],
additionalProperties: false,
},
},
},
required: ['pods'],
additionalProperties: false,
}
phase('Discover')
const { pods } = await agent(
`List Kubernetes pods to inspect for this request: ${target}. Use kubectl if needed.`,
{ schema: POD_LIST_SCHEMA },
)
phase('Inspect')
const inspections = await parallel(pods.map((pod) => async () => {
const [inspection] = await pipeline(
[pod],
(currentPod) => agent(
`Get recent metrics and status for pod ${currentPod.namespace}/${currentPod.name}.
Include restarts, readiness, CPU/memory, events, and current phase.`,
{ phase: 'Inspect' },
),
(metrics, currentPod) => agent(
`Get relevant recent logs for pod ${currentPod.namespace}/${currentPod.name}.
Focus on errors, crashes, probes, and startup failures.
Metrics/status context:
${metrics}`,
{ phase: 'Inspect' },
),
)
return { pod, inspection }
}))
phase('Summarize')
const diagnostics = await agent(
`Summarize Kubernetes pod diagnostics for: ${target}
For each pod, identify likely status, evidence, severity, and next actions.
${JSON.stringify(inspections, null, 2)}`,
)
export default { target, pods, inspections, diagnostics }
```
Save the code above to `pod-diagnostics.mjs`, then run it with your preferred provider and a natural-language target:
```sh
smol-wf run ./pod-diagnostics.mjs \
--agent-provider pi \
--args-target "coredns pods under kube-system" \
| tee pod-diagnostics.json \
| jq -r '.results.diagnostics'
```
Example diagnostic output:
```md
[phase] Discover
[phase] Inspect
[phase] Summarize
## Kubernetes Pod Diagnostics Summary: CoreDNS pods in `kube-system`
### Overall assessment
Both CoreDNS pods appear healthy and operational.
The only recurring log messages are warnings about missing optional CoreDNS customization files:
- `No files matching import glob pattern: custom/*.override`
- `No files matching import glob pattern: custom/*.server`
These are typically benign when no custom CoreDNS override/server files are configured.
### Pod: `kube-system/coredns-...-2ngp4`
- Status: Running
- Readiness: 1/1 Ready
- Restarts: 0
- Events: none
- Severity: Low / Informational
### Pod: `kube-system/coredns-...-vck6j`
- Status: Running
- Readiness: 1/1 Ready
- Restarts: 0
- Events: none
- Severity: Low / Informational
### Final conclusion
Both requested CoreDNS pods are Running, Ready, and not restarting. There is no evidence of operational failure from the provided diagnostics.
```
`smol-wf run` writes progress such as `[phase] ...` and `[log] ...` to stderr, and writes the final JSON report to stdout. The JSON report includes:
- `runID` — workflow run identifier;
- `tokenUsage` — aggregate `inputTokens`, `outputTokens`, and `totalTokens`;
- `results` — the workflow's returned data.
Explore more workflows under the [`examples`](examples) folder.
You can inspect persisted run records later with `smol-wf history`:
```sh
# List recent workflow runs from the default platform app-state workflows.db
smol-wf history
# Show details for a specific run, including attempts, steps, usage, sessions,
# model metadata, and isolation metadata when present
smol-wf history run_01kt...
# Machine-readable detail output
smol-wf history run_01kt... --output json | jq '.steps'
```
### Installing in Code Agents
Ask your code agent to read the [harness installation instructions](https://github.com/b4fun/smol-workflows/blob/main/harness/README.md) and install the smol-workflows harness integration for itself.
These integrations add smol-workflows skills/tools to the host agent. They do not install the `smol-wf` binary itself; the bundled helper resolves an existing binary, builds from a nearby checkout when possible, or downloads a release archive.
## Workflow shape and primitives
Workflows are ES modules. The Rust runner injects workflow primitives before evaluating the script. A primitive is a small built-in capability the workflow can call directly, such as running an agent, fanning work out in parallel, or marking progress. The TypeScript definitions for these globals live in [`ts/sdk/src/index.ts`](ts/sdk/src/index.ts) and [`ts/sdk/src/pipeline.ts`](ts/sdk/src/pipeline.ts).
A workflow script should export:
```ts
export const meta: WorkflowMetadata
export default resultOrWorkflowFunction
```
### `args` -- Workflow input values supplied by the CLI or parent workflow
```ts
args: Record
```
Details
Input arguments passed by `smol-wf run --args-*` or `--args-from-file`. Treat values as untrusted JSON and narrow them before use.
### `agent(prompt, options?)` -- Send a prompt to a provider and get text or structured JSON back
```ts
agent(prompt: string, options?: AgentRunOptions): Promise
agent(prompt: string, { schema, ...options }): Promise | null>
```
Details
Runs one agent/provider call. By default it returns text. With `schema`, it requests structured JSON and the engine validates the result against that JSON Schema. Useful options include:
- `phase` — associate the call with a phase for tracing and token usage grouping;
- `schema` — JSON Schema for structured output;
- `model` — provider-specific model override;
- `provider` — provider override such as `pi`, `opencode`, `codex`, or `claude-code`;
- `isolation: "worktree"` — run the agent in an engine-managed temporary git worktree;
- `agentType` — provider-specific subagent/agent selection.
If an agent call fails inside `parallel` or `pipeline`, that item/task resolves to `null`; otherwise errors reject the workflow.
### `parallel(tasks)` -- Run independent async tasks concurrently and collect their results
```ts
parallel(tasks: Array<() => Awaitable>): Promise>
```
Details
Runs independent tasks concurrently and returns results in input order. If one task throws, only that task becomes `null`; sibling tasks can still complete. Use this when there is a real barrier before the next workflow step.
### `pipeline(items, ...stages)` -- Move each item through ordered stages as soon as it is ready
```ts
pipeline(
items: readonly Item[],
...stages: Array<(previous, item, index) => Awaitable>
): Promise>
```
Details
Runs each item through sequential stages without a global barrier between stages. Each stage receives:
- `previous` — the previous stage result, or the original item for the first stage;
- `item` — the original input item;
- `index` — the item index.
If a stage throws for one item, that item becomes `null` and later stages for that item are skipped.
### `workflow(nameOrRef, args?)` -- Invoke another workflow as a child step
```ts
workflow(nameOrRef: string | { scriptPath: string }, args?: unknown): Promise
```
Details
Runs another workflow as a child workflow. Relative `scriptPath` values resolve from the current workflow file. The current engine supports one level of child workflow nesting.
### `budget` -- Inspect the shared token-budget view for budget-aware prompts and decisions
```ts
budget: {
total: number | null
spent(): number
remaining(): number
}
```
Details
Exposes the soft output-token budget. `total` is set by `--budget-allowance`; `spent()` is accumulated from provider-reported output tokens; `remaining()` is `Infinity` when no allowance is configured.
### `phase(name, options?)` -- Mark the current workflow phase for logs, tracing, and phase defaults
```ts
phase(name: string, options?: unknown): void
```
Details
Marks workflow progress. Phases are printed to stderr, included in run history, and used to apply phase-level metadata defaults such as `model` and `provider`.
### `log(...values)` -- Write progress/debug messages without changing workflow results
```ts
log(...values: unknown[]): void
```
Details
Writes workflow progress/debug information to stderr without changing the workflow result.
## Usage
### CLI
| Command group | Purpose | Reference |
| --- | --- | --- |
| `smol-wf run` | Run a workflow script. | [`docs/usages/run.md`](docs/usages/run.md) |
| `smol-wf tui` | Interactively stream or replay workflow events in a terminal UI. | [`docs/usages/tui.md`](docs/usages/tui.md) |
| `smol-wf history` | Get workflow runs history. | [`docs/usages/history.md`](docs/usages/history.md) |
| `smol-wf llm` | LLM-facing helper commands, such as workflow discovery. | [`docs/usages/llm.md`](docs/usages/llm.md) |
### Terminal UI
Use the TUI to observe workflows live or replay a saved event stream.

```sh
# Run a workflow with live event visibility.
smol-wf tui run ./workflow.mjs --agent-provider pi --args-target "coredns pods under kube-system"
# Save an event stream from a normal run, then replay it.
smol-wf run ./workflow.mjs --events > events.jsonl
smol-wf tui replay events.jsonl
# Validate/summarize an event stream without opening the interactive UI.
smol-wf tui replay events.jsonl --check
```
The live TUI shows workflow scopes, provider events, token usage, an event-rate graph, selected-event details, and raw/pretty views. Live event logs start unsaved; after the workflow stops, press `s` or choose `save & quit` to write `$PWD/.jsonl`. Replay starts paused with zero revealed events and advances according to `elapsedNanos`, with long gaps capped by `--max-delay` (`50ms` by default).
See [`docs/usages/tui.md`](docs/usages/tui.md) for keybindings and replay options.
## Agent providers
The engine includes built-in agent providers for `debug`, `codex`, `claude-code`, `pi`, and `opencode`. Providers can be selected globally with `--agent-provider` or per call with `agent(prompt, { provider })`.
Structured output schemas are validated by the Rust engine, with one retry using a schema-validation prompt when a provider result does not match. Agent calls can also opt into per-call provider retries with `agent(prompt, { retry: { maxAttempts, backoffMs } })`. See [`docs/workflow/retry.md`](docs/workflow/retry.md) and [`docs/harness-capabilities`](docs/harness-capabilities) for provider capability notes, including provider-specific structured-output behavior, input/environment capability expectations, and budget/usage tracking behavior.
## Durable backends
Durable workflow runs use the Rust SQLite backend. The CLI uses this backend by default and stores run/task/step state, completed agent checkpoints, provider results, and budget ledger entries in the platform app-state `workflows.db` unless `--db` is provided. Runs are not retried globally; use per-agent retry settings for transient provider failures and `--resume-run ` to explicitly continue an existing run. See [`docs/usages/config.md`](docs/usages/config.md) for default database locations.
## What is in this repo
- `rust/engine` — Rust workflow engine with a sandboxed QuickJS runtime, metadata extraction, schema validation, budget accounting, built-in agent providers, and a SQLite durable backend.
- `rust/cli` — `smol-wf` command-line interface for running and discovering workflows.
- `ts/sdk` — TypeScript types for workflow authors (`@smol-workflows/sdk`).
- `harness` — integrations and skills for code-agent hosts.
- `examples` — runnable workflow scripts.
- `docs` — design notes, workflow API reference, and harness capability notes.
## TODOs
- [ ] improve context passing between agents; provide primitives for propagated context and workflow/pre-defined memory data
- [ ] environment abstraction
- [ ] environment sandbox / isolation
- [ ] remote environment support
- [ ] human in the loop / steering support
- [ ] cross-run aggregate budget reporting