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

type ApprovalGateProps = {
  id: string;
  output: OutputTarget; // z.ZodObject<z.ZodRawShape> | { $inferSelect: Record<string, unknown> } | string
  request: { title: string; summary?: string; metadata?: Record<string, unknown> };
  when: boolean; // true => require human; false => auto-approve immediately
  onDeny?: "fail" | "continue" | "skip"; // if omitted, denial fails the task
  skipIf?: boolean;
  timeoutMs?: number;
  heartbeatTimeoutMs?: number;
  heartbeatTimeout?: number;
  retries?: number;
  retryPolicy?: { backoff?: "fixed" | "linear" | "exponential"; initialDelayMs?: number };
  continueOnFail?: boolean;
};
const risk = ctx.output(outputs.riskScore, { nodeId: "risk" });

<Workflow name="deploy-pipeline">
  <Sequence>
    <Task id="risk" output={outputs.riskScore} agent={riskAgent}>
      Assess deploy risk.
    </Task>
    <ApprovalGate
      id="deploy-approval"
      output={outputs.deployDecision}
      when={risk.level === "high"}
      request={{
        title: "Approve high-risk deploy?",
        summary: `Risk score: ${risk.score}/100`,
      }}
      onDeny="fail"
    />
    <Task id="deploy" output={outputs.deploy}>
      {{ deployed: true }}
    </Task>
  </Sequence>
</Workflow>

Notes

  • Auto-approve emits a valid ApprovalDecision ({ approved: true, note: "auto-approved", ... }); downstream branching stays uniform.
  • onDeny applies only to the human path; auto-approve always succeeds.

Source

The <ApprovalGate> implementation and the files it imports, straight from the package source. This section is generated; edit the source, not this block.
// @smithers-type-exports-begin
/** @typedef {import("./ApprovalGateProps.ts").ApprovalGateProps} ApprovalGateProps */
// @smithers-type-exports-end

import React from "react";
import { Branch } from "./Branch.js";
import { Approval } from "./Approval.js";
import { Task } from "./Task.js";
/**
 * Conditional approval gate. Requires human approval only when `when` is true;
 * otherwise auto-approves with a static `{ approved: true }` decision.
 *
 * Composes Branch + Approval + Task internally.
 * @param {ApprovalGateProps} props
 */
export function ApprovalGate(props) {
    if (props.skipIf)
        return null;
    return React.createElement(Branch, {
        if: props.when,
        then: React.createElement(Approval, {
            id: props.id,
            output: props.output,
            request: props.request,
            onDeny: props.onDeny,
            timeoutMs: props.timeoutMs,
            heartbeatTimeoutMs: props.heartbeatTimeoutMs,
            heartbeatTimeout: props.heartbeatTimeout,
            retries: props.retries,
            retryPolicy: props.retryPolicy,
            continueOnFail: props.continueOnFail,
        }),
        else: React.createElement(Task, {
            id: props.id,
            output: props.output,
            label: `${props.request.title} (auto-approved)`,
            children: {
                approved: true,
                note: "auto-approved",
                decidedBy: null,
                decidedAt: null,
            },
        }),
    });
}