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