Skip to main content
ClaudeWave
Skill696 repo starsupdated today

workflows

**Workflows** Author and execute short JavaScript/TypeScript scripts that orchestrate many concurrent leaf agents in a sandbox. Use this skill when a problem breaks into multiple similar subtasks that can run in parallel, such as processing batches of items, analyzing multiple documents, or handling lists of requests. Launch a workflow with `run_workflow`, providing either an inline script or a saved workflow name, and receive a `runId` immediately while the asynchronous execution completes in the background.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/vellum-ai/vellum-assistant /tmp/workflows && cp -r /tmp/workflows/assistant/src/config/bundled-skills/workflows ~/.claude/skills/workflows
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

A workflow is a short JS/TS script you author that runs in a sandbox and fans work
out across many short-lived **leaf agents**, orchestrated deterministically. Launch
one with `run_workflow` (inline `script` OR saved `name`, exactly one). It returns a
`runId` immediately; the run is asynchronous and you are notified in this
conversation when it completes — **do NOT poll**.

Reach for one when a task decomposes into many similar small sub-tasks that can run
concurrently. For a single task or a quick lookup, do it inline.

## The script model

These are the load-bearing invariants. Get them wrong and the run misbehaves silently.

### Scripts are SYNCHRONOUS — never `await`

Host functions block and return their result directly. Write straight-line code.

```js
const r = agent("Summarize this thread."); // r is the result, right here
```

Do **not** write `await agent(...)`, and do **not** make the script `async`. An
`async` script deadlocks on its second host call — the sandbox can suspend the main
evaluation stack but not a promise continuation.

### Every script begins with a literal `meta`

The first statement must be a pure-literal export — no computed values, template
strings, or concatenation:

```js
export const meta = {
  name: "triage-inbox",
  description: "Triage and label inbox messages",
};
```

`meta` is extracted **statically**, without executing the script, so it must be a
plain object literal with string `name` and `description`. The `name` is how a saved
workflow is referenced by `workflow(name)` and the scheduler.

### You must `return` the result

The script body runs as a function. Its result is whatever it `return`s at the top
level — a bare trailing expression (e.g. `result;`) is **discarded** and the run
finishes with no result. Always `return` the value you want surfaced.

```js
const result = agent(`Write the final summary: ${JSON.stringify(parts)}`);
return result;
```

### Determinism (this is what makes runs resumable)

Every leaf call is journaled by sequence number and input hash, so a resumed run can
replay the unchanged prefix instead of re-spawning agents. That only holds if the
script is deterministic, so `Date.now()`, `Math.random()`, and argless `new Date()`
**throw**. Pass any timestamps or random seeds in through `args`.

## Host API

All functions are synchronous from the script's perspective.

| Function                     | Returns                                        | Notes                                                                                                       |
| ---------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `agent(prompt, opts?)`       | the leaf's result                              | Runs ONE leaf. **Throws** on leaf failure (fails the whole run).                                            |
| `leaf(prompt, opts?)`        | a leaf descriptor                              | Runs nothing on its own; used inside `parallel`/`map`/`pipeline`.                                           |
| `parallel(specs)`            | `results[]`                                    | Runs an array of `leaf(...)` descriptors concurrently, results in input order. A failed leaf becomes `null` (never throws). |
| `map(items, build)`          | `results[]`                                    | `build(item, i)` returns a `leaf(...)` descriptor per item; runs them like `parallel`.                     |
| `pipeline(items, ...stages)` | `results[]`                                    | Each `stage(prev, i)` returns a `leaf(...)` descriptor (run an agent) OR a plain value (pass through unchanged — filter/transform locally, no agent spent). Per-stage barrier: stage N+1 starts only after all of stage N finishes. |
| `phase(title)`               | —                                              | Marks a named phase for progress reporting.                                                                |
| `log(msg)`                   | —                                              | Emits a progress log line.                                                                                  |
| `usage()`                    | `{ agentsSpawned, inputTokens, outputTokens }` | Live snapshot so a script can self-moderate.                                                               |
| `workflow(name, args?)`      | the child's result                             | Runs a SAVED workflow inline, depth 1 only (a child may not call `workflow()`).                            |
| `args`                       | the run input                                  | The `args` object passed to `run_workflow`.                                                                |

Use `agent` for a single sequential leaf (throws on failure). Use `parallel`/`map`/
`pipeline` for fan-out (a failed leaf is `null`, so a batch survives a few bad items).

## Leaf options (`opts` for `agent` / `leaf`)

| Option    | Type                       | Effect                                                                                  |
| --------- | -------------------------- | --------------------------------------------------------------------------------------- |
| `schema`  | JSON Schema object literal | Forces structured output via a tool. A schema leaf runs with **no tools** (pure judge/extractor). Use a plain JSON Schema literal, not Zod. |
| `label`   | string                     | Short display/diagnostic label for the leaf.                                            |
| `profile` | string                     | Overrides the model profile. Must exist in `llm.profiles` or the leaf throws. See [Listing profiles](#listing-available-profiles). |
| `persona` | boolean                    | `true` makes the leaf speak AS the assistant (identity + memory) — use for output meant to be in the assistant's voice. Default is anonymous — use for impartial judgin