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 { generateUuid } from '../../../../base/common/uuid.js';
7
>
import { localize } from '../../../../nls.js';
8
>
import { FEEDBACK_ANNOTATION_META_KEY, readFeedbackAnnotationMeta, VIEW_UNREVIEWED_COMMENTS_TOOL_NAME, ADD_COMMENT_TOOL_NAME, type IFeedbackAnnotationMeta } from '../../common/meta/agentFeedbackAnnotations.js';
9
>
import { buildAnnotationsUri } from '../../common/annotationsUri.js';
10
>
import type { AnnotationsAction } from '../../common/state/sessionActions.js';
11
>
import { ActionType } from '../../common/state/protocol/common/actions.js';
12
>
import { parseChatUri, type Annotation, type AnnotationsState, type StringOrMarkdown, type TextRange, type ToolDefinition } from '../../common/state/sessionState.js';
13
>
import type { IServerToolDisplay, IServerToolDisplayResult, IServerToolGroup } from './agentServerToolHost.js';
14
>
15
>
/**
16
>
* Server-side implementation of the agent feedback ("comments") tools.
17
>
*
18
>
* These tools used to be registered on the client (agents window) and keyed
19
>
* off an in-memory store. For agent-host sessions they now execute on the
20
>
* server against the session's annotations channel: each comment is an
21
>
* {@link Annotation} on `<session>/annotations`, with feedback semantics
22
>
* carried in {@link Annotation._meta} under {@link FEEDBACK_ANNOTATION_META_KEY}
23
>
* (see `agentFeedbackAnnotations.ts`). The functions here are pure — they read
24
>
* the current {@link AnnotationsState} and return the annotation actions to
25
>
* dispatch plus a textual tool result — so they can be unit tested without a
26
>
* running state manager. The host wiring (reading the snapshot, dispatching
27
>
* the actions) lives in the caller.
28
>
*/
29
>
30
>
export const addCommentToolName = ADD_COMMENT_TOOL_NAME;
31
>
export const listCommentsToolName = 'listComments';
32
>
export const deleteCommentsToolName = 'deleteComments';
33
>
export const resolveCommentsToolName = 'resolveComments';
34
>
export const viewUnreviewedCommentsToolName = VIEW_UNREVIEWED_COMMENTS_TOOL_NAME;
35
>
36
>
/**
37
>
* Feedback kinds that originate from a review the user is expected to triage
38
>
* (a pull request review or an in-product code review) rather than being
39
>
* authored by the user directly. Comments of these kinds that are still in the
40
>
* `created` state are surfaced to the agent via the {@link listCommentsToolName}
41
>
* note and revealed through {@link viewUnreviewedCommentsToolName}.
42
>
*/
43
>
const REVIEWABLE_FEEDBACK_KINDS: ReadonlySet<string> = new Set(['prReview', 'codeReview']);
44
>
45
>
/**
46
>
* Server tools that must not be auto-approved: invoking them surfaces a
47
>
* confirmation to the user (rendered by a custom client content part) before
48
>
* the tool body runs. Providers consult {@link feedbackToolRequiresConfirmation}
49
>
* (via the host) to exclude these from their server-tool auto-approve lists.
50
>
*/
51
>
const feedbackConfirmationToolNames: ReadonlySet<string> = new Set([viewUnreviewedCommentsToolName]);
52
>
53
>
/** Whether the given feedback server tool requires user confirmation before it runs. */
54
>
export function feedbackToolRequiresConfirmation(toolName: string): boolean {
55
return feedbackConfirmationToolNames.has(toolName);
56
}
58
>
const addCommentInputSchema: ToolDefinition['inputSchema'] = {
59
>
type: 'object',
60
>
properties: {
61
>
resourceUri: { type: 'string', description: 'URI of the file to add a comment to.' },
62
>
range: {
63
>
type: 'object',
64
>
description: 'One-based text range to comment on.',
65
>
properties: {
66
>
startLineNumber: { type: 'number', description: 'One-based start line number.' },
67
>
startColumn: { type: 'number', description: 'One-based start column.' },
68
>
endLineNumber: { type: 'number', description: 'One-based end line number.' },
69
>
endColumn: { type: 'number', description: 'One-based end column.' },
70
>
},
71
>
required: ['startLineNumber', 'startColumn', 'endLineNumber', 'endColumn'],
72
>
},
73
>
text: { type: 'string', description: 'Comment text to add.' },
74
>
},
75
>
required: ['resourceUri', 'range', 'text'],
76
>
};
77
>
78
>
const listCommentsInputSchema: ToolDefinition['inputSchema'] = {
79
>
type: 'object',
80
>
properties: {},
81
>
};
82
>
83
>
const viewUnreviewedCommentsInputSchema: ToolDefinition['inputSchema'] = {
84
>
type: 'object',
85
>
properties: {},
86
>
};
87
>
88
>
const deleteCommentsInputSchema: ToolDefinition['inputSchema'] = {
89
>
type: 'object',
90
>
properties: {
91
>
commentIds: { type: 'array', items: { type: 'string' }, description: 'Comment IDs to delete.' },
92
>
},
93
>
required: ['commentIds'],
94
>
};
95
>
96
>
const resolveCommentsInputSchema: ToolDefinition['inputSchema'] = {
97
>
type: 'object',
98
>
properties: {
99
>
commentIds: { type: 'array', items: { type: 'string' }, description: 'Comment IDs to update.' },
100
>
resolved: { type: 'boolean', description: 'Whether the comments should be marked as resolved. Defaults to true.' },
101
>
},
102
>
required: ['commentIds'],
103
>
};
104
>
105
>
/**
106
>
* Protocol {@link ToolDefinition}s for the feedback server tools, advertised on
107
>
* {@link SessionState.serverTools} so clients know these tools are owned and
108
>
* executed by the agent host.
109
>
*/
110
>
export const feedbackServerToolDefinitions: ToolDefinition[] = [
111
>
{
112
>
name: addCommentToolName,
113
>
title: 'Add Comment (Agent Feedback)',
114
>
description: 'Add a comment to a file range.',
115
>
inputSchema: addCommentInputSchema,
116
>
annotations: { readOnlyHint: false },
117
>
},
118
>
{
119
>
name: listCommentsToolName,
120
>
title: 'List Comments (Agent Feedback)',
121
>
description: 'List comments for this session.',
122
>
inputSchema: listCommentsInputSchema,
123
>
annotations: { readOnlyHint: true },
124
>
},
125
>
{
126
>
name: deleteCommentsToolName,
127
>
title: 'Delete Comments (Agent Feedback)',
128
>
description: 'Delete comments for this session.',
129
>
inputSchema: deleteCommentsInputSchema,
130
>
annotations: { readOnlyHint: false, destructiveHint: true },
131
>
},
132
>
{
133
>
name: resolveCommentsToolName,
134
>
title: 'Resolve Comments (Agent Feedback)',
135
>
description: 'Mark comments for this session as resolved or unresolved.',
136
>
inputSchema: resolveCommentsInputSchema,
137
>
annotations: { readOnlyHint: false },
138
>
},
139
>
{
140
>
name: viewUnreviewedCommentsToolName,
141
>
title: 'View Unreviewed Comments (Agent Feedback)',
142
>
description: 'View pull request or code review comments that the user has not reviewed yet. Calling this asks the user to choose which of those comments to reveal; only the comments the user reveals are returned.',
143
>
inputSchema: viewUnreviewedCommentsInputSchema,
144
>
annotations: { readOnlyHint: true },
145
>
},
146
>
];
147
>
148
>
// --- Argument validation ------------------------------------------------------
149
>
150
>
interface IOneBasedRange {
151
>
readonly startLineNumber: number;
152
>
readonly startColumn: number;
153
>
readonly endLineNumber: number;
154
>
readonly endColumn: number;
155
>
}
156
>
157
>
interface IAddCommentArgs {
158
>
readonly resourceUri?: unknown;
159
>
readonly range?: unknown;
160
>
readonly text?: unknown;
161
>
}
162
>
163
>
interface IDeleteCommentsArgs {
164
>
readonly commentIds?: unknown;
165
>
}
166
>
167
>
interface IResolveCommentsArgs {
168
>
readonly commentIds?: unknown;
169
>
readonly resolved?: unknown;
170
>
}
171
>
172
function getRequiredString(value: unknown, field: string, toolName: string): string {
173
if (typeof value !== 'string' || value.length === 0) {