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

type TokenBudgetConfig = {
  max: number;
  perTask?: number;
  onExceeded?: "fail" | "warn" | "skip-remaining"; // default "fail"
};

type LatencySloConfig = {
  maxMs: number;
  perTask?: number;
  onExceeded?: "fail" | "warn"; // default "fail"
};

type TrackingConfig = { tokens?: boolean; latency?: boolean };

type AspectsProps = {
  tokenBudget?: TokenBudgetConfig;
  latencySlo?: LatencySloConfig;
  tracking?: TrackingConfig; // default all true
  children?: ReactNode;
};
<Workflow name="budgeted-workflow">
  <Aspects
    tokenBudget={{ max: 100_000, perTask: 25_000, onExceeded: "warn" }}
    latencySlo={{ maxMs: 30_000, onExceeded: "fail" }}
  >
    <Task id="analyze" output={outputs.analysis} agent={codeAgent}>
      Analyze the repository.
    </Task>
    <Task id="review" output={outputs.review} agent={reviewAgent}>
      Review the analysis.
    </Task>
  </Aspects>
</Workflow>

Notes

  • Nested <Aspects> inherit outer budget configs wholesale. If an inner <Aspects> sets tokenBudget or latencySlo, that field’s entire parent config object is replaced (no per-field merge). The tracking config is the exception: tokens and latency each fall back to the parent value independently.
  • Budgets enforce at task-dispatch time. Before each descendant task runs, the engine compares the run’s accumulated token total (tokenBudget.max) and wall-clock since the run started (latencySlo.maxMs) against the budget and applies onExceeded: fail raises ASPECT_BUDGET_EXCEEDED and fails the run, warn logs and continues, skip-remaining skips the task. The per-task limits (perTask) are not enforced yet.
  • Budgets enforce regardless of tracking; tracking controls metric emission only.
  • Accumulated token usage is per run and survives resume (re-seeded from persisted token-usage events).