@@ -33,6 +33,7 @@ import { EditorManager } from '@theia/editor/lib/browser';
3333import { FileService } from '@theia/filesystem/lib/browser/file-service' ;
3434import { WorkspaceService } from '@theia/workspace/lib/browser' ;
3535import {
36+ AskUserQuestionInput ,
3637 ContentBlock ,
3738 EditInput ,
3839 MultiEditInput ,
@@ -59,6 +60,8 @@ export const CLAUDE_APPROVAL_TOOL_INPUTS_KEY = 'claudeApprovalToolInputs';
5960export const CLAUDE_MODEL_NAME_KEY = 'claudeModelName' ;
6061export const CLAUDE_COST_KEY = 'claudeCost' ;
6162export const CLAUDE_SESSION_APPROVED_TOOLS_KEY = 'claudeSessionApprovedTools' ;
63+ export const CLAUDE_ASK_USER_QUESTION_IDS_KEY = 'claudeAskUserQuestionIds' ;
64+ export const CLAUDE_PENDING_ASK_USER_QUESTIONS_KEY = 'claudePendingAskUserQuestions' ;
6265
6366const APPROVAL_OPTIONS = [
6467 { text : nls . localizeByDefault ( 'Allow' ) , value : 'allow' } ,
@@ -354,6 +357,11 @@ export class ClaudeCodeChatAgent implements ChatAgent {
354357 approvalRequest : ToolApprovalRequestMessage ,
355358 request : MutableChatRequestModel
356359 ) : void {
360+ if ( approvalRequest . toolName === 'AskUserQuestion' ) {
361+ this . handleAskUserQuestion ( approvalRequest , request ) ;
362+ return ;
363+ }
364+
357365 if ( this . isToolApprovedForSession ( request , approvalRequest . toolName ) ) {
358366 const response : ToolApprovalResponseMessage = {
359367 type : 'tool-approval-response' ,
@@ -421,12 +429,103 @@ export class ClaudeCodeChatAgent implements ChatAgent {
421429
422430 this . claudeCode . sendApprovalResponse ( response ) ;
423431
424- // Only stop waiting for input if there are no more pending approvals
425- if ( pendingApprovals . size === 0 ) {
432+ // Only stop waiting for input if there are no more pending approvals or questions
433+ if ( pendingApprovals . size === 0 && this . getPendingAskUserQuestions ( request ) . size === 0 ) {
426434 request . response . stopWaitingForInput ( ) ;
427435 }
428436 }
429437
438+ protected handleAskUserQuestion (
439+ approvalRequest : ToolApprovalRequestMessage ,
440+ request : MutableChatRequestModel
441+ ) : void {
442+ const toolInput = approvalRequest . toolInput ;
443+ if ( ! AskUserQuestionInput . is ( toolInput ) ) {
444+ const response : ToolApprovalResponseMessage = {
445+ type : 'tool-approval-response' ,
446+ requestId : approvalRequest . requestId ,
447+ approved : false ,
448+ message : 'Invalid AskUserQuestion input format'
449+ } ;
450+ this . claudeCode . sendApprovalResponse ( response ) ;
451+ return ;
452+ }
453+
454+ const questions = toolInput . questions ;
455+ const answers : Record < string , string > = { } ;
456+ let answeredCount = 0 ;
457+ const totalQuestions = questions . length ;
458+
459+ this . getPendingAskUserQuestions ( request ) . add ( approvalRequest . requestId ) ;
460+
461+ for ( const questionItem of questions ) {
462+ const options = questionItem . options . map ( opt => ( {
463+ text : opt . label ,
464+ value : opt . label ,
465+ description : opt . description
466+ } ) ) ;
467+
468+ const questionText = questionItem . header
469+ ? `**${ questionItem . header } :** ${ questionItem . question } `
470+ : questionItem . question ;
471+
472+ const questionContent = new QuestionResponseContentImpl (
473+ questionText ,
474+ options ,
475+ request ,
476+ selectedOption => {
477+ // Key by question text to match the Claude Code SDK's expected answers format
478+ answers [ questionItem . question ] = selectedOption . value ?? selectedOption . text ;
479+ answeredCount ++ ;
480+
481+ if ( answeredCount === totalQuestions ) {
482+ this . getPendingAskUserQuestions ( request ) . delete ( approvalRequest . requestId ) ;
483+
484+ const updatedInput : AskUserQuestionInput = {
485+ ...toolInput ,
486+ answers
487+ } ;
488+
489+ const response : ToolApprovalResponseMessage = {
490+ type : 'tool-approval-response' ,
491+ requestId : approvalRequest . requestId ,
492+ approved : true ,
493+ updatedInput
494+ } ;
495+
496+ this . claudeCode . sendApprovalResponse ( response ) ;
497+
498+ if ( this . getPendingApprovals ( request ) . size === 0 && this . getPendingAskUserQuestions ( request ) . size === 0 ) {
499+ request . response . stopWaitingForInput ( ) ;
500+ }
501+ }
502+ }
503+ ) ;
504+
505+ request . response . response . addContent ( questionContent ) ;
506+ }
507+
508+ request . response . waitForInput ( ) ;
509+ }
510+
511+ protected getPendingAskUserQuestions ( request : MutableChatRequestModel ) : Set < string > {
512+ let ids = request . getDataByKey ( CLAUDE_PENDING_ASK_USER_QUESTIONS_KEY ) as Set < string > | undefined ;
513+ if ( ! ids ) {
514+ ids = new Set < string > ( ) ;
515+ request . addData ( CLAUDE_PENDING_ASK_USER_QUESTIONS_KEY , ids ) ;
516+ }
517+ return ids ;
518+ }
519+
520+ protected getAskUserQuestionToolUseIds ( request : MutableChatRequestModel ) : Set < string > {
521+ let ids = request . getDataByKey ( CLAUDE_ASK_USER_QUESTION_IDS_KEY ) as Set < string > | undefined ;
522+ if ( ! ids ) {
523+ ids = new Set < string > ( ) ;
524+ request . addData ( CLAUDE_ASK_USER_QUESTION_IDS_KEY , ids ) ;
525+ }
526+ return ids ;
527+ }
528+
430529 protected getEditToolUses ( request : MutableChatRequestModel ) : Map < string , ToolUseBlock > | undefined {
431530 return request . getDataByKey ( CLAUDE_EDIT_TOOL_USES_KEY ) ;
432531 }
@@ -577,6 +676,12 @@ export class ClaudeCodeChatAgent implements ChatAgent {
577676 break ;
578677 case 'tool_use' :
579678 case 'server_tool_use' :
679+ // Suppress AskUserQuestion - already shown as interactive questions via the approval flow
680+ if ( block . name === 'AskUserQuestion' ) {
681+ this . getAskUserQuestionToolUseIds ( request ) . add ( block . id ) ;
682+ break ;
683+ }
684+
580685 if ( block . name === 'Task' && TaskInput . is ( block . input ) ) {
581686 request . response . response . addContent ( new MarkdownChatResponseContentImpl ( `\n\n### Task: ${ block . input . description } \n\n${ block . input . prompt } ` ) ) ;
582687 }
@@ -594,6 +699,11 @@ export class ClaudeCodeChatAgent implements ChatAgent {
594699 request . response . response . addContent ( new ClaudeCodeToolCallChatResponseContent ( block . id , block . name , JSON . stringify ( block . input ) ) ) ;
595700 break ;
596701 case 'tool_result' :
702+ // Suppress AskUserQuestion tool results
703+ if ( this . getAskUserQuestionToolUseIds ( request ) . has ( block . tool_use_id ) ) {
704+ break ;
705+ }
706+
597707 if ( this . getEditToolUses ( request ) ?. has ( block . tool_use_id ) ) {
598708 const toolUse = this . getEditToolUses ( request ) ?. get ( block . tool_use_id ) ;
599709 if ( toolUse ) {
0 commit comments