@theia/ai-chat 1.66.0-next.67 → 1.66.0-next.80
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.map +1 -1
- package/lib/browser/agent-delegation-tool.js +4 -2
- package/lib/browser/agent-delegation-tool.js.map +1 -1
- package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-chat-frontend-module.js +15 -0
- package/lib/browser/ai-chat-frontend-module.js.map +1 -1
- package/lib/browser/change-set-file-element-deserializer.d.ts +7 -0
- package/lib/browser/change-set-file-element-deserializer.d.ts.map +1 -0
- package/lib/browser/change-set-file-element-deserializer.js +61 -0
- package/lib/browser/change-set-file-element-deserializer.js.map +1 -0
- package/lib/browser/change-set-file-element.d.ts +2 -0
- package/lib/browser/change-set-file-element.d.ts.map +1 -1
- package/lib/browser/change-set-file-element.js +17 -0
- package/lib/browser/change-set-file-element.js.map +1 -1
- package/lib/browser/chat-session-store-impl.d.ts +36 -0
- package/lib/browser/chat-session-store-impl.d.ts.map +1 -0
- package/lib/browser/chat-session-store-impl.js +287 -0
- package/lib/browser/chat-session-store-impl.js.map +1 -0
- package/lib/common/change-set-element-deserializer.d.ts +30 -0
- package/lib/common/change-set-element-deserializer.d.ts.map +1 -0
- package/lib/common/change-set-element-deserializer.js +81 -0
- package/lib/common/change-set-element-deserializer.js.map +1 -0
- package/lib/common/change-set.d.ts +7 -0
- package/lib/common/change-set.d.ts.map +1 -1
- package/lib/common/change-set.js.map +1 -1
- package/lib/common/chat-auto-save.spec.d.ts +2 -0
- package/lib/common/chat-auto-save.spec.d.ts.map +1 -0
- package/lib/common/chat-auto-save.spec.js +304 -0
- package/lib/common/chat-auto-save.spec.js.map +1 -0
- package/lib/common/chat-content-deserializer.d.ts +161 -0
- package/lib/common/chat-content-deserializer.d.ts.map +1 -0
- package/lib/common/chat-content-deserializer.js +166 -0
- package/lib/common/chat-content-deserializer.js.map +1 -0
- package/lib/common/chat-content-deserializer.spec.d.ts +2 -0
- package/lib/common/chat-content-deserializer.spec.d.ts.map +1 -0
- package/lib/common/chat-content-deserializer.spec.js +307 -0
- package/lib/common/chat-content-deserializer.spec.js.map +1 -0
- package/lib/common/chat-model-serialization.d.ts +110 -0
- package/lib/common/chat-model-serialization.d.ts.map +1 -0
- package/lib/common/chat-model-serialization.js +20 -0
- package/lib/common/chat-model-serialization.js.map +1 -0
- package/lib/common/chat-model-serialization.spec.d.ts +2 -0
- package/lib/common/chat-model-serialization.spec.d.ts.map +1 -0
- package/lib/common/chat-model-serialization.spec.js +278 -0
- package/lib/common/chat-model-serialization.spec.js.map +1 -0
- package/lib/common/chat-model.d.ts +163 -14
- package/lib/common/chat-model.d.ts.map +1 -1
- package/lib/common/chat-model.js +444 -36
- package/lib/common/chat-model.js.map +1 -1
- package/lib/common/chat-service-deletion.spec.d.ts +2 -0
- package/lib/common/chat-service-deletion.spec.d.ts.map +1 -0
- package/lib/common/chat-service-deletion.spec.js +221 -0
- package/lib/common/chat-service-deletion.spec.js.map +1 -0
- package/lib/common/chat-service.d.ts +30 -2
- package/lib/common/chat-service.d.ts.map +1 -1
- package/lib/common/chat-service.js +182 -10
- package/lib/common/chat-service.js.map +1 -1
- package/lib/common/chat-session-store.d.ts +43 -0
- package/lib/common/chat-session-store.d.ts.map +1 -0
- package/lib/common/chat-session-store.js +20 -0
- package/lib/common/chat-session-store.js.map +1 -0
- package/lib/common/index.d.ts +3 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +3 -0
- package/lib/common/index.js.map +1 -1
- package/package.json +9 -9
- package/src/browser/agent-delegation-tool.ts +4 -2
- package/src/browser/ai-chat-frontend-module.ts +27 -0
- package/src/browser/change-set-file-element-deserializer.ts +62 -0
- package/src/browser/change-set-file-element.ts +19 -0
- package/src/browser/chat-session-store-impl.ts +326 -0
- package/src/common/change-set-element-deserializer.ts +90 -0
- package/src/common/change-set.ts +8 -0
- package/src/common/chat-auto-save.spec.ts +372 -0
- package/src/common/chat-content-deserializer.spec.ts +375 -0
- package/src/common/chat-content-deserializer.ts +327 -0
- package/src/common/chat-model-serialization.spec.ts +343 -0
- package/src/common/chat-model-serialization.ts +133 -0
- package/src/common/chat-model.ts +644 -41
- package/src/common/chat-service-deletion.spec.ts +269 -0
- package/src/common/chat-service.ts +227 -10
- package/src/common/chat-session-store.ts +63 -0
- package/src/common/index.ts +3 -0
package/src/common/chat-model.ts
CHANGED
|
@@ -34,6 +34,16 @@ import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markd
|
|
|
34
34
|
import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
35
35
|
import { ChangeSet, ChangeSetElement, ChangeSetImpl, ChatUpdateChangeSetEvent } from './change-set';
|
|
36
36
|
import { ChatAgentLocation } from './chat-agents';
|
|
37
|
+
import {
|
|
38
|
+
SerializedChatModel,
|
|
39
|
+
SerializableChatRequestData,
|
|
40
|
+
SerializableChatResponseContentData,
|
|
41
|
+
SerializableChatResponseData,
|
|
42
|
+
SerializableHierarchy,
|
|
43
|
+
SerializableHierarchyBranch,
|
|
44
|
+
SerializableHierarchyBranchItem,
|
|
45
|
+
SerializableChangeSetElement
|
|
46
|
+
} from './chat-model-serialization';
|
|
37
47
|
import { ParsedChatRequest } from './parsed-chat-request';
|
|
38
48
|
import debounce = require('@theia/core/shared/lodash.debounce');
|
|
39
49
|
export { ChangeSet, ChangeSetElement, ChangeSetImpl };
|
|
@@ -54,6 +64,7 @@ export type ChatChangeEvent =
|
|
|
54
64
|
| ChatEditRequestEvent
|
|
55
65
|
| ChatEditCancelEvent
|
|
56
66
|
| ChatEditSubmitEvent
|
|
67
|
+
| ChatResponseChangedEvent
|
|
57
68
|
| ChatChangeHierarchyBranchEvent;
|
|
58
69
|
|
|
59
70
|
export interface ChatAddRequestEvent {
|
|
@@ -107,6 +118,10 @@ export interface ChatSuggestionsChangedEvent {
|
|
|
107
118
|
suggestions: ChatSuggestion[];
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
export interface ChatResponseChangedEvent {
|
|
122
|
+
kind: 'responseChanged';
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
export namespace ChatChangeEvent {
|
|
111
126
|
export function isChangeSetEvent(event: ChatChangeEvent): event is ChatUpdateChangeSetEvent {
|
|
112
127
|
return event.kind === 'updateChangeSet';
|
|
@@ -141,7 +156,8 @@ export interface ChatRequestHierarchy<TRequest extends ChatRequestModel = ChatRe
|
|
|
141
156
|
findRequest(requestId: string): TRequest | undefined;
|
|
142
157
|
findBranch(requestId: string): ChatHierarchyBranch<TRequest> | undefined;
|
|
143
158
|
|
|
144
|
-
notifyChange(event: ChangeActiveBranchEvent<TRequest>): void
|
|
159
|
+
notifyChange(event: ChangeActiveBranchEvent<TRequest>): void;
|
|
160
|
+
toSerializable(): SerializableHierarchy;
|
|
145
161
|
}
|
|
146
162
|
|
|
147
163
|
export interface ChangeActiveBranchEvent<TRequest extends ChatRequestModel = ChatRequestModel> {
|
|
@@ -193,6 +209,7 @@ export interface ChatModel {
|
|
|
193
209
|
getRequests(): ChatRequestModel[];
|
|
194
210
|
getBranches(): ChatHierarchyBranch<ChatRequestModel>[];
|
|
195
211
|
isEmpty(): boolean;
|
|
212
|
+
toSerializable(): SerializedChatModel;
|
|
196
213
|
}
|
|
197
214
|
|
|
198
215
|
export interface ChatSuggestionCallback {
|
|
@@ -251,6 +268,7 @@ export interface ChatRequestModel {
|
|
|
251
268
|
readonly context: ChatContext;
|
|
252
269
|
readonly agentId?: string;
|
|
253
270
|
readonly data?: { [key: string]: unknown };
|
|
271
|
+
toSerializable(): SerializableChatRequestData;
|
|
254
272
|
}
|
|
255
273
|
|
|
256
274
|
export namespace ChatRequestModel {
|
|
@@ -321,6 +339,7 @@ export interface ChatResponseContent {
|
|
|
321
339
|
asDisplayString?(): string | undefined;
|
|
322
340
|
merge?(nextChatResponseContent: ChatResponseContent): boolean;
|
|
323
341
|
toLanguageModelMessage?(): LanguageModelMessage | LanguageModelMessage[];
|
|
342
|
+
toSerializable?(): SerializableChatResponseContentData;
|
|
324
343
|
}
|
|
325
344
|
|
|
326
345
|
export namespace ChatResponseContent {
|
|
@@ -354,6 +373,71 @@ export namespace ChatResponseContent {
|
|
|
354
373
|
}
|
|
355
374
|
}
|
|
356
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Data interfaces for chat response content serialization.
|
|
378
|
+
* These define the structure of the data property in SerializableChatResponseContentData.
|
|
379
|
+
*/
|
|
380
|
+
|
|
381
|
+
export interface TextContentData {
|
|
382
|
+
content: string;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export interface ThinkingContentData {
|
|
386
|
+
content: string;
|
|
387
|
+
signature: string;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export interface MarkdownContentData {
|
|
391
|
+
content: string;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export interface InformationalContentData {
|
|
395
|
+
content: string;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export interface CodeContentData {
|
|
399
|
+
code: string;
|
|
400
|
+
language?: string;
|
|
401
|
+
location?: Location;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export interface ToolCallContentData {
|
|
405
|
+
id?: string;
|
|
406
|
+
name?: string;
|
|
407
|
+
arguments?: string;
|
|
408
|
+
finished?: boolean;
|
|
409
|
+
result?: ToolCallResult;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export interface CommandContentData {
|
|
413
|
+
commandId?: string;
|
|
414
|
+
commandLabel?: string;
|
|
415
|
+
arguments?: unknown[];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export interface HorizontalLayoutContentData {
|
|
419
|
+
content: SerializableChatResponseContentData[];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export interface ProgressContentData {
|
|
423
|
+
message: string;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export interface ErrorContentData {
|
|
427
|
+
message: string;
|
|
428
|
+
stack?: string;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Restored questions display the question, options, and any previously selected answer,
|
|
433
|
+
* but do not allow new selections.
|
|
434
|
+
*/
|
|
435
|
+
export interface QuestionContentData {
|
|
436
|
+
question: string;
|
|
437
|
+
options: { text: string; value?: string }[];
|
|
438
|
+
selectedOption?: { text: string; value?: string };
|
|
439
|
+
}
|
|
440
|
+
|
|
357
441
|
export interface TextChatResponseContent
|
|
358
442
|
extends Required<ChatResponseContent> {
|
|
359
443
|
kind: 'text';
|
|
@@ -566,8 +650,13 @@ export interface QuestionResponseContent extends ChatResponseContent {
|
|
|
566
650
|
question: string;
|
|
567
651
|
options: { text: string, value?: string }[];
|
|
568
652
|
selectedOption?: { text: string, value?: string };
|
|
569
|
-
handler
|
|
570
|
-
request
|
|
653
|
+
handler?: QuestionResponseHandler;
|
|
654
|
+
request?: MutableChatRequestModel;
|
|
655
|
+
/**
|
|
656
|
+
* Whether this question is read-only (restored from persistence without handler).
|
|
657
|
+
* When true, the UI should disable option selection.
|
|
658
|
+
*/
|
|
659
|
+
readonly isReadOnly: boolean;
|
|
571
660
|
}
|
|
572
661
|
|
|
573
662
|
export namespace QuestionResponseContent {
|
|
@@ -585,10 +674,9 @@ export namespace QuestionResponseContent {
|
|
|
585
674
|
typeof (option as { text: unknown }).text === 'string' &&
|
|
586
675
|
('value' in option ? typeof (option as { value: unknown }).value === 'string' || typeof (option as { value: unknown }).value === 'undefined' : true)
|
|
587
676
|
) &&
|
|
588
|
-
|
|
589
|
-
typeof (obj as { handler: unknown }).handler === 'function' &&
|
|
590
|
-
'request' in obj
|
|
591
|
-
obj.request instanceof MutableChatRequestModel
|
|
677
|
+
// handler and request are optional (undefined for restored/read-only questions)
|
|
678
|
+
('handler' in obj ? (obj as { handler: unknown }).handler === undefined || typeof (obj as { handler: unknown }).handler === 'function' : true) &&
|
|
679
|
+
('request' in obj ? (obj as { request: unknown }).request === undefined || (obj as { request: unknown }).request instanceof MutableChatRequestModel : true)
|
|
592
680
|
);
|
|
593
681
|
}
|
|
594
682
|
}
|
|
@@ -652,6 +740,7 @@ export interface ChatResponseModel {
|
|
|
652
740
|
* This can be used to store and retrieve such data.
|
|
653
741
|
*/
|
|
654
742
|
readonly data: { [key: string]: unknown };
|
|
743
|
+
toSerializable(): SerializableChatResponseData;
|
|
655
744
|
}
|
|
656
745
|
|
|
657
746
|
/**********************
|
|
@@ -668,16 +757,29 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
668
757
|
protected _id: string;
|
|
669
758
|
protected _suggestions: readonly ChatSuggestion[] = [];
|
|
670
759
|
protected readonly _contextManager = new ChatContextManagerImpl();
|
|
671
|
-
protected
|
|
760
|
+
protected _changeSet: ChatTreeChangeSet;
|
|
672
761
|
protected _settings: { [key: string]: unknown };
|
|
762
|
+
protected _location: ChatAgentLocation;
|
|
673
763
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
764
|
+
get location(): ChatAgentLocation {
|
|
765
|
+
return this._location;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
constructor(
|
|
769
|
+
locationOrSerializedData: ChatAgentLocation | SerializedChatModel = ChatAgentLocation.Panel
|
|
770
|
+
) {
|
|
771
|
+
// Check if we're restoring from serialized data
|
|
772
|
+
if (this.isSerializedChatModel(locationOrSerializedData)) {
|
|
773
|
+
this.restoreFromSerializedData(locationOrSerializedData);
|
|
774
|
+
} else {
|
|
775
|
+
// Normal creation path
|
|
776
|
+
this._location = locationOrSerializedData;
|
|
777
|
+
this._id = generateUuid();
|
|
778
|
+
this._hierarchy = new ChatRequestHierarchyImpl<MutableChatRequestModel>();
|
|
779
|
+
this._changeSet = new ChatTreeChangeSet(this._hierarchy);
|
|
780
|
+
this.toDispose.push(this._changeSet);
|
|
781
|
+
this._changeSet.onDidChange(this._onDidChangeEmitter.fire, this._onDidChangeEmitter, this.toDispose);
|
|
782
|
+
}
|
|
681
783
|
|
|
682
784
|
this.toDispose.pushAll([
|
|
683
785
|
this._onDidChangeEmitter,
|
|
@@ -691,6 +793,44 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
691
793
|
]);
|
|
692
794
|
}
|
|
693
795
|
|
|
796
|
+
/**
|
|
797
|
+
* Type guard to determine if we're receiving serialized data
|
|
798
|
+
*/
|
|
799
|
+
protected isSerializedChatModel(data: ChatAgentLocation | SerializedChatModel): data is SerializedChatModel {
|
|
800
|
+
return typeof data === 'object' && 'sessionId' in data && 'requests' in data && 'responses' in data;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Restore this chat model from serialized data
|
|
805
|
+
*
|
|
806
|
+
* Does not restore response content or changesets.
|
|
807
|
+
* This handled by the chat service using the deserializer registries.
|
|
808
|
+
*/
|
|
809
|
+
protected restoreFromSerializedData(data: SerializedChatModel): void {
|
|
810
|
+
this._id = data.sessionId;
|
|
811
|
+
this._location = data.location;
|
|
812
|
+
|
|
813
|
+
// First, create all request models and build a map
|
|
814
|
+
const requestMap = new Map<string, MutableChatRequestModel>();
|
|
815
|
+
for (const reqData of data.requests) {
|
|
816
|
+
const respData = data.responses.find(r => r.requestId === reqData.id);
|
|
817
|
+
const requestModel = new MutableChatRequestModel(this, reqData, respData);
|
|
818
|
+
requestMap.set(requestModel.id, requestModel);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Restore the hierarchy structure with all alternatives
|
|
822
|
+
this._hierarchy = new ChatRequestHierarchyImpl<MutableChatRequestModel>(data.hierarchy, requestMap);
|
|
823
|
+
|
|
824
|
+
// Register all requests with changeset
|
|
825
|
+
this._changeSet = new ChatTreeChangeSet(this._hierarchy);
|
|
826
|
+
this.toDispose.push(this._changeSet);
|
|
827
|
+
this._changeSet.onDidChange(this._onDidChangeEmitter.fire, this._onDidChangeEmitter, this.toDispose);
|
|
828
|
+
|
|
829
|
+
for (const requestModel of requestMap.values()) {
|
|
830
|
+
this._changeSet.registerRequest(requestModel);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
694
834
|
get id(): string {
|
|
695
835
|
return this._id;
|
|
696
836
|
}
|
|
@@ -769,6 +909,52 @@ export class MutableChatModel implements ChatModel, Disposable {
|
|
|
769
909
|
return this.getRequests().length === 0;
|
|
770
910
|
}
|
|
771
911
|
|
|
912
|
+
toSerializable(): SerializedChatModel {
|
|
913
|
+
const hierarchy = this._hierarchy.toSerializable();
|
|
914
|
+
|
|
915
|
+
const allRequests = this.getAllRequests();
|
|
916
|
+
|
|
917
|
+
const serializedRequests: SerializableChatRequestData[] = allRequests.map(req => req.toSerializable());
|
|
918
|
+
const serializedResponses: SerializableChatResponseData[] = allRequests
|
|
919
|
+
.filter(req => req.response)
|
|
920
|
+
.map(req => req.response.toSerializable());
|
|
921
|
+
|
|
922
|
+
return {
|
|
923
|
+
sessionId: this._id,
|
|
924
|
+
location: this.location,
|
|
925
|
+
hierarchy,
|
|
926
|
+
requests: serializedRequests,
|
|
927
|
+
responses: serializedResponses
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Get all requests from the hierarchy.
|
|
933
|
+
* This is used for operations that need to process all requests, such as serialization.
|
|
934
|
+
*/
|
|
935
|
+
getAllRequests(): MutableChatRequestModel[] {
|
|
936
|
+
const allRequests: MutableChatRequestModel[] = [];
|
|
937
|
+
const visited = new Set<string>();
|
|
938
|
+
|
|
939
|
+
const collectFromBranch = (branch: ChatHierarchyBranch<MutableChatRequestModel>): void => {
|
|
940
|
+
for (const item of branch.items) {
|
|
941
|
+
// Avoid duplicates
|
|
942
|
+
if (!visited.has(item.element.id)) {
|
|
943
|
+
visited.add(item.element.id);
|
|
944
|
+
allRequests.push(item.element);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Recursively collect from next branches
|
|
948
|
+
if (item.next) {
|
|
949
|
+
collectFromBranch(item.next);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
collectFromBranch(this._hierarchy.branch);
|
|
955
|
+
return allRequests;
|
|
956
|
+
}
|
|
957
|
+
|
|
772
958
|
dispose(): void {
|
|
773
959
|
this.toDispose.dispose();
|
|
774
960
|
}
|
|
@@ -902,7 +1088,89 @@ export class ChatRequestHierarchyImpl<TRequest extends ChatRequestModel = ChatRe
|
|
|
902
1088
|
protected readonly onDidChangeActiveBranchEmitter = new Emitter<ChangeActiveBranchEvent<TRequest>>();
|
|
903
1089
|
readonly onDidChange = this.onDidChangeActiveBranchEmitter.event;
|
|
904
1090
|
|
|
905
|
-
readonly branch: ChatHierarchyBranch<TRequest
|
|
1091
|
+
readonly branch: ChatHierarchyBranch<TRequest>;
|
|
1092
|
+
|
|
1093
|
+
constructor(
|
|
1094
|
+
serializedHierarchy?: SerializableHierarchy,
|
|
1095
|
+
requestMap?: Map<string, TRequest>) {
|
|
1096
|
+
this.branch = new ChatRequestHierarchyBranchImpl<TRequest>(this);
|
|
1097
|
+
if (serializedHierarchy && requestMap) {
|
|
1098
|
+
this.restoreFromSerialized(serializedHierarchy, requestMap);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Restore the hierarchy from serialized data.
|
|
1104
|
+
*/
|
|
1105
|
+
protected restoreFromSerialized(
|
|
1106
|
+
serializedHierarchy: SerializableHierarchy,
|
|
1107
|
+
requestMap: Map<string, TRequest>
|
|
1108
|
+
): void {
|
|
1109
|
+
// Build a map of branch IDs to restored branch objects
|
|
1110
|
+
const branchMap = new Map<string, ChatHierarchyBranch<TRequest>>();
|
|
1111
|
+
|
|
1112
|
+
// Function to restore a branch and its descendants
|
|
1113
|
+
const restoreBranch = (branchId: string): ChatHierarchyBranch<TRequest> => {
|
|
1114
|
+
// Check if already restored
|
|
1115
|
+
if (branchMap.has(branchId)) {
|
|
1116
|
+
return branchMap.get(branchId)!;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const serializedBranch = serializedHierarchy.branches[branchId];
|
|
1120
|
+
if (!serializedBranch) {
|
|
1121
|
+
throw new Error(`Cannot find serialized branch with id: ${branchId}`);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// Restore items in this branch
|
|
1125
|
+
const items: ChatHierarchyBranchItem<TRequest>[] = serializedBranch.items.map(serializedItem => {
|
|
1126
|
+
const request = requestMap.get(serializedItem.requestId);
|
|
1127
|
+
if (!request) {
|
|
1128
|
+
throw new Error(`Cannot find request with id: ${serializedItem.requestId}`);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Restore next branch if present
|
|
1132
|
+
const next = serializedItem.nextBranchId
|
|
1133
|
+
? restoreBranch(serializedItem.nextBranchId)
|
|
1134
|
+
: undefined;
|
|
1135
|
+
|
|
1136
|
+
return {
|
|
1137
|
+
element: request,
|
|
1138
|
+
next
|
|
1139
|
+
};
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
// Determine if this is the root branch
|
|
1143
|
+
const isRoot = branchId === serializedHierarchy.rootBranchId;
|
|
1144
|
+
|
|
1145
|
+
if (isRoot) {
|
|
1146
|
+
// For root branch, we need to replace the existing branch's internals
|
|
1147
|
+
// Cast is safe here as we know this.branch is a ChatRequestHierarchyBranchImpl
|
|
1148
|
+
const rootBranch = this.branch as ChatRequestHierarchyBranchImpl<TRequest>;
|
|
1149
|
+
// Use Object.assign to update the readonly properties
|
|
1150
|
+
Object.assign(rootBranch, {
|
|
1151
|
+
id: branchId,
|
|
1152
|
+
items,
|
|
1153
|
+
_activeIndex: serializedBranch.activeBranchIndex
|
|
1154
|
+
});
|
|
1155
|
+
branchMap.set(branchId, rootBranch);
|
|
1156
|
+
return rootBranch;
|
|
1157
|
+
} else {
|
|
1158
|
+
// For non-root branches, use constructor-based deserialization
|
|
1159
|
+
const restoredBranch = new ChatRequestHierarchyBranchImpl<TRequest>(
|
|
1160
|
+
this,
|
|
1161
|
+
undefined, // previous will be set by parent
|
|
1162
|
+
items,
|
|
1163
|
+
serializedBranch.activeBranchIndex,
|
|
1164
|
+
branchId
|
|
1165
|
+
);
|
|
1166
|
+
branchMap.set(branchId, restoredBranch);
|
|
1167
|
+
return restoredBranch;
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
// Start restoration from the root branch
|
|
1172
|
+
restoreBranch(serializedHierarchy.rootBranchId);
|
|
1173
|
+
}
|
|
906
1174
|
|
|
907
1175
|
append(request: TRequest): ChatHierarchyBranch<TRequest> {
|
|
908
1176
|
const branches = this.activeBranches();
|
|
@@ -965,6 +1233,39 @@ export class ChatRequestHierarchyImpl<TRequest extends ChatRequestModel = ChatRe
|
|
|
965
1233
|
this.onDidChangeActiveBranchEmitter.fire(event);
|
|
966
1234
|
}
|
|
967
1235
|
|
|
1236
|
+
toSerializable(): SerializableHierarchy {
|
|
1237
|
+
const branches: { [branchId: string]: SerializableHierarchyBranch } = {};
|
|
1238
|
+
|
|
1239
|
+
// Recursively serialize all branches starting from the root
|
|
1240
|
+
this.serializeBranch(this.branch, branches);
|
|
1241
|
+
|
|
1242
|
+
return {
|
|
1243
|
+
rootBranchId: this.branch.id,
|
|
1244
|
+
branches
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
protected serializeBranch(
|
|
1249
|
+
branch: ChatHierarchyBranch<TRequest>,
|
|
1250
|
+
branches: { [branchId: string]: SerializableHierarchyBranch }
|
|
1251
|
+
): void {
|
|
1252
|
+
const items: SerializableHierarchyBranchItem[] = branch.items.map(item => {
|
|
1253
|
+
if (item.next) {
|
|
1254
|
+
this.serializeBranch(item.next, branches);
|
|
1255
|
+
}
|
|
1256
|
+
return {
|
|
1257
|
+
requestId: item.element.id,
|
|
1258
|
+
nextBranchId: item.next?.id
|
|
1259
|
+
};
|
|
1260
|
+
});
|
|
1261
|
+
|
|
1262
|
+
branches[branch.id] = {
|
|
1263
|
+
id: branch.id,
|
|
1264
|
+
items,
|
|
1265
|
+
activeBranchIndex: branch.activeBranchIndex
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
|
|
968
1269
|
dispose(): void {
|
|
969
1270
|
this.onDidChangeActiveBranchEmitter.dispose();
|
|
970
1271
|
this.branch.dispose();
|
|
@@ -972,14 +1273,17 @@ export class ChatRequestHierarchyImpl<TRequest extends ChatRequestModel = ChatRe
|
|
|
972
1273
|
}
|
|
973
1274
|
|
|
974
1275
|
export class ChatRequestHierarchyBranchImpl<TRequest extends ChatRequestModel> implements ChatHierarchyBranch<TRequest> {
|
|
975
|
-
readonly id
|
|
1276
|
+
readonly id: string;
|
|
976
1277
|
|
|
977
1278
|
constructor(
|
|
978
1279
|
readonly hierarchy: ChatRequestHierarchy<TRequest>,
|
|
979
1280
|
readonly previous?: ChatHierarchyBranch<TRequest>,
|
|
980
1281
|
readonly items: ChatHierarchyBranchItem<TRequest>[] = [],
|
|
981
|
-
protected _activeIndex = -1
|
|
982
|
-
|
|
1282
|
+
protected _activeIndex = -1,
|
|
1283
|
+
id?: string
|
|
1284
|
+
) {
|
|
1285
|
+
this.id = id ?? generateUuid();
|
|
1286
|
+
}
|
|
983
1287
|
|
|
984
1288
|
get activeBranchIndex(): number {
|
|
985
1289
|
return this._activeIndex;
|
|
@@ -1147,7 +1451,7 @@ export class ChatContextManagerImpl implements ChatContextManager {
|
|
|
1147
1451
|
export class MutableChatRequestModel implements ChatRequestModel, EditableChatRequestModel, Disposable {
|
|
1148
1452
|
protected readonly _onDidChangeEmitter = new Emitter<ChatChangeEvent>();
|
|
1149
1453
|
onDidChange: Event<ChatChangeEvent> = this._onDidChangeEmitter.event;
|
|
1150
|
-
protected
|
|
1454
|
+
protected _id: string;
|
|
1151
1455
|
protected _session: MutableChatModel;
|
|
1152
1456
|
protected _request: ChatRequest;
|
|
1153
1457
|
protected _response: MutableChatResponseModel;
|
|
@@ -1156,26 +1460,93 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
|
|
|
1156
1460
|
protected _agentId?: string;
|
|
1157
1461
|
protected _data: { [key: string]: unknown };
|
|
1158
1462
|
protected _isEditing = false;
|
|
1463
|
+
readonly message: ParsedChatRequest;
|
|
1159
1464
|
|
|
1160
1465
|
protected readonly toDispose = new DisposableCollection();
|
|
1161
1466
|
readonly editContextManager: ChatContextManagerImpl;
|
|
1162
1467
|
|
|
1163
|
-
constructor(
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1468
|
+
constructor(
|
|
1469
|
+
session: MutableChatModel,
|
|
1470
|
+
messageOrData: ParsedChatRequest | SerializableChatRequestData,
|
|
1471
|
+
agentIdOrResponseData?: string | SerializableChatResponseData,
|
|
1472
|
+
context: ChatContext = { variables: [] },
|
|
1473
|
+
data: { [key: string]: unknown } = {}
|
|
1474
|
+
) {
|
|
1168
1475
|
this._session = session;
|
|
1169
|
-
this._response = new MutableChatResponseModel(this._id, agentId);
|
|
1170
|
-
this._context = context;
|
|
1171
|
-
this._agentId = agentId;
|
|
1172
|
-
this._data = data;
|
|
1173
1476
|
|
|
1174
|
-
|
|
1477
|
+
// Check if we're restoring from serialized data
|
|
1478
|
+
if (this.isSerializedRequestData(messageOrData)) {
|
|
1479
|
+
this.restoreFromSerializedData(messageOrData, agentIdOrResponseData as SerializableChatResponseData | undefined);
|
|
1480
|
+
} else {
|
|
1481
|
+
// Normal creation path
|
|
1482
|
+
this._request = messageOrData.request;
|
|
1483
|
+
this._id = generateUuid();
|
|
1484
|
+
this._response = new MutableChatResponseModel(this._id, agentIdOrResponseData as string | undefined);
|
|
1485
|
+
this._context = context;
|
|
1486
|
+
this._agentId = agentIdOrResponseData as string | undefined;
|
|
1487
|
+
this._data = data;
|
|
1488
|
+
// Store the parsed message
|
|
1489
|
+
this.message = messageOrData;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
this.editContextManager = new ChatContextManagerImpl(this._context);
|
|
1175
1493
|
this.editContextManager.onDidChange(this._onDidChangeEmitter.fire, this._onDidChangeEmitter, this.toDispose);
|
|
1494
|
+
|
|
1495
|
+
// Wire response changes to propagate through request to session
|
|
1496
|
+
this._response.onDidChange(() => {
|
|
1497
|
+
// Fire a generic addVariable event to propagate response changes
|
|
1498
|
+
this._onDidChangeEmitter.fire({ kind: 'responseChanged' });
|
|
1499
|
+
}, this, this.toDispose);
|
|
1500
|
+
|
|
1176
1501
|
this.toDispose.push(this._onDidChangeEmitter);
|
|
1177
1502
|
}
|
|
1178
1503
|
|
|
1504
|
+
/**
|
|
1505
|
+
* Type guard to determine if we're receiving serialized data
|
|
1506
|
+
*/
|
|
1507
|
+
protected isSerializedRequestData(data: ParsedChatRequest | SerializableChatRequestData): data is SerializableChatRequestData {
|
|
1508
|
+
return 'id' in data && 'text' in data && !('request' in data);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Restore this request model from serialized data
|
|
1513
|
+
*/
|
|
1514
|
+
protected restoreFromSerializedData(
|
|
1515
|
+
reqData: SerializableChatRequestData,
|
|
1516
|
+
respData?: SerializableChatResponseData
|
|
1517
|
+
): void {
|
|
1518
|
+
this._id = reqData.id;
|
|
1519
|
+
this._request = { text: reqData.text };
|
|
1520
|
+
this._agentId = reqData.agentId;
|
|
1521
|
+
this._data = {};
|
|
1522
|
+
|
|
1523
|
+
// Create minimal context
|
|
1524
|
+
this._context = { variables: [] };
|
|
1525
|
+
|
|
1526
|
+
// TODO: More sophisticated restoration?
|
|
1527
|
+
// Casting required because 'message' is readonly
|
|
1528
|
+
(this as { message: ParsedChatRequest }).message = {
|
|
1529
|
+
request: this._request,
|
|
1530
|
+
parts: [{
|
|
1531
|
+
kind: 'text',
|
|
1532
|
+
text: reqData.text,
|
|
1533
|
+
promptText: reqData.text,
|
|
1534
|
+
range: { start: 0, endExclusive: reqData.text.length }
|
|
1535
|
+
}],
|
|
1536
|
+
toolRequests: new Map(),
|
|
1537
|
+
variables: []
|
|
1538
|
+
};
|
|
1539
|
+
|
|
1540
|
+
// Restore response if present
|
|
1541
|
+
if (respData) {
|
|
1542
|
+
this._response = new MutableChatResponseModel(this._id, this._agentId, respData);
|
|
1543
|
+
} else {
|
|
1544
|
+
this._response = new MutableChatResponseModel(this._id, this._agentId);
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
// Note: ChangeSet restoration will be handled by ChatService using deserializer registry
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1179
1550
|
get changeSet(): ChangeSetImpl | undefined {
|
|
1180
1551
|
return this._changeSet;
|
|
1181
1552
|
}
|
|
@@ -1265,6 +1636,18 @@ export class MutableChatRequestModel implements ChatRequestModel, EditableChatRe
|
|
|
1265
1636
|
this.response.cancel();
|
|
1266
1637
|
}
|
|
1267
1638
|
|
|
1639
|
+
toSerializable(): SerializableChatRequestData {
|
|
1640
|
+
return {
|
|
1641
|
+
id: this.id,
|
|
1642
|
+
text: this.request.text,
|
|
1643
|
+
agentId: this.agentId,
|
|
1644
|
+
changeSet: this._changeSet ? {
|
|
1645
|
+
title: this._changeSet.title,
|
|
1646
|
+
elements: this._changeSet.getElements().map(elem => elem.toSerializable?.()).filter((elem): elem is SerializableChangeSetElement => elem !== undefined)
|
|
1647
|
+
} : undefined
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1268
1651
|
dispose(): void {
|
|
1269
1652
|
this.toDispose.dispose();
|
|
1270
1653
|
}
|
|
@@ -1323,6 +1706,15 @@ export class ErrorChatResponseContentImpl implements ErrorChatResponseContent {
|
|
|
1323
1706
|
asString(): string | undefined {
|
|
1324
1707
|
return undefined;
|
|
1325
1708
|
}
|
|
1709
|
+
toSerializable(): SerializableChatResponseContentData<ErrorContentData> {
|
|
1710
|
+
return {
|
|
1711
|
+
kind: 'error',
|
|
1712
|
+
data: {
|
|
1713
|
+
message: this._error.message,
|
|
1714
|
+
stack: this._error.stack
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1326
1718
|
}
|
|
1327
1719
|
|
|
1328
1720
|
export class TextChatResponseContentImpl implements TextChatResponseContent {
|
|
@@ -1349,6 +1741,7 @@ export class TextChatResponseContentImpl implements TextChatResponseContent {
|
|
|
1349
1741
|
this._content += nextChatResponseContent.content;
|
|
1350
1742
|
return true;
|
|
1351
1743
|
}
|
|
1744
|
+
|
|
1352
1745
|
toLanguageModelMessage(): TextMessage {
|
|
1353
1746
|
return {
|
|
1354
1747
|
actor: 'ai',
|
|
@@ -1356,6 +1749,13 @@ export class TextChatResponseContentImpl implements TextChatResponseContent {
|
|
|
1356
1749
|
text: this.content
|
|
1357
1750
|
};
|
|
1358
1751
|
}
|
|
1752
|
+
|
|
1753
|
+
toSerializable(): SerializableChatResponseContentData<TextContentData> {
|
|
1754
|
+
return {
|
|
1755
|
+
kind: 'text',
|
|
1756
|
+
data: { content: this._content }
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1359
1759
|
}
|
|
1360
1760
|
|
|
1361
1761
|
export class ThinkingChatResponseContentImpl implements ThinkingChatResponseContent {
|
|
@@ -1401,6 +1801,16 @@ export class ThinkingChatResponseContentImpl implements ThinkingChatResponseCont
|
|
|
1401
1801
|
signature: this.signature
|
|
1402
1802
|
};
|
|
1403
1803
|
}
|
|
1804
|
+
|
|
1805
|
+
toSerializable(): SerializableChatResponseContentData<ThinkingContentData> {
|
|
1806
|
+
return {
|
|
1807
|
+
kind: 'thinking',
|
|
1808
|
+
data: {
|
|
1809
|
+
content: this._content,
|
|
1810
|
+
signature: this._signature
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1404
1814
|
}
|
|
1405
1815
|
|
|
1406
1816
|
export class MarkdownChatResponseContentImpl implements MarkdownChatResponseContent {
|
|
@@ -1435,6 +1845,13 @@ export class MarkdownChatResponseContentImpl implements MarkdownChatResponseCont
|
|
|
1435
1845
|
text: this.content.value
|
|
1436
1846
|
};
|
|
1437
1847
|
}
|
|
1848
|
+
|
|
1849
|
+
toSerializable(): SerializableChatResponseContentData<MarkdownContentData> {
|
|
1850
|
+
return {
|
|
1851
|
+
kind: 'markdownContent',
|
|
1852
|
+
data: { content: this._content.value }
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1438
1855
|
}
|
|
1439
1856
|
|
|
1440
1857
|
export class InformationalChatResponseContentImpl implements InformationalChatResponseContent {
|
|
@@ -1457,6 +1874,13 @@ export class InformationalChatResponseContentImpl implements InformationalChatRe
|
|
|
1457
1874
|
this._content.appendMarkdown(nextChatResponseContent.content.value);
|
|
1458
1875
|
return true;
|
|
1459
1876
|
}
|
|
1877
|
+
|
|
1878
|
+
toSerializable(): SerializableChatResponseContentData<InformationalContentData> {
|
|
1879
|
+
return {
|
|
1880
|
+
kind: 'informational',
|
|
1881
|
+
data: { content: this._content.value }
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1460
1884
|
}
|
|
1461
1885
|
|
|
1462
1886
|
export class CodeChatResponseContentImpl implements CodeChatResponseContent {
|
|
@@ -1491,6 +1915,17 @@ export class CodeChatResponseContentImpl implements CodeChatResponseContent {
|
|
|
1491
1915
|
this._code += `${nextChatResponseContent.code}`;
|
|
1492
1916
|
return true;
|
|
1493
1917
|
}
|
|
1918
|
+
|
|
1919
|
+
toSerializable(): SerializableChatResponseContentData<CodeContentData> {
|
|
1920
|
+
return {
|
|
1921
|
+
kind: 'code',
|
|
1922
|
+
data: {
|
|
1923
|
+
code: this._code,
|
|
1924
|
+
language: this._language,
|
|
1925
|
+
location: this._location
|
|
1926
|
+
}
|
|
1927
|
+
};
|
|
1928
|
+
}
|
|
1494
1929
|
}
|
|
1495
1930
|
|
|
1496
1931
|
export class ToolCallChatResponseContentImpl implements ToolCallChatResponseContent {
|
|
@@ -1622,6 +2057,19 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
|
|
|
1622
2057
|
name: this.name ?? ''
|
|
1623
2058
|
}];
|
|
1624
2059
|
}
|
|
2060
|
+
|
|
2061
|
+
toSerializable(): SerializableChatResponseContentData<ToolCallContentData> {
|
|
2062
|
+
return {
|
|
2063
|
+
kind: 'toolCall',
|
|
2064
|
+
data: {
|
|
2065
|
+
id: this._id,
|
|
2066
|
+
name: this._name,
|
|
2067
|
+
arguments: this._arguments,
|
|
2068
|
+
finished: this._finished,
|
|
2069
|
+
result: this._result
|
|
2070
|
+
}
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
1625
2073
|
}
|
|
1626
2074
|
|
|
1627
2075
|
export const COMMAND_CHAT_RESPONSE_COMMAND: Command = {
|
|
@@ -1639,6 +2087,17 @@ export class CommandChatResponseContentImpl implements CommandChatResponseConten
|
|
|
1639
2087
|
asString(): string {
|
|
1640
2088
|
return this.command?.id || this.customCallback?.label || 'command';
|
|
1641
2089
|
}
|
|
2090
|
+
|
|
2091
|
+
toSerializable(): SerializableChatResponseContentData<CommandContentData> {
|
|
2092
|
+
return {
|
|
2093
|
+
kind: 'command',
|
|
2094
|
+
data: {
|
|
2095
|
+
commandId: this.command?.id,
|
|
2096
|
+
commandLabel: this.customCallback?.label,
|
|
2097
|
+
arguments: this.args
|
|
2098
|
+
}
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
1642
2101
|
}
|
|
1643
2102
|
|
|
1644
2103
|
export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayoutChatResponseContent {
|
|
@@ -1669,20 +2128,58 @@ export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayout
|
|
|
1669
2128
|
}
|
|
1670
2129
|
return true;
|
|
1671
2130
|
}
|
|
2131
|
+
|
|
2132
|
+
toSerializable(): SerializableChatResponseContentData<HorizontalLayoutContentData> {
|
|
2133
|
+
return {
|
|
2134
|
+
kind: 'horizontal',
|
|
2135
|
+
data: {
|
|
2136
|
+
content: this._content.map(child => {
|
|
2137
|
+
const serialized = child.toSerializable?.();
|
|
2138
|
+
if (!serialized) {
|
|
2139
|
+
return {
|
|
2140
|
+
kind: child.kind,
|
|
2141
|
+
fallbackMessage: child.asString?.(),
|
|
2142
|
+
data: undefined
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
return {
|
|
2146
|
+
...serialized,
|
|
2147
|
+
fallbackMessage: child.asString?.()
|
|
2148
|
+
};
|
|
2149
|
+
})
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
1672
2153
|
}
|
|
1673
2154
|
|
|
1674
2155
|
/**
|
|
1675
2156
|
* Default implementation for the QuestionResponseContent.
|
|
2157
|
+
* Can be created with or without handler/request for read-only (restored) mode.
|
|
1676
2158
|
*/
|
|
1677
2159
|
export class QuestionResponseContentImpl implements QuestionResponseContent {
|
|
1678
2160
|
readonly kind = 'question';
|
|
1679
2161
|
protected _selectedOption: { text: string; value?: string } | undefined;
|
|
1680
|
-
|
|
1681
|
-
|
|
2162
|
+
|
|
2163
|
+
constructor(
|
|
2164
|
+
public question: string,
|
|
2165
|
+
public options: { text: string, value?: string }[],
|
|
2166
|
+
public request: MutableChatRequestModel | undefined,
|
|
2167
|
+
public handler: QuestionResponseHandler | undefined,
|
|
2168
|
+
selectedOption?: { text: string; value?: string }
|
|
2169
|
+
) {
|
|
2170
|
+
this._selectedOption = selectedOption;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
get isReadOnly(): boolean {
|
|
2174
|
+
return !this.handler || !this.request;
|
|
1682
2175
|
}
|
|
2176
|
+
|
|
1683
2177
|
set selectedOption(option: { text: string; value?: string; } | undefined) {
|
|
1684
2178
|
this._selectedOption = option;
|
|
1685
|
-
|
|
2179
|
+
// Only trigger change notification if request is available (not in read-only mode)
|
|
2180
|
+
if (this.request) {
|
|
2181
|
+
this.request.response.response.responseContentChanged();
|
|
2182
|
+
}
|
|
1686
2183
|
}
|
|
1687
2184
|
get selectedOption(): { text: string; value?: string; } | undefined {
|
|
1688
2185
|
return this._selectedOption;
|
|
@@ -1694,6 +2191,16 @@ ${this.selectedOption ? `Answer: ${this.selectedOption?.text}` : 'No answer'}`;
|
|
|
1694
2191
|
merge?(): boolean {
|
|
1695
2192
|
return false;
|
|
1696
2193
|
}
|
|
2194
|
+
toSerializable(): SerializableChatResponseContentData<QuestionContentData> {
|
|
2195
|
+
return {
|
|
2196
|
+
kind: 'question',
|
|
2197
|
+
data: {
|
|
2198
|
+
question: this.question,
|
|
2199
|
+
options: this.options,
|
|
2200
|
+
selectedOption: this._selectedOption
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
1697
2204
|
}
|
|
1698
2205
|
|
|
1699
2206
|
class ChatResponseImpl implements ChatResponse {
|
|
@@ -1704,7 +2211,6 @@ class ChatResponseImpl implements ChatResponse {
|
|
|
1704
2211
|
protected _responseRepresentationForDisplay: string;
|
|
1705
2212
|
|
|
1706
2213
|
constructor() {
|
|
1707
|
-
// TODO accept serialized data as a parameter to restore a previously saved ChatResponse
|
|
1708
2214
|
this._content = [];
|
|
1709
2215
|
}
|
|
1710
2216
|
|
|
@@ -1815,18 +2321,52 @@ export class MutableChatResponseModel implements ChatResponseModel {
|
|
|
1815
2321
|
protected _errorObject: Error | undefined;
|
|
1816
2322
|
protected _cancellationToken: CancellationTokenSource;
|
|
1817
2323
|
|
|
1818
|
-
constructor(
|
|
1819
|
-
|
|
2324
|
+
constructor(
|
|
2325
|
+
requestId: string,
|
|
2326
|
+
agentId?: string,
|
|
2327
|
+
serializedData?: SerializableChatResponseData
|
|
2328
|
+
) {
|
|
1820
2329
|
this._requestId = requestId;
|
|
1821
|
-
this.
|
|
1822
|
-
this.
|
|
2330
|
+
this._agentId = agentId;
|
|
2331
|
+
this._cancellationToken = new CancellationTokenSource();
|
|
2332
|
+
|
|
2333
|
+
// Check if we're restoring from serialized data
|
|
2334
|
+
if (serializedData) {
|
|
2335
|
+
this.restoreFromSerializedData(serializedData);
|
|
2336
|
+
} else {
|
|
2337
|
+
// Normal creation path
|
|
2338
|
+
this._id = generateUuid();
|
|
2339
|
+
this._progressMessages = [];
|
|
2340
|
+
this._isComplete = false;
|
|
2341
|
+
this._isWaitingForInput = false;
|
|
2342
|
+
this._isError = false;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
1823
2345
|
const response = new ChatResponseImpl();
|
|
1824
2346
|
response.onDidChange(() => this._onDidChangeEmitter.fire());
|
|
1825
2347
|
this._response = response;
|
|
1826
|
-
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
/**
|
|
2351
|
+
* Restore this response model from serialized data
|
|
2352
|
+
*/
|
|
2353
|
+
protected restoreFromSerializedData(data: SerializableChatResponseData): void {
|
|
2354
|
+
this._id = data.id;
|
|
2355
|
+
// Always mark restored responses as complete since there's no active agent
|
|
2356
|
+
this._isComplete = true;
|
|
2357
|
+
this._isError = data.isError;
|
|
2358
|
+
|
|
2359
|
+
// Do not restore waitingForInput state - when a session is restored,
|
|
2360
|
+
// the agent that was waiting for input is no longer running
|
|
1827
2361
|
this._isWaitingForInput = false;
|
|
1828
|
-
|
|
1829
|
-
this.
|
|
2362
|
+
// TODO: Restore progressMessages?
|
|
2363
|
+
this._progressMessages = [];
|
|
2364
|
+
|
|
2365
|
+
if (data.errorMessage) {
|
|
2366
|
+
this._errorObject = new Error(data.errorMessage);
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
// Note: Content restoration will be handled by ChatService using deserializer registry
|
|
1830
2370
|
}
|
|
1831
2371
|
|
|
1832
2372
|
get id(): string {
|
|
@@ -1949,6 +2489,31 @@ export class MutableChatResponseModel implements ChatResponseModel {
|
|
|
1949
2489
|
get isError(): boolean {
|
|
1950
2490
|
return this._isError;
|
|
1951
2491
|
}
|
|
2492
|
+
|
|
2493
|
+
toSerializable(): SerializableChatResponseData {
|
|
2494
|
+
return {
|
|
2495
|
+
id: this.id,
|
|
2496
|
+
requestId: this.requestId,
|
|
2497
|
+
isComplete: this.isComplete,
|
|
2498
|
+
isError: this.isError,
|
|
2499
|
+
errorMessage: this.errorObject?.message,
|
|
2500
|
+
content: this.response.content.map(c => {
|
|
2501
|
+
const serialized = c.toSerializable?.();
|
|
2502
|
+
if (!serialized) {
|
|
2503
|
+
// Fallback if toSerializable not implemented
|
|
2504
|
+
return {
|
|
2505
|
+
kind: c.kind,
|
|
2506
|
+
fallbackMessage: c.asString?.(),
|
|
2507
|
+
data: undefined
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
return {
|
|
2511
|
+
...serialized,
|
|
2512
|
+
fallbackMessage: c.asString?.()
|
|
2513
|
+
};
|
|
2514
|
+
})
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
1952
2517
|
}
|
|
1953
2518
|
|
|
1954
2519
|
export class ErrorChatResponseModel extends MutableChatResponseModel {
|
|
@@ -1993,4 +2558,42 @@ export class ProgressChatResponseContentImpl implements ProgressChatResponseCont
|
|
|
1993
2558
|
text: this.message
|
|
1994
2559
|
};
|
|
1995
2560
|
}
|
|
2561
|
+
toSerializable(): SerializableChatResponseContentData<ProgressContentData> {
|
|
2562
|
+
return {
|
|
2563
|
+
kind: 'progress',
|
|
2564
|
+
data: { message: this._message }
|
|
2565
|
+
};
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
/**
|
|
2570
|
+
* Fallback content for unknown content types.
|
|
2571
|
+
* Used when a deserializer is not available (e.g., content from removed extension).
|
|
2572
|
+
*/
|
|
2573
|
+
export interface UnknownChatResponseContent extends ChatResponseContent {
|
|
2574
|
+
kind: 'unknown';
|
|
2575
|
+
originalKind: string;
|
|
2576
|
+
fallbackMessage?: string;
|
|
2577
|
+
data: unknown;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
export class UnknownChatResponseContentImpl implements UnknownChatResponseContent {
|
|
2581
|
+
readonly kind = 'unknown';
|
|
2582
|
+
|
|
2583
|
+
constructor(
|
|
2584
|
+
public readonly originalKind: string,
|
|
2585
|
+
public readonly fallbackMessage: string | undefined,
|
|
2586
|
+
public readonly data: unknown
|
|
2587
|
+
) { }
|
|
2588
|
+
|
|
2589
|
+
asString(): string | undefined {
|
|
2590
|
+
return this.fallbackMessage;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
toSerializable(): SerializableChatResponseContentData {
|
|
2594
|
+
return {
|
|
2595
|
+
kind: 'unknown',
|
|
2596
|
+
data: this.data
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
1996
2599
|
}
|