Put movie night in a workflow
Six friends want a funny movie, enough pizza, and a total under $60. One agent could try to manage all of that in its own loop. AgentRun lets you make the plan explicit: which jobs run, what each job receives, how their results connect, and what must pass before the answer is ready.
The DSL combines three kinds of work:
| Piece | Movie-night job | Who does it |
|---|---|---|
| Code | Add the prices and enforce the budget. | An exact rule in the workflow. |
| Jev system one decision | Decide whether a movie description fits a lighthearted mood. | A focused typed question through runJudge. |
| Agent step | Inspect choices, find a better movie, or write the plan. | An ordinary agent session supplied through runNode. |
An agent step can contain its usual read → tool → result → next action loop. The workflow controls how that step connects to the other pieces; the host controls the session and its tools.
Run the whole example
npm ci
npm run build
node examples/movie-primer.mjs
node examples/movie-night.mjs repair
The catalog is fictional and the default answers are scripted. The real interpreter executes the code, judgments, branches, parallel work, and checks. No API key is needed. No live agent loop is demonstrated, and nothing is bought or ordered.
The repair case produces Space Pancakes ($8) + two large cheese pizzas ($36) = $44, enough for six friends and within their $60 budget.
Connect small jobs into one plan
The workflow is data describing the following composition:
- Route the request. A movie-only question starts one movie lookup. A full plan starts both a movie picker and a food picker.
- Run independent jobs in parallel. Each picker receives its own input mapping and writes its own result. The workflow joins those results before judging the movie.
- Judge, then branch. The classifier returns
good_fit,too_scary, orunknown. Code creates a problem-solving job only for a scary choice. Missing information ends in a handoff to a friend. - Write and review. The writer submits a structured plan. A rejected draft can be repaired in the same session, within two submission reviews.
- Check the hard facts. Final code checks catalog choices, prices, arithmetic, food for six, and the $60 limit before marking the plan delivered.
These connections live in the DSL. They are inspectable even when a particular agent session takes several internal turns.
Make each boundary clear
An agent node names its job, an explicit state mapping, an output schema through out, and the state key where its result lands through as. In this example, the movie picker writes movieChoice; the food picker writes foodChoice. Parallel branches do not overwrite each other's results.
The classifier then receives just the movie description and requested mood:
{
"node": "judge",
"label": "movie-classifier",
"state": {
"description": "{evidence.movie.description}",
"mood": "{mood}"
},
"out": "Classification",
"as": "classification"
}
Classification contains the answer choices and the complete rubric. A funny description supports good_fit; ghosts and jump scares support too_scary; no description means unknown. Jev can answer that question through the adapter. Code then reads the typed answer to decide what runs next.
A different question needs no inference:
(state) => ({ withinBudget: state.total <= state.budget });
The primer runs that function as a code node. $72 fails the $60 limit; $44 passes. Understanding a movie's mood and comparing two numbers have different owners.
Test one piece, then test the connections
The movie primer reuses the classifier node and its whole Classification schema in a small workflow. Only the input mapping changes: saved descriptions arrive directly instead of through the two picker jobs. The rubric, answer types, and classifier behavior remain the ones used by the full workflow.
Each saved case has an expected answer outside the candidate. The primer then deliberately returns good_fit for the scary movie. That answer has a valid type, so the interpreter completes; the independent evaluation catches the wrong judgment. No upstream agent needs to run for this check.
Then run the complete workflow tests. A good classifier alone does not prove that the router starts the right jobs, both parallel results reach the next step, a rejected draft gets repaired, or missing information stops the writer.
node --test examples/movie-night.test.mjs
The tests witness both parallel jobs overlapping, preserve their outputs, exercise every route, and force review failures and handoffs. They also let a judge falsely approve invalid money or serving claims and verify that final code still rejects the plan.
The fixture's simple description rules and exact-text checks are teaching substitutes. They are not a production classifier, a semantic verifier for arbitrary prose, or measured Jev quality.
Try the four paths
| Command | What changes |
|---|---|
node examples/movie-night.mjs ready |
The funny movie and two pizzas make a $44 plan on the first submission. |
node examples/movie-night.mjs repair |
A problem solver swaps the scary movie. The writer's $72 party-pack draft fails; the $44 correction passes. |
node examples/movie-night.mjs quick |
One movie question skips both planners and returns an answer without food. |
node examples/movie-night.mjs missing |
The description and pizza price are absent. Ask a friend; no writer starts. |
Add --json to inspect the input, result, adapter calls, and actual interpreter events. The handoff retains the evidence and says:
Ask a friend for the movie description and the pizza price before planning the night.
The workflow also makes the amount of work visible:
| Path | Agent sessions | Judge requests, including reviews |
|---|---|---|
| Ready | 3 | 3 |
| Repair | 4 | 4 |
| Quick | 2 | 3 |
| Missing information | 2 | 2 |
These are observed host-call counts, not price or speed estimates. A session can contain several model turns. Actual spending depends on the host's models, tools, token use, and pricing.
Keep repair and execution limits separate
The writer's verify clause permits at most two submission reviews. In the repair fixture, its first draft uses real catalog prices: $8 for the movie plus a $64 party pack equals $72. The review rejects the budget violation, and the writer chooses the $36 pizza option instead.
The adapter connects review(candidate) to the session's submission tool so the same agent can read the rejection and correct its draft. Returning a rejected draft without repairing it fails the run. Rejecting both submissions exhausts the review bound. This is bounded draft repair, not an automatic retry of the whole job or an external purchase.
The final code guard independently enforces exact catalog fields, prices, servings, totals, and the requested budget and group size. A judge's approval cannot bypass those checks. Movie suitability remains a semantic judgment.
Two reviews does not limit every model turn inside a session. The host owns session budgets, permissions, credentials, cancellation, and isolation. The DSL is not a security sandbox.
Use your agents in a workflow
One integration direction is workflow → agent. Supply runNode with an adapter that runs a session in your existing host:
import { runMovieNight } from "./examples/movie-night.mjs";
const run = await runMovieNight("repair", {
runNode: yourAgentAdapter,
runJudge: yourJudgeAdapter,
onEvent: (event) => console.log(event.type, event.label),
});
yourAgentAdapter is your application code. It receives system instructions, the projected state as JSON in user, the output schema, an optional review hook, and cancellation signal. It returns the structured submission or throws. Connect those fields to your host's existing session and submission interface; AgentRun does not automatically convert a free-running loop into a workflow.
The judgment adapter can be Jev through createJevRunner(). Connect Jev shows the server-side setup without choosing a model. Host mode requires both adapters and labels the result mode: "host".
The other direction is agent → workflow: expose a small workflow as a tool, return its structured result to the same agent, and let that agent continue its own loop. The Jev decision-tool example demonstrates that boundary. You can adopt either direction without replacing the host.
Source: movie workflow, isolated classifier and primer, tests. See the DSL guide for all primitive contracts.