Write Your First JavaScript Workflow

Use the you-agent-factory JavaScript orchestrator: the runtime bindings a workflow script gets, how it dispatches agents, and how it bounds, checkpoints, and returns its work.

What It Is

The JavaScript orchestrator runs a single workflow script instead of moving work between workstations. The script is an async function body: it reads its invocation arguments, calls agents, and returns the final answer. The factory definition around it carries the source, the argument schema, and the execution policy.

When To Use

Choose it when the workflow is a procedure rather than a topology — when the next step depends on a computed value, a loop count, or a branch that is awkward to express as states and transitions. Choose the graph form instead when you want durable per-item state without writing it yourself.

The Runtime Bindings

Fifteen symbols are published across eight top-level names. There is no module system and no import: these are simply in scope when the script starts.
SymbolWhat it does
args, metaInvocation arguments and metadata, bound as mutable snapshots at script start. Writing to them changes only the in-script view, never the original request.
agent.run(spec)Dispatch one child agent and await its result.
parallel(items)Run an array of run specs or item functions concurrently and await all of their results.
pipeline(items, worker, next)Map each item through a worker function and an optional second stage, running the stages in order per item.
phase(name), log(message, fields)Record a named phase transition, or emit a progress message, for anyone watching the run.
workflow.budget / checkpoint / resumeState / artifact / log / finalRead the execution budget, persist and restore resume state, publish an artifact, and terminate with a final value.

Dispatching One Agent

A single child dispatch takes a spec object and resolves to a child result. Only the prompt is required; a label makes the dispatch identifiable in progress output, and the provider, model, preset, and reasoning-effort fields override what the operator configured for this one call.
const result = await agent.run({
  label: "summarizer",
  prompt: "Summarise the request below.\n\n" + args.request,
  modelProvider: args.modelProvider || "",
  model: args.model || "",
});
if (result.status !== "COMPLETED") {
  throw "summarizer failed";
}
return result.output.text.trim();
Always check the status before reading the output. A dispatch that did not complete still returns a result object, so skipping the check turns a failure into an empty string that quietly poisons everything downstream.

Fanning Out

Concurrent work takes an array of run specs or item functions and resolves to an array of child results in the same order. Sequential per-item work takes a list plus a worker function, and optionally a second stage, running the stages in order for each item.
const results = await parallel([
  { label: "review-accuracy", prompt: "Judge this draft for factual accuracy.\n\n" + draft },
  { label: "review-completeness", prompt: "Judge this draft for completeness.\n\n" + draft },
]);
Use concurrent fan-out when the items are independent and you need all the results together. Use the per-item pipeline when each item passes through more than one stage and you would rather not make every item wait for the slowest one at each stage boundary.

Staying Inside The Budget

A workflow that dispatches more agents than its policy allows fails partway through, after spending real work. Read the budget first and fail immediately instead. The snapshot always carries the maximum agent count and the concurrency limit, and may carry sandbox mode, duration and output caps, and a token ceiling.
const budget = workflow.budget();
if (args.count + 2 > budget.maxAgents) {
  throw "needs " + (args.count + 2) + " agent calls but maxAgents is " + budget.maxAgents;
}
The budget reflects the default policy declared in the factory definition, with any runtime override applied. It is authoritative: exceeding it is not a soft warning.

Checkpoints And Resume

Script variables do not survive a restart, so anything expensive to recompute should be written to a labeled checkpoint with JSON-compatible state. A resumed session reads that state back, or gets nothing when the session is starting fresh.
workflow.checkpoint({ label: "plan-complete", state: { tasks } });

const resumed = workflow.resumeState();
const tasks = resumed?.state?.tasks ?? (await planTasks());
Two other workflow calls matter for output. Publishing an artifact records a labeled, kinded, JSON-compatible payload alongside the run. Terminating with a final value wins over whatever the script returns, so use one or the other rather than both.

Common Pitfalls

Three habits cause most trouble. Not checking a child result's status, so a failed dispatch silently becomes empty text downstream. Writing retries, logging, and error handling that duplicate what the runtime already does. And letting the script grow past what a reader can follow — at that point the workflow probably wanted to be a graph.

Tags