@theia/ai-chat 1.58.3 → 1.59.0-next.72

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.
Files changed (128) hide show
  1. package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
  2. package/lib/browser/ai-chat-frontend-module.js +16 -13
  3. package/lib/browser/ai-chat-frontend-module.js.map +1 -1
  4. package/lib/browser/ai-chat-preferences.d.ts +1 -0
  5. package/lib/browser/ai-chat-preferences.d.ts.map +1 -1
  6. package/lib/browser/ai-chat-preferences.js +12 -3
  7. package/lib/browser/ai-chat-preferences.js.map +1 -1
  8. package/lib/browser/change-set-file-element.d.ts +31 -6
  9. package/lib/browser/change-set-file-element.d.ts.map +1 -1
  10. package/lib/browser/change-set-file-element.js +108 -15
  11. package/lib/browser/change-set-file-element.js.map +1 -1
  12. package/lib/browser/change-set-file-resource.d.ts +40 -8
  13. package/lib/browser/change-set-file-resource.d.ts.map +1 -1
  14. package/lib/browser/change-set-file-resource.js +123 -37
  15. package/lib/browser/change-set-file-resource.js.map +1 -1
  16. package/lib/browser/change-set-file-service.d.ts +9 -3
  17. package/lib/browser/change-set-file-service.d.ts.map +1 -1
  18. package/lib/browser/change-set-file-service.js +35 -13
  19. package/lib/browser/change-set-file-service.js.map +1 -1
  20. package/lib/browser/change-set-variable.d.ts +11 -0
  21. package/lib/browser/change-set-variable.d.ts.map +1 -0
  22. package/lib/browser/change-set-variable.js +56 -0
  23. package/lib/browser/change-set-variable.js.map +1 -0
  24. package/lib/browser/context-file-variable-label-provider.d.ts +14 -0
  25. package/lib/browser/context-file-variable-label-provider.d.ts.map +1 -0
  26. package/lib/browser/context-file-variable-label-provider.js +63 -0
  27. package/lib/browser/context-file-variable-label-provider.js.map +1 -0
  28. package/lib/browser/context-variable-label-provider.d.ts +9 -0
  29. package/lib/browser/context-variable-label-provider.d.ts.map +1 -0
  30. package/lib/browser/context-variable-label-provider.js +55 -0
  31. package/lib/browser/context-variable-label-provider.js.map +1 -0
  32. package/lib/browser/file-chat-variable-contribution.d.ts +18 -0
  33. package/lib/browser/file-chat-variable-contribution.d.ts.map +1 -0
  34. package/lib/browser/file-chat-variable-contribution.js +135 -0
  35. package/lib/browser/file-chat-variable-contribution.js.map +1 -0
  36. package/lib/browser/frontend-chat-service.d.ts +10 -3
  37. package/lib/browser/frontend-chat-service.d.ts.map +1 -1
  38. package/lib/browser/frontend-chat-service.js +39 -19
  39. package/lib/browser/frontend-chat-service.js.map +1 -1
  40. package/lib/common/chat-agents.d.ts +54 -24
  41. package/lib/common/chat-agents.d.ts.map +1 -1
  42. package/lib/common/chat-agents.js +58 -20
  43. package/lib/common/chat-agents.js.map +1 -1
  44. package/lib/common/chat-history-entry.js +1 -1
  45. package/lib/common/chat-history-entry.js.map +1 -1
  46. package/lib/common/chat-model.d.ts +84 -36
  47. package/lib/common/chat-model.d.ts.map +1 -1
  48. package/lib/common/chat-model.js +142 -51
  49. package/lib/common/chat-model.js.map +1 -1
  50. package/lib/common/chat-service.d.ts +27 -10
  51. package/lib/common/chat-service.d.ts.map +1 -1
  52. package/lib/common/chat-service.js +80 -20
  53. package/lib/common/chat-service.js.map +1 -1
  54. package/lib/common/chat-string-utils.d.ts +3 -0
  55. package/lib/common/chat-string-utils.d.ts.map +1 -0
  56. package/lib/common/chat-string-utils.js +27 -0
  57. package/lib/common/chat-string-utils.js.map +1 -0
  58. package/lib/common/chat-tool-request-service.d.ts +5 -5
  59. package/lib/common/chat-tool-request-service.d.ts.map +1 -1
  60. package/lib/common/chat-tool-request-service.js.map +1 -1
  61. package/lib/common/context-details-variable.d.ts +9 -0
  62. package/lib/common/context-details-variable.d.ts.map +1 -0
  63. package/lib/common/context-details-variable.js +57 -0
  64. package/lib/common/context-details-variable.js.map +1 -0
  65. package/lib/common/context-summary-variable.d.ts +9 -0
  66. package/lib/common/context-summary-variable.d.ts.map +1 -0
  67. package/lib/common/context-summary-variable.js +57 -0
  68. package/lib/common/context-summary-variable.js.map +1 -0
  69. package/lib/common/context-variables.d.ts +4 -0
  70. package/lib/common/context-variables.d.ts.map +1 -0
  71. package/lib/common/context-variables.js +22 -0
  72. package/lib/common/context-variables.js.map +1 -0
  73. package/lib/common/custom-chat-agent.d.ts +6 -10
  74. package/lib/common/custom-chat-agent.d.ts.map +1 -1
  75. package/lib/common/custom-chat-agent.js +8 -11
  76. package/lib/common/custom-chat-agent.js.map +1 -1
  77. package/lib/common/index.d.ts +1 -3
  78. package/lib/common/index.d.ts.map +1 -1
  79. package/lib/common/index.js +1 -3
  80. package/lib/common/index.js.map +1 -1
  81. package/lib/common/parse-contents.d.ts +2 -2
  82. package/lib/common/parse-contents.d.ts.map +1 -1
  83. package/lib/common/parse-contents.js.map +1 -1
  84. package/lib/common/parse-contents.spec.d.ts.map +1 -1
  85. package/lib/common/parse-contents.spec.js.map +1 -1
  86. package/lib/common/response-content-matcher.d.ts +3 -3
  87. package/lib/common/response-content-matcher.d.ts.map +1 -1
  88. package/lib/common/response-content-matcher.js.map +1 -1
  89. package/package.json +12 -10
  90. package/src/browser/ai-chat-frontend-module.ts +20 -18
  91. package/src/browser/ai-chat-preferences.ts +13 -2
  92. package/src/browser/change-set-file-element.ts +109 -20
  93. package/src/browser/change-set-file-resource.ts +125 -39
  94. package/src/browser/change-set-file-service.ts +38 -16
  95. package/src/browser/change-set-variable.ts +54 -0
  96. package/src/browser/context-file-variable-label-provider.ts +62 -0
  97. package/src/browser/context-variable-label-provider.ts +56 -0
  98. package/src/browser/file-chat-variable-contribution.ts +143 -0
  99. package/src/browser/frontend-chat-service.ts +40 -26
  100. package/src/common/chat-agents.ts +87 -30
  101. package/src/common/chat-history-entry.ts +1 -1
  102. package/src/common/chat-model.ts +204 -70
  103. package/src/common/chat-service.ts +92 -24
  104. package/src/common/chat-string-utils.ts +23 -0
  105. package/src/common/chat-tool-request-service.ts +5 -5
  106. package/src/common/context-details-variable.ts +53 -0
  107. package/src/common/context-summary-variable.ts +53 -0
  108. package/src/common/context-variables.ts +19 -0
  109. package/src/common/custom-chat-agent.ts +9 -20
  110. package/src/common/index.ts +1 -3
  111. package/src/common/parse-contents.spec.ts +2 -2
  112. package/src/common/parse-contents.ts +2 -2
  113. package/src/common/response-content-matcher.ts +3 -3
  114. package/lib/common/command-chat-agents.d.ts +0 -33
  115. package/lib/common/command-chat-agents.d.ts.map +0 -1
  116. package/lib/common/command-chat-agents.js +0 -329
  117. package/lib/common/command-chat-agents.js.map +0 -1
  118. package/lib/common/orchestrator-chat-agent.d.ts +0 -22
  119. package/lib/common/orchestrator-chat-agent.d.ts.map +0 -1
  120. package/lib/common/orchestrator-chat-agent.js +0 -167
  121. package/lib/common/orchestrator-chat-agent.js.map +0 -1
  122. package/lib/common/universal-chat-agent.d.ts +0 -16
  123. package/lib/common/universal-chat-agent.d.ts.map +0 -1
  124. package/lib/common/universal-chat-agent.js +0 -109
  125. package/lib/common/universal-chat-agent.js.map +0 -1
  126. package/src/common/command-chat-agents.ts +0 -354
  127. package/src/common/orchestrator-chat-agent.ts +0 -179
  128. package/src/common/universal-chat-agent.ts +0 -117
