Skip to main content
import { Loop } from "smithers-orchestrator";

type LoopProps = {
  id?: string; // auto-generated from tree position
  until: boolean;
  maxIterations?: number; // default 5
  onMaxReached?: "fail" | "return-last"; // default "return-last"
  continueAsNewEvery?: number; // checkpoint every N iters to bound history
  skipIf?: boolean;
  children?: ReactNode;
};
export default smithers((ctx) => {
  const latestReview = ctx.latest("review", "review");

  return (
    <Workflow name="refine-loop">
      <Loop until={latestReview?.approved === true} maxIterations={5}>
        <Sequence>
          <Task id="write" output={outputs.draft} agent={writer}>
            {latestReview
              ? `Improve the draft. Feedback: ${latestReview.feedback}`
              : `Write a draft about: ${ctx.input.topic}`}
          </Task>
          <Task id="review" output={outputs.review} agent={reviewer}>
            {`Review the latest draft.`}
          </Task>
        </Sequence>
      </Loop>
    </Workflow>
  );
});

Notes

  • ctx.latest(table, nodeId) reads the highest-iteration output; until must use ctx.outputMaybe() since output is absent on iter 0.
  • Direct nesting of <Loop> in <Loop> throws; wrap the inner loop in <Sequence>.
  • Custom Drizzle tables for loop tasks require iteration in the primary key.