@theia/ai-chat 1.63.0-next.0 → 1.63.0-next.52
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/lib/browser/agent-delegation-tool.d.ts +25 -0
- package/lib/browser/agent-delegation-tool.d.ts.map +1 -0
- package/lib/browser/agent-delegation-tool.js +171 -0
- package/lib/browser/agent-delegation-tool.js.map +1 -0
- package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-frontend-module.js +15 -1
- package/lib/browser/ai-chat-frontend-module.js.map +1 -1
- package/lib/browser/change-set-file-element.d.ts +28 -7
- package/lib/browser/change-set-file-element.d.ts.map +1 -1
- package/lib/browser/change-set-file-element.js +86 -18
- package/lib/browser/change-set-file-element.js.map +1 -1
- package/lib/browser/change-set-variable.js +1 -2
- package/lib/browser/change-set-variable.js.map +1 -1
- package/lib/browser/chat-tool-preferences.d.ts +54 -0
- package/lib/browser/chat-tool-preferences.d.ts.map +1 -0
- package/lib/browser/chat-tool-preferences.js +170 -0
- package/lib/browser/chat-tool-preferences.js.map +1 -0
- package/lib/browser/chat-tool-request-service.d.ts +20 -0
- package/lib/browser/chat-tool-request-service.d.ts.map +1 -0
- package/lib/browser/chat-tool-request-service.js +89 -0
- package/lib/browser/chat-tool-request-service.js.map +1 -0
- package/lib/browser/delegation-response-content.d.ts +20 -0
- package/lib/browser/delegation-response-content.d.ts.map +1 -0
- package/lib/browser/delegation-response-content.js +51 -0
- package/lib/browser/delegation-response-content.js.map +1 -0
- package/lib/browser/file-chat-variable-contribution.d.ts +15 -1
- package/lib/browser/file-chat-variable-contribution.d.ts.map +1 -1
- package/lib/browser/file-chat-variable-contribution.js +111 -5
- package/lib/browser/file-chat-variable-contribution.js.map +1 -1
- package/lib/browser/frontend-chat-service.d.ts.map +1 -1
- package/lib/browser/frontend-chat-service.js +2 -6
- package/lib/browser/frontend-chat-service.js.map +1 -1
- package/lib/browser/image-context-variable-contribution.d.ts +27 -0
- package/lib/browser/image-context-variable-contribution.d.ts.map +1 -0
- package/lib/browser/image-context-variable-contribution.js +149 -0
- package/lib/browser/image-context-variable-contribution.js.map +1 -0
- package/lib/browser/task-context-service.d.ts +9 -3
- package/lib/browser/task-context-service.d.ts.map +1 -1
- package/lib/browser/task-context-service.js +111 -9
- package/lib/browser/task-context-service.js.map +1 -1
- package/lib/browser/task-context-storage-service.d.ts +1 -0
- package/lib/browser/task-context-storage-service.d.ts.map +1 -1
- package/lib/browser/task-context-storage-service.js +4 -1
- package/lib/browser/task-context-storage-service.js.map +1 -1
- package/lib/common/change-set.d.ts +78 -0
- package/lib/common/change-set.d.ts.map +1 -0
- package/lib/common/change-set.js +133 -0
- package/lib/common/change-set.js.map +1 -0
- package/lib/common/chat-agent-service.d.ts +1 -0
- package/lib/common/chat-agent-service.d.ts.map +1 -1
- package/lib/common/chat-agent-service.js +2 -1
- package/lib/common/chat-agent-service.js.map +1 -1
- package/lib/common/chat-agents.d.ts +2 -2
- package/lib/common/chat-agents.d.ts.map +1 -1
- package/lib/common/chat-agents.js +25 -6
- package/lib/common/chat-agents.js.map +1 -1
- package/lib/common/chat-model.d.ts +68 -80
- package/lib/common/chat-model.d.ts.map +1 -1
- package/lib/common/chat-model.js +224 -136
- package/lib/common/chat-model.js.map +1 -1
- package/lib/common/chat-request-parser.d.ts.map +1 -1
- package/lib/common/chat-request-parser.js +3 -0
- package/lib/common/chat-request-parser.js.map +1 -1
- package/lib/common/chat-service.d.ts +6 -5
- package/lib/common/chat-service.d.ts.map +1 -1
- package/lib/common/chat-service.js +9 -11
- package/lib/common/chat-service.js.map +1 -1
- package/lib/common/image-context-variable.d.ts +29 -0
- package/lib/common/image-context-variable.d.ts.map +1 -0
- package/lib/common/image-context-variable.js +99 -0
- package/lib/common/image-context-variable.js.map +1 -0
- package/package.json +10 -9
- package/src/browser/agent-delegation-tool.ts +207 -0
- package/src/browser/ai-chat-frontend-module.ts +28 -3
- package/src/browser/change-set-file-element.ts +97 -25
- package/src/browser/change-set-variable.ts +1 -1
- package/src/browser/chat-tool-preferences.ts +178 -0
- package/src/browser/chat-tool-request-service.ts +93 -0
- package/src/browser/delegation-response-content.ts +55 -0
- package/src/browser/file-chat-variable-contribution.ts +120 -6
- package/src/browser/frontend-chat-service.ts +3 -6
- package/src/browser/image-context-variable-contribution.ts +153 -0
- package/src/browser/task-context-service.ts +115 -9
- package/src/browser/task-context-storage-service.ts +5 -1
- package/src/common/change-set.ts +197 -0
- package/src/common/chat-agent-service.ts +1 -0
- package/src/common/chat-agents.ts +40 -19
- package/src/common/chat-model.ts +258 -208
- package/src/common/chat-request-parser.ts +3 -0
- package/src/common/chat-service.ts +11 -13
- package/src/common/image-context-variable.ts +116 -0
package/src/common/chat-model.ts
CHANGED
|
@@ -19,12 +19,23 @@
|
|
|
19
19
|
*--------------------------------------------------------------------------------------------*/
|
|
20
20
|
// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatModel.ts
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
|
|
22
|
+
import {
|
|
23
|
+
AIVariableResolutionRequest,
|
|
24
|
+
LanguageModelMessage,
|
|
25
|
+
ResolvedAIContextVariable,
|
|
26
|
+
TextMessage,
|
|
27
|
+
ThinkingMessage,
|
|
28
|
+
ToolResultMessage,
|
|
29
|
+
ToolUseMessage
|
|
30
|
+
} from '@theia/ai-core';
|
|
31
|
+
import { ArrayUtils, CancellationToken, CancellationTokenSource, Command, Disposable, DisposableCollection, Emitter, Event, generateUuid, URI } from '@theia/core';
|
|
24
32
|
import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
|
|
25
33
|
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
34
|
+
import { ChangeSet, ChangeSetElement, ChangeSetImpl, ChatUpdateChangeSetEvent } from './change-set';
|
|
26
35
|
import { ChatAgentLocation } from './chat-agents';
|
|
27
36
|
import { ParsedChatRequest } from './parsed-chat-request';
|
|
37
|
+
import debounce = require('@theia/core/shared/lodash.debounce');
|
|
38
|
+
export { ChangeSet, ChangeSetElement, ChangeSetImpl };
|
|
28
39
|
|
|
29
40
|
/**********************
|
|
30
41
|
* INTERFACES AND TYPE GUARDS
|
|
@@ -37,10 +48,8 @@ export type ChatChangeEvent =
|
|
|
37
48
|
| ChatRemoveVariableEvent
|
|
38
49
|
| ChatSetVariablesEvent
|
|
39
50
|
| ChatRemoveRequestEvent
|
|
40
|
-
| ChatSetChangeSetEvent
|
|
41
51
|
| ChatSuggestionsChangedEvent
|
|
42
52
|
| ChatUpdateChangeSetEvent
|
|
43
|
-
| ChatRemoveChangeSetEvent
|
|
44
53
|
| ChatEditRequestEvent
|
|
45
54
|
| ChatEditCancelEvent
|
|
46
55
|
| ChatEditSubmitEvent
|
|
@@ -80,22 +89,6 @@ export interface ChatAddResponseEvent {
|
|
|
80
89
|
response: ChatResponseModel;
|
|
81
90
|
}
|
|
82
91
|
|
|
83
|
-
export interface ChatSetChangeSetEvent {
|
|
84
|
-
kind: 'setChangeSet';
|
|
85
|
-
changeSet: ChangeSet;
|
|
86
|
-
oldChangeSet?: ChangeSet;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface ChatUpdateChangeSetEvent {
|
|
90
|
-
kind: 'updateChangeSet';
|
|
91
|
-
changeSet: ChangeSet;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface ChatRemoveChangeSetEvent {
|
|
95
|
-
kind: 'removeChangeSet';
|
|
96
|
-
changeSet: ChangeSet;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
92
|
export interface ChatAddVariableEvent {
|
|
100
93
|
kind: 'addVariable';
|
|
101
94
|
}
|
|
@@ -114,8 +107,8 @@ export interface ChatSuggestionsChangedEvent {
|
|
|
114
107
|
}
|
|
115
108
|
|
|
116
109
|
export namespace ChatChangeEvent {
|
|
117
|
-
export function isChangeSetEvent(event: ChatChangeEvent): event is
|
|
118
|
-
return event.kind === '
|
|
110
|
+
export function isChangeSetEvent(event: ChatChangeEvent): event is ChatUpdateChangeSetEvent {
|
|
111
|
+
return event.kind === 'updateChangeSet';
|
|
119
112
|
}
|
|
120
113
|
}
|
|
121
114
|
|
|
@@ -136,7 +129,7 @@ export interface ChatRemoveRequestEvent {
|
|
|
136
129
|
* - Within each branch, the requests are stored in a list. Those requests are the alternatives to the original request.
|
|
137
130
|
* Each of those items can have a next branch, which is the next request in the hierarchy.
|
|
138
131
|
*/
|
|
139
|
-
export interface ChatRequestHierarchy<TRequest extends ChatRequestModel = ChatRequestModel> {
|
|
132
|
+
export interface ChatRequestHierarchy<TRequest extends ChatRequestModel = ChatRequestModel> extends Disposable {
|
|
140
133
|
readonly branch: ChatHierarchyBranch<TRequest>
|
|
141
134
|
|
|
142
135
|
onDidChange: Event<ChangeActiveBranchEvent<TRequest>>;
|
|
@@ -160,7 +153,7 @@ export interface ChangeActiveBranchEvent<TRequest extends ChatRequestModel = Cha
|
|
|
160
153
|
* It contains a list of items, each representing a request.
|
|
161
154
|
* Those items can have a next branch, which is the next request in the hierarchy.
|
|
162
155
|
*/
|
|
163
|
-
export interface ChatHierarchyBranch<TRequest extends ChatRequestModel = ChatRequestModel> {
|
|
156
|
+
export interface ChatHierarchyBranch<TRequest extends ChatRequestModel = ChatRequestModel> extends Disposable {
|
|
164
157
|
readonly id: string;
|
|
165
158
|
readonly hierarchy: ChatRequestHierarchy<TRequest>;
|
|
166
159
|
readonly previous?: ChatHierarchyBranch<TRequest>;
|
|
@@ -192,28 +185,15 @@ export interface ChatModel {
|
|
|
192
185
|
readonly onDidChange: Event<ChatChangeEvent>;
|
|
193
186
|
readonly id: string;
|
|
194
187
|
readonly location: ChatAgentLocation;
|
|
195
|
-
readonly changeSet?: ChangeSet;
|
|
196
188
|
readonly context: ChatContextManager;
|
|
197
189
|
readonly suggestions: readonly ChatSuggestion[];
|
|
198
190
|
readonly settings?: { [key: string]: unknown };
|
|
191
|
+
readonly changeSet: ChangeSet;
|
|
199
192
|
getRequests(): ChatRequestModel[];
|
|
200
193
|
getBranches(): ChatHierarchyBranch<ChatRequestModel>[];
|
|
201
194
|
isEmpty(): boolean;
|
|
202
195
|
}
|
|
203
196
|
|
|
204
|
-
export interface ChangeSet extends Disposable {
|
|
205
|
-
onDidChange: Event<ChangeSetChangeEvent>;
|
|
206
|
-
readonly title: string;
|
|
207
|
-
getElements(): ChangeSetElement[];
|
|
208
|
-
/**
|
|
209
|
-
* Find an element by URI.
|
|
210
|
-
* @param uri The URI to look for.
|
|
211
|
-
* @returns The element with the given URI, or undefined if not found.
|
|
212
|
-
*/
|
|
213
|
-
getElementByURI(uri: URI): ChangeSetElement | undefined;
|
|
214
|
-
dispose(): void;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
197
|
export interface ChatSuggestionCallback {
|
|
218
198
|
kind: 'callback',
|
|
219
199
|
callback: () => unknown;
|
|
@@ -240,25 +220,6 @@ export interface ChatContextManager {
|
|
|
240
220
|
clear(): void;
|
|
241
221
|
}
|
|
242
222
|
|
|
243
|
-
export interface ChangeSetElement {
|
|
244
|
-
readonly uri: URI;
|
|
245
|
-
|
|
246
|
-
onDidChange?: Event<void>
|
|
247
|
-
readonly name?: string;
|
|
248
|
-
readonly icon?: string;
|
|
249
|
-
readonly additionalInfo?: string;
|
|
250
|
-
|
|
251
|
-
readonly state?: 'pending' | 'applied' | 'stale';
|
|
252
|
-
readonly type?: 'add' | 'modify' | 'delete';
|
|
253
|
-
readonly data?: { [key: string]: unknown };
|
|
254
|
-
|
|
255
|
-
open?(): Promise<void>;
|
|
256
|
-
openChange?(): Promise<void>;
|
|
257
|
-
apply?(): Promise<void>;
|
|
258
|
-
revert?(): Promise<void>;
|
|
259
|
-
dispose?(): void;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
223
|
export interface ChangeSetDecoration {
|
|
263
224
|
readonly priority?: number;
|
|
264
225
|
readonly additionalInfoSuffixIcon?: string[];
|
|
@@ -429,6 +390,9 @@ export interface ToolCallChatResponseContent extends Required<ChatResponseConten
|
|
|
429
390
|
arguments?: string;
|
|
430
391
|
finished: boolean;
|
|
431
392
|
result?: string;
|
|
393
|
+
confirmed: Promise<boolean>;
|
|
394
|
+
confirm(): void;
|
|
395
|
+
deny(): void;
|
|
432
396
|
}
|
|
433
397
|
|
|
434
398
|
export interface ThinkingChatResponseContent
|
|
@@ -700,26 +664,39 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
700
664
|
|
|
701
665
|
protected _hierarchy: ChatRequestHierarchy<MutableChatRequestModel>;
|
|
702
666
|
protected _id: string;
|
|
703
|
-
protected _changeSet?: ChangeSetImpl;
|
|
704
667
|
protected _suggestions: readonly ChatSuggestion[] = [];
|
|
705
668
|
protected readonly _contextManager = new ChatContextManagerImpl();
|
|
669
|
+
protected readonly _changeSet: ChatTreeChangeSet;
|
|
706
670
|
protected _settings: { [key: string]: unknown };
|
|
707
671
|
|
|
708
672
|
constructor(public readonly location = ChatAgentLocation.Panel) {
|
|
709
673
|
// TODO accept serialized data as a parameter to restore a previously saved ChatModel
|
|
710
674
|
this._hierarchy = new ChatRequestHierarchyImpl<MutableChatRequestModel>();
|
|
675
|
+
this._changeSet = new ChatTreeChangeSet(this._hierarchy);
|
|
676
|
+
this.toDispose.push(this._changeSet);
|
|
677
|
+
this._changeSet.onDidChange(this._onDidChangeEmitter.fire, this._onDidChangeEmitter, this.toDispose);
|
|
711
678
|
this._id = generateUuid();
|
|
712
679
|
|
|
713
680
|
this.toDispose.pushAll([
|
|
714
681
|
this._onDidChangeEmitter,
|
|
715
|
-
this._contextManager.onDidChange(
|
|
716
|
-
this._hierarchy.onDidChange(event =>
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
682
|
+
this._contextManager.onDidChange(this._onDidChangeEmitter.fire, this._onDidChangeEmitter),
|
|
683
|
+
this._hierarchy.onDidChange(event => {
|
|
684
|
+
this._onDidChangeEmitter.fire({
|
|
685
|
+
kind: 'changeHierarchyBranch',
|
|
686
|
+
branch: event.branch,
|
|
687
|
+
});
|
|
688
|
+
}),
|
|
720
689
|
]);
|
|
721
690
|
}
|
|
722
691
|
|
|
692
|
+
get id(): string {
|
|
693
|
+
return this._id;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
get changeSet(): ChangeSet {
|
|
697
|
+
return this._changeSet;
|
|
698
|
+
}
|
|
699
|
+
|
|
723
700
|
getBranches(): ChatHierarchyBranch<ChatRequestModel>[] {
|
|
724
701
|
return this._hierarchy.activeBranches();
|
|
725
702
|
}
|
|
@@ -736,14 +713,6 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
736
713
|
return this.getRequests().find(request => request.id === id);
|
|
737
714
|
}
|
|
738
715
|
|
|
739
|
-
get id(): string {
|
|
740
|
-
return this._id;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
get changeSet(): ChangeSetImpl | undefined {
|
|
744
|
-
return this._changeSet;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
716
|
get suggestions(): readonly ChatSuggestion[] {
|
|
748
717
|
return this._suggestions;
|
|
749
718
|
}
|
|
@@ -760,46 +729,18 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
760
729
|
this._settings = settings;
|
|
761
730
|
}
|
|
762
731
|
|
|
763
|
-
setChangeSet(changeSet: ChangeSetImpl | undefined): void {
|
|
764
|
-
if (!changeSet) {
|
|
765
|
-
return this.removeChangeSet();
|
|
766
|
-
}
|
|
767
|
-
const oldChangeSet = this._changeSet;
|
|
768
|
-
oldChangeSet?.dispose();
|
|
769
|
-
this._changeSet = changeSet;
|
|
770
|
-
this._onDidChangeEmitter.fire({
|
|
771
|
-
kind: 'setChangeSet',
|
|
772
|
-
changeSet,
|
|
773
|
-
oldChangeSet,
|
|
774
|
-
});
|
|
775
|
-
changeSet.onDidChange(() => {
|
|
776
|
-
this._onDidChangeEmitter.fire({
|
|
777
|
-
kind: 'updateChangeSet',
|
|
778
|
-
changeSet,
|
|
779
|
-
});
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
removeChangeSet(): void {
|
|
784
|
-
if (this._changeSet) {
|
|
785
|
-
const oldChangeSet = this._changeSet;
|
|
786
|
-
this._changeSet = undefined;
|
|
787
|
-
oldChangeSet.dispose();
|
|
788
|
-
this._onDidChangeEmitter.fire({
|
|
789
|
-
kind: 'removeChangeSet',
|
|
790
|
-
changeSet: oldChangeSet,
|
|
791
|
-
});
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
732
|
addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string, context: ChatContext = { variables: [] }): MutableChatRequestModel {
|
|
796
|
-
|
|
797
|
-
return this.applyEdit(parsedChatRequest, agentId, context);
|
|
798
|
-
}
|
|
799
|
-
|
|
733
|
+
const add = this.getTargetForRequestAddition(parsedChatRequest);
|
|
800
734
|
const requestModel = new MutableChatRequestModel(this, parsedChatRequest, agentId, context);
|
|
801
|
-
|
|
802
|
-
|
|
735
|
+
requestModel.onDidChange(event => {
|
|
736
|
+
if (!ChatChangeEvent.isChangeSetEvent(event)) {
|
|
737
|
+
this._onDidChangeEmitter.fire(event);
|
|
738
|
+
}
|
|
739
|
+
}, this, this.toDispose);
|
|
740
|
+
|
|
741
|
+
add(requestModel);
|
|
742
|
+
this._changeSet.registerRequest(requestModel);
|
|
743
|
+
|
|
803
744
|
this._onDidChangeEmitter.fire({
|
|
804
745
|
kind: 'addRequest',
|
|
805
746
|
request: requestModel,
|
|
@@ -807,6 +748,13 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
807
748
|
return requestModel;
|
|
808
749
|
}
|
|
809
750
|
|
|
751
|
+
protected getTargetForRequestAddition(request: ParsedChatRequest): (addendum: MutableChatRequestModel) => void {
|
|
752
|
+
const requestId = request.request.referencedRequestId;
|
|
753
|
+
const branch = requestId !== undefined && this._hierarchy.findBranch(requestId);
|
|
754
|
+
if (requestId !== undefined && !branch) { throw new Error(`Cannot find branch for requestId: ${requestId}`); }
|
|
755
|
+
return branch ? branch.add.bind(branch) : this._hierarchy.append.bind(this._hierarchy);
|
|
756
|
+
}
|
|
757
|
+
|
|
810
758
|
setSuggestions(suggestions: ChatSuggestion[]): void {
|
|
811
759
|
this._suggestions = Object.freeze(suggestions);
|
|
812
760
|
this._onDidChangeEmitter.fire({
|
|
@@ -819,33 +767,132 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
819
767
|
return this.getRequests().length === 0;
|
|
820
768
|
}
|
|
821
769
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
770
|
+
dispose(): void {
|
|
771
|
+
this.toDispose.dispose();
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
export class ChatTreeChangeSet implements Omit<ChangeSet, 'onDidChange'> {
|
|
776
|
+
protected readonly onDidChangeEmitter = new Emitter<ChatUpdateChangeSetEvent>();
|
|
777
|
+
get onDidChange(): Event<ChatUpdateChangeSetEvent> {
|
|
778
|
+
return this.onDidChangeEmitter.event;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
protected readonly toDispose = new DisposableCollection();
|
|
782
|
+
|
|
783
|
+
constructor(protected readonly hierarchy: ChatRequestHierarchy<MutableChatRequestModel>) {
|
|
784
|
+
hierarchy.onDidChange(this.handleChangeSetChange, this, this.toDispose);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
get title(): string {
|
|
788
|
+
return this.getCurrentChangeSet()?.title ?? '';
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
removeElements(...uris: URI[]): boolean {
|
|
792
|
+
return this.getMutableChangeSet().removeElements(...uris);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
addElements(...elements: ChangeSetElement[]): boolean {
|
|
796
|
+
return this.getMutableChangeSet().addElements(...elements);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
setElements(...elements: ChangeSetElement[]): void {
|
|
800
|
+
this.getMutableChangeSet().setElements(...elements);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
setTitle(title: string): void {
|
|
804
|
+
this.getMutableChangeSet().setTitle(title);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
getElementByURI(uri: URI): ChangeSetElement | undefined {
|
|
808
|
+
return this.currentElements.find(candidate => candidate.uri.isEqual(uri));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
protected currentElements: ChangeSetElement[] = [];
|
|
812
|
+
protected handleChangeSetChange = debounce(this.doHandleChangeSetChange.bind(this), 100, { leading: false, trailing: true });
|
|
813
|
+
protected doHandleChangeSetChange(): void {
|
|
814
|
+
const newElements = this.computeChangeSetElements();
|
|
815
|
+
this.handleElementChange(newElements);
|
|
816
|
+
this.currentElements = newElements;
|
|
817
|
+
this.onDidChangeEmitter.fire({ kind: 'updateChangeSet', elements: this.currentElements, title: this.getCurrentChangeSet()?.title });
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
getElements(): ChangeSetElement[] {
|
|
821
|
+
return this.currentElements;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
protected computeChangeSetElements(): ChangeSetElement[] {
|
|
825
|
+
const allElements = ChangeSetImpl.combine((function* (requests: MutableChatRequestModel[]): IterableIterator<ChangeSetImpl> {
|
|
826
|
+
for (let i = requests.length - 1; i >= 0; i--) {
|
|
827
|
+
const changeSet = requests[i].changeSet;
|
|
828
|
+
if (changeSet) { yield changeSet; }
|
|
829
|
+
}
|
|
830
|
+
})(this.hierarchy.activeRequests()));
|
|
831
|
+
return ArrayUtils.coalesce(Array.from(allElements.values()));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
protected handleElementChange(newElements: ChangeSetElement[]): void {
|
|
835
|
+
const old = new Set(this.currentElements);
|
|
836
|
+
for (const element of newElements) {
|
|
837
|
+
if (!old.delete(element)) {
|
|
838
|
+
element.onShow?.();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
for (const element of old) {
|
|
842
|
+
element.onHide?.();
|
|
827
843
|
}
|
|
844
|
+
}
|
|
828
845
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
this.
|
|
846
|
+
protected toDisposeOnRequestAdded = new DisposableCollection();
|
|
847
|
+
registerRequest(request: MutableChatRequestModel): void {
|
|
848
|
+
request.onDidChange(event => event.kind === 'updateChangeSet' && this.handleChangeSetChange(), this, this.toDispose);
|
|
849
|
+
if (this.localChangeSet) {
|
|
850
|
+
request.changeSet = this.localChangeSet;
|
|
851
|
+
this.localChangeSet = undefined;
|
|
852
|
+
}
|
|
853
|
+
this.toDisposeOnRequestAdded.dispose();
|
|
854
|
+
}
|
|
833
855
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
856
|
+
protected localChangeSet?: ChangeSetImpl;
|
|
857
|
+
protected getMutableChangeSet(): ChangeSetImpl {
|
|
858
|
+
const tipRequest = this.hierarchy.activeRequests().at(-1);
|
|
859
|
+
const existingChangeSet = tipRequest?.changeSet;
|
|
860
|
+
if (existingChangeSet) {
|
|
861
|
+
return existingChangeSet;
|
|
862
|
+
}
|
|
863
|
+
if (this.localChangeSet && tipRequest) {
|
|
864
|
+
throw new Error('Non-empty chat model retained reference to own change set. This is unexpected!');
|
|
865
|
+
}
|
|
866
|
+
if (this.localChangeSet) {
|
|
867
|
+
return this.localChangeSet;
|
|
868
|
+
}
|
|
869
|
+
const newChangeSet = new ChangeSetImpl();
|
|
870
|
+
if (tipRequest) {
|
|
871
|
+
tipRequest.changeSet = newChangeSet;
|
|
872
|
+
} else {
|
|
873
|
+
this.localChangeSet = newChangeSet;
|
|
874
|
+
newChangeSet.onDidChange(this.handleChangeSetChange, this, this.toDisposeOnRequestAdded);
|
|
875
|
+
}
|
|
876
|
+
return newChangeSet;
|
|
877
|
+
}
|
|
838
878
|
|
|
839
|
-
|
|
879
|
+
protected getCurrentChangeSet(): ChangeSet | undefined {
|
|
880
|
+
const holder = this.getBranchParent(candidate => !!candidate.get().changeSet);
|
|
881
|
+
return holder?.get().changeSet ?? this.localChangeSet;
|
|
840
882
|
}
|
|
841
883
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
this.
|
|
884
|
+
/** Returns the lowest node among active nodes that satisfies {@link criterion} */
|
|
885
|
+
getBranchParent(criterion: (branch: ChatHierarchyBranch<MutableChatRequestModel>) => boolean): ChatHierarchyBranch<MutableChatRequestModel> | undefined {
|
|
886
|
+
const branches = this.hierarchy.activeBranches();
|
|
887
|
+
for (let i = branches.length - 1; i >= 0; i--) {
|
|
888
|
+
const branch = branches[i];
|
|
889
|
+
if (criterion?.(branch)) { return branch; }
|
|
890
|
+
}
|
|
891
|
+
return branches.at(0);
|
|
845
892
|
}
|
|
846
893
|
|
|
847
|
-
|
|
848
|
-
this.
|
|
894
|
+
dispose(): void {
|
|
895
|
+
this.toDispose.dispose();
|
|
849
896
|
}
|
|
850
897
|
}
|
|
851
898
|
|
|
@@ -915,6 +962,11 @@ export class ChatRequestHierarchyImpl<TRequest extends ChatRequestModel = ChatRe
|
|
|
915
962
|
notifyChange(event: ChangeActiveBranchEvent<TRequest>): void {
|
|
916
963
|
this.onDidChangeActiveBranchEmitter.fire(event);
|
|
917
964
|
}
|
|
965
|
+
|
|
966
|
+
dispose(): void {
|
|
967
|
+
this.onDidChangeActiveBranchEmitter.dispose();
|
|
968
|
+
this.branch.dispose();
|
|
969
|
+
}
|
|
918
970
|
}
|
|
919
971
|
|
|
920
972
|
export class ChatRequestHierarchyBranchImpl<TRequest extends ChatRequestModel> implements ChatHierarchyBranch<TRequest> {
|
|
@@ -1019,82 +1071,12 @@ export class ChatRequestHierarchyBranchImpl<TRequest extends ChatRequestModel> i
|
|
|
1019
1071
|
|
|
1020
1072
|
return branches;
|
|
1021
1073
|
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
interface ChangeSetChangeEvent {
|
|
1025
|
-
added?: URI[],
|
|
1026
|
-
removed?: URI[],
|
|
1027
|
-
modified?: URI[],
|
|
1028
|
-
/** Fired when only the state of a given element changes, not its contents */
|
|
1029
|
-
state?: URI[],
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
export class ChangeSetImpl implements ChangeSet {
|
|
1033
|
-
protected readonly _onDidChangeEmitter = new Emitter<ChangeSetChangeEvent>();
|
|
1034
|
-
onDidChange: Event<ChangeSetChangeEvent> = this._onDidChangeEmitter.event;
|
|
1035
|
-
|
|
1036
|
-
protected _elements: ChangeSetElement[] = [];
|
|
1037
|
-
|
|
1038
|
-
constructor(public readonly title: string, elements: ChangeSetElement[] = []) {
|
|
1039
|
-
this.addElements(...elements);
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
getElements(): ChangeSetElement[] {
|
|
1043
|
-
return this._elements;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
/**
|
|
1047
|
-
* Find an element by URI.
|
|
1048
|
-
* @param uri The URI to look for.
|
|
1049
|
-
* @returns The element with the given URI, or undefined if not found.
|
|
1050
|
-
*/
|
|
1051
|
-
getElementByURI(uri: URI): ChangeSetElement | undefined {
|
|
1052
|
-
const uriString = uri.toString();
|
|
1053
|
-
for (const element of this._elements) {
|
|
1054
|
-
if (element.uri.toString() === uriString) {
|
|
1055
|
-
return element;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
return undefined;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
/** Will replace any element that is already present, using URI as identity criterion. */
|
|
1062
|
-
addElements(...elements: ChangeSetElement[]): void {
|
|
1063
|
-
const added: URI[] = [];
|
|
1064
|
-
const modified: URI[] = [];
|
|
1065
|
-
const toDispose: ChangeSetElement[] = [];
|
|
1066
|
-
const current = new Map(this.getElements().map((element, index) => [element.uri.toString(), index]));
|
|
1067
|
-
elements.forEach(element => {
|
|
1068
|
-
const existingIndex = current.get(element.uri.toString());
|
|
1069
|
-
if (existingIndex !== undefined) {
|
|
1070
|
-
modified.push(element.uri);
|
|
1071
|
-
toDispose.push(this._elements[existingIndex]);
|
|
1072
|
-
this._elements[existingIndex] = element;
|
|
1073
|
-
} else {
|
|
1074
|
-
added.push(element.uri);
|
|
1075
|
-
this._elements.push(element);
|
|
1076
|
-
}
|
|
1077
|
-
element.onDidChange?.(() => this.notifyChange({ state: [element.uri] }));
|
|
1078
|
-
});
|
|
1079
|
-
toDispose.forEach(element => element.dispose?.());
|
|
1080
|
-
this.notifyChange({ added, modified });
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
removeElements(...indices: number[]): void {
|
|
1084
|
-
// From highest to lowest so that we don't affect lower indices with our splicing.
|
|
1085
|
-
const sorted = indices.slice().sort((left, right) => left - right);
|
|
1086
|
-
const deletions = sorted.flatMap(index => this._elements.splice(index, 1));
|
|
1087
|
-
deletions.forEach(deleted => deleted.dispose?.());
|
|
1088
|
-
this.notifyChange({ removed: deletions.map(element => element.uri) });
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
protected notifyChange(change: ChangeSetChangeEvent): void {
|
|
1092
|
-
this._onDidChangeEmitter.fire(change);
|
|
1093
|
-
}
|
|
1094
1074
|
|
|
1095
1075
|
dispose(): void {
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1076
|
+
if (Disposable.is(this.get())) {
|
|
1077
|
+
this.items.forEach(({ element }) => Disposable.is(element) && element.dispose());
|
|
1078
|
+
}
|
|
1079
|
+
this.items.length = 0;
|
|
1098
1080
|
}
|
|
1099
1081
|
}
|
|
1100
1082
|
|
|
@@ -1161,10 +1143,13 @@ export class ChatContextManagerImpl implements ChatContextManager {
|
|
|
1161
1143
|
}
|
|
1162
1144
|
|
|
1163
1145
|
export class MutableChatRequestModel implements ChatRequestModel, EditableChatRequestModel, Disposable {
|
|
1146
|
+
protected readonly _onDidChangeEmitter = new Emitter<ChatChangeEvent>();
|
|
1147
|
+
onDidChange: Event<ChatChangeEvent> = this._onDidChangeEmitter.event;
|
|
1164
1148
|
protected readonly _id: string;
|
|
1165
1149
|
protected _session: MutableChatModel;
|
|
1166
1150
|
protected _request: ChatRequest;
|
|
1167
1151
|
protected _response: MutableChatResponseModel;
|
|
1152
|
+
protected _changeSet?: ChangeSetImpl;
|
|
1168
1153
|
protected _context: ChatContext;
|
|
1169
1154
|
protected _agentId?: string;
|
|
1170
1155
|
protected _data: { [key: string]: unknown };
|
|
@@ -1185,9 +1170,20 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
|
|
|
1185
1170
|
this._data = data;
|
|
1186
1171
|
|
|
1187
1172
|
this.editContextManager = new ChatContextManagerImpl(context);
|
|
1188
|
-
this.
|
|
1189
|
-
|
|
1190
|
-
|
|
1173
|
+
this.editContextManager.onDidChange(this._onDidChangeEmitter.fire, this._onDidChangeEmitter, this.toDispose);
|
|
1174
|
+
this.toDispose.push(this._onDidChangeEmitter);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
get changeSet(): ChangeSetImpl | undefined {
|
|
1178
|
+
return this._changeSet;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
set changeSet(changeSet: ChangeSetImpl) {
|
|
1182
|
+
this._changeSet?.dispose();
|
|
1183
|
+
this._changeSet = changeSet;
|
|
1184
|
+
this.toDispose.push(changeSet);
|
|
1185
|
+
changeSet.onDidChange(() => this._onDidChangeEmitter.fire({ kind: 'updateChangeSet', elements: changeSet.getElements(), title: changeSet.title }), this, this.toDispose);
|
|
1186
|
+
this._onDidChangeEmitter.fire({ kind: 'updateChangeSet', elements: changeSet.getElements(), title: changeSet.title });
|
|
1191
1187
|
}
|
|
1192
1188
|
|
|
1193
1189
|
get isEditing(): boolean {
|
|
@@ -1280,7 +1276,7 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
|
|
|
1280
1276
|
if (!branch) {
|
|
1281
1277
|
throw new Error(`Cannot find hierarchy for requestId: ${request.id}`);
|
|
1282
1278
|
}
|
|
1283
|
-
this.
|
|
1279
|
+
this._onDidChangeEmitter.fire({
|
|
1284
1280
|
kind: 'enableEdit',
|
|
1285
1281
|
request,
|
|
1286
1282
|
branch,
|
|
@@ -1292,7 +1288,7 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
|
|
|
1292
1288
|
if (!branch) {
|
|
1293
1289
|
throw new Error(`Cannot find branch for requestId: ${request.id}`);
|
|
1294
1290
|
}
|
|
1295
|
-
this.
|
|
1291
|
+
this._onDidChangeEmitter.fire({
|
|
1296
1292
|
kind: 'cancelEdit',
|
|
1297
1293
|
request,
|
|
1298
1294
|
branch,
|
|
@@ -1304,7 +1300,7 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
|
|
|
1304
1300
|
if (!branch) {
|
|
1305
1301
|
throw new Error(`Cannot find branch for requestId: ${request.id}`);
|
|
1306
1302
|
}
|
|
1307
|
-
this.
|
|
1303
|
+
this._onDidChangeEmitter.fire({
|
|
1308
1304
|
kind: 'submitEdit',
|
|
1309
1305
|
request,
|
|
1310
1306
|
branch,
|
|
@@ -1359,6 +1355,7 @@ export class TextChatResponseContentImpl implements TextChatResponseContent {
|
|
|
1359
1355
|
};
|
|
1360
1356
|
}
|
|
1361
1357
|
}
|
|
1358
|
+
|
|
1362
1359
|
export class ThinkingChatResponseContentImpl implements ThinkingChatResponseContent {
|
|
1363
1360
|
readonly kind = 'thinking';
|
|
1364
1361
|
protected _content: string;
|
|
@@ -1501,6 +1498,9 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
1501
1498
|
protected _arguments?: string;
|
|
1502
1499
|
protected _finished?: boolean;
|
|
1503
1500
|
protected _result?: string;
|
|
1501
|
+
protected _confirmed: Promise<boolean>;
|
|
1502
|
+
protected _confirmationResolver?: (value: boolean) => void;
|
|
1503
|
+
protected _confirmationRejecter?: (reason?: unknown) => void;
|
|
1504
1504
|
|
|
1505
1505
|
constructor(id?: string, name?: string, arg_string?: string, finished?: boolean, result?: string) {
|
|
1506
1506
|
this._id = id;
|
|
@@ -1508,6 +1508,8 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
1508
1508
|
this._arguments = arg_string;
|
|
1509
1509
|
this._finished = finished;
|
|
1510
1510
|
this._result = result;
|
|
1511
|
+
// Initialize the confirmation promise immediately
|
|
1512
|
+
this._confirmed = this.createConfirmationPromise();
|
|
1511
1513
|
}
|
|
1512
1514
|
|
|
1513
1515
|
get id(): string | undefined {
|
|
@@ -1529,6 +1531,53 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
1529
1531
|
return this._result;
|
|
1530
1532
|
}
|
|
1531
1533
|
|
|
1534
|
+
get confirmed(): Promise<boolean> {
|
|
1535
|
+
return this._confirmed;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Create a confirmation promise that can be resolved/rejected later
|
|
1540
|
+
*/
|
|
1541
|
+
createConfirmationPromise(): Promise<boolean> {
|
|
1542
|
+
// The promise is always created, just ensure we have resolution handlers
|
|
1543
|
+
if (!this._confirmationResolver) {
|
|
1544
|
+
this._confirmed = new Promise<boolean>((resolve, reject) => {
|
|
1545
|
+
this._confirmationResolver = resolve;
|
|
1546
|
+
this._confirmationRejecter = reject;
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
return this._confirmed;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* Confirm the tool execution
|
|
1554
|
+
*/
|
|
1555
|
+
confirm(): void {
|
|
1556
|
+
if (this._confirmationResolver) {
|
|
1557
|
+
this._confirmationResolver(true);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/**
|
|
1562
|
+
* Deny the tool execution
|
|
1563
|
+
*/
|
|
1564
|
+
deny(): void {
|
|
1565
|
+
if (this._confirmationResolver) {
|
|
1566
|
+
this._confirmationResolver(false);
|
|
1567
|
+
this._finished = true;
|
|
1568
|
+
this._result = 'Tool execution denied by user';
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* Cancel the confirmation (reject the promise)
|
|
1574
|
+
*/
|
|
1575
|
+
cancelConfirmation(reason?: unknown): void {
|
|
1576
|
+
if (this._confirmationRejecter) {
|
|
1577
|
+
this._confirmationRejecter(reason);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1532
1581
|
asString(): string {
|
|
1533
1582
|
return '';
|
|
1534
1583
|
}
|
|
@@ -1543,6 +1592,7 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
1543
1592
|
this._result = nextChatResponseContent.result;
|
|
1544
1593
|
const args = nextChatResponseContent.arguments;
|
|
1545
1594
|
this._arguments = (args && args.length > 0) ? args : this._arguments;
|
|
1595
|
+
// Don't merge confirmation promises - they should be managed separately
|
|
1546
1596
|
return true;
|
|
1547
1597
|
}
|
|
1548
1598
|
if (nextChatResponseContent.name !== undefined) {
|
|
@@ -104,6 +104,9 @@ export class ChatRequestParserImpl implements ChatRequestParser {
|
|
|
104
104
|
const parts: ParsedChatRequestPart[] = [];
|
|
105
105
|
const variables = new Map<string, AIVariable>();
|
|
106
106
|
const toolRequests = new Map<string, ToolRequest>();
|
|
107
|
+
if (!request.text) {
|
|
108
|
+
return { parts, toolRequests, variables };
|
|
109
|
+
}
|
|
107
110
|
const message = request.text;
|
|
108
111
|
for (let i = 0; i < message.length; i++) {
|
|
109
112
|
const previousChar = message.charAt(i - 1);
|