Skip to content

feat: add inline question prompt flow for tool calls#44

Open
tanishqkancharla wants to merge 6 commits into
mainfrom
tk-question-toolcall-accordion
Open

feat: add inline question prompt flow for tool calls#44
tanishqkancharla wants to merge 6 commits into
mainfrom
tk-question-toolcall-accordion

Conversation

@tanishqkancharla
Copy link
Copy Markdown
Contributor

@tanishqkancharla tanishqkancharla commented Mar 9, 2026

Summary

  • render question requests inline in tool call rows via a dedicated QuestionToolCall component
  • add question-to-tool matching utilities (call/message lookup) so question UI appears where the question tool is invoked
  • persist pending question state across session switches and improve session-tree aggregation for child/sub-agent sessions
  • add robust question event indexing/cleanup and bootstrap syncing for question state
  • add unit and e2e coverage for the accordion question flow, including session switch/return behavior

Testing

  • pnpm exec playwright test tests/e2e/questions-flow.spec.ts --workers=1
  • pnpm exec vitest run src/webview/components/parts/QuestionToolCall.test.ts src/webview/state/eventHandlers.test.ts
  • pnpm exec tsc --noEmit

Open with Devin

@claude
Copy link
Copy Markdown

claude Bot commented Mar 9, 2026

Claude finished @tanishqkancharla's task in 2m 35s —— View job


PR Review

  • Gather changed files and commit history
  • Read and understand the spec/goals
  • Review implementation for simplicity and correctness
  • Check for orphan edits
  • Provide final review summary
📋 Detailed Review

Spec Analysis

Based on specs/question-prompts-webview-mvp.md, the implementation goals are:

  • Render question requests inline in tool call rows via accordion-style UI
  • Add question-to-tool matching utilities (call/message lookup)
  • Persist pending question state across session switches
  • Support session tree aggregation for child/sub-agent sessions
  • Add robust question event indexing/cleanup and bootstrap syncing
  • Provide unit and e2e coverage for the accordion question flow

Changed Files

  • package.json: Minor version-related change
  • specs/question-prompts-webview-mvp.md: Detailed spec document for the feature
  • src/webview/App.css: CSS styles for the accordion question prompt UI
  • src/webview/App.tsx: Wires question prompt rendering, submit/reject handlers, blocks input while questions pending
  • src/webview/components/MessageItem.tsx: Passes question props through component hierarchy
  • src/webview/components/MessageList.tsx: Passes question props through component hierarchy
  • src/webview/components/MessagePartRenderer.tsx: Passes question props to ToolCall component
  • src/webview/components/QuestionPrompt.tsx: Core accordion component with state management helpers
  • src/webview/components/QuestionPrompt.test.ts: Unit tests for state helpers
  • src/webview/components/parts/QuestionToolCall.tsx: Inline question rendering for question tool calls
  • src/webview/components/parts/QuestionToolCall.test.ts: Unit tests for matching logic
  • src/webview/components/parts/ToolCall.tsx: Integrates question detection with fallback to specialized renderers
  • src/webview/components/parts/questionToolMatching.ts: Utility for matching questions to tool calls
  • src/webview/hooks/questionApi.ts: SDK wrapper functions for question reply/reject/list
  • src/webview/hooks/useOpenCode.question.test.ts: Unit tests for SDK wrappers
  • src/webview/hooks/useOpenCode.tsx: Exposes question API helpers to components
  • src/webview/state/MockSyncProvider.tsx: Adds mock question state for testing
  • src/webview/state/bootstrap.ts: Fetches pending questions on bootstrap, builds indexes
  • src/webview/state/bootstrap.test.ts: Unit tests for bootstrap commit
  • src/webview/state/eventHandlers.ts: Handles question.asked/replied/rejected events with indexing
  • src/webview/state/eventHandlers.test.ts: Unit tests for event handlers
  • src/webview/state/sync.tsx: Exposes question selectors and aggregation across session tree
  • src/webview/state/types.ts: Adds question state fields to SyncState
  • tests/e2e/questions-flow.spec.ts: E2E test for full accordion flow with session switching
  • tests/frontend/bootstrap.test.ts: Extended bootstrap tests
  • tests/frontend/simple-bootstrap.test.ts: Extended bootstrap tests

Simplicity Assessment

The implementation is well-structured with appropriate separation of concerns:

  • State helpers in QuestionPrompt.tsx:61-137 are pure functions that are easy to test and reason about. The auto-advance logic is cleanly implemented via shouldAutoAdvance().

  • questionToolMatching.ts is a focused 29-line utility with clear matching priority (callID > messageID).

  • questionApi.ts extracts SDK error handling into reusable functions, avoiding duplication.

  • Event handlers follow the existing pattern for permissions, making the codebase consistent.

One minor observation: In QuestionToolCall.tsx:55-71, there's an effect that fetches questions from the server as a fallback. This adds complexity but appears necessary for edge cases where SSE events are missed. The cleanup pattern with cancelled flag is appropriate.

