August 2026 - Human in the Loop
@shadcn/helpers can now mock human-in-the-loop flows for the AI SDK. A scripted conversation can pause for real user input, wait for an approval, and continue with whatever the user decided.
Everything streams through the real
useChat lifecycle, so your tool cards, approval prompts, and question flows
behave exactly as they would in production.
import { createChat } from "@shadcn/helpers/ai-sdk"
const chat = createChat<ChatMessage>()
.user("Help me plan the next prototype.")
.assistant(({ writer }) => {
writer.text("A couple of questions before I start.")
writer.tool("askQuestions", { input: { questions } })
})
.assistant(({ writer, toolCall }) => {
writer.text(
toolCall?.name === "askQuestions" && toolCall.output
? `Starting with ${toolCall.output.answers.direction}.`
: "Starting now."
)
})Pass needsApproval to pause behind the user's decision. The scripted output
streams after approval, and denial streams automatically.
chat
.assistant(({ writer }) => {
writer.text("That will archive 3 drafts. I need your approval.")
writer.tool("archiveDrafts", {
input: { count: 3 },
needsApproval: true,
output: { archived: 3 },
})
})
.assistant(({ writer, toolCall }) => {
writer.text(
toolCall?.approved ? "Archived 3 drafts." : "Okay, leaving them in place."
)
})