@@ -23,8 +23,8 @@ import { CancellationToken, CancellationTokenSource, Command, Disposable, Emitte
23
23
  import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
24
24
  import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
25
25
  import { ChatAgentLocation } from './chat-agents';
26
- import { ParsedChatRequest, ParsedChatRequestVariablePart } from './parsed-chat-request';
27
- import { ResolvedAIVariable } from '@theia/ai-core';
26
+ import { ParsedChatRequest } from './parsed-chat-request';
27
+ import { AIVariableResolutionRequest, ResolvedAIContextVariable } from '@theia/ai-core';
28
28
 
29
29
  /**********************
30
30
  * INTERFACES AND TYPE GUARDS
@@ -33,9 +33,10 @@ import { ResolvedAIVariable } from '@theia/ai-core';
33
33
  export type ChatChangeEvent =
34
34
  | ChatAddRequestEvent
35
35
  | ChatAddResponseEvent
36
+ | ChatAddVariableEvent
37
+ | ChatRemoveVariableEvent
36
38
  | ChatRemoveRequestEvent
37
39
  | ChatSetChangeSetEvent
38
- | ChatSetChangeDeleteEvent
39
40
  | ChatUpdateChangeSetEvent
40
41
  | ChatRemoveChangeSetEvent;
41
42
 
@@ -52,10 +53,7 @@ export interface ChatAddResponseEvent {
52
53
  export interface ChatSetChangeSetEvent {
53
54
  kind: 'setChangeSet';
54
55
  changeSet: ChangeSet;
55
- }
56
-
57
- export interface ChatSetChangeDeleteEvent {
58
- kind: 'deleteChangeSet';
56
+ oldChangeSet?: ChangeSet;
59
57
  }
60
58
 
61
59
  export interface ChatUpdateChangeSetEvent {
@@ -68,9 +66,17 @@ export interface ChatRemoveChangeSetEvent {
68
66
  changeSet: ChangeSet;
69
67
  }
70
68
 
69
+ export interface ChatAddVariableEvent {
70
+ kind: 'addVariable';
71
+ }
72
+
73
+ export interface ChatRemoveVariableEvent {
74
+ kind: 'removeVariable';
75
+ }
76
+
71
77
  export namespace ChatChangeEvent {
72
78
  export function isChangeSetEvent(event: ChatChangeEvent): event is ChatSetChangeSetEvent | ChatUpdateChangeSetEvent | ChatRemoveChangeSetEvent {
73
- return event.kind === 'setChangeSet' || event.kind === 'deleteChangeSet' || event.kind === 'removeChangeSet' || event.kind === 'updateChangeSet';
79
+ return event.kind === 'setChangeSet' || event.kind === 'removeChangeSet' || event.kind === 'updateChangeSet';
74
80
  }
75
81
  }
76
82
 
@@ -88,30 +94,43 @@ export interface ChatModel {
88
94
  readonly id: string;
89
95
  readonly location: ChatAgentLocation;
90
96
  readonly changeSet?: ChangeSet;
97
+ readonly context: ChatContextManager;
91
98
  getRequests(): ChatRequestModel[];
92
99
  isEmpty(): boolean;
93
100
  }
94
101
 
95
- export interface ChangeSet {
102
+ export interface ChangeSet extends Disposable {
103
+ onDidChange: Event<ChangeSetChangeEvent>;
96
104
  readonly title: string;
97
105
  getElements(): ChangeSetElement[];
106
+ dispose(): void;
107
+ }
108
+
109
+ export interface ChatContextManager {
110
+ onDidChange: Event<ChatAddVariableEvent | ChatRemoveVariableEvent>;
111
+ getVariables(): readonly AIVariableResolutionRequest[]
112
+ addVariables(...variables: AIVariableResolutionRequest[]): void;
113
+ deleteVariables(...indices: number[]): void;
114
+ clear(): void;
98
115
  }
99
116
 
100
117
  export interface ChangeSetElement {
101
118
  readonly uri: URI;
102
119
 
120
+ onDidChange?: Event<void>
103
121
  readonly name?: string;
104
122
  readonly icon?: string;
105
123
  readonly additionalInfo?: string;
106
124
 
107
- readonly state?: 'pending' | 'applied' | 'discarded';
125
+ readonly state?: 'pending' | 'applied' | 'stale';
108
126
  readonly type?: 'add' | 'modify' | 'delete';
109
127
  readonly data?: { [key: string]: unknown };
110
128
 
111
129
  open?(): Promise<void>;
112
130
  openChange?(): Promise<void>;
113
- accept?(): Promise<void>;
114
- discard?(): Promise<void>;
131
+ apply?(): Promise<void>;
132
+ revert?(): Promise<void>;
133
+ dispose?(): void;
115
134
  }
116
135
 
117
136
  export interface ChatRequest {
@@ -119,12 +138,17 @@ export interface ChatRequest {
119
138
  readonly displayText?: string;
120
139
  }
121
140
 
141
+ export interface ChatContext {
142
+ variables: ResolvedAIContextVariable[];
143
+ }
144
+
122
145
  export interface ChatRequestModel {
123
146
  readonly id: string;
124
147
  readonly session: ChatModel;
125
148
  readonly request: ChatRequest;
126
149
  readonly response: ChatResponseModel;
127
150
  readonly message: ParsedChatRequest;
151
+ readonly context: ChatContext;
128
152
  readonly agentId?: string;
129
153
  readonly data?: { [key: string]: unknown };
130
154
  }
@@ -171,6 +195,7 @@ export interface ChatResponseContent {
171
195
  * representation of the response.
172
196
  */
173
197
  asString?(): string | undefined;
198
+ asDisplayString?(): string | undefined;
174
199
  merge?(nextChatResponseContent: ChatResponseContent): boolean;
175
200
  }
176
201
 
@@ -188,6 +213,11 @@ export namespace ChatResponseContent {
188
213
  ): obj is Required<Pick<ChatResponseContent, 'asString'>> & ChatResponseContent {
189
214
  return typeof obj.asString === 'function';
190
215
  }
216
+ export function hasDisplayString(
217
+ obj: ChatResponseContent
218
+ ): obj is Required<Pick<ChatResponseContent, 'asDisplayString'>> & ChatResponseContent {
219
+ return typeof obj.asDisplayString === 'function';
220
+ }
191
221
  export function hasMerge(
192
222
  obj: ChatResponseContent
193
223
  ): obj is Required<Pick<ChatResponseContent, 'merge'>> & ChatResponseContent {
@@ -369,7 +399,7 @@ export interface QuestionResponseContent extends ChatResponseContent {
369
399
  options: { text: string, value?: string }[];
370
400
  selectedOption?: { text: string, value?: string };
371
401
  handler: QuestionResponseHandler;
372
- request: ChatRequestModelImpl;
402
+ request: MutableChatRequestModel;
373
403
  }
374
404
 
375
405
  export namespace QuestionResponseContent {
@@ -390,7 +420,7 @@ export namespace QuestionResponseContent {
390
420
  'handler' in obj &&
391
421
  typeof (obj as { handler: unknown }).handler === 'function' &&
392
422
  'request' in obj &&
393
- obj.request instanceof ChatRequestModelImpl
423
+ obj.request instanceof MutableChatRequestModel
394
424
  );
395
425
  }
396
426
  }
@@ -398,6 +428,7 @@ export namespace QuestionResponseContent {
398
428
  export interface ChatResponse {
399
429
  readonly content: ChatResponseContent[];
400
430
  asString(): string;
431
+ asDisplayString(): string;
401
432
  }
402
433
 
403
434
  /**
@@ -459,26 +490,27 @@ export interface ChatResponseModel {
459
490
  * Implementations
460
491
  **********************/
461
492
 
462
- export class ChatModelImpl implements ChatModel {
493
+ export class MutableChatModel implements ChatModel, Disposable {
463
494
  protected readonly _onDidChangeEmitter = new Emitter<ChatChangeEvent>();
464
495
  onDidChange: Event<ChatChangeEvent> = this._onDidChangeEmitter.event;
465
496
 
466
- protected _requests: ChatRequestModelImpl[];
497
+ protected _requests: MutableChatRequestModel[];
467
498
  protected _id: string;
468
- protected _changeSetListener?: Disposable;
469
499
  protected _changeSet?: ChangeSetImpl;
500
+ protected readonly _contextManager = new ChatContextManagerImpl();
470
501
 
471
502
  constructor(public readonly location = ChatAgentLocation.Panel) {
472
503
  // TODO accept serialized data as a parameter to restore a previously saved ChatModel
473
504
  this._requests = [];
474
505
  this._id = generateUuid();
506
+ this._contextManager.onDidChange(e => this._onDidChangeEmitter.fire(e));
475
507
  }
476
508
 
477
- getRequests(): ChatRequestModelImpl[] {
509
+ getRequests(): MutableChatRequestModel[] {
478
510
  return this._requests;
479
511
  }
480
512
 
481
- getRequest(id: string): ChatRequestModelImpl | undefined {
513
+ getRequest(id: string): MutableChatRequestModel | undefined {
482
514
  return this._requests.find(request => request.id === id);
483
515
  }
484
516
 
@@ -490,23 +522,26 @@ export class ChatModelImpl implements ChatModel {
490
522
  return this._changeSet;
491
523
  }
492
524
 
525
+ get context(): ChatContextManager {
526
+ return this._contextManager;
527
+ }
528
+
493
529
  setChangeSet(changeSet: ChangeSetImpl | undefined): void {
494
- this._changeSet = changeSet;
495
- if (this._changeSet === undefined) {
496
- this._changeSetListener?.dispose();
497
- this._onDidChangeEmitter.fire({
498
- kind: 'deleteChangeSet',
499
- });
500
- return;
530
+ if (!changeSet) {
531
+ return this.removeChangeSet();
501
532
  }
533
+ const oldChangeSet = this._changeSet;
534
+ oldChangeSet?.dispose();
535
+ this._changeSet = changeSet;
502
536
  this._onDidChangeEmitter.fire({
503
537
  kind: 'setChangeSet',
504
- changeSet: this._changeSet,
538
+ changeSet,
539
+ oldChangeSet,
505
540
  });
506
- this._changeSetListener = this._changeSet.onDidChange(() => {
541
+ changeSet.onDidChange(() => {
507
542
  this._onDidChangeEmitter.fire({
508
543
  kind: 'updateChangeSet',
509
- changeSet: this._changeSet!,
544
+ changeSet,
510
545
  });
511
546
  });
512
547
  }
@@ -515,6 +550,7 @@ export class ChatModelImpl implements ChatModel {
515
550
  if (this._changeSet) {
516
551
  const oldChangeSet = this._changeSet;
517
552
  this._changeSet = undefined;
553
+ oldChangeSet.dispose();
518
554
  this._onDidChangeEmitter.fire({
519
555
  kind: 'removeChangeSet',
520
556
  changeSet: oldChangeSet,
@@ -522,8 +558,8 @@ export class ChatModelImpl implements ChatModel {
522
558
  }
523
559
  }
524
560
 
525
- addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string, context: ResolvedAIVariable[] = []): ChatRequestModelImpl {
526
- const requestModel = new ChatRequestModelImpl(this, parsedChatRequest, agentId, context);
561
+ addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string, context: ChatContext = { variables: [] }): MutableChatRequestModel {
562
+ const requestModel = new MutableChatRequestModel(this, parsedChatRequest, agentId, context);
527
563
  this._requests.push(requestModel);
528
564
  this._onDidChangeEmitter.fire({
529
565
  kind: 'addRequest',
@@ -535,74 +571,137 @@ export class ChatModelImpl implements ChatModel {
535
571
  isEmpty(): boolean {
536
572
  return this._requests.length === 0;
537
573
  }
574
+
575
+ dispose(): void {
576
+ this.removeChangeSet(); // Signal disposal of last change set.
577
+ this._onDidChangeEmitter.dispose();
578
+ }
579
+ }
580
+
581
+ interface ChangeSetChangeEvent {
582
+ added?: URI[],
583
+ removed?: URI[],
584
+ modified?: URI[],
585
+ /** Fired when only the state of a given element changes, not its contents */
586
+ state?: URI[],
538
587
  }
539
588
 
540
589
  export class ChangeSetImpl implements ChangeSet {
541
- protected readonly _onDidChangeEmitter = new Emitter<void>();
542
- onDidChange: Event<void> = this._onDidChangeEmitter.event;
590
+ protected readonly _onDidChangeEmitter = new Emitter<ChangeSetChangeEvent>();
591
+ onDidChange: Event<ChangeSetChangeEvent> = this._onDidChangeEmitter.event;
543
592
 
544
593
  protected _elements: ChangeSetElement[] = [];
545
594
 
546
595
  constructor(public readonly title: string, elements: ChangeSetElement[] = []) {
547
- this.addElements(elements);
596
+ this.addElements(...elements);
548
597
  }
549
598
 
550
599
  getElements(): ChangeSetElement[] {
551
600
  return this._elements;
552
601
  }
553
602
 
554
- addElement(element: ChangeSetElement): void {
555
- this.addElements([element]);
603
+ /** Will replace any element that is already present, using URI as identity criterion. */
604
+ addElements(...elements: ChangeSetElement[]): void {
605
+ const added: URI[] = [];
606
+ const modified: URI[] = [];
607
+ const toDispose: ChangeSetElement[] = [];
608
+ const current = new Map(this.getElements().map((element, index) => [element.uri.toString(), index]));
609
+ elements.forEach(element => {
610
+ const existingIndex = current.get(element.uri.toString());
611
+ if (existingIndex !== undefined) {
612
+ modified.push(element.uri);
613
+ toDispose.push(this._elements[existingIndex]);
614
+ this._elements[existingIndex] = element;
615
+ } else {
616
+ added.push(element.uri);
617
+ this._elements.push(element);
618
+ }
619
+ element.onDidChange?.(() => this.notifyChange({ state: [element.uri] }));
620
+ });
621
+ toDispose.forEach(element => element.dispose?.());
622
+ this.notifyChange({ added, modified });
556
623
  }
557
624
 
558
- addElements(elements: ChangeSetElement[]): void {
559
- this._elements.push(...elements);
560
- this.notifyChange();
625
+ removeElements(...indices: number[]): void {
626
+ // From highest to lowest so that we don't affect lower indices with our splicing.
627
+ const sorted = indices.slice().sort((left, right) => left - right);
628
+ const deletions = sorted.flatMap(index => this._elements.splice(index, 1));
629
+ deletions.forEach(deleted => deleted.dispose?.());
630
+ this.notifyChange({ removed: deletions.map(element => element.uri) });
561
631
  }
562
632
 
563
- replaceElement(element: ChangeSetElement): boolean {
564
- const index = this._elements.findIndex(e => e.uri.toString() === element.uri.toString());
565
- if (index < 0) {
566
- return false;
567
- }
568
- this._elements[index] = element;
569
- this.notifyChange();
570
- return true;
633
+ protected notifyChange(change: ChangeSetChangeEvent): void {
634
+ this._onDidChangeEmitter.fire(change);
635
+ }
636
+
637
+ dispose(): void {
638
+ this._onDidChangeEmitter.dispose();
639
+ this._elements.forEach(element => element.dispose?.());
640
+ }
641
+ }
642
+
643
+ export class ChatContextManagerImpl implements ChatContextManager {
644
+ protected readonly variables = new Array<AIVariableResolutionRequest>();
645
+ protected readonly onDidChangeEmitter = new Emitter<ChatAddVariableEvent | ChatRemoveVariableEvent>();
646
+ get onDidChange(): Event<ChatAddVariableEvent | ChatRemoveVariableEvent> {
647
+ return this.onDidChangeEmitter.event;
648
+ }
649
+
650
+ getVariables(): readonly AIVariableResolutionRequest[] {
651
+ const result = this.variables.slice();
652
+ Object.freeze(result);
653
+ return result;
571
654
  }
572
655
 
573
- addOrReplaceElement(element: ChangeSetElement): void {
574
- if (!this.replaceElement(element)) {
575
- this.addElement(element);
656
+ addVariables(...variables: AIVariableResolutionRequest[]): void {
657
+ let modified = false;
658
+ variables.forEach(variable => {
659
+ if (this.variables.some(existing => existing.variable.id === variable.variable.id && existing.arg === variable.arg)) {
660
+ return;
661
+ }
662
+ this.variables.push(variable);
663
+ modified = true;
664
+ });
665
+ if (modified) {
666
+ this.onDidChangeEmitter.fire({ kind: 'addVariable' });
576
667
  }
577
668
  }
578
669
 
579
- removeElement(index: number): void {
580
- this._elements.splice(index, 1);
581
- this.notifyChange();
670
+ deleteVariables(...indices: number[]): void {
671
+ const toDelete = indices.filter(candidate => candidate <= this.variables.length).sort((left, right) => right - left);
672
+ if (toDelete.length) {
673
+ toDelete.forEach(index => {
674
+ this.variables.splice(index, 1);
675
+ });
676
+ this.onDidChangeEmitter.fire({ kind: 'removeVariable' });
677
+ }
582
678
  }
583
679
 
584
- notifyChange(): void {
585
- this._onDidChangeEmitter.fire();
680
+ clear(): void {
681
+ if (this.variables.length) {
682
+ this.variables.length = 0;
683
+ this.onDidChangeEmitter.fire({ kind: 'removeVariable' });
684
+ }
586
685
  }
587
686
  }
588
687
 
589
- export class ChatRequestModelImpl implements ChatRequestModel {
688
+ export class MutableChatRequestModel implements ChatRequestModel {
590
689
  protected readonly _id: string;
591
- protected _session: ChatModelImpl;
690
+ protected _session: MutableChatModel;
592
691
  protected _request: ChatRequest;
593
- protected _response: ChatResponseModelImpl;
594
- protected _context: ResolvedAIVariable[];
692
+ protected _response: MutableChatResponseModel;
693
+ protected _context: ChatContext;
595
694
  protected _agentId?: string;
596
695
  protected _data: { [key: string]: unknown };
597
696
 
598
- constructor(session: ChatModelImpl, public readonly message: ParsedChatRequest, agentId?: string,
599
- context: ResolvedAIVariable[] = [], data: { [key: string]: unknown } = {}) {
697
+ constructor(session: MutableChatModel, public readonly message: ParsedChatRequest, agentId?: string,
698
+ context: ChatContext = { variables: [] }, data: { [key: string]: unknown } = {}) {
600
699
  // TODO accept serialized data as a parameter to restore a previously saved ChatRequestModel
601
700
  this._request = message.request;
602
701
  this._id = generateUuid();
603
702
  this._session = session;
604
- this._response = new ChatResponseModelImpl(this._id, agentId);
605
- this._context = context.concat(message.parts.filter(part => part.kind === 'var').map(part => (part as ParsedChatRequestVariablePart).resolution));
703
+ this._response = new MutableChatResponseModel(this._id, agentId);
704
+ this._context = context;
606
705
  this._agentId = agentId;
607
706
  this._data = data;
608
707
  }
@@ -623,7 +722,7 @@ export class ChatRequestModelImpl implements ChatRequestModel {
623
722
  return this._id;
624
723
  }
625
724
 
626
- get session(): ChatModelImpl {
725
+ get session(): MutableChatModel {
627
726
  return this._session;
628
727
  }
629
728
 
@@ -631,10 +730,14 @@ export class ChatRequestModelImpl implements ChatRequestModel {
631
730
  return this._request;
632
731
  }
633
732
 
634
- get response(): ChatResponseModelImpl {
733
+ get response(): MutableChatResponseModel {
635
734
  return this._response;
636
735
  }
637
736
 
737
+ get context(): ChatContext {
738
+ return this._context;
739
+ }
740
+
638
741
  get agentId(): string | undefined {
639
742
  return this._agentId;
640
743
  }
@@ -674,6 +777,10 @@ export class TextChatResponseContentImpl implements TextChatResponseContent {
674
777
  return this._content;
675
778
  }
676
779
 
780
+ asDisplayString(): string | undefined {
781
+ return this.asString();
782
+ }
783
+
677
784
  merge(nextChatResponseContent: TextChatResponseContent): boolean {
678
785
  this._content += nextChatResponseContent.content;
679
786
  return true;
@@ -696,6 +803,10 @@ export class MarkdownChatResponseContentImpl implements MarkdownChatResponseCont
696
803
  return this._content.value;
697
804
  }
698
805
 
806
+ asDisplayString(): string | undefined {
807
+ return this.asString();
808
+ }
809
+
699
810
  merge(nextChatResponseContent: MarkdownChatResponseContent): boolean {
700
811
  this._content.appendMarkdown(nextChatResponseContent.content.value);
701
812
  return true;
@@ -794,6 +905,10 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
794
905
  }
795
906
 
796
907
  asString(): string {
908
+ return '';
909
+ }
910
+
911
+ asDisplayString(): string {
797
912
  return `Tool call: ${this._name}(${this._arguments ?? ''})`;
798
913
  }
799
914
  merge(nextChatResponseContent: ToolCallChatResponseContent): boolean {
@@ -847,6 +962,10 @@ export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayout
847
962
  return this._content.map(child => child.asString && child.asString()).join(' ');
848
963
  }
849
964
 
965
+ asDisplayString(): string | undefined {
966
+ return this.asString();
967
+ }
968
+
850
969
  merge(nextChatResponseContent: ChatResponseContent): boolean {
851
970
  if (HorizontalLayoutChatResponseContent.is(nextChatResponseContent)) {
852
971
  this._content.push(...nextChatResponseContent.content);
@@ -864,7 +983,7 @@ export class QuestionResponseContentImpl implements QuestionResponseContent {
864
983
  readonly kind = 'question';
865
984
  protected _selectedOption: { text: string; value?: string } | undefined;
866
985
  constructor(public question: string, public options: { text: string, value?: string }[],
867
- public request: ChatRequestModelImpl, public handler: QuestionResponseHandler) {
986
+ public request: MutableChatRequestModel, public handler: QuestionResponseHandler) {
868
987
  }
869
988
  set selectedOption(option: { text: string; value?: string; } | undefined) {
870
989
  this._selectedOption = option;
@@ -887,6 +1006,7 @@ class ChatResponseImpl implements ChatResponse {
887
1006
  onDidChange: Event<void> = this._onDidChangeEmitter.event;
888
1007
  protected _content: ChatResponseContent[];
889
1008
  protected _responseRepresentation: string;
1009
+ protected _responseRepresentationForDisplay: string;
890
1010
 
891
1011
  constructor() {
892
1012
  // TODO accept serialized data as a parameter to restore a previously saved ChatResponse
@@ -940,8 +1060,18 @@ class ChatResponseImpl implements ChatResponse {
940
1060
  }
941
1061
 
942
1062
  protected _updateResponseRepresentation(): void {
943
- this._responseRepresentation = this._content
1063
+ this._responseRepresentation = this.responseRepresentationsToString(this._content, 'asString');
1064
+ this._responseRepresentationForDisplay = this.responseRepresentationsToString(this.content, 'asDisplayString');
1065
+ }
1066
+
1067
+ protected responseRepresentationsToString(content: ChatResponseContent[], collect: 'asString' | 'asDisplayString'): string {
1068
+ return content
944
1069
  .map(responseContent => {
1070
+ if (collect === 'asDisplayString') {
1071
+ if (ChatResponseContent.hasDisplayString(responseContent)) {
1072
+ return responseContent.asDisplayString();
1073
+ }
1074
+ }
945
1075
  if (ChatResponseContent.hasAsString(responseContent)) {
946
1076
  return responseContent.asString();
947
1077
  }
@@ -954,16 +1084,20 @@ class ChatResponseImpl implements ChatResponse {
954
1084
  );
955
1085
  return undefined;
956
1086
  })
957
- .filter(text => text !== undefined)
1087
+ .filter(text => (text !== undefined && text !== ''))
958
1088
  .join('\n\n');
959
1089
  }
960
1090
 
961
1091
  asString(): string {
962
1092
  return this._responseRepresentation;
963
1093
  }
1094
+
1095
+ asDisplayString(): string {
1096
+ return this._responseRepresentationForDisplay;
1097
+ }
964
1098
  }
965
1099
 
966
- class ChatResponseModelImpl implements ChatResponseModel {
1100
+ class MutableChatResponseModel implements ChatResponseModel {
967
1101
  protected readonly _onDidChangeEmitter = new Emitter<void>();
968
1102
  onDidChange: Event<void> = this._onDidChangeEmitter.event;
969
1103
 
@@ -1103,7 +1237,7 @@ class ChatResponseModelImpl implements ChatResponseModel {
1103
1237
  }
1104
1238
  }
1105
1239
 
1106
- export class ErrorChatResponseModelImpl extends ChatResponseModelImpl {
1240
+ export class ErrorChatResponseModel extends MutableChatResponseModel {
1107
1241
  constructor(requestId: string, error: Error, agentId?: string) {
1108
1242
  super(requestId, agentId);
1109
1243
  this.error(error);