Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/common/toolPermissionHandlers/askUserQuestionHandler.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { AskUserQuestionInput } from '@anthropic-ai/claude-agent-sdk/sdk-tools';6import { CancellationToken } from '../../../../../util/vs/base/common/cancellation';7import { LanguageModelTextPart } from '../../../../../vscodeTypes';8import { IAnswerResult } from '../../../../tools/common/askQuestionsTypes';9import { ToolName } from '../../../../tools/common/toolNames';10import { IToolsService } from '../../../../tools/common/toolsService';11import {12ClaudeToolPermissionContext,13ClaudeToolPermissionResult,14IClaudeToolPermissionHandler15} from '../claudeToolPermission';16import { registerToolPermissionHandler } from '../claudeToolPermissionRegistry';17import { ClaudeToolNames } from '../claudeTools';1819/**20* Handler for the AskUserQuestion tool.21* Delegates to the core vscode_askQuestions tool for improved UX with step navigation,22* back button support, and custom text input.23*/24export class AskUserQuestionHandler implements IClaudeToolPermissionHandler<ClaudeToolNames.AskUserQuestion> {25public readonly toolNames = [ClaudeToolNames.AskUserQuestion] as const;2627constructor(28@IToolsService private readonly toolsService: IToolsService,29) { }3031public async handle(32_toolName: ClaudeToolNames.AskUserQuestion,33input: AskUserQuestionInput,34context: ClaudeToolPermissionContext35): Promise<ClaudeToolPermissionResult> {36try {37const result = await this.toolsService.invokeTool(ToolName.CoreAskQuestions, {38input,39toolInvocationToken: context.toolInvocationToken,40}, CancellationToken.None);4142// Parse the result43const firstPart = result.content.at(0);44if (!(firstPart instanceof LanguageModelTextPart)) {45return {46behavior: 'deny',47message: 'The user cancelled the question'48};49}5051const toolResult: IAnswerResult = JSON.parse(firstPart.value);5253// Check if all questions were skipped54const allSkipped = Object.values(toolResult.answers).every(a => a.skipped);55if (allSkipped) {56return {57behavior: 'deny',58message: 'The user cancelled the question'59};60}6162// Transform result back to SDK expected format (answers keyed by question text)63const answers: Record<string, string> = {};64for (const questionItem of input.questions) {65const answer = toolResult.answers[questionItem.header];66if (answer && !answer.skipped) {67// Combine selected options and free text68const parts: string[] = [...answer.selected];69if (answer.freeText) {70parts.push(answer.freeText);71}72answers[questionItem.question] = parts.join(', ');73}74}7576return {77behavior: 'allow',78updatedInput: {79...input,80answers81}82};83} catch {84return {85behavior: 'deny',86message: 'The user cancelled the question'87};88}89}90}9192// Self-register the handler93registerToolPermissionHandler(94[ClaudeToolNames.AskUserQuestion],95AskUserQuestionHandler96);979899