@theia/ai-chat-ui 1.68.0-next.8 → 1.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/lib/browser/ai-chat-ui-contribution.d.ts +2 -1
- package/lib/browser/ai-chat-ui-contribution.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-contribution.js +41 -32
- package/lib/browser/ai-chat-ui-contribution.js.map +1 -1
- package/lib/browser/ai-chat-ui-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-ui-frontend-module.js +15 -6
- package/lib/browser/ai-chat-ui-frontend-module.js.map +1 -1
- package/lib/browser/change-set-actions/change-set-accept-action.d.ts +0 -1
- package/lib/browser/change-set-actions/change-set-accept-action.d.ts.map +1 -1
- package/lib/browser/change-set-actions/change-set-action-service.d.ts +0 -1
- package/lib/browser/change-set-actions/change-set-action-service.d.ts.map +1 -1
- package/lib/browser/change-set-actions/change-set-action-service.js +2 -2
- package/lib/browser/change-set-actions/change-set-action-service.js.map +1 -1
- package/lib/browser/chat-focus-contribution.d.ts +14 -0
- package/lib/browser/chat-focus-contribution.d.ts.map +1 -0
- package/lib/browser/chat-focus-contribution.js +102 -0
- package/lib/browser/chat-focus-contribution.js.map +1 -0
- package/lib/browser/chat-input-agent-suggestions.d.ts +0 -1
- package/lib/browser/chat-input-agent-suggestions.d.ts.map +1 -1
- package/lib/browser/chat-input-agent-suggestions.js +1 -1
- package/lib/browser/chat-input-agent-suggestions.js.map +1 -1
- package/lib/browser/chat-input-history-contribution.js +6 -10
- package/lib/browser/chat-input-history-contribution.js.map +1 -1
- package/lib/browser/chat-input-mode-contribution.js +1 -2
- package/lib/browser/chat-input-mode-contribution.js.map +1 -1
- package/lib/browser/chat-input-widget.d.ts +0 -1
- package/lib/browser/chat-input-widget.d.ts.map +1 -1
- package/lib/browser/chat-input-widget.js +106 -96
- package/lib/browser/chat-input-widget.js.map +1 -1
- package/lib/browser/chat-progress-message.d.ts +0 -1
- package/lib/browser/chat-progress-message.d.ts.map +1 -1
- package/lib/browser/chat-response-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/ai-selection-resolver.js +4 -5
- package/lib/browser/chat-response-renderer/ai-selection-resolver.js.map +1 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/code-part-renderer.js +6 -9
- package/lib/browser/chat-response-renderer/code-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/command-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/command-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/command-part-renderer.js +7 -4
- package/lib/browser/chat-response-renderer/command-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/delegation-response-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/delegation-response-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/delegation-response-renderer.js +3 -4
- package/lib/browser/chat-response-renderer/delegation-response-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/error-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/error-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/horizontal-layout-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/horizontal-layout-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/index.d.ts +1 -0
- package/lib/browser/chat-response-renderer/index.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/index.js +1 -0
- package/lib/browser/chat-response-renderer/index.js.map +1 -1
- package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/markdown-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/markdown-part-renderer.js +7 -9
- package/lib/browser/chat-response-renderer/markdown-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/not-available-toolcall-renderer.d.ts +17 -0
- package/lib/browser/chat-response-renderer/not-available-toolcall-renderer.d.ts.map +1 -0
- package/lib/browser/chat-response-renderer/not-available-toolcall-renderer.js +56 -0
- package/lib/browser/chat-response-renderer/not-available-toolcall-renderer.js.map +1 -0
- package/lib/browser/chat-response-renderer/progress-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/progress-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/question-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/question-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/question-part-renderer.js +6 -9
- package/lib/browser/chat-response-renderer/question-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/text-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/text-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/thinking-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/tool-confirmation.d.ts +29 -10
- package/lib/browser/chat-response-renderer/tool-confirmation.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/tool-confirmation.js +219 -65
- package/lib/browser/chat-response-renderer/tool-confirmation.js.map +1 -1
- package/lib/browser/chat-response-renderer/tool-confirmation.spec.d.ts +2 -0
- package/lib/browser/chat-response-renderer/tool-confirmation.spec.d.ts.map +1 -0
- package/lib/browser/chat-response-renderer/tool-confirmation.spec.js +100 -0
- package/lib/browser/chat-response-renderer/tool-confirmation.spec.js.map +1 -0
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts +5 -3
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js +21 -11
- package/lib/browser/chat-response-renderer/toolcall-part-renderer.js.map +1 -1
- package/lib/browser/chat-response-renderer/unknown-part-renderer.d.ts +0 -1
- package/lib/browser/chat-response-renderer/unknown-part-renderer.d.ts.map +1 -1
- package/lib/browser/chat-response-renderer/unknown-part-renderer.js +1 -2
- package/lib/browser/chat-response-renderer/unknown-part-renderer.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-container.js +1 -2
- package/lib/browser/chat-tree-view/chat-view-tree-container.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-input-widget.js +2 -3
- package/lib/browser/chat-tree-view/chat-view-tree-input-widget.js.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts +3 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.d.ts.map +1 -1
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js +49 -42
- package/lib/browser/chat-tree-view/chat-view-tree-widget.js.map +1 -1
- package/lib/browser/chat-tree-view/prompt-variant-badge.d.ts +0 -1
- package/lib/browser/chat-tree-view/prompt-variant-badge.d.ts.map +1 -1
- package/lib/browser/chat-tree-view/sub-chat-widget.d.ts +0 -1
- package/lib/browser/chat-tree-view/sub-chat-widget.d.ts.map +1 -1
- package/lib/browser/chat-view-commands.d.ts.map +1 -1
- package/lib/browser/chat-view-commands.js +2 -2
- package/lib/browser/chat-view-commands.js.map +1 -1
- package/lib/browser/chat-view-contribution.js +7 -7
- package/lib/browser/chat-view-contribution.js.map +1 -1
- package/lib/browser/chat-view-language-contribution.js +3 -4
- package/lib/browser/chat-view-language-contribution.js.map +1 -1
- package/lib/browser/chat-view-widget.js +9 -10
- package/lib/browser/chat-view-widget.js.map +1 -1
- package/lib/browser/context-variable-picker.js +9 -13
- package/lib/browser/context-variable-picker.js.map +1 -1
- package/lib/browser/session-storage-preference-renderer.d.ts +127 -0
- package/lib/browser/session-storage-preference-renderer.d.ts.map +1 -0
- package/lib/browser/session-storage-preference-renderer.js +490 -0
- package/lib/browser/session-storage-preference-renderer.js.map +1 -0
- package/package.json +12 -11
- package/src/browser/ai-chat-ui-contribution.ts +19 -7
- package/src/browser/ai-chat-ui-frontend-module.ts +13 -0
- package/src/browser/chat-focus-contribution.ts +102 -0
- package/src/browser/chat-input-widget.tsx +99 -27
- package/src/browser/chat-response-renderer/index.ts +1 -0
- package/src/browser/chat-response-renderer/not-available-toolcall-renderer.tsx +57 -0
- package/src/browser/chat-response-renderer/tool-confirmation.spec.ts +108 -0
- package/src/browser/chat-response-renderer/tool-confirmation.tsx +379 -95
- package/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +27 -13
- package/src/browser/chat-tree-view/chat-view-tree-widget.tsx +33 -5
- package/src/browser/chat-view-commands.ts +2 -2
- package/src/browser/session-storage-preference-renderer.ts +657 -0
- package/src/browser/style/index.css +73 -26
- package/src/browser/style/session-storage-preference.css +134 -0
|
@@ -16,114 +16,214 @@
|
|
|
16
16
|
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
18
|
import { nls } from '@theia/core/lib/common/nls';
|
|
19
|
-
import { codicon } from '@theia/core/lib/browser';
|
|
19
|
+
import { codicon, ContextMenuRenderer } from '@theia/core/lib/browser';
|
|
20
20
|
import { ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
|
|
21
|
+
import { ToolRequest } from '@theia/ai-core';
|
|
22
|
+
import { CommandMenu, ContextExpressionMatcher, MenuPath } from '@theia/core/lib/common/menu';
|
|
23
|
+
import { GroupImpl } from '@theia/core/lib/browser/menu/composite-menu-node';
|
|
24
|
+
import { ToolConfirmationMode as ToolConfirmationPreferenceMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
|
|
25
|
+
import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
|
|
21
26
|
|
|
22
|
-
/**
|
|
23
|
-
* States the tool confirmation component can be in
|
|
24
|
-
*/
|
|
25
27
|
export type ToolConfirmationState = 'waiting' | 'allowed' | 'denied' | 'rejected';
|
|
26
28
|
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
export type ConfirmationScope = 'once' | 'session' | 'forever';
|
|
30
|
+
|
|
31
|
+
export interface ToolConfirmationCallbacks {
|
|
32
|
+
toolRequest?: ToolRequest;
|
|
33
|
+
onAllow: (scope: ConfirmationScope) => void;
|
|
34
|
+
onDeny: (scope: ConfirmationScope, reason?: string) => void;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const [state, setState] = React.useState<ToolConfirmationState>('waiting');
|
|
38
|
-
// Track selected mode for each action
|
|
39
|
-
const [allowMode, setAllowMode] = React.useState<'once' | 'session' | 'forever'>('once');
|
|
40
|
-
const [denyMode, setDenyMode] = React.useState<'once' | 'session' | 'forever'>('once');
|
|
41
|
-
const [dropdownOpen, setDropdownOpen] = React.useState<'allow' | 'deny' | undefined>(undefined);
|
|
37
|
+
export interface ToolConfirmationActionsProps extends ToolConfirmationCallbacks {
|
|
38
|
+
toolName: string;
|
|
39
|
+
contextMenuRenderer: ContextMenuRenderer;
|
|
40
|
+
}
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
class InlineActionMenuNode implements CommandMenu {
|
|
43
|
+
constructor(
|
|
44
|
+
readonly id: string,
|
|
45
|
+
readonly label: string,
|
|
46
|
+
private readonly action: () => void,
|
|
47
|
+
readonly sortString: string,
|
|
48
|
+
readonly icon?: string
|
|
49
|
+
) { }
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}, [onDeny, denyMode]);
|
|
51
|
+
isVisible<T>(_effectiveMenuPath: MenuPath, _contextMatcher: ContextExpressionMatcher<T>, _context: T | undefined): boolean {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
return
|
|
55
|
-
<div className="theia-tool-confirmation-status allowed">
|
|
56
|
-
<span className={codicon('check')}></span> {nls.localize('theia/ai/chat-ui/toolconfirmation/allowed', 'Tool execution allowed')}
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
55
|
+
isEnabled(): boolean {
|
|
56
|
+
return true;
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
59
|
+
isToggled(): boolean {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async run(): Promise<void> {
|
|
64
|
+
this.action();
|
|
67
65
|
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const ToolConfirmationActions: React.FC<ToolConfirmationActionsProps> = ({
|
|
69
|
+
toolName,
|
|
70
|
+
toolRequest,
|
|
71
|
+
onAllow,
|
|
72
|
+
onDeny,
|
|
73
|
+
contextMenuRenderer
|
|
74
|
+
}) => {
|
|
75
|
+
const [allowScope, setAllowScope] = React.useState<ConfirmationScope>('once');
|
|
76
|
+
const [denyScope, setDenyScope] = React.useState<ConfirmationScope>('once');
|
|
77
|
+
const [showAlwaysAllowConfirmation, setShowAlwaysAllowConfirmation] = React.useState(false);
|
|
78
|
+
const [showDenyReasonInput, setShowDenyReasonInput] = React.useState(false);
|
|
79
|
+
const [denyReason, setDenyReason] = React.useState('');
|
|
80
|
+
// eslint-disable-next-line no-null/no-null
|
|
81
|
+
const denyReasonInputRef = React.useRef<HTMLInputElement>(null);
|
|
82
|
+
|
|
83
|
+
const handleAllow = React.useCallback(() => {
|
|
84
|
+
if ((allowScope === 'forever' || allowScope === 'session') && toolRequest?.confirmAlwaysAllow) {
|
|
85
|
+
setShowAlwaysAllowConfirmation(true);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
onAllow(allowScope);
|
|
89
|
+
}, [onAllow, allowScope, toolRequest]);
|
|
90
|
+
|
|
91
|
+
const handleConfirmAlwaysAllow = React.useCallback(() => {
|
|
92
|
+
setShowAlwaysAllowConfirmation(false);
|
|
93
|
+
onAllow(allowScope);
|
|
94
|
+
}, [onAllow, allowScope]);
|
|
95
|
+
|
|
96
|
+
const handleCancelAlwaysAllow = React.useCallback(() => {
|
|
97
|
+
setShowAlwaysAllowConfirmation(false);
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
const handleDeny = React.useCallback(() => {
|
|
101
|
+
onDeny(denyScope);
|
|
102
|
+
}, [onDeny, denyScope]);
|
|
103
|
+
|
|
104
|
+
const handleDenyWithReason = React.useCallback(() => {
|
|
105
|
+
setShowDenyReasonInput(true);
|
|
106
|
+
}, []);
|
|
107
|
+
|
|
108
|
+
const handleSubmitDenyReason = React.useCallback(() => {
|
|
109
|
+
onDeny('once', denyReason.trim() || undefined);
|
|
110
|
+
setShowDenyReasonInput(false);
|
|
111
|
+
setDenyReason('');
|
|
112
|
+
}, [onDeny, denyReason]);
|
|
113
|
+
|
|
114
|
+
const handleCancelDenyReason = React.useCallback(() => {
|
|
115
|
+
setShowDenyReasonInput(false);
|
|
116
|
+
setDenyReason('');
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
const handleDenyReasonKeyDown = React.useCallback((e: React.KeyboardEvent) => {
|
|
120
|
+
if (e.key === 'Enter') {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
handleSubmitDenyReason();
|
|
123
|
+
} else if (e.key === 'Escape') {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
handleCancelDenyReason();
|
|
126
|
+
}
|
|
127
|
+
}, [handleSubmitDenyReason, handleCancelDenyReason]);
|
|
128
|
+
|
|
129
|
+
React.useEffect(() => {
|
|
130
|
+
if (showDenyReasonInput && denyReasonInputRef.current) {
|
|
131
|
+
denyReasonInputRef.current.focus();
|
|
132
|
+
}
|
|
133
|
+
}, [showDenyReasonInput]);
|
|
68
134
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const modeLabel = (type: 'allow' | 'deny', mode: 'once' | 'session' | 'forever') => {
|
|
135
|
+
const SCOPES: ConfirmationScope[] = ['once', 'session', 'forever'];
|
|
136
|
+
|
|
137
|
+
const scopeLabel = (type: 'allow' | 'deny', scope: ConfirmationScope): string => {
|
|
73
138
|
if (type === 'allow') {
|
|
74
|
-
switch (
|
|
139
|
+
switch (scope) {
|
|
75
140
|
case 'once': return nls.localizeByDefault('Allow');
|
|
76
141
|
case 'session': return nls.localize('theia/ai/chat-ui/toolconfirmation/allow-session', 'Allow for this Chat');
|
|
77
142
|
case 'forever': return nls.localizeByDefault('Always Allow');
|
|
78
143
|
}
|
|
79
144
|
} else {
|
|
80
|
-
switch (
|
|
145
|
+
switch (scope) {
|
|
81
146
|
case 'once': return nls.localizeByDefault('Deny');
|
|
82
147
|
case 'session': return nls.localize('theia/ai/chat-ui/toolconfirmation/deny-session', 'Deny for this Chat');
|
|
83
148
|
case 'forever': return nls.localize('theia/ai/chat-ui/toolconfirmation/deny-forever', 'Always Deny');
|
|
84
149
|
}
|
|
85
150
|
}
|
|
86
151
|
};
|
|
87
|
-
// Main button label is always the same as the dropdown label for the selected mode
|
|
88
|
-
const mainButtonLabel = modeLabel; // Use the same function for both
|
|
89
152
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
switch (mode) {
|
|
94
|
-
case 'once': return nls.localize('theia/ai/chat-ui/toolconfirmation/allow-tooltip', 'Allow this tool call once');
|
|
95
|
-
case 'session': return nls.localize('theia/ai/chat-ui/toolconfirmation/allow-session-tooltip', 'Allow all calls of this tool for this chat session');
|
|
96
|
-
case 'forever': return nls.localize('theia/ai/chat-ui/toolconfirmation/allow-forever-tooltip', 'Always allow this tool');
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
switch (mode) {
|
|
100
|
-
case 'once': return nls.localize('theia/ai/chat-ui/toolconfirmation/deny-tooltip', 'Deny this tool call once');
|
|
101
|
-
case 'session': return nls.localize('theia/ai/chat-ui/toolconfirmation/deny-session-tooltip', 'Deny all calls of this tool for this chat session');
|
|
102
|
-
case 'forever': return nls.localize('theia/ai/chat-ui/toolconfirmation/deny-forever-tooltip', 'Always deny this tool');
|
|
103
|
-
}
|
|
153
|
+
const getAlwaysAllowWarning = (): string => {
|
|
154
|
+
if (typeof toolRequest?.confirmAlwaysAllow === 'string') {
|
|
155
|
+
return toolRequest.confirmAlwaysAllow;
|
|
104
156
|
}
|
|
157
|
+
return nls.localize(
|
|
158
|
+
'theia/ai/chat-ui/toolconfirmation/alwaysAllowGenericWarning',
|
|
159
|
+
'This tool requires confirmation before auto-approval can be enabled. ' +
|
|
160
|
+
'Once enabled, all future invocations will execute without confirmation. ' +
|
|
161
|
+
'Only enable this if you trust this tool and understand the potential risks.'
|
|
162
|
+
);
|
|
105
163
|
};
|
|
106
164
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
165
|
+
const showDropdownMenu = React.useCallback((
|
|
166
|
+
event: React.MouseEvent<HTMLButtonElement>,
|
|
167
|
+
type: 'allow' | 'deny',
|
|
168
|
+
selectedScope: ConfirmationScope,
|
|
169
|
+
setScope: (scope: ConfirmationScope) => void
|
|
170
|
+
) => {
|
|
171
|
+
const otherScopes = SCOPES.filter(s => s !== selectedScope);
|
|
172
|
+
const menu = new GroupImpl('tool-confirmation-dropdown');
|
|
173
|
+
|
|
174
|
+
const scopesGroup = new GroupImpl('scopes', '1');
|
|
175
|
+
otherScopes.forEach((scope, index) => {
|
|
176
|
+
scopesGroup.addNode(new InlineActionMenuNode(
|
|
177
|
+
`tool-confirmation-${type}-${scope}`,
|
|
178
|
+
scopeLabel(type, scope),
|
|
179
|
+
() => setScope(scope),
|
|
180
|
+
String(index)
|
|
181
|
+
));
|
|
182
|
+
});
|
|
183
|
+
menu.addNode(scopesGroup);
|
|
184
|
+
|
|
185
|
+
if (type === 'deny') {
|
|
186
|
+
const reasonGroup = new GroupImpl('reason', '2');
|
|
187
|
+
reasonGroup.addNode(new InlineActionMenuNode(
|
|
188
|
+
'tool-confirmation-deny-with-reason',
|
|
189
|
+
nls.localize('theia/ai/chat-ui/toolconfirmation/deny-with-reason', 'Deny with reason...'),
|
|
190
|
+
handleDenyWithReason,
|
|
191
|
+
'0'
|
|
192
|
+
));
|
|
193
|
+
menu.addNode(reasonGroup);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const splitButtonContainer = event.currentTarget.parentElement;
|
|
197
|
+
const containerRect = splitButtonContainer?.getBoundingClientRect() ?? event.currentTarget.getBoundingClientRect();
|
|
198
|
+
contextMenuRenderer.render({
|
|
199
|
+
menuPath: ['tool-confirmation-context-menu'],
|
|
200
|
+
menu,
|
|
201
|
+
anchor: { x: containerRect.left, y: containerRect.bottom },
|
|
202
|
+
context: event.currentTarget,
|
|
203
|
+
skipSingleRootNode: true
|
|
204
|
+
});
|
|
205
|
+
}, [contextMenuRenderer, handleDenyWithReason, scopeLabel]);
|
|
206
|
+
|
|
207
|
+
const renderSplitButton = (type: 'allow' | 'deny'): React.ReactNode => {
|
|
208
|
+
const selectedScope = type === 'allow' ? allowScope : denyScope;
|
|
209
|
+
const setScope = type === 'allow' ? setAllowScope : setDenyScope;
|
|
111
210
|
const handleMain = type === 'allow' ? handleAllow : handleDeny;
|
|
112
|
-
|
|
211
|
+
|
|
113
212
|
return (
|
|
114
|
-
<div
|
|
115
|
-
|
|
213
|
+
<div
|
|
214
|
+
className={`theia-tool-confirmation-split-button ${type}`}
|
|
215
|
+
style={{ display: 'inline-flex', position: 'relative' }}
|
|
216
|
+
>
|
|
116
217
|
<button
|
|
117
|
-
className={`theia-button ${type === 'allow' ? '
|
|
218
|
+
className={`theia-button ${type === 'allow' ? 'main' : 'secondary'} theia-tool-confirmation-main-btn`}
|
|
118
219
|
onClick={handleMain}
|
|
119
220
|
>
|
|
120
|
-
{
|
|
221
|
+
{scopeLabel(type, selectedScope)}
|
|
121
222
|
</button>
|
|
122
223
|
<button
|
|
123
|
-
className={`theia-button ${type === 'allow' ? '
|
|
124
|
-
onClick={
|
|
224
|
+
className={`theia-button ${type === 'allow' ? 'main' : 'secondary'} theia-tool-confirmation-chevron-btn`}
|
|
225
|
+
onClick={e => showDropdownMenu(e, type, selectedScope, setScope)}
|
|
125
226
|
aria-haspopup="true"
|
|
126
|
-
aria-expanded={dropdownOpen === type}
|
|
127
227
|
tabIndex={0}
|
|
128
228
|
title={type === 'allow'
|
|
129
229
|
? nls.localize('theia/ai/chat-ui/toolconfirmation/allow-options-dropdown-tooltip', 'More Allow Options')
|
|
@@ -131,30 +231,112 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, on
|
|
|
131
231
|
>
|
|
132
232
|
<span className={codicon('chevron-down')}></span>
|
|
133
233
|
</button>
|
|
134
|
-
{dropdownOpen === type && (
|
|
135
|
-
<ul
|
|
136
|
-
className="theia-tool-confirmation-dropdown-menu"
|
|
137
|
-
onMouseLeave={() => setDropdownOpen(undefined)}
|
|
138
|
-
>
|
|
139
|
-
{otherModes.map(mode => (
|
|
140
|
-
<li
|
|
141
|
-
key={mode}
|
|
142
|
-
className="theia-tool-confirmation-dropdown-item"
|
|
143
|
-
onClick={() => {
|
|
144
|
-
setMode(mode);
|
|
145
|
-
setDropdownOpen(undefined);
|
|
146
|
-
}}
|
|
147
|
-
title={modeTooltip(type, mode)}
|
|
148
|
-
>
|
|
149
|
-
{modeLabel(type, mode)}
|
|
150
|
-
</li>
|
|
151
|
-
))}
|
|
152
|
-
</ul>
|
|
153
|
-
)}
|
|
154
234
|
</div>
|
|
155
235
|
);
|
|
156
236
|
};
|
|
157
237
|
|
|
238
|
+
if (showAlwaysAllowConfirmation) {
|
|
239
|
+
return (
|
|
240
|
+
<div className="theia-tool-confirmation-always-allow-modal">
|
|
241
|
+
<div className="theia-tool-confirmation-header">
|
|
242
|
+
<span className={codicon('warning')}></span>
|
|
243
|
+
{nls.localize('theia/ai/chat-ui/toolconfirmation/alwaysAllowTitle', 'Enable Auto-Approval for "{0}"?', toolName)}
|
|
244
|
+
</div>
|
|
245
|
+
<div className="theia-tool-confirmation-warning">
|
|
246
|
+
{getAlwaysAllowWarning()}
|
|
247
|
+
</div>
|
|
248
|
+
<div className="theia-tool-confirmation-actions">
|
|
249
|
+
<button
|
|
250
|
+
className="theia-button secondary"
|
|
251
|
+
onClick={handleCancelAlwaysAllow}
|
|
252
|
+
>
|
|
253
|
+
{nls.localizeByDefault('Cancel')}
|
|
254
|
+
</button>
|
|
255
|
+
<button
|
|
256
|
+
className="theia-button main"
|
|
257
|
+
onClick={handleConfirmAlwaysAllow}
|
|
258
|
+
>
|
|
259
|
+
{nls.localize('theia/ai/chat-ui/toolconfirmation/alwaysAllowConfirm', 'I understand, enable auto-approval')}
|
|
260
|
+
</button>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (showDenyReasonInput) {
|
|
267
|
+
return (
|
|
268
|
+
<div className="theia-tool-confirmation-deny-reason">
|
|
269
|
+
<input
|
|
270
|
+
ref={denyReasonInputRef}
|
|
271
|
+
type="text"
|
|
272
|
+
className="theia-input theia-tool-confirmation-deny-reason-input"
|
|
273
|
+
placeholder={nls.localize('theia/ai/chat-ui/toolconfirmation/deny-reason-placeholder', 'Enter reason for denial...')}
|
|
274
|
+
value={denyReason}
|
|
275
|
+
onChange={e => setDenyReason(e.target.value)}
|
|
276
|
+
onKeyDown={handleDenyReasonKeyDown}
|
|
277
|
+
/>
|
|
278
|
+
<div className="theia-tool-confirmation-deny-reason-actions">
|
|
279
|
+
<button
|
|
280
|
+
className="theia-button secondary"
|
|
281
|
+
onClick={handleCancelDenyReason}
|
|
282
|
+
>
|
|
283
|
+
{nls.localizeByDefault('Cancel')}
|
|
284
|
+
</button>
|
|
285
|
+
<button
|
|
286
|
+
className="theia-button main"
|
|
287
|
+
onClick={handleSubmitDenyReason}
|
|
288
|
+
>
|
|
289
|
+
{nls.localizeByDefault('Deny')}
|
|
290
|
+
</button>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<div className="theia-tool-confirmation-actions">
|
|
298
|
+
{renderSplitButton('deny')}
|
|
299
|
+
{renderSplitButton('allow')}
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export interface ToolConfirmationProps extends Pick<ToolConfirmationCallbacks, 'toolRequest'> {
|
|
305
|
+
response: ToolCallChatResponseContent;
|
|
306
|
+
onAllow: (scope?: ConfirmationScope) => void;
|
|
307
|
+
onDeny: (scope?: ConfirmationScope, reason?: string) => void;
|
|
308
|
+
contextMenuRenderer: ContextMenuRenderer;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, toolRequest, onAllow, onDeny, contextMenuRenderer }) => {
|
|
312
|
+
const [state, setState] = React.useState<ToolConfirmationState>('waiting');
|
|
313
|
+
|
|
314
|
+
const handleAllow = React.useCallback((scope: ConfirmationScope) => {
|
|
315
|
+
setState('allowed');
|
|
316
|
+
onAllow(scope);
|
|
317
|
+
}, [onAllow]);
|
|
318
|
+
|
|
319
|
+
const handleDeny = React.useCallback((scope: ConfirmationScope, reason?: string) => {
|
|
320
|
+
setState('denied');
|
|
321
|
+
onDeny(scope, reason);
|
|
322
|
+
}, [onDeny]);
|
|
323
|
+
|
|
324
|
+
if (state === 'allowed') {
|
|
325
|
+
return (
|
|
326
|
+
<div className="theia-tool-confirmation-status allowed">
|
|
327
|
+
<span className={codicon('check')}></span> {nls.localize('theia/ai/chat-ui/toolconfirmation/allowed', 'Tool execution allowed')}
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (state === 'denied') {
|
|
333
|
+
return (
|
|
334
|
+
<div className="theia-tool-confirmation-status denied">
|
|
335
|
+
<span className={codicon('close')}></span> {nls.localize('theia/ai/chat-ui/toolconfirmation/denied', 'Tool execution denied')}
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
158
340
|
return (
|
|
159
341
|
<div className="theia-tool-confirmation">
|
|
160
342
|
<div className="theia-tool-confirmation-header">
|
|
@@ -166,10 +348,112 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({ response, on
|
|
|
166
348
|
<span className="value">{response.name}</span>
|
|
167
349
|
</div>
|
|
168
350
|
</div>
|
|
169
|
-
<
|
|
170
|
-
{
|
|
171
|
-
{
|
|
172
|
-
|
|
351
|
+
<ToolConfirmationActions
|
|
352
|
+
toolName={response.name ?? 'unknown'}
|
|
353
|
+
toolRequest={toolRequest}
|
|
354
|
+
onAllow={handleAllow}
|
|
355
|
+
onDeny={handleDeny}
|
|
356
|
+
contextMenuRenderer={contextMenuRenderer}
|
|
357
|
+
/>
|
|
173
358
|
</div>
|
|
174
359
|
);
|
|
175
360
|
};
|
|
361
|
+
|
|
362
|
+
export interface WithToolCallConfirmationProps {
|
|
363
|
+
response: ToolCallChatResponseContent;
|
|
364
|
+
confirmationMode: ToolConfirmationPreferenceMode;
|
|
365
|
+
toolConfirmationManager: ToolConfirmationManager;
|
|
366
|
+
toolRequest?: ToolRequest;
|
|
367
|
+
chatId: string;
|
|
368
|
+
requestCanceled: boolean;
|
|
369
|
+
contextMenuRenderer: ContextMenuRenderer;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function withToolCallConfirmation<P extends object>(
|
|
373
|
+
WrappedComponent: React.ComponentType<P>
|
|
374
|
+
): React.FC<P & WithToolCallConfirmationProps> {
|
|
375
|
+
const WithConfirmation: React.FC<P & WithToolCallConfirmationProps> = props => {
|
|
376
|
+
const {
|
|
377
|
+
response,
|
|
378
|
+
confirmationMode,
|
|
379
|
+
toolConfirmationManager,
|
|
380
|
+
toolRequest,
|
|
381
|
+
chatId,
|
|
382
|
+
requestCanceled,
|
|
383
|
+
contextMenuRenderer,
|
|
384
|
+
...componentProps
|
|
385
|
+
} = props;
|
|
386
|
+
|
|
387
|
+
const [confirmationState, setConfirmationState] = React.useState<ToolConfirmationState>('waiting');
|
|
388
|
+
|
|
389
|
+
React.useEffect(() => {
|
|
390
|
+
if (confirmationMode === ToolConfirmationPreferenceMode.ALWAYS_ALLOW) {
|
|
391
|
+
response.confirm();
|
|
392
|
+
setConfirmationState('allowed');
|
|
393
|
+
return;
|
|
394
|
+
} else if (confirmationMode === ToolConfirmationPreferenceMode.DISABLED) {
|
|
395
|
+
response.deny();
|
|
396
|
+
setConfirmationState('denied');
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
response.confirmed
|
|
400
|
+
.then(confirmed => {
|
|
401
|
+
setConfirmationState(confirmed === true ? 'allowed' : 'denied');
|
|
402
|
+
})
|
|
403
|
+
.catch(() => {
|
|
404
|
+
setConfirmationState('rejected');
|
|
405
|
+
});
|
|
406
|
+
}, [response, confirmationMode]);
|
|
407
|
+
|
|
408
|
+
const handleAllow = React.useCallback((scope: ConfirmationScope = 'once') => {
|
|
409
|
+
if (scope === 'forever' && response.name) {
|
|
410
|
+
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationPreferenceMode.ALWAYS_ALLOW, toolRequest);
|
|
411
|
+
} else if (scope === 'session' && response.name) {
|
|
412
|
+
toolConfirmationManager.setSessionConfirmationMode(response.name, ToolConfirmationPreferenceMode.ALWAYS_ALLOW, chatId);
|
|
413
|
+
}
|
|
414
|
+
response.confirm();
|
|
415
|
+
}, [response, toolConfirmationManager, chatId, toolRequest]);
|
|
416
|
+
|
|
417
|
+
const handleDeny = React.useCallback((scope: ConfirmationScope = 'once', reason?: string) => {
|
|
418
|
+
if (scope === 'forever' && response.name) {
|
|
419
|
+
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationPreferenceMode.DISABLED);
|
|
420
|
+
} else if (scope === 'session' && response.name) {
|
|
421
|
+
toolConfirmationManager.setSessionConfirmationMode(response.name, ToolConfirmationPreferenceMode.DISABLED, chatId);
|
|
422
|
+
}
|
|
423
|
+
response.deny(reason);
|
|
424
|
+
}, [response, toolConfirmationManager, chatId]);
|
|
425
|
+
|
|
426
|
+
if (confirmationState === 'rejected' || (requestCanceled && !response.finished)) {
|
|
427
|
+
return (
|
|
428
|
+
<div className="theia-tool-confirmation-status rejected">
|
|
429
|
+
<span className={codicon('error')}></span> {nls.localize('theia/ai/chat-ui/toolconfirmation/canceled', 'Tool execution canceled')}
|
|
430
|
+
</div>
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (confirmationState === 'denied') {
|
|
435
|
+
return (
|
|
436
|
+
<div className="theia-tool-confirmation-status denied">
|
|
437
|
+
<span className={codicon('error')}></span> {nls.localize('theia/ai/chat-ui/toolconfirmation/executionDenied', 'Tool execution denied')}
|
|
438
|
+
</div>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (confirmationState === 'waiting' && !requestCanceled && !response.finished) {
|
|
443
|
+
return (
|
|
444
|
+
<ToolConfirmation
|
|
445
|
+
response={response}
|
|
446
|
+
toolRequest={toolRequest}
|
|
447
|
+
onAllow={handleAllow}
|
|
448
|
+
onDeny={handleDeny}
|
|
449
|
+
contextMenuRenderer={contextMenuRenderer}
|
|
450
|
+
/>
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return <WrappedComponent {...componentProps as P} />;
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
WithConfirmation.displayName = `withToolCallConfirmation(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
|
|
458
|
+
return WithConfirmation;
|
|
459
|
+
}
|
|
@@ -19,13 +19,13 @@ import { inject, injectable } from '@theia/core/shared/inversify';
|
|
|
19
19
|
import { ChatResponseContent, ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
|
|
20
20
|
import { ReactNode } from '@theia/core/shared/react';
|
|
21
21
|
import { nls } from '@theia/core/lib/common/nls';
|
|
22
|
-
import { codicon, OpenerService } from '@theia/core/lib/browser';
|
|
22
|
+
import { codicon, ContextMenuRenderer, OpenerService } from '@theia/core/lib/browser';
|
|
23
23
|
import * as React from '@theia/core/shared/react';
|
|
24
24
|
import { ToolConfirmation, ToolConfirmationState } from './tool-confirmation';
|
|
25
25
|
import { ToolConfirmationMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
|
|
26
26
|
import { ResponseNode } from '../chat-tree-view';
|
|
27
27
|
import { useMarkdownRendering } from './markdown-part-renderer';
|
|
28
|
-
import { ToolCallResult } from '@theia/ai-core';
|
|
28
|
+
import { ToolCallResult, ToolInvocationRegistry, ToolRequest } from '@theia/ai-core';
|
|
29
29
|
import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
|
|
30
30
|
|
|
31
31
|
@injectable()
|
|
@@ -37,6 +37,12 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
|
|
|
37
37
|
@inject(OpenerService)
|
|
38
38
|
protected openerService: OpenerService;
|
|
39
39
|
|
|
40
|
+
@inject(ToolInvocationRegistry)
|
|
41
|
+
protected toolInvocationRegistry: ToolInvocationRegistry;
|
|
42
|
+
|
|
43
|
+
@inject(ContextMenuRenderer)
|
|
44
|
+
protected contextMenuRenderer: ContextMenuRenderer;
|
|
45
|
+
|
|
40
46
|
canHandle(response: ChatResponseContent): number {
|
|
41
47
|
if (ToolCallChatResponseContent.is(response)) {
|
|
42
48
|
return 10;
|
|
@@ -46,15 +52,18 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
|
|
|
46
52
|
|
|
47
53
|
render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
48
54
|
const chatId = parentNode.sessionId;
|
|
49
|
-
const
|
|
55
|
+
const toolRequest = response.name ? this.toolInvocationRegistry.getFunction(response.name) : undefined;
|
|
56
|
+
const confirmationMode = response.name ? this.getToolConfirmationSettings(response.name, chatId, toolRequest) : ToolConfirmationMode.DISABLED;
|
|
50
57
|
return <ToolCallContent
|
|
51
58
|
response={response}
|
|
52
59
|
confirmationMode={confirmationMode}
|
|
53
60
|
toolConfirmationManager={this.toolConfirmationManager}
|
|
61
|
+
toolRequest={toolRequest}
|
|
54
62
|
chatId={chatId}
|
|
55
63
|
renderCollapsibleArguments={this.renderCollapsibleArguments.bind(this)}
|
|
56
64
|
responseRenderer={this.renderResult.bind(this)}
|
|
57
|
-
requestCanceled={parentNode.response.isCanceled}
|
|
65
|
+
requestCanceled={parentNode.response.isCanceled}
|
|
66
|
+
contextMenuRenderer={this.contextMenuRenderer} />;
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
protected renderResult(response: ToolCallChatResponseContent): ReactNode {
|
|
@@ -102,8 +111,8 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer<ToolCallCh
|
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
protected getToolConfirmationSettings(responseId: string, chatId: string): ToolConfirmationMode {
|
|
106
|
-
return this.toolConfirmationManager.getConfirmationMode(responseId, chatId);
|
|
114
|
+
protected getToolConfirmationSettings(responseId: string, chatId: string, toolRequest?: ToolRequest): ToolConfirmationMode {
|
|
115
|
+
return this.toolConfirmationManager.getConfirmationMode(responseId, chatId, toolRequest);
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
protected renderCollapsibleArguments(args: string | undefined): ReactNode {
|
|
@@ -137,10 +146,12 @@ interface ToolCallContentProps {
|
|
|
137
146
|
response: ToolCallChatResponseContent;
|
|
138
147
|
confirmationMode: ToolConfirmationMode;
|
|
139
148
|
toolConfirmationManager: ToolConfirmationManager;
|
|
149
|
+
toolRequest?: ToolRequest;
|
|
140
150
|
chatId: string;
|
|
141
151
|
renderCollapsibleArguments: (args: string | undefined) => ReactNode;
|
|
142
152
|
responseRenderer: (response: ToolCallChatResponseContent) => ReactNode | undefined;
|
|
143
153
|
requestCanceled: boolean;
|
|
154
|
+
contextMenuRenderer: ContextMenuRenderer;
|
|
144
155
|
}
|
|
145
156
|
|
|
146
157
|
/**
|
|
@@ -150,10 +161,12 @@ const ToolCallContent: React.FC<ToolCallContentProps> = ({
|
|
|
150
161
|
response,
|
|
151
162
|
confirmationMode,
|
|
152
163
|
toolConfirmationManager,
|
|
164
|
+
toolRequest,
|
|
153
165
|
chatId,
|
|
154
166
|
responseRenderer,
|
|
155
167
|
renderCollapsibleArguments,
|
|
156
|
-
requestCanceled
|
|
168
|
+
requestCanceled,
|
|
169
|
+
contextMenuRenderer
|
|
157
170
|
}) => {
|
|
158
171
|
const [confirmationState, setConfirmationState] = React.useState<ToolConfirmationState>('waiting');
|
|
159
172
|
const [rejectionReason, setRejectionReason] = React.useState<unknown>(undefined);
|
|
@@ -201,20 +214,20 @@ const ToolCallContent: React.FC<ToolCallContentProps> = ({
|
|
|
201
214
|
|
|
202
215
|
const handleAllow = React.useCallback((mode: 'once' | 'session' | 'forever' = 'once') => {
|
|
203
216
|
if (mode === 'forever' && response.name) {
|
|
204
|
-
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationMode.ALWAYS_ALLOW);
|
|
217
|
+
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationMode.ALWAYS_ALLOW, toolRequest);
|
|
205
218
|
} else if (mode === 'session' && response.name) {
|
|
206
219
|
toolConfirmationManager.setSessionConfirmationMode(response.name, ToolConfirmationMode.ALWAYS_ALLOW, chatId);
|
|
207
220
|
}
|
|
208
221
|
response.confirm();
|
|
209
|
-
}, [response, toolConfirmationManager, chatId]);
|
|
222
|
+
}, [response, toolConfirmationManager, chatId, toolRequest]);
|
|
210
223
|
|
|
211
|
-
const handleDeny = React.useCallback((mode: 'once' | 'session' | 'forever' = 'once') => {
|
|
224
|
+
const handleDeny = React.useCallback((mode: 'once' | 'session' | 'forever' = 'once', reason?: string) => {
|
|
212
225
|
if (mode === 'forever' && response.name) {
|
|
213
226
|
toolConfirmationManager.setConfirmationMode(response.name, ToolConfirmationMode.DISABLED);
|
|
214
227
|
} else if (mode === 'session' && response.name) {
|
|
215
228
|
toolConfirmationManager.setSessionConfirmationMode(response.name, ToolConfirmationMode.DISABLED, chatId);
|
|
216
229
|
}
|
|
217
|
-
response.deny();
|
|
230
|
+
response.deny(reason);
|
|
218
231
|
}, [response, toolConfirmationManager, chatId]);
|
|
219
232
|
|
|
220
233
|
const reasonText = formatReason(rejectionReason);
|
|
@@ -252,13 +265,14 @@ const ToolCallContent: React.FC<ToolCallContentProps> = ({
|
|
|
252
265
|
)
|
|
253
266
|
)}
|
|
254
267
|
|
|
255
|
-
{
|
|
256
|
-
{confirmationState === 'waiting' && !requestCanceled && (
|
|
268
|
+
{confirmationState === 'waiting' && !requestCanceled && !response.finished && (
|
|
257
269
|
<span className='theia-toolCall-waiting'>
|
|
258
270
|
<ToolConfirmation
|
|
259
271
|
response={response}
|
|
272
|
+
toolRequest={toolRequest}
|
|
260
273
|
onAllow={handleAllow}
|
|
261
274
|
onDeny={handleDeny}
|
|
275
|
+
contextMenuRenderer={contextMenuRenderer}
|
|
262
276
|
/>
|
|
263
277
|
</span>
|
|
264
278
|
)}
|