choice picks one of your labels and gives a probability for each, a noul gives the probability that the answer is yes, and a score places the answer on a scale you define. The supervisor compares those numbers directly, with nothing to parse.
Goal: Write a supervise function that reads the transcript and returns 'continue', 'nudge', or 'halt', and call it from your loop every CHECK_EVERY steps.
Outcome: A looping run stops at the first check that catches it. A blocked run gets a message telling it to change course. A healthy run carries on untouched, even when a check itself fails. Every check logs its probabilities and cost, so you can see why it acted.
1. Define the questions
Write the questions once, as a constant. For thechoice, pick labels that can’t overlap and describe each one in criteria, since the model classifies against that text. Use a noul for a separate yes-or-no check, and a score when the answer is a matter of degree. A score’s criteria is an ordered list, and the answer is a position on it, from 0 to criteria.length - 1.
2. Call the Decisions API
judgeRun sends the transcript and the questions, and checks the answers against a schema. If the response is an error or doesn’t match the schema, judgeRun throws. Step 3 decides what that means for the loop.
bun test packages/billing/invoice.test.ts sixty times and fails the same way each time.
score is the weighted average of the positions, here 0.0929 on a scale of 0 to 2, so the agent is nowhere near done. Each question reads the whole transcript, so usage.input_tokens counts it once per question: asking only is_repeating used 5161 input tokens.
3. Turn the answers into an action
Decide on the probabilities, not just the label:choice says looping whether the model is 41% sure or 95% sure. Stopping the run takes two signals agreeing, run_state and is_repeating, which cuts false stops. One strong signal on its own only earns a nudge.
If a check itself fails, the supervisor lets the run continue: supervise logs the error and returns 'continue', so a Decisions outage or a schema change never kills a healthy run. A tool-call gate does the opposite and blocks on error, because a wrongly approved tool call can do harm. Here, a missed check costs at most a few more steps of a run that may already be wasted.
4. Hook it into the loop
Check everyCHECK_EVERY steps, not every step. renderTranscript is your own function that turns the message history, including tool calls and their results, into the text Classify should judge. On halt, stop the loop and report the last log line. On nudge, add a user message telling the agent what the supervisor saw.
Worked example
The three transcripts below are made up, but they’re the ones the captured output came from, so you can run them yourself. All three start from the same task. The looping transcript runs one failing test command sixty times. The stuck transcript tries twelve different ways to read a fixture file owned by root, then gives up. The progressing transcript reads the test, finds the import-timeDate.now(), injects a clock, runs the tests, and opens a PR. Save steps 1 to 3 and these two blocks as one file and run it with bun run supervise.ts.
supervise log line per transcript, probabilities rounded. We ran each transcript three times; the table has the ranges.
The stuck transcript scored
0.4698 for stuck, with the rest split between progressing (0.24) and looping (0.29). An earlier capture of the same transcript scored 0.617, and blocked transcripts in a second test scored around 0.53, so the score for a blocked run is rarely dominant and it moves between model versions. That’s why NUDGE_STUCK is 0.4: set it higher and a blocked agent keeps going until it gives up on its own. Treat it as a starting value with little margin, not a safe default. A blocked run that scores 0.38 gets no nudge, so log real runs, see where your blocked ones land, and set the threshold below them.
Stopping needs both signals because an agent making progress through a run of similar calls can score looping well above 0.5 while is_repeating stays low. That combination gets a nudge, never a stop. If your agent normally makes long runs of similar calls, say so in the progressing description. The looping transcript’s is_repeating landed between 0.83 and 0.90, so HALT_REPEATING at 0.8 has little margin either; a loop that scores under it still gets a nudge from looping.
Check your work
- Response shape.
answers.run_state.typeis'choice'with a probability for each of the three labels,answers.is_repeating.noulis between 0 and 1, andanswers.completion.scoreis between 0 and 2. - Looping run. A transcript that repeats one failing call scores
loopingandis_repeatingat0.8or higher, and the loop halts. Ours did in 3 of 3 runs. - Healthy run. A transcript with varied actions and new results returns
continue. Ours did in 3 of 3. - Blocked run. A transcript that tries several things, fails each time, and gives up returns
nudge. Ours did in 3 of 3. - Fail open. Break the API key or the URL.
superviselogs the error, returnscontinue, and the loop keeps going. - Stable. Run the same transcript several times and count how often the action changes. In our nine checks it never did; the stuck and progressing transcripts returned identical numbers every time, and the looping transcript’s
is_repeatingmoved between 0.83 and 0.90.
Variations
Change the questions and the same request classifies other text: the outcome of a sales call (won, lost, escalation, or churn risk), the type and risk of a contract, which team should handle a support ticket, or a risk rating for a full privacy policy, using ascore question. Classify also reads images and documents, so it can tell an invoice from a receipt on a scanned page or check a screenshot for an error. Send the file as a base64 string in state.document, with its type in state.document_mime_type.
Next steps
- Compact Long Agent Sessions with ScaleDown Summarize to keep the transcript the supervisor reads inside the context window
- Gate Agent Tool Calls with Jev for a Decisions check on single tool calls that blocks on error
- Build a Long-Horizon Agent for the cost and step limits this supervisor sits beside
- Jev documentation hub for more on
choice,noul, andscore - Decisions API reference