Back to Mind & Machine
→
→
→
→
→
OpenCode Multiple Choice Interaction
How the Plan Agent Talks to You
The Big Picture
When the AI needs to ask you something, it doesn't just pop up a dialog. There's a whole system behind it!
How Pieces Connect
Frontend — SolidJS UI, Question Dock
↓ REST + SSE
Backend — Runs agents, handles API requests
↓ executes
Agent — Plan, Build, or any agent
↓ uses
Tools — read, write, Question.ask(), etc.
Two Ways the Agent Asks
The question system handles different scenarios:
Agent Switching
"Ready to switch to build agent?"
Plan ↔ Build mode transitions
Plan ↔ Build mode transitions
Clarifying Requirements
"What framework should I use?"
Filling in missing details
Filling in missing details
Why both matter: The plan agent uses questions in two situations: when you want to switch modes, and when it encounters unclear requirements. This prevents the AI from guessing wrong!
When the Agent Asks a Question
Whether it's agent switching (like pressing Tab to enter Plan mode) or the agent needs clarification mid-flight, here's the complete flow:
Agent calls
Question.ask()
Question.ask()
Promise
paused
paused
Event sent
via SSE
via SSE
Question Dock
appears
appears
You answer
"Yes"
"Yes"
Promise
resolves
resolves
Tool
continues
continues
How It Works (Under the Hood)
1. Agent asks a question
plan.ts
const answers = await Question.ask({
sessionID: "session_123",
questions: [{
question: "Switch to build agent?",
header: "Build Agent",
custom: false,
options: [
{ label: "Yes", description: "Switch" },
{ label: "No", description: "Stay" }
]
}]
})
// Execution PAUSES here until user answers!
2. User replies via REST API
HTTP Request
POST /question/question_abc/reply
{
"answers": [["Yes"]]
}
3. How the agent resumes
The magic of async/await: When the tool calls
await Question.ask(), the function literally pauses at that line. It's like hitting a breakpoint! The server keeps the request pending, and when your answer arrives via REST API, it calls resolve(answers). This unwinds the stack and the tool continues executing with your answer!
Key Pieces
| Piece | Location | Job |
|---|---|---|
| Question Module | question/index.ts |
Schemas + ask/reply logic |
| Plan Tools | tool/plan.ts |
Agent switching |
| Question Tool | tool/question.ts |
General clarifying questions |
| Server Routes | server/routes/question.ts |
REST API endpoints |
| Event Stream | server/routes/global.ts |
SSE to frontend |
| Question Dock | session-question-dock.tsx |
SolidJS UI component |
Why It's Elegant
- Promise-based blocking — Agent waits without blocking the server
- Event-driven — Frontend doesn't poll, it subscribes via SSE
- Type-safe — Zod schemas everywhere
- Multi-question support — Can ask several in a row
- Rich UI — Radio buttons, checkboxes, custom input
Think of it like... The agent is running down a path, then hits a "pause" barrier. It stays there frozen until YOU push the button to let it continue. No polling, no timeout, just pure waiting!