@theia/ai-chat 1.55.1 → 1.57.0-next.112

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 (88) hide show
  1. package/README.md +2 -1
  2. package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
  3. package/lib/browser/ai-chat-frontend-module.js +14 -4
  4. package/lib/browser/ai-chat-frontend-module.js.map +1 -1
  5. package/lib/browser/change-set-file-element.d.ts +44 -0
  6. package/lib/browser/change-set-file-element.d.ts.map +1 -0
  7. package/lib/browser/change-set-file-element.js +113 -0
  8. package/lib/browser/change-set-file-element.js.map +1 -0
  9. package/lib/browser/change-set-file-resource.d.ts +13 -0
  10. package/lib/browser/change-set-file-resource.d.ts.map +1 -0
  11. package/lib/browser/change-set-file-resource.js +73 -0
  12. package/lib/browser/change-set-file-resource.js.map +1 -0
  13. package/lib/browser/change-set-file-service.d.ts +26 -0
  14. package/lib/browser/change-set-file-service.d.ts.map +1 -0
  15. package/lib/browser/change-set-file-service.js +139 -0
  16. package/lib/browser/change-set-file-service.js.map +1 -0
  17. package/lib/common/chat-agents.d.ts +16 -9
  18. package/lib/common/chat-agents.d.ts.map +1 -1
  19. package/lib/common/chat-agents.js +51 -67
  20. package/lib/common/chat-agents.js.map +1 -1
  21. package/lib/common/chat-history-entry.d.ts +7 -0
  22. package/lib/common/chat-history-entry.d.ts.map +1 -0
  23. package/lib/common/chat-history-entry.js +42 -0
  24. package/lib/common/chat-history-entry.js.map +1 -0
  25. package/lib/common/chat-model-util.d.ts +7 -0
  26. package/lib/common/chat-model-util.d.ts.map +1 -0
  27. package/lib/common/chat-model-util.js +50 -0
  28. package/lib/common/chat-model-util.js.map +1 -0
  29. package/lib/common/chat-model.d.ts +176 -7
  30. package/lib/common/chat-model.d.ts.map +1 -1
  31. package/lib/common/chat-model.js +193 -9
  32. package/lib/common/chat-model.js.map +1 -1
  33. package/lib/common/chat-service.d.ts +6 -0
  34. package/lib/common/chat-service.d.ts.map +1 -1
  35. package/lib/common/chat-service.js +12 -0
  36. package/lib/common/chat-service.js.map +1 -1
  37. package/lib/common/chat-tool-request-service.d.ts +17 -0
  38. package/lib/common/chat-tool-request-service.d.ts.map +1 -0
  39. package/lib/common/chat-tool-request-service.js +52 -0
  40. package/lib/common/chat-tool-request-service.js.map +1 -0
  41. package/lib/common/command-chat-agents.d.ts.map +1 -1
  42. package/lib/common/command-chat-agents.js +4 -2
  43. package/lib/common/command-chat-agents.js.map +1 -1
  44. package/lib/common/index.d.ts +1 -0
  45. package/lib/common/index.d.ts.map +1 -1
  46. package/lib/common/index.js +1 -0
  47. package/lib/common/index.js.map +1 -1
  48. package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
  49. package/lib/common/orchestrator-chat-agent.js +16 -14
  50. package/lib/common/orchestrator-chat-agent.js.map +1 -1
  51. package/lib/common/parse-contents.d.ts +2 -2
  52. package/lib/common/parse-contents.d.ts.map +1 -1
  53. package/lib/common/parse-contents.js +4 -4
  54. package/lib/common/parse-contents.js.map +1 -1
  55. package/lib/common/parse-contents.spec.d.ts.map +1 -1
  56. package/lib/common/parse-contents.spec.js +14 -13
  57. package/lib/common/parse-contents.spec.js.map +1 -1
  58. package/lib/common/response-content-matcher.d.ts +3 -3
  59. package/lib/common/response-content-matcher.d.ts.map +1 -1
  60. package/lib/common/response-content-matcher.js +2 -2
  61. package/lib/common/response-content-matcher.js.map +1 -1
  62. package/lib/common/universal-chat-agent.d.ts +1 -0
  63. package/lib/common/universal-chat-agent.d.ts.map +1 -1
  64. package/lib/common/universal-chat-agent.js +10 -3
  65. package/lib/common/universal-chat-agent.js.map +1 -1
  66. package/package.json +10 -8
  67. package/src/browser/ai-chat-frontend-module.ts +17 -6
  68. package/src/browser/change-set-file-element.ts +137 -0
  69. package/src/browser/change-set-file-resource.ts +74 -0
  70. package/src/browser/change-set-file-service.ts +136 -0
  71. package/src/common/chat-agents.ts +56 -78
  72. package/src/common/chat-history-entry.ts +47 -0
  73. package/src/common/chat-model-util.ts +44 -0
  74. package/src/common/chat-model.ts +325 -14
  75. package/src/common/chat-service.ts +17 -0
  76. package/src/common/chat-tool-request-service.ts +59 -0
  77. package/src/common/command-chat-agents.ts +4 -2
  78. package/src/common/index.ts +1 -0
  79. package/src/common/orchestrator-chat-agent.ts +17 -14
  80. package/src/common/parse-contents.spec.ts +16 -14
  81. package/src/common/parse-contents.ts +5 -4
  82. package/src/common/response-content-matcher.ts +4 -3
  83. package/src/common/universal-chat-agent.ts +10 -2
  84. package/lib/common/o1-chat-agent.d.ts +0 -13
  85. package/lib/common/o1-chat-agent.d.ts.map +0 -1
  86. package/lib/common/o1-chat-agent.js +0 -45
  87. package/lib/common/o1-chat-agent.js.map +0 -1
  88. package/src/common/o1-chat-agent.ts +0 -51
