Back to Mind & Machine
Jiuhe Wang Feb 21, 2025

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
Clarifying Requirements
"What framework should I use?"
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()
→
Promise
paused
→
Event sent
via SSE
Question Dock
appears
→
You answer
"Yes"
→
Promise
resolves
→
Tool
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!