// approval-gate.tsx
import {
Approval,
Sequence,
Task,
approvalDecisionSchema,
createSmithers,
} from "smithers-orchestrator";
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { z } from "zod";
const { Workflow, smithers, outputs } = createSmithers({
draft: z.object({
title: z.string(),
content: z.string(),
}),
publishApproval: approvalDecisionSchema,
published: z.object({
url: z.string(),
publishedAt: z.string(),
}),
});
const writer = new Agent({
model: anthropic("claude-sonnet-4-5-20250929"),
instructions:
"You are a technical writer. Draft a blog post with a title and full content based on the given topic.",
});
const publisher = new Agent({
model: anthropic("claude-sonnet-4-5-20250929"),
instructions:
"You are a publishing agent. Take the approved draft and return a URL and timestamp for the published post.",
});
export default smithers((ctx) => {
const draft = ctx.outputMaybe(outputs.draft, { nodeId: "write-draft" });
const decision = ctx.outputMaybe(outputs.publishApproval, {
nodeId: "approve-publish",
});
return (
<Workflow name="approval-gate">
<Sequence>
<Task id="write-draft" output={outputs.draft} agent={writer}>
Write a blog post about deterministic AI workflows and why resumability
matters for production systems.
</Task>
<Approval
id="approve-publish"
output={outputs.publishApproval}
request={{
title: "Publish blog post",
summary: draft
? `Publish "${draft.title}" to the public site.`
: "Publish the current draft.",
}}
/>
{decision?.approved ? (
<Task id="publish" output={outputs.published} agent={publisher}>
Publish this approved draft:{"\n\n"}
Title: {draft?.title}
{"\n\n"}
{draft?.content}
</Task>
) : null}
</Sequence>
</Workflow>
);
});