@@ -19,11 +19,12 @@
19
19
  *--------------------------------------------------------------------------------------------*/
20
20
  // Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatModel.ts
21
21
 
22
- import { Command, Emitter, Event, generateUuid, URI } from '@theia/core';
22
+ import { CancellationToken, CancellationTokenSource, Command, Disposable, Emitter, Event, generateUuid, URI } from '@theia/core';
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 } from './parsed-chat-request';
26
+ import { ParsedChatRequest, ParsedChatRequestVariablePart } from './parsed-chat-request';
27
+ import { ResolvedAIVariable } from '@theia/ai-core';
27
28
 
28
29
  /**********************
29
30
  * INTERFACES AND TYPE GUARDS
@@ -32,7 +33,11 @@ import { ParsedChatRequest } from './parsed-chat-request';
32
33
  export type ChatChangeEvent =
33
34
  | ChatAddRequestEvent
34
35
  | ChatAddResponseEvent
35
- | ChatRemoveRequestEvent;
36
+ | ChatRemoveRequestEvent
37
+ | ChatSetChangeSetEvent
38
+ | ChatSetChangeDeleteEvent
39
+ | ChatUpdateChangeSetEvent
40
+ | ChatRemoveChangeSetEvent;
36
41
 
37
42
  export interface ChatAddRequestEvent {
38
43
  kind: 'addRequest';
@@ -44,6 +49,31 @@ export interface ChatAddResponseEvent {
44
49
  response: ChatResponseModel;
45
50
  }
46
51
 
52
+ export interface ChatSetChangeSetEvent {
53
+ kind: 'setChangeSet';
54
+ changeSet: ChangeSet;
55
+ }
56
+
57
+ export interface ChatSetChangeDeleteEvent {
58
+ kind: 'deleteChangeSet';
59
+ }
60
+
61
+ export interface ChatUpdateChangeSetEvent {
62
+ kind: 'updateChangeSet';
63
+ changeSet: ChangeSet;
64
+ }
65
+
66
+ export interface ChatRemoveChangeSetEvent {
67
+ kind: 'removeChangeSet';
68
+ changeSet: ChangeSet;
69
+ }
70
+
71
+ export namespace ChatChangeEvent {
72
+ export function isChangeSetEvent(event: ChatChangeEvent): event is ChatSetChangeSetEvent | ChatUpdateChangeSetEvent | ChatRemoveChangeSetEvent {
73
+ return event.kind === 'setChangeSet' || event.kind === 'deleteChangeSet' || event.kind === 'removeChangeSet' || event.kind === 'updateChangeSet';
74
+ }
75
+ }
76
+
47
77
  export type ChatRequestRemovalReason = 'removal' | 'resend' | 'adoption';
48
78
 
49
79
  export interface ChatRemoveRequestEvent {
@@ -57,10 +87,33 @@ export interface ChatModel {
57
87
  readonly onDidChange: Event<ChatChangeEvent>;
58
88
  readonly id: string;
59
89
  readonly location: ChatAgentLocation;
90
+ readonly changeSet?: ChangeSet;
60
91
  getRequests(): ChatRequestModel[];
61
92
  isEmpty(): boolean;
62
93
  }
63
94
 
95
+ export interface ChangeSet {
96
+ readonly title: string;
97
+ getElements(): ChangeSetElement[];
98
+ }
99
+
100
+ export interface ChangeSetElement {
101
+ readonly uri: URI;
102
+
103
+ readonly name?: string;
104
+ readonly icon?: string;
105
+ readonly additionalInfo?: string;
106
+
107
+ readonly state?: 'pending' | 'applied' | 'discarded';
108
+ readonly type?: 'add' | 'modify' | 'delete';
109
+ readonly data?: { [key: string]: unknown };
110
+
111
+ open?(): Promise<void>;
112
+ openChange?(): Promise<void>;
113
+ accept?(): Promise<void>;
114
+ discard?(): Promise<void>;
115
+ }
116
+
64
117
  export interface ChatRequest {
65
118
  readonly text: string;
66
119
  readonly displayText?: string;
@@ -76,10 +129,37 @@ export interface ChatRequestModel {
76
129
  readonly data?: { [key: string]: unknown };
77
130
  }
78
131
 
132
+ export namespace ChatRequestModel {
133
+ export function is(request: unknown): request is ChatRequestModel {
134
+ return !!(
135
+ request &&
136
+ typeof request === 'object' &&
137
+ 'id' in request &&
138
+ typeof (request as { id: unknown }).id === 'string' &&
139
+ 'session' in request &&
140
+ 'request' in request &&
141
+ 'response' in request &&
142
+ 'message' in request
143
+ );
144
+ }
145
+ export function isInProgress(request: ChatRequestModel | undefined): boolean {
146
+ if (!request) {
147
+ return false;
148
+ }
149
+ const response = request.response;
150
+ return !(
151
+ response.isComplete ||
152
+ response.isCanceled ||
153
+ response.isError
154
+ );
155
+ }
156
+ }
157
+
79
158
  export interface ChatProgressMessage {
80
159
  kind: 'progressMessage';
81
160
  id: string;
82
161
  status: 'inProgress' | 'completed' | 'failed';
162
+ show: 'untilFirstContent' | 'whileIncomplete' | 'forever';
83
163
  content: string;
84
164
  }
85
165
 
@@ -279,22 +359,100 @@ export namespace ErrorChatResponseContent {
279
359
  }
280
360
  }
281
361
 
362
+ export type QuestionResponseHandler = (
363
+ selectedOption: { text: string, value?: string },
364
+ ) => void;
365
+
366
+ export interface QuestionResponseContent extends ChatResponseContent {
367
+ kind: 'question';
368
+ question: string;
369
+ options: { text: string, value?: string }[];
370
+ selectedOption?: { text: string, value?: string };
371
+ handler: QuestionResponseHandler;
372
+ request: ChatRequestModelImpl;
373
+ }
374
+
375
+ export namespace QuestionResponseContent {
376
+ export function is(obj: unknown): obj is QuestionResponseContent {
377
+ return (
378
+ ChatResponseContent.is(obj) &&
379
+ obj.kind === 'question' &&
380
+ 'question' in obj &&
381
+ typeof (obj as { question: unknown }).question === 'string' &&
382
+ 'options' in obj &&
383
+ Array.isArray((obj as { options: unknown }).options) &&
384
+ (obj as { options: unknown[] }).options.every(option =>
385
+ typeof option === 'object' &&
386
+ option && 'text' in option &&
387
+ typeof (option as { text: unknown }).text === 'string' &&
388
+ ('value' in option ? typeof (option as { value: unknown }).value === 'string' || typeof (option as { value: unknown }).value === 'undefined' : true)
389
+ ) &&
390
+ 'handler' in obj &&
391
+ typeof (obj as { handler: unknown }).handler === 'function' &&
392
+ 'request' in obj &&
393
+ obj.request instanceof ChatRequestModelImpl
394
+ );
395
+ }
396
+ }
397
+
282
398
  export interface ChatResponse {
283
399
  readonly content: ChatResponseContent[];
284
400
  asString(): string;
285
401
  }
286
402
 
403
+ /**
404
+ * The ChatResponseModel wraps the actual ChatResponse with additional information like the current state, progress messages, a unique id etc.
405
+ */
287
406
  export interface ChatResponseModel {
407
+ /**
408
+ * Use this to be notified for any change in the response model
409
+ */
288
410
  readonly onDidChange: Event<void>;
411
+ /**
412
+ * The unique identifier of the response model
413
+ */
289
414
  readonly id: string;
415
+ /**
416
+ * The unique identifier of the request model this response is associated with
417
+ */
290
418
  readonly requestId: string;
419
+ /**
420
+ * In case there are progress messages, then they will be stored here
421
+ */
291
422
  readonly progressMessages: ChatProgressMessage[];
423
+ /**
424
+ * The actual response content
425
+ */
292
426
  readonly response: ChatResponse;
427
+ /**
428
+ * Indicates whether this response is complete. No further changes are expected if 'true'.
429
+ */
293
430
  readonly isComplete: boolean;
431
+ /**
432
+ * Indicates whether this response is canceled. No further changes are expected if 'true'.
433
+ */
294
434
  readonly isCanceled: boolean;
435
+ /**
436
+ * Some agents might need to wait for user input to continue. This flag indicates that.
437
+ */
438
+ readonly isWaitingForInput: boolean;
439
+ /**
440
+ * Indicates whether an error occurred when processing the response. No further changes are expected if 'true'.
441
+ */
295
442
  readonly isError: boolean;
443
+ /**
444
+ * The agent who produced the response content, if there is one.
445
+ */
296
446
  readonly agentId?: string
447
+ /**
448
+ * An optional error object that caused the response to be in an error state.
449
+ */
297
450
  readonly errorObject?: Error;
451
+ /**
452
+ * Some functionality might want to store some data associated with the response.
453
+ * This can be used to store and retrieve such data.
454
+ */
455
+ readonly data: { [key: string]: unknown };
298
456
  }
299
457
 
300
458
  /**********************
@@ -307,6 +465,8 @@ export class ChatModelImpl implements ChatModel {
307
465
 
308
466
  protected _requests: ChatRequestModelImpl[];
309
467
  protected _id: string;
468
+ protected _changeSetListener?: Disposable;
469
+ protected _changeSet?: ChangeSetImpl;
310
470
 
311
471
  constructor(public readonly location = ChatAgentLocation.Panel) {
312
472
  // TODO accept serialized data as a parameter to restore a previously saved ChatModel
@@ -318,12 +478,52 @@ export class ChatModelImpl implements ChatModel {
318
478
  return this._requests;
319
479
  }
320
480
 
481
+ getRequest(id: string): ChatRequestModelImpl | undefined {
482
+ return this._requests.find(request => request.id === id);
483
+ }
484
+
321
485
  get id(): string {
322
486
  return this._id;
323
487
  }
324
488
 
325
- addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string): ChatRequestModelImpl {
326
- const requestModel = new ChatRequestModelImpl(this, parsedChatRequest, agentId);
489
+ get changeSet(): ChangeSetImpl | undefined {
490
+ return this._changeSet;
491
+ }
492
+
493
+ 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;
501
+ }
502
+ this._onDidChangeEmitter.fire({
503
+ kind: 'setChangeSet',
504
+ changeSet: this._changeSet,
505
+ });
506
+ this._changeSetListener = this._changeSet.onDidChange(() => {
507
+ this._onDidChangeEmitter.fire({
508
+ kind: 'updateChangeSet',
509
+ changeSet: this._changeSet!,
510
+ });
511
+ });
512
+ }
513
+
514
+ removeChangeSet(): void {
515
+ if (this._changeSet) {
516
+ const oldChangeSet = this._changeSet;
517
+ this._changeSet = undefined;
518
+ this._onDidChangeEmitter.fire({
519
+ kind: 'removeChangeSet',
520
+ changeSet: oldChangeSet,
521
+ });
522
+ }
523
+ }
524
+
525
+ addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string, context: ResolvedAIVariable[] = []): ChatRequestModelImpl {
526
+ const requestModel = new ChatRequestModelImpl(this, parsedChatRequest, agentId, context);
327
527
  this._requests.push(requestModel);
328
528
  this._onDidChangeEmitter.fire({
329
529
  kind: 'addRequest',
@@ -337,21 +537,72 @@ export class ChatModelImpl implements ChatModel {
337
537
  }
338
538
  }
339
539
 
540
+ export class ChangeSetImpl implements ChangeSet {
541
+ protected readonly _onDidChangeEmitter = new Emitter<void>();
542
+ onDidChange: Event<void> = this._onDidChangeEmitter.event;
543
+
544
+ protected _elements: ChangeSetElement[] = [];
545
+
546
+ constructor(public readonly title: string, elements: ChangeSetElement[] = []) {
547
+ this.addElements(elements);
548
+ }
549
+
550
+ getElements(): ChangeSetElement[] {
551
+ return this._elements;
552
+ }
553
+
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;
571
+ }
572
+
573
+ addOrReplaceElement(element: ChangeSetElement): void {
574
+ if (!this.replaceElement(element)) {
575
+ this.addElement(element);
576
+ }
577
+ }
578
+
579
+ removeElement(index: number): void {
580
+ this._elements.splice(index, 1);
581
+ this.notifyChange();
582
+ }
583
+
584
+ notifyChange(): void {
585
+ this._onDidChangeEmitter.fire();
586
+ }
587
+ }
588
+
340
589
  export class ChatRequestModelImpl implements ChatRequestModel {
341
590
  protected readonly _id: string;
342
- protected _session: ChatModel;
591
+ protected _session: ChatModelImpl;
343
592
  protected _request: ChatRequest;
344
593
  protected _response: ChatResponseModelImpl;
594
+ protected _context: ResolvedAIVariable[];
345
595
  protected _agentId?: string;
346
596
  protected _data: { [key: string]: unknown };
347
597
 
348
- constructor(session: ChatModel, public readonly message: ParsedChatRequest, agentId?: string,
349
- data: { [key: string]: unknown } = {}) {
598
+ constructor(session: ChatModelImpl, public readonly message: ParsedChatRequest, agentId?: string,
599
+ context: ResolvedAIVariable[] = [], data: { [key: string]: unknown } = {}) {
350
600
  // TODO accept serialized data as a parameter to restore a previously saved ChatRequestModel
351
601
  this._request = message.request;
352
602
  this._id = generateUuid();
353
603
  this._session = session;
354
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));
355
606
  this._agentId = agentId;
356
607
  this._data = data;
357
608
  }
@@ -372,7 +623,7 @@ export class ChatRequestModelImpl implements ChatRequestModel {
372
623
  return this._id;
373
624
  }
374
625
 
375
- get session(): ChatModel {
626
+ get session(): ChatModelImpl {
376
627
  return this._session;
377
628
  }
378
629
 
@@ -387,6 +638,10 @@ export class ChatRequestModelImpl implements ChatRequestModel {
387
638
  get agentId(): string | undefined {
388
639
  return this._agentId;
389
640
  }
641
+
642
+ cancel(): void {
643
+ this.response.cancel();
644
+ }
390
645
  }
391
646
 
392
647
  export class ErrorChatResponseContentImpl implements ErrorChatResponseContent {
@@ -602,6 +857,31 @@ export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayout
602
857
  }
603
858
  }
604
859
 
860
+ /**
861
+ * Default implementation for the QuestionResponseContent.
862
+ */
863
+ export class QuestionResponseContentImpl implements QuestionResponseContent {
864
+ readonly kind = 'question';
865
+ protected _selectedOption: { text: string; value?: string } | undefined;
866
+ constructor(public question: string, public options: { text: string, value?: string }[],
867
+ public request: ChatRequestModelImpl, public handler: QuestionResponseHandler) {
868
+ }
869
+ set selectedOption(option: { text: string; value?: string; } | undefined) {
870
+ this._selectedOption = option;
871
+ this.request.response.response.responseContentChanged();
872
+ }
873
+ get selectedOption(): { text: string; value?: string; } | undefined {
874
+ return this._selectedOption;
875
+ }
876
+ asString?(): string | undefined {
877
+ return `Question: ${this.question}
878
+ ${this.selectedOption ? `Answer: ${this.selectedOption?.text}` : 'No answer'}`;
879
+ }
880
+ merge?(): boolean {
881
+ return false;
882
+ }
883
+ }
884
+
605
885
  class ChatResponseImpl implements ChatResponse {
606
886
  protected readonly _onDidChangeEmitter = new Emitter<void>();
607
887
  onDidChange: Event<void> = this._onDidChangeEmitter.event;
@@ -654,6 +934,11 @@ class ChatResponseImpl implements ChatResponse {
654
934
  this._updateResponseRepresentation();
655
935
  }
656
936
 
937
+ responseContentChanged(): void {
938
+ this._updateResponseRepresentation();
939
+ this._onDidChangeEmitter.fire();
940
+ }
941
+
657
942
  protected _updateResponseRepresentation(): void {
658
943
  this._responseRepresentation = this._content
659
944
  .map(responseContent => {
@@ -682,15 +967,18 @@ class ChatResponseModelImpl implements ChatResponseModel {
682
967
  protected readonly _onDidChangeEmitter = new Emitter<void>();
683
968
  onDidChange: Event<void> = this._onDidChangeEmitter.event;
684
969
 
970
+ data = {};
971
+
685
972
  protected _id: string;
686
973
  protected _requestId: string;
687
974
  protected _progressMessages: ChatProgressMessage[];
688
975
  protected _response: ChatResponseImpl;
689
976
  protected _isComplete: boolean;
690
- protected _isCanceled: boolean;
977
+ protected _isWaitingForInput: boolean;
691
978
  protected _agentId?: string;
692
979
  protected _isError: boolean;
693
980
  protected _errorObject: Error | undefined;
981
+ protected _cancellationToken: CancellationTokenSource;
694
982
 
695
983
  constructor(requestId: string, agentId?: string) {
696
984
  // TODO accept serialized data as a parameter to restore a previously saved ChatResponseModel
@@ -701,8 +989,9 @@ class ChatResponseModelImpl implements ChatResponseModel {
701
989
  response.onDidChange(() => this._onDidChangeEmitter.fire());
702
990
  this._response = response;
703
991
  this._isComplete = false;
704
- this._isCanceled = false;
992
+ this._isWaitingForInput = false;
705
993
  this._agentId = agentId;
994
+ this._cancellationToken = new CancellationTokenSource();
706
995
  }
707
996
 
708
997
  get id(): string {
@@ -728,6 +1017,7 @@ class ChatResponseModelImpl implements ChatResponseModel {
728
1017
  kind: 'progressMessage',
729
1018
  id,
730
1019
  status: message.status ?? 'inProgress',
1020
+ show: message.show ?? 'untilFirstContent',
731
1021
  ...message,
732
1022
  };
733
1023
  this._progressMessages.push(newMessage);
@@ -756,7 +1046,11 @@ class ChatResponseModelImpl implements ChatResponseModel {
756
1046
  }
757
1047
 
758
1048
  get isCanceled(): boolean {
759
- return this._isCanceled;
1049
+ return this._cancellationToken.token.isCancellationRequested;
1050
+ }
1051
+
1052
+ get isWaitingForInput(): boolean {
1053
+ return this._isWaitingForInput;
760
1054
  }
761
1055
 
762
1056
  get agentId(): string | undefined {
@@ -769,17 +1063,34 @@ class ChatResponseModelImpl implements ChatResponseModel {
769
1063
 
770
1064
  complete(): void {
771
1065
  this._isComplete = true;
1066
+ this._isWaitingForInput = false;
772
1067
  this._onDidChangeEmitter.fire();
773
1068
  }
774
1069
 
775
1070
  cancel(): void {
1071
+ this._cancellationToken.cancel();
776
1072
  this._isComplete = true;
777
- this._isCanceled = true;
1073
+ this._isWaitingForInput = false;
1074
+ this._onDidChangeEmitter.fire();
1075
+ }
1076
+
1077
+ get cancellationToken(): CancellationToken {
1078
+ return this._cancellationToken.token;
1079
+ }
1080
+
1081
+ waitForInput(): void {
1082
+ this._isWaitingForInput = true;
778
1083
  this._onDidChangeEmitter.fire();
779
1084
  }
1085
+
1086
+ stopWaitingForInput(): void {
1087
+ this._isWaitingForInput = false;
1088
+ this._onDidChangeEmitter.fire();
1089
+ }
1090
+
780
1091
  error(error: Error): void {
781
1092
  this._isComplete = true;
782
- this._isCanceled = false;
1093
+ this._isWaitingForInput = false;
783
1094
  this._isError = true;
784
1095
  this._errorObject = error;
785
1096
  this._onDidChangeEmitter.fire();
@@ -86,6 +86,11 @@ export interface ChatService {
86
86
  sessionId: string,
87
87
  request: ChatRequest
88
88
  ): Promise<ChatRequestInvocation | undefined>;
89
+
90
+ deleteChangeSet(sessionId: string): void;
91
+ deleteChangeSetElement(sessionId: string, index: number): void;
92
+
93
+ cancelRequest(sessionId: string, requestId: string): Promise<void>;
89
94
  }
90
95
 
91
96
  interface ChatSessionInternal extends ChatSession {
@@ -219,6 +224,10 @@ export class ChatServiceImpl implements ChatService {
219
224
  return invocation;
220
225
  }
221
226
 
227
+ async cancelRequest(sessionId: string, requestId: string): Promise<void> {
228
+ return this.getSession(sessionId)?.model.getRequest(requestId)?.response.cancel();
229
+ }
230
+
222
231
  protected getAgent(parsedRequest: ParsedChatRequest): ChatAgent | undefined {
223
232
  const agentPart = this.getMentionedAgent(parsedRequest);
224
233
  if (agentPart) {
@@ -233,4 +242,12 @@ export class ChatServiceImpl implements ChatService {
233
242
  protected getMentionedAgent(parsedRequest: ParsedChatRequest): ParsedChatRequestAgentPart | undefined {
234
243
  return parsedRequest.parts.find(p => p instanceof ParsedChatRequestAgentPart) as ParsedChatRequestAgentPart | undefined;
235
244
  }
245
+
246
+ deleteChangeSet(sessionId: string): void {
247
+ this.getSession(sessionId)?.model.setChangeSet(undefined);
248
+ }
249
+
250
+ deleteChangeSetElement(sessionId: string, index: number): void {
251
+ this.getSession(sessionId)?.model.changeSet?.removeElement(index);
252
+ }
236
253
  }
@@ -0,0 +1,59 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ToolRequest } from '@theia/ai-core';
18
+ import { injectable } from '@theia/core/shared/inversify';
19
+ import { ChatRequestModelImpl } from './chat-model';
20
+
21
+ export interface ChatToolRequest extends ToolRequest {
22
+ handler: (
23
+ arg_string: string,
24
+ context: ChatRequestModelImpl,
25
+ ) => Promise<unknown>;
26
+ }
27
+
28
+ /**
29
+ * Wraps tool requests in a chat context.
30
+ *
31
+ * This service extracts tool requests from a given chat request model and wraps their
32
+ * handler functions to provide additional context, such as the chat request model.
33
+ */
34
+ @injectable()
35
+ export class ChatToolRequestService {
36
+
37
+ getChatToolRequests(request: ChatRequestModelImpl): ChatToolRequest[] {
38
+ const toolRequests = request.message.toolRequests.size > 0 ? [...request.message.toolRequests.values()] : undefined;
39
+ if (!toolRequests) {
40
+ return [];
41
+ }
42
+ return this.toChatToolRequests(toolRequests, request);
43
+ }
44
+
45
+ toChatToolRequests(toolRequests: ToolRequest[] | undefined, request: ChatRequestModelImpl): ChatToolRequest[] {
46
+ if (!toolRequests) {
47
+ return [];
48
+ }
49
+ return toolRequests.map(toolRequest => this.toChatToolRequest(toolRequest, request));
50
+ }
51
+
52
+ protected toChatToolRequest(toolRequest: ToolRequest, request: ChatRequestModelImpl): ChatToolRequest {
53
+ return {
54
+ ...toolRequest,
55
+ handler: async (arg_string: string) => toolRequest.handler(arg_string, request)
56
+ };
57
+ }
58
+
59
+ }
@@ -36,7 +36,9 @@ import {
36
36
 
37
37
  export const commandTemplate: PromptTemplate = {
38
38
  id: 'command-system',
39
- template: `# System Prompt
39
+ template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here:
40
+ https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
41
+ # System Prompt
40
42
 
41
43
  You are a service that helps users find commands to execute in an IDE.
42
44
  You reply with stringified JSON Objects that tell the user which command to execute and its arguments, if any.
@@ -313,7 +315,7 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent<Parsed
313
315
  const theiaCommand = this.commandRegistry.getCommand(parsedCommand.commandId);
314
316
  if (theiaCommand === undefined) {
315
317
  console.error(`No Theia Command with id ${parsedCommand.commandId}`);
316
- request.response.cancel();
318
+ request.cancel();
317
319
  }
318
320
  const args = parsedCommand.arguments !== undefined &&
319
321
  parsedCommand.arguments.length > 0
@@ -16,6 +16,7 @@
16
16
  export * from './chat-agents';
17
17
  export * from './chat-agent-service';
18
18
  export * from './chat-model';
19
+ export * from './chat-model-util';
19
20
  export * from './chat-request-parser';
20
21
  export * from './chat-service';
21
22
  export * from './command-chat-agents';