AskUserQuestion
Let the agent ask interactive questions during a run
AskUserQuestion is a dynamic tool that lets the agent ask the user questions during execution. Your UI renders the questions and submits answers to an answer API. The runner waits for an approval file in the sandbox, then continues.
Architecture
Runner (inside sandbox)
- Polls approval file every 500ms
- Continues when status == "completed"
↕
Answer API (server)
- Receives { toolCallId, questions, answers }
- Writes approval file via submitAnswer()
↕
UI
- Renders questions
- Submits answers on selectionKey points:
- The runner only reads/polls the file — it does not create it
- The answer API overwrites the file on every update
- If timeout occurs, the runner may continue with partial answers
1. Answer API
Use the same workdir as your chat sandbox.
import path from "node:path";
import { LocalSandbox, submitAnswer, type Question } from "@sandagent/sdk";
export async function POST(request: Request) {
const { toolCallId, questions, answers } = await request.json();
const sandbox = new LocalSandbox({
workdir: path.join(process.cwd(), "workspace"),
});
await submitAnswer(sandbox, { toolCallId, questions, answers });
return Response.json({ success: true });
}2. Render in UI
Detect the tool part and render a component using useAskUserQuestion.
import { useAskUserQuestion } from "@sandagent/sdk/react";
import type { DynamicToolUIPart } from "ai";
function AskUserQuestionUI({ part }: { part: DynamicToolUIPart }) {
const {
questions,
answers,
isCompleted,
isWaitingForInput,
selectAnswer,
isSelected,
} = useAskUserQuestion({
part,
answerEndpoint: "/api/answer",
});
// Render questions/options; call selectAnswer on click
return null;
}3. Flow Summary
- Agent emits
AskUserQuestiontool call - UI renders the tool and submits answers
- Answer API writes approval file via
submitAnswer - Runner reads the file, continues, and the tool becomes completed
Data Structures
Input
interface AskUserQuestionInput {
questions: Array<{
question: string;
header?: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}>;
}Output
interface AskUserQuestionOutput {
questions: Array<{...}>;
answers: Record<string, string>; // multi-select = comma-separated
}Approval File
Location: {workdir}/.sandagent/approvals/{toolCallId}.json
{
"questions": [
{
"question": "What is your preferred language?",
"options": [
{ "label": "TypeScript" },
{ "label": "Python" }
]
}
],
"answers": {
"What is your preferred language?": "TypeScript"
},
"status": "completed",
"timestamp": "2025-01-30T12:00:00.000Z"
}Troubleshooting
- No progress → verify both APIs use the same
workdir - UI not rendering → ensure you handle
dynamic-toolparts withtoolName === "AskUserQuestion" - Answers not applied → confirm
answerEndpointmatches your API route