Correctness Assessment

  • Event handling in eventHandlers.ts:571-625 correctly handles all three question events:

    • question.asked: Adds/updates question, indexes by callID/messageID
    • question.replied/question.rejected: Removes question, cleans up indexes
    • Properly handles missing sessionID by searching all sessions (lines 596-606)
  • Bootstrap sync in bootstrap.ts:165-179 correctly fetches and groups questions by sessionID during initialization.

  • Session tree aggregation in sync.tsx:179-212 correctly includes questions from child sessions and questions targeting visible messages even when child session metadata is unavailable.

  • Submit gating in App.tsx:492-498 correctly blocks new prompts while questions are pending, with appropriate error messaging.

  • In-flight marker clearing in App.tsx:474-484 correctly clears the marker when a question arrives, allowing transition to question-response mode.

  • State persistence across session switches in sync.tsx:94-111 correctly preserves permission/question state (comment at line 98-99 explains this intentional behavior).

Summary

This is a well-implemented feature that adds comprehensive question prompt support to the webview client. The code follows existing patterns for permissions, maintains good separation of concerns between state management, SDK integration, and UI components, and includes thorough test coverage. The accordion UX with auto-advance behavior matches the spec requirements.

Required Actions

None - the implementation is solid and ready for merge.

Suggestions

  1. Consider adding keyboard navigation (noted as future work in the spec) - pressing Enter after selecting an option could auto-advance, and Tab could cycle through accordion items. This would improve accessibility but is explicitly scoped as future work.

  2. Minor: Consider extracting the error boundary pattern - The showMissingRequestError state and retry button in QuestionToolCall.tsx:97-122 could potentially be extracted into a reusable error boundary component if this pattern is needed elsewhere, but this is optional polish.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +84 to +88
return {
...state,
answers,
expandedIndex: shouldAutoAdvance(state.expandedIndex, questionIndex, questionCount),
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Multi-select auto-advance collapses panel after first toggle, preventing multiple selections

toggleMultiAnswer calls shouldAutoAdvance on every toggle, which collapses the current question's panel and advances to the next question after the user selects their first option. For multi-select questions, the user needs to select multiple options before advancing, but the panel auto-closes after the very first toggle. They must re-open the accordion header each time to add another selection.

Code path that causes the issue

At src/webview/components/QuestionPrompt.tsx:87, toggleMultiAnswer passes through to shouldAutoAdvance(state.expandedIndex, questionIndex, questionCount) at line 31, which returns nextIndex (or null for the last question) whenever expandedIndex === answerIndex. This means the panel collapses on the first toggle. For single-select (setSingleAnswer) this is correct, but for multi-select it should keep the panel open until the user explicitly moves on.

Suggested change
return {
...state,
answers,
expandedIndex: shouldAutoAdvance(state.expandedIndex, questionIndex, questionCount),
};
return {
...state,
answers,
expandedIndex: state.expandedIndex,
};
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/webview/App.tsx
Comment on lines +474 to +483
createEffect(() => {
const inflight = inFlightMessage();
if (!inflight) return;

for (const [, question] of pendingQuestions().entries()) {
if (question.sessionID === inflight.sessionId) {
setInFlightMessage(null);
return;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Queued messages never drain after question-interrupted in-flight session

When a question arrives for an in-flight session, the createEffect at src/webview/App.tsx:474-483 clears inFlightMessage to null. After the question is answered and the backend resumes, a session.idle event eventually fires. However, the onSessionIdle callback at src/webview/App.tsx:455-468 checks inflight?.sessionId !== sessionId — since inflight is already null, this check is true and the callback returns early without calling processNextQueuedMessage(). The queued messages remain stuck indefinitely.

Scenario
  1. User sends a message → inFlightMessage set for this session
  2. Backend asks a question → createEffect clears inFlightMessage to null
  3. User answers the question → question removed from pending
  4. Backend resumes, session eventually goes idle → session.idle event fires
  5. onSessionIdle callback sees inFlightMessage() is null, returns early
  6. processNextQueuedMessage() is never called → queue is permanently stuck
Prompt for agents
In src/webview/App.tsx, the createEffect at lines 474-483 clears inFlightMessage when a question arrives, but does not arrange for the message queue to drain after the question is resolved. The onSessionIdle callback at lines 455-468 only triggers processNextQueuedMessage when it matches an in-flight message, which is already null by that point.

Fix: When clearing inFlightMessage due to a question arrival, store the sessionId so the onSessionIdle callback can still match it. Alternatively, add a separate createEffect that watches hasPendingQuestions() transitioning from true to false and calls processNextQueuedMessage() at that point. For example:

createEffect(() => {
  if (!hasPendingQuestions() && messageQueue().length > 0 && !inFlightMessage()) {
    queueMicrotask(() => void processNextQueuedMessage());
  }
});

This would need to be placed after the processNextQueuedMessage definition (around line 693) and should be careful not to create infinite loops.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant