@theia/ai-chat 1.58.2 → 1.59.0-next.62

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 (103) hide show
  1. package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
  2. package/lib/browser/ai-chat-frontend-module.js +10 -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 +24 -6
  9. package/lib/browser/change-set-file-element.d.ts.map +1 -1
  10. package/lib/browser/change-set-file-element.js +105 -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/context-file-variable-label-provider.d.ts +14 -0
  21. package/lib/browser/context-file-variable-label-provider.d.ts.map +1 -0
  22. package/lib/browser/context-file-variable-label-provider.js +63 -0
  23. package/lib/browser/context-file-variable-label-provider.js.map +1 -0
  24. package/lib/browser/context-variable-label-provider.d.ts +9 -0
  25. package/lib/browser/context-variable-label-provider.d.ts.map +1 -0
  26. package/lib/browser/context-variable-label-provider.js +55 -0
  27. package/lib/browser/context-variable-label-provider.js.map +1 -0
  28. package/lib/browser/file-chat-variable-contribution.d.ts +18 -0
  29. package/lib/browser/file-chat-variable-contribution.d.ts.map +1 -0
  30. package/lib/browser/file-chat-variable-contribution.js +135 -0
  31. package/lib/browser/file-chat-variable-contribution.js.map +1 -0
  32. package/lib/browser/frontend-chat-service.d.ts +10 -3
  33. package/lib/browser/frontend-chat-service.d.ts.map +1 -1
  34. package/lib/browser/frontend-chat-service.js +39 -19
  35. package/lib/browser/frontend-chat-service.js.map +1 -1
  36. package/lib/common/chat-agents.d.ts +47 -24
  37. package/lib/common/chat-agents.d.ts.map +1 -1
  38. package/lib/common/chat-agents.js +50 -19
  39. package/lib/common/chat-agents.js.map +1 -1
  40. package/lib/common/chat-history-entry.js +1 -1
  41. package/lib/common/chat-history-entry.js.map +1 -1
  42. package/lib/common/chat-model.d.ts +58 -35
  43. package/lib/common/chat-model.d.ts.map +1 -1
  44. package/lib/common/chat-model.js +96 -53
  45. package/lib/common/chat-model.js.map +1 -1
  46. package/lib/common/chat-service.d.ts +32 -12
  47. package/lib/common/chat-service.d.ts.map +1 -1
  48. package/lib/common/chat-service.js +77 -19
  49. package/lib/common/chat-service.js.map +1 -1
  50. package/lib/common/chat-tool-request-service.d.ts +5 -5
  51. package/lib/common/chat-tool-request-service.d.ts.map +1 -1
  52. package/lib/common/chat-tool-request-service.js.map +1 -1
  53. package/lib/common/custom-chat-agent.d.ts +7 -10
  54. package/lib/common/custom-chat-agent.d.ts.map +1 -1
  55. package/lib/common/custom-chat-agent.js +7 -11
  56. package/lib/common/custom-chat-agent.js.map +1 -1
  57. package/lib/common/index.d.ts +0 -3
  58. package/lib/common/index.d.ts.map +1 -1
  59. package/lib/common/index.js +0 -3
  60. package/lib/common/index.js.map +1 -1
  61. package/lib/common/parse-contents.d.ts +2 -2
  62. package/lib/common/parse-contents.d.ts.map +1 -1
  63. package/lib/common/parse-contents.js.map +1 -1
  64. package/lib/common/parse-contents.spec.d.ts.map +1 -1
  65. package/lib/common/parse-contents.spec.js.map +1 -1
  66. package/lib/common/response-content-matcher.d.ts +3 -3
  67. package/lib/common/response-content-matcher.d.ts.map +1 -1
  68. package/lib/common/response-content-matcher.js.map +1 -1
  69. package/package.json +12 -10
  70. package/src/browser/ai-chat-frontend-module.ts +14 -18
  71. package/src/browser/ai-chat-preferences.ts +13 -2
  72. package/src/browser/change-set-file-element.ts +99 -20
  73. package/src/browser/change-set-file-resource.ts +125 -39
  74. package/src/browser/change-set-file-service.ts +38 -16
  75. package/src/browser/context-file-variable-label-provider.ts +62 -0
  76. package/src/browser/context-variable-label-provider.ts +56 -0
  77. package/src/browser/file-chat-variable-contribution.ts +143 -0
  78. package/src/browser/frontend-chat-service.ts +40 -26
  79. package/src/common/chat-agents.ts +72 -27
  80. package/src/common/chat-history-entry.ts +1 -1
  81. package/src/common/chat-model.ts +138 -74
  82. package/src/common/chat-service.ts +96 -23
  83. package/src/common/chat-tool-request-service.ts +5 -5
  84. package/src/common/custom-chat-agent.ts +8 -20
  85. package/src/common/index.ts +0 -3
  86. package/src/common/parse-contents.spec.ts +2 -2
  87. package/src/common/parse-contents.ts +2 -2
  88. package/src/common/response-content-matcher.ts +3 -3
  89. package/lib/common/command-chat-agents.d.ts +0 -33
  90. package/lib/common/command-chat-agents.d.ts.map +0 -1
  91. package/lib/common/command-chat-agents.js +0 -329
  92. package/lib/common/command-chat-agents.js.map +0 -1
  93. package/lib/common/orchestrator-chat-agent.d.ts +0 -22
  94. package/lib/common/orchestrator-chat-agent.d.ts.map +0 -1
  95. package/lib/common/orchestrator-chat-agent.js +0 -167
  96. package/lib/common/orchestrator-chat-agent.js.map +0 -1
  97. package/lib/common/universal-chat-agent.d.ts +0 -16
  98. package/lib/common/universal-chat-agent.d.ts.map +0 -1
  99. package/lib/common/universal-chat-agent.js +0 -109
  100. package/lib/common/universal-chat-agent.js.map +0 -1
  101. package/src/common/command-chat-agents.ts +0 -354
  102. package/src/common/orchestrator-chat-agent.ts +0 -179
  103. package/src/common/universal-chat-agent.ts +0 -117
@@ -40,7 +40,7 @@ export namespace ChatHistoryEntry {
40
40
  agentId: agentId,
41
41
  sessionId: request.session.id,
42
42
  requestId: request.id,
43
- response: request.response.response.asString(),
43
+ response: request.response.response.asDisplayString(),
44
44
  ...args,
45
45
  };
46
46
  }
@@ -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 { ResolvedAIContextVariable } from '@theia/ai-core';
28
28
 
29
29
  /**********************
30
30
  * INTERFACES AND TYPE GUARDS
@@ -35,7 +35,6 @@ export type ChatChangeEvent =
35
35
  | ChatAddResponseEvent
36
36
  | ChatRemoveRequestEvent
37
37
  | ChatSetChangeSetEvent
38
- | ChatSetChangeDeleteEvent
39
38
  | ChatUpdateChangeSetEvent
40
39
  | ChatRemoveChangeSetEvent;
41
40
 
@@ -52,10 +51,7 @@ export interface ChatAddResponseEvent {
52
51
  export interface ChatSetChangeSetEvent {
53
52
  kind: 'setChangeSet';
54
53
  changeSet: ChangeSet;
55
- }
56
-
57
- export interface ChatSetChangeDeleteEvent {
58
- kind: 'deleteChangeSet';
54
+ oldChangeSet?: ChangeSet;
59
55
  }
60
56
 
61
57
  export interface ChatUpdateChangeSetEvent {
@@ -70,7 +66,7 @@ export interface ChatRemoveChangeSetEvent {
70
66
 
71
67
  export namespace ChatChangeEvent {
72
68
  export function isChangeSetEvent(event: ChatChangeEvent): event is ChatSetChangeSetEvent | ChatUpdateChangeSetEvent | ChatRemoveChangeSetEvent {
73
- return event.kind === 'setChangeSet' || event.kind === 'deleteChangeSet' || event.kind === 'removeChangeSet' || event.kind === 'updateChangeSet';
69
+ return event.kind === 'setChangeSet' || event.kind === 'removeChangeSet' || event.kind === 'updateChangeSet';
74
70
  }
75
71
  }
76
72
 
@@ -93,25 +89,29 @@ export interface ChatModel {
93
89
  }
94
90
 
95
91
  export interface ChangeSet {
92
+ onDidChange: Event<ChangeSetChangeEvent>;
96
93
  readonly title: string;
97
94
  getElements(): ChangeSetElement[];
95
+ dispose(): void;
98
96
  }
99
97
 
100
98
  export interface ChangeSetElement {
101
99
  readonly uri: URI;
102
100
 
101
+ onDidChange?: Event<void>
103
102
  readonly name?: string;
104
103
  readonly icon?: string;
105
104
  readonly additionalInfo?: string;
106
105
 
107
- readonly state?: 'pending' | 'applied' | 'discarded';
106
+ readonly state?: 'pending' | 'applied' | 'stale';
108
107
  readonly type?: 'add' | 'modify' | 'delete';
109
108
  readonly data?: { [key: string]: unknown };
110
109
 
111
110
  open?(): Promise<void>;
112
111
  openChange?(): Promise<void>;
113
- accept?(): Promise<void>;
114
- discard?(): Promise<void>;
112
+ apply?(): Promise<void>;
113
+ revert?(): Promise<void>;
114
+ dispose?(): void;
115
115
  }
116
116
 
117
117
  export interface ChatRequest {
@@ -119,12 +119,17 @@ export interface ChatRequest {
119
119
  readonly displayText?: string;
120
120
  }
121
121
 
122
+ export interface ChatContext {
123
+ variables: ResolvedAIContextVariable[];
124
+ }
125
+
122
126
  export interface ChatRequestModel {
123
127
  readonly id: string;
124
128
  readonly session: ChatModel;
125
129
  readonly request: ChatRequest;
126
130
  readonly response: ChatResponseModel;
127
131
  readonly message: ParsedChatRequest;
132
+ readonly context: ChatContext;
128
133
  readonly agentId?: string;
129
134
  readonly data?: { [key: string]: unknown };
130
135
  }
@@ -171,6 +176,7 @@ export interface ChatResponseContent {
171
176
  * representation of the response.
172
177
  */
173
178
  asString?(): string | undefined;
179
+ asDisplayString?(): string | undefined;
174
180
  merge?(nextChatResponseContent: ChatResponseContent): boolean;
175
181
  }
176
182
 
@@ -188,6 +194,11 @@ export namespace ChatResponseContent {
188
194
  ): obj is Required<Pick<ChatResponseContent, 'asString'>> & ChatResponseContent {
189
195
  return typeof obj.asString === 'function';
190
196
  }
197
+ export function hasDisplayString(
198
+ obj: ChatResponseContent
199
+ ): obj is Required<Pick<ChatResponseContent, 'asDisplayString'>> & ChatResponseContent {
200
+ return typeof obj.asDisplayString === 'function';
201
+ }
191
202
  export function hasMerge(
192
203
  obj: ChatResponseContent
193
204
  ): obj is Required<Pick<ChatResponseContent, 'merge'>> & ChatResponseContent {
@@ -369,7 +380,7 @@ export interface QuestionResponseContent extends ChatResponseContent {
369
380
  options: { text: string, value?: string }[];
370
381
  selectedOption?: { text: string, value?: string };
371
382
  handler: QuestionResponseHandler;
372
- request: ChatRequestModelImpl;
383
+ request: MutableChatRequestModel;
373
384
  }
374
385
 
375
386
  export namespace QuestionResponseContent {
@@ -390,7 +401,7 @@ export namespace QuestionResponseContent {
390
401
  'handler' in obj &&
391
402
  typeof (obj as { handler: unknown }).handler === 'function' &&
392
403
  'request' in obj &&
393
- obj.request instanceof ChatRequestModelImpl
404
+ obj.request instanceof MutableChatRequestModel
394
405
  );
395
406
  }
396
407
  }
@@ -398,6 +409,7 @@ export namespace QuestionResponseContent {
398
409
  export interface ChatResponse {
399
410
  readonly content: ChatResponseContent[];
400
411
  asString(): string;
412
+ asDisplayString(): string;
401
413
  }
402
414
 
403
415
  /**
@@ -459,13 +471,12 @@ export interface ChatResponseModel {
459
471
  * Implementations
460
472
  **********************/
461
473
 
462
- export class ChatModelImpl implements ChatModel {
474
+ export class MutableChatModel implements ChatModel, Disposable {
463
475
  protected readonly _onDidChangeEmitter = new Emitter<ChatChangeEvent>();
464
476
  onDidChange: Event<ChatChangeEvent> = this._onDidChangeEmitter.event;
465
477
 
466
- protected _requests: ChatRequestModelImpl[];
478
+ protected _requests: MutableChatRequestModel[];
467
479
  protected _id: string;
468
- protected _changeSetListener?: Disposable;
469
480
  protected _changeSet?: ChangeSetImpl;
470
481
 
471
482
  constructor(public readonly location = ChatAgentLocation.Panel) {
@@ -474,11 +485,11 @@ export class ChatModelImpl implements ChatModel {
474
485
  this._id = generateUuid();
475
486
  }
476
487
 
477
- getRequests(): ChatRequestModelImpl[] {
488
+ getRequests(): MutableChatRequestModel[] {
478
489
  return this._requests;
479
490
  }
480
491
 
481
- getRequest(id: string): ChatRequestModelImpl | undefined {
492
+ getRequest(id: string): MutableChatRequestModel | undefined {
482
493
  return this._requests.find(request => request.id === id);
483
494
  }
484
495
 
@@ -491,22 +502,21 @@ export class ChatModelImpl implements ChatModel {
491
502
  }
492
503
 
493
504
  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;
505
+ if (!changeSet) {
506
+ return this.removeChangeSet();
501
507
  }
508
+ const oldChangeSet = this._changeSet;
509
+ oldChangeSet?.dispose();
510
+ this._changeSet = changeSet;
502
511
  this._onDidChangeEmitter.fire({
503
512
  kind: 'setChangeSet',
504
- changeSet: this._changeSet,
513
+ changeSet,
514
+ oldChangeSet,
505
515
  });
506
- this._changeSetListener = this._changeSet.onDidChange(() => {
516
+ changeSet.onDidChange(() => {
507
517
  this._onDidChangeEmitter.fire({
508
518
  kind: 'updateChangeSet',
509
- changeSet: this._changeSet!,
519
+ changeSet,
510
520
  });
511
521
  });
512
522
  }
@@ -515,6 +525,7 @@ export class ChatModelImpl implements ChatModel {
515
525
  if (this._changeSet) {
516
526
  const oldChangeSet = this._changeSet;
517
527
  this._changeSet = undefined;
528
+ oldChangeSet.dispose();
518
529
  this._onDidChangeEmitter.fire({
519
530
  kind: 'removeChangeSet',
520
531
  changeSet: oldChangeSet,
@@ -522,8 +533,8 @@ export class ChatModelImpl implements ChatModel {
522
533
  }
523
534
  }
524
535
 
525
- addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string, context: ResolvedAIVariable[] = []): ChatRequestModelImpl {
526
- const requestModel = new ChatRequestModelImpl(this, parsedChatRequest, agentId, context);
536
+ addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string, context: ChatContext = { variables: [] }): MutableChatRequestModel {
537
+ const requestModel = new MutableChatRequestModel(this, parsedChatRequest, agentId, context);
527
538
  this._requests.push(requestModel);
528
539
  this._onDidChangeEmitter.fire({
529
540
  kind: 'addRequest',
@@ -535,74 +546,92 @@ export class ChatModelImpl implements ChatModel {
535
546
  isEmpty(): boolean {
536
547
  return this._requests.length === 0;
537
548
  }
549
+
550
+ dispose(): void {
551
+ this.removeChangeSet(); // Signal disposal of last change set.
552
+ this._onDidChangeEmitter.dispose();
553
+ }
554
+ }
555
+
556
+ interface ChangeSetChangeEvent {
557
+ added?: URI[],
558
+ removed?: URI[],
559
+ modified?: URI[],
560
+ /** Fired when only the state of a given element changes, not its contents */
561
+ state?: URI[],
538
562
  }
539
563
 
540
564
  export class ChangeSetImpl implements ChangeSet {
541
- protected readonly _onDidChangeEmitter = new Emitter<void>();
542
- onDidChange: Event<void> = this._onDidChangeEmitter.event;
565
+ protected readonly _onDidChangeEmitter = new Emitter<ChangeSetChangeEvent>();
566
+ onDidChange: Event<ChangeSetChangeEvent> = this._onDidChangeEmitter.event;
543
567
 
544
568
  protected _elements: ChangeSetElement[] = [];
545
569
 
546
570
  constructor(public readonly title: string, elements: ChangeSetElement[] = []) {
547
- this.addElements(elements);
571
+ this.addElements(...elements);
548
572
  }
549
573
 
550
574
  getElements(): ChangeSetElement[] {
551
575
  return this._elements;
552
576
  }
553
577
 
554
- addElement(element: ChangeSetElement): void {
555
- this.addElements([element]);
556
- }
557
-
558
- addElements(elements: ChangeSetElement[]): void {
559
- this._elements.push(...elements);
560
- this.notifyChange();
561
- }
562
-
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;
578
+ /** Will replace any element that is already present, using URI as identity criterion. */
579
+ addElements(...elements: ChangeSetElement[]): void {
580
+ const added: URI[] = [];
581
+ const modified: URI[] = [];
582
+ const toDispose: ChangeSetElement[] = [];
583
+ const current = new Map(this.getElements().map((element, index) => [element.uri.toString(), index]));
584
+ elements.forEach(element => {
585
+ const existingIndex = current.get(element.uri.toString());
586
+ if (existingIndex !== undefined) {
587
+ modified.push(element.uri);
588
+ toDispose.push(this._elements[existingIndex]);
589
+ this._elements[existingIndex] = element;
590
+ } else {
591
+ added.push(element.uri);
592
+ this._elements.push(element);
593
+ }
594
+ element.onDidChange?.(() => this.notifyChange({ state: [element.uri] }));
595
+ });
596
+ toDispose.forEach(element => element.dispose?.());
597
+ this.notifyChange({ added, modified });
571
598
  }
572
599
 
573
- addOrReplaceElement(element: ChangeSetElement): void {
574
- if (!this.replaceElement(element)) {
575
- this.addElement(element);
576
- }
600
+ removeElements(...indices: number[]): void {
601
+ // From highest to lowest so that we don't affect lower indices with our splicing.
602
+ const sorted = indices.slice().sort((left, right) => left - right);
603
+ const deletions = sorted.flatMap(index => this._elements.splice(index, 1));
604
+ deletions.forEach(deleted => deleted.dispose?.());
605
+ this.notifyChange({ removed: deletions.map(element => element.uri) });
577
606
  }
578
607
 
579
- removeElement(index: number): void {
580
- this._elements.splice(index, 1);
581
- this.notifyChange();
608
+ protected notifyChange(change: ChangeSetChangeEvent): void {
609
+ this._onDidChangeEmitter.fire(change);
582
610
  }
583
611
 
584
- notifyChange(): void {
585
- this._onDidChangeEmitter.fire();
612
+ dispose(): void {
613
+ this._elements.forEach(element => element.dispose?.());
614
+ this._onDidChangeEmitter.dispose();
586
615
  }
587
616
  }
588
617
 
589
- export class ChatRequestModelImpl implements ChatRequestModel {
618
+ export class MutableChatRequestModel implements ChatRequestModel {
590
619
  protected readonly _id: string;
591
- protected _session: ChatModelImpl;
620
+ protected _session: MutableChatModel;
592
621
  protected _request: ChatRequest;
593
- protected _response: ChatResponseModelImpl;
594
- protected _context: ResolvedAIVariable[];
622
+ protected _response: MutableChatResponseModel;
623
+ protected _context: ChatContext;
595
624
  protected _agentId?: string;
596
625
  protected _data: { [key: string]: unknown };
597
626
 
598
- constructor(session: ChatModelImpl, public readonly message: ParsedChatRequest, agentId?: string,
599
- context: ResolvedAIVariable[] = [], data: { [key: string]: unknown } = {}) {
627
+ constructor(session: MutableChatModel, public readonly message: ParsedChatRequest, agentId?: string,
628
+ context: ChatContext = { variables: [] }, data: { [key: string]: unknown } = {}) {
600
629
  // TODO accept serialized data as a parameter to restore a previously saved ChatRequestModel
601
630
  this._request = message.request;
602
631
  this._id = generateUuid();
603
632
  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));
633
+ this._response = new MutableChatResponseModel(this._id, agentId);
634
+ this._context = context;
606
635
  this._agentId = agentId;
607
636
  this._data = data;
608
637
  }
@@ -623,7 +652,7 @@ export class ChatRequestModelImpl implements ChatRequestModel {
623
652
  return this._id;
624
653
  }
625
654
 
626
- get session(): ChatModelImpl {
655
+ get session(): MutableChatModel {
627
656
  return this._session;
628
657
  }
629
658
 
@@ -631,10 +660,14 @@ export class ChatRequestModelImpl implements ChatRequestModel {
631
660
  return this._request;
632
661
  }
633
662
 
634
- get response(): ChatResponseModelImpl {
663
+ get response(): MutableChatResponseModel {
635
664
  return this._response;
636
665
  }
637
666
 
667
+ get context(): ChatContext {
668
+ return this._context;
669
+ }
670
+
638
671
  get agentId(): string | undefined {
639
672
  return this._agentId;
640
673
  }
@@ -674,6 +707,10 @@ export class TextChatResponseContentImpl implements TextChatResponseContent {
674
707
  return this._content;
675
708
  }
676
709
 
710
+ asDisplayString(): string | undefined {
711
+ return this.asString();
712
+ }
713
+
677
714
  merge(nextChatResponseContent: TextChatResponseContent): boolean {
678
715
  this._content += nextChatResponseContent.content;
679
716
  return true;
@@ -696,6 +733,10 @@ export class MarkdownChatResponseContentImpl implements MarkdownChatResponseCont
696
733
  return this._content.value;
697
734
  }
698
735
 
736
+ asDisplayString(): string | undefined {
737
+ return this.asString();
738
+ }
739
+
699
740
  merge(nextChatResponseContent: MarkdownChatResponseContent): boolean {
700
741
  this._content.appendMarkdown(nextChatResponseContent.content.value);
701
742
  return true;
@@ -794,6 +835,10 @@ export class ToolCallChatResponseContentImpl implements ToolCallChatResponseCont
794
835
  }
795
836
 
796
837
  asString(): string {
838
+ return '';
839
+ }
840
+
841
+ asDisplayString(): string {
797
842
  return `Tool call: ${this._name}(${this._arguments ?? ''})`;
798
843
  }
799
844
  merge(nextChatResponseContent: ToolCallChatResponseContent): boolean {
@@ -847,6 +892,10 @@ export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayout
847
892
  return this._content.map(child => child.asString && child.asString()).join(' ');
848
893
  }
849
894
 
895
+ asDisplayString(): string | undefined {
896
+ return this.asString();
897
+ }
898
+
850
899
  merge(nextChatResponseContent: ChatResponseContent): boolean {
851
900
  if (HorizontalLayoutChatResponseContent.is(nextChatResponseContent)) {
852
901
  this._content.push(...nextChatResponseContent.content);
@@ -864,7 +913,7 @@ export class QuestionResponseContentImpl implements QuestionResponseContent {
864
913
  readonly kind = 'question';
865
914
  protected _selectedOption: { text: string; value?: string } | undefined;
866
915
  constructor(public question: string, public options: { text: string, value?: string }[],
867
- public request: ChatRequestModelImpl, public handler: QuestionResponseHandler) {
916
+ public request: MutableChatRequestModel, public handler: QuestionResponseHandler) {
868
917
  }
869
918
  set selectedOption(option: { text: string; value?: string; } | undefined) {
870
919
  this._selectedOption = option;
@@ -887,6 +936,7 @@ class ChatResponseImpl implements ChatResponse {
887
936
  onDidChange: Event<void> = this._onDidChangeEmitter.event;
888
937
  protected _content: ChatResponseContent[];
889
938
  protected _responseRepresentation: string;
939
+ protected _responseRepresentationForDisplay: string;
890
940
 
891
941
  constructor() {
892
942
  // TODO accept serialized data as a parameter to restore a previously saved ChatResponse
@@ -940,8 +990,18 @@ class ChatResponseImpl implements ChatResponse {
940
990
  }
941
991
 
942
992
  protected _updateResponseRepresentation(): void {
943
- this._responseRepresentation = this._content
993
+ this._responseRepresentation = this.responseRepresentationsToString(this._content, 'asString');
994
+ this._responseRepresentationForDisplay = this.responseRepresentationsToString(this.content, 'asDisplayString');
995
+ }
996
+
997
+ protected responseRepresentationsToString(content: ChatResponseContent[], collect: 'asString' | 'asDisplayString'): string {
998
+ return content
944
999
  .map(responseContent => {
1000
+ if (collect === 'asDisplayString') {
1001
+ if (ChatResponseContent.hasDisplayString(responseContent)) {
1002
+ return responseContent.asDisplayString();
1003
+ }
1004
+ }
945
1005
  if (ChatResponseContent.hasAsString(responseContent)) {
946
1006
  return responseContent.asString();
947
1007
  }
@@ -954,16 +1014,20 @@ class ChatResponseImpl implements ChatResponse {
954
1014
  );
955
1015
  return undefined;
956
1016
  })
957
- .filter(text => text !== undefined)
1017
+ .filter(text => (text !== undefined && text !== ''))
958
1018
  .join('\n\n');
959
1019
  }
960
1020
 
961
1021
  asString(): string {
962
1022
  return this._responseRepresentation;
963
1023
  }
1024
+
1025
+ asDisplayString(): string {
1026
+ return this._responseRepresentationForDisplay;
1027
+ }
964
1028
  }
965
1029
 
966
- class ChatResponseModelImpl implements ChatResponseModel {
1030
+ class MutableChatResponseModel implements ChatResponseModel {
967
1031
  protected readonly _onDidChangeEmitter = new Emitter<void>();
968
1032
  onDidChange: Event<void> = this._onDidChangeEmitter.event;
969
1033
 
@@ -1103,7 +1167,7 @@ class ChatResponseModelImpl implements ChatResponseModel {
1103
1167
  }
1104
1168
  }
1105
1169
 
1106
- export class ErrorChatResponseModelImpl extends ChatResponseModelImpl {
1170
+ export class ErrorChatResponseModel extends MutableChatResponseModel {
1107
1171
  constructor(requestId: string, error: Error, agentId?: string) {
1108
1172
  super(requestId, agentId);
1109
1173
  this.error(error);