Workflow.until()

The .until() method repeats a step until a specified condition becomes true. This creates a loop that continues executing the specified step until the condition is satisfied.

Usage

workflow
  .step(incrementStep)
  .until(condition, incrementStep)
  .then(finalStep);

Parameters

condition:

Function | ReferenceCondition
A function or reference condition that determines when to stop looping

step:

Step
The step to repeat until the condition is met

Condition Types

Function Condition

You can use a function that returns a boolean:

workflow
  .step(incrementStep)
  .until(async ({ context }) => {
    const result = context.getStepResult<{ value: number }>('increment');
    return (result?.value ?? 0) >= 10; // Stop when value reaches or exceeds 10
  }, incrementStep)
  .then(finalStep);

Reference Condition

You can use a reference-based condition with comparison operators:

workflow
  .step(incrementStep)
  .until(
    {
      ref: { step: incrementStep, path: 'value' },
      query: { $gte: 10 }, // Stop when value is greater than or equal to 10
    },
    incrementStep
  )
  .then(finalStep);

Comparison Operators

When using reference-based conditions, you can use these comparison operators:

OperatorDescriptionExample
$eqEqual to{ $eq: 10 }
$neNot equal to{ $ne: 0 }
$gtGreater than{ $gt: 5 }
$gteGreater than or equal to{ $gte: 10 }
$ltLess than{ $lt: 20 }
$lteLess than or equal to{ $lte: 15 }

Returns

workflow:

Workflow
The workflow instance for chaining

Example

import { Workflow, Step } from '@mastra/core';
import { z } from 'zod';
 
// Create a step that increments a counter
const incrementStep = new Step({
  id: 'increment',
  description: 'Increments the counter by 1',
  outputSchema: z.object({
    value: z.number(),
  }),
  execute: async ({ context }) => {
    // Get current value from previous execution or start at 0
    const currentValue =
      context.getStepResult<{ value: number }>('increment')?.value ||
      context.getStepResult<{ startValue: number }>('trigger')?.startValue ||
      0;
 
    // Increment the value
    const value = currentValue + 1;
    console.log(`Incrementing to ${value}`);
 
    return { value };
  },
});
 
// Create a final step
const finalStep = new Step({
  id: 'final',
  description: 'Final step after loop completes',
  execute: async ({ context }) => {
    const finalValue = context.getStepResult<{ value: number }>('increment')?.value;
    console.log(`Loop completed with final value: ${finalValue}`);
    return { finalValue };
  },
});
 
// Create the workflow
const counterWorkflow = new Workflow({
  name: 'counter-workflow',
  triggerSchema: z.object({
    startValue: z.number(),
    targetValue: z.number(),
  }),
});
 
// Configure the workflow with an until loop
counterWorkflow
  .step(incrementStep)
  .until(async ({ context }) => {
    const targetValue = context.triggerData.targetValue;
    const currentValue = context.getStepResult<{ value: number }>('increment')?.value ?? 0;
    return currentValue >= targetValue;
  }, incrementStep)
  .then(finalStep)
  .commit();
 
// Execute the workflow
const run = counterWorkflow.createRun();
const result = await run.start({ triggerData: { startValue: 0, targetValue: 5 } });
// Will increment from 0 to 5, then stop and execute finalStep