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

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 (60) hide show
  1. package/lib/browser/ai-chat-frontend-module.d.ts.map +1 -1
  2. package/lib/browser/ai-chat-frontend-module.js +6 -0
  3. package/lib/browser/ai-chat-frontend-module.js.map +1 -1
  4. package/lib/browser/change-set-file-element.d.ts +7 -0
  5. package/lib/browser/change-set-file-element.d.ts.map +1 -1
  6. package/lib/browser/change-set-file-element.js +3 -0
  7. package/lib/browser/change-set-file-element.js.map +1 -1
  8. package/lib/browser/change-set-variable.d.ts +11 -0
  9. package/lib/browser/change-set-variable.d.ts.map +1 -0
  10. package/lib/browser/change-set-variable.js +56 -0
  11. package/lib/browser/change-set-variable.js.map +1 -0
  12. package/lib/common/chat-agents.d.ts +10 -3
  13. package/lib/common/chat-agents.d.ts.map +1 -1
  14. package/lib/common/chat-agents.js +11 -4
  15. package/lib/common/chat-agents.js.map +1 -1
  16. package/lib/common/chat-model.d.ts +28 -3
  17. package/lib/common/chat-model.d.ts.map +1 -1
  18. package/lib/common/chat-model.js +50 -2
  19. package/lib/common/chat-model.js.map +1 -1
  20. package/lib/common/chat-service.d.ts +4 -7
  21. package/lib/common/chat-service.d.ts.map +1 -1
  22. package/lib/common/chat-service.js +10 -8
  23. package/lib/common/chat-service.js.map +1 -1
  24. package/lib/common/chat-string-utils.d.ts +3 -0
  25. package/lib/common/chat-string-utils.d.ts.map +1 -0
  26. package/lib/common/chat-string-utils.js +27 -0
  27. package/lib/common/chat-string-utils.js.map +1 -0
  28. package/lib/common/context-details-variable.d.ts +9 -0
  29. package/lib/common/context-details-variable.d.ts.map +1 -0
  30. package/lib/common/context-details-variable.js +57 -0
  31. package/lib/common/context-details-variable.js.map +1 -0
  32. package/lib/common/context-summary-variable.d.ts +9 -0
  33. package/lib/common/context-summary-variable.d.ts.map +1 -0
  34. package/lib/common/context-summary-variable.js +57 -0
  35. package/lib/common/context-summary-variable.js.map +1 -0
  36. package/lib/common/context-variables.d.ts +4 -0
  37. package/lib/common/context-variables.d.ts.map +1 -0
  38. package/lib/common/context-variables.js +22 -0
  39. package/lib/common/context-variables.js.map +1 -0
  40. package/lib/common/custom-chat-agent.d.ts +0 -1
  41. package/lib/common/custom-chat-agent.d.ts.map +1 -1
  42. package/lib/common/custom-chat-agent.js +2 -1
  43. package/lib/common/custom-chat-agent.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/package.json +11 -11
  49. package/src/browser/ai-chat-frontend-module.ts +6 -0
  50. package/src/browser/change-set-file-element.ts +10 -0
  51. package/src/browser/change-set-variable.ts +54 -0
  52. package/src/common/chat-agents.ts +17 -5
  53. package/src/common/chat-model.ts +73 -3
  54. package/src/common/chat-service.ts +12 -17
  55. package/src/common/chat-string-utils.ts +23 -0
  56. package/src/common/context-details-variable.ts +53 -0
  57. package/src/common/context-summary-variable.ts +53 -0
  58. package/src/common/context-variables.ts +19 -0
  59. package/src/common/custom-chat-agent.ts +2 -1
  60. package/src/common/index.ts +1 -0
@@ -42,6 +42,9 @@ import { ChangeSetFileService } from './change-set-file-service';
42
42
  import { ContextVariableLabelProvider } from './context-variable-label-provider';
43
43
  import { ContextFileVariableLabelProvider } from './context-file-variable-label-provider';
44
44
  import { FileChatVariableContribution } from './file-chat-variable-contribution';
45
+ import { ContextSummaryVariableContribution } from '../common/context-summary-variable';
46
+ import { ContextDetailsVariableContribution } from '../common/context-details-variable';
47
+ import { ChangeSetVariableContribution } from './change-set-variable';
45
48
 
46
49
  export default new ContainerModule(bind => {
47
50
  bindContributionProvider(bind, Agent);
@@ -102,4 +105,7 @@ export default new ContainerModule(bind => {
102
105
  bind(ResourceResolver).toService(ChangeSetFileResourceResolver);
103
106
  bind(ToolCallChatResponseContentFactory).toSelf().inSingletonScope();
104
107
  bind(AIVariableContribution).to(FileChatVariableContribution).inSingletonScope();
108
+ bind(AIVariableContribution).to(ContextSummaryVariableContribution).inSingletonScope();
109
+ bind(AIVariableContribution).to(ContextDetailsVariableContribution).inSingletonScope();
110
+ bind(AIVariableContribution).to(ChangeSetVariableContribution).inSingletonScope();
105
111
  });
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { DisposableCollection, Emitter, URI } from '@theia/core';
18
18
  import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
19
+ import { Replacement } from '@theia/core/lib/common/content-replacer';
19
20
  import { ChangeSetElement, ChangeSetImpl } from '../common';
20
21
  import { ChangeSetFileResourceResolver, createChangeSetFileUri, UpdatableReferenceResource } from './change-set-file-resource';
21
22
  import { ChangeSetFileService } from './change-set-file-service';
@@ -39,6 +40,11 @@ export interface ChangeSetElementArgs extends Partial<ChangeSetElement> {
39
40
  * If `undefined`, there is no change.
40
41
  */
41
42
  targetState?: string;
43
+ /**
44
+ * An array of replacements used to create the new content for the targetState.
45
+ * This is only available if the agent was able to provide replacements and we were able to apply them.
46
+ */
47
+ replacements?: Replacement[];
42
48
  };
43
49
 
44
50
  @injectable()
@@ -149,6 +155,10 @@ export class ChangeSetFileElement implements ChangeSetElement {
149
155
  }
150
156
  }
151
157
 
158
+ get replacements(): Replacement[] | undefined {
159
+ return this.elementProps.replacements;
160
+ }
161
+
152
162
  get type(): 'add' | 'modify' | 'delete' | undefined {
153
163
  return this.elementProps.type;
154
164
  }
@@ -0,0 +1,54 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH and others.
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 { MaybePromise, nls } from '@theia/core';
18
+ import { inject, injectable } from '@theia/core/shared/inversify';
19
+ import { AIVariable, ResolvedAIVariable, AIVariableContribution, AIVariableResolver, AIVariableService, AIVariableResolutionRequest, AIVariableContext } from '@theia/ai-core';
20
+ import { WorkspaceService } from '@theia/workspace/lib/browser';
21
+ import { ChatSessionContext } from '../common';
22
+
23
+ export const CHANGE_SET_SUMMARY_VARIABLE: AIVariable = {
24
+ id: 'changeSetSummary',
25
+ description: nls.localize('theia/ai/core/changeSetSummaryVariable/description', 'Provides a summary of the files in a change set and their contents.'),
26
+
27
+ name: 'changeSetSummary',
28
+ };
29
+
30
+ @injectable()
31
+ export class ChangeSetVariableContribution implements AIVariableContribution, AIVariableResolver {
32
+ @inject(WorkspaceService)
33
+ protected readonly workspaceService: WorkspaceService;
34
+
35
+ registerVariables(service: AIVariableService): void {
36
+ service.registerResolver(CHANGE_SET_SUMMARY_VARIABLE, this);
37
+ }
38
+
39
+ canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise<number> {
40
+ return request.variable.name === CHANGE_SET_SUMMARY_VARIABLE.name ? 50 : 0;
41
+ }
42
+
43
+ async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
44
+ if (!ChatSessionContext.is(context) || request.variable.name !== CHANGE_SET_SUMMARY_VARIABLE.name || !context.model.changeSet?.getElements().length) { return undefined; }
45
+ const entries = await Promise.all(
46
+ context.model.changeSet.getElements().map(async element => `- file: ${await this.workspaceService.getWorkspaceRelativePath(element.uri)}, status: ${element.state}`)
47
+ );
48
+ return {
49
+ variable: CHANGE_SET_SUMMARY_VARIABLE,
50
+ value: entries.join('\n')
51
+ };
52
+ }
53
+ }
54
+
@@ -21,6 +21,7 @@
21
21
 
22
22
  import {
23
23
  AgentSpecificVariables,
24
+ AIVariableContext,
24
25
  CommunicationRecordingService,
25
26
  getTextOfResponse,
26
27
  LanguageModel,
@@ -50,7 +51,8 @@ import {
50
51
  ChatResponseContent,
51
52
  ErrorChatResponseContentImpl,
52
53
  MarkdownChatResponseContentImpl,
53
- ToolCallChatResponseContentImpl
54
+ ToolCallChatResponseContentImpl,
55
+ ChatRequestModel
54
56
  } from './chat-model';
55
57
  import { findFirstMatch, parseContents } from './parse-contents';
56
58
  import { DefaultResponseContentFactory, ResponseContentMatcher, ResponseContentMatcherProvider } from './response-content-matcher';
@@ -86,6 +88,17 @@ export namespace SystemMessageDescription {
86
88
  }
87
89
  }
88
90
 
91
+ export interface ChatSessionContext extends AIVariableContext {
92
+ request?: ChatRequestModel;
93
+ model: ChatModel;
94
+ }
95
+
96
+ export namespace ChatSessionContext {
97
+ export function is(candidate: unknown): candidate is ChatSessionContext {
98
+ return typeof candidate === 'object' && !!candidate && 'model' in candidate;
99
+ }
100
+ }
101
+
89
102
  /**
90
103
  * The location from where an chat agent may be invoked.
91
104
  * Based on the location, a different context may be available.
@@ -168,8 +181,7 @@ export abstract class AbstractChatAgent implements ChatAgent {
168
181
  if (!languageModel) {
169
182
  throw new Error('Couldn\'t find a matching language model. Please check your setup!');
170
183
  }
171
-
172
- const systemMessageDescription = await this.getSystemMessageDescription();
184
+ const systemMessageDescription = await this.getSystemMessageDescription({ model: request.session, request } satisfies ChatSessionContext);
173
185
  const messages = await this.getMessages(request.session);
174
186
  if (this.defaultLogging) {
175
187
  this.recordingService.recordRequest(
@@ -244,11 +256,11 @@ export abstract class AbstractChatAgent implements ChatAgent {
244
256
  return languageModel;
245
257
  }
246
258
 
247
- protected async getSystemMessageDescription(): Promise<SystemMessageDescription | undefined> {
259
+ protected async getSystemMessageDescription(context: AIVariableContext): Promise<SystemMessageDescription | undefined> {
248
260
  if (this.systemPromptId === undefined) {
249
261
  return undefined;
250
262
  }
251
- const resolvedPrompt = await this.promptService.getPrompt(this.systemPromptId);
263
+ const resolvedPrompt = await this.promptService.getPrompt(this.systemPromptId, undefined, context);
252
264
  return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined;
253
265
  }
254
266
 
@@ -24,7 +24,7 @@ import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markd
24
24
  import { Position } from '@theia/core/shared/vscode-languageserver-protocol';
25
25
  import { ChatAgentLocation } from './chat-agents';
26
26
  import { ParsedChatRequest } from './parsed-chat-request';
27
- import { ResolvedAIContextVariable } from '@theia/ai-core';
27
+ import { AIVariableResolutionRequest, ResolvedAIContextVariable } from '@theia/ai-core';
28
28
 
29
29
  /**********************
30
30
  * INTERFACES AND TYPE GUARDS
@@ -33,6 +33,8 @@ import { ResolvedAIContextVariable } 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
40
  | ChatUpdateChangeSetEvent
@@ -64,6 +66,14 @@ export interface ChatRemoveChangeSetEvent {
64
66
  changeSet: ChangeSet;
65
67
  }
66
68
 
69
+ export interface ChatAddVariableEvent {
70
+ kind: 'addVariable';
71
+ }
72
+
73
+ export interface ChatRemoveVariableEvent {
74
+ kind: 'removeVariable';
75
+ }
76
+
67
77
  export namespace ChatChangeEvent {
68
78
  export function isChangeSetEvent(event: ChatChangeEvent): event is ChatSetChangeSetEvent | ChatUpdateChangeSetEvent | ChatRemoveChangeSetEvent {
69
79
  return event.kind === 'setChangeSet' || event.kind === 'removeChangeSet' || event.kind === 'updateChangeSet';
@@ -84,17 +94,26 @@ export interface ChatModel {
84
94
  readonly id: string;
85
95
  readonly location: ChatAgentLocation;
86
96
  readonly changeSet?: ChangeSet;
97
+ readonly context: ChatContextManager;
87
98
  getRequests(): ChatRequestModel[];
88
99
  isEmpty(): boolean;
89
100
  }
90
101
 
91
- export interface ChangeSet {
102
+ export interface ChangeSet extends Disposable {
92
103
  onDidChange: Event<ChangeSetChangeEvent>;
93
104
  readonly title: string;
94
105
  getElements(): ChangeSetElement[];
95
106
  dispose(): void;
96
107
  }
97
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;
115
+ }
116
+
98
117
  export interface ChangeSetElement {
99
118
  readonly uri: URI;
100
119
 
@@ -478,11 +497,13 @@ export class MutableChatModel implements ChatModel, Disposable {
478
497
  protected _requests: MutableChatRequestModel[];
479
498
  protected _id: string;
480
499
  protected _changeSet?: ChangeSetImpl;
500
+ protected readonly _contextManager = new ChatContextManagerImpl();
481
501
 
482
502
  constructor(public readonly location = ChatAgentLocation.Panel) {
483
503
  // TODO accept serialized data as a parameter to restore a previously saved ChatModel
484
504
  this._requests = [];
485
505
  this._id = generateUuid();
506
+ this._contextManager.onDidChange(e => this._onDidChangeEmitter.fire(e));
486
507
  }
487
508
 
488
509
  getRequests(): MutableChatRequestModel[] {
@@ -501,6 +522,10 @@ export class MutableChatModel implements ChatModel, Disposable {
501
522
  return this._changeSet;
502
523
  }
503
524
 
525
+ get context(): ChatContextManager {
526
+ return this._contextManager;
527
+ }
528
+
504
529
  setChangeSet(changeSet: ChangeSetImpl | undefined): void {
505
530
  if (!changeSet) {
506
531
  return this.removeChangeSet();
@@ -610,8 +635,53 @@ export class ChangeSetImpl implements ChangeSet {
610
635
  }
611
636
 
612
637
  dispose(): void {
613
- this._elements.forEach(element => element.dispose?.());
614
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;
654
+ }
655
+
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' });
667
+ }
668
+ }
669
+
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
+ }
678
+ }
679
+
680
+ clear(): void {
681
+ if (this.variables.length) {
682
+ this.variables.length = 0;
683
+ this.onDidChangeEmitter.fire({ kind: 'removeVariable' });
684
+ }
615
685
  }
616
686
  }
617
687
 
@@ -24,7 +24,7 @@ import { Emitter, ILogger, generateUuid } from '@theia/core';
24
24
  import { inject, injectable, optional } from '@theia/core/shared/inversify';
25
25
  import { Event } from '@theia/core/shared/vscode-languageserver-protocol';
26
26
  import { ChatAgentService } from './chat-agent-service';
27
- import { ChatAgent, ChatAgentLocation } from './chat-agents';
27
+ import { ChatAgent, ChatAgentLocation, ChatSessionContext } from './chat-agents';
28
28
  import {
29
29
  ChatModel,
30
30
  MutableChatModel,
@@ -60,10 +60,6 @@ export interface ChatSession {
60
60
  pinnedAgent?: ChatAgent;
61
61
  }
62
62
 
63
- export interface ChatContextRequest {
64
- variableRequests: AIVariableResolutionRequest[]
65
- }
66
-
67
63
  export interface ActiveSessionChangedEvent {
68
64
  sessionId: string | undefined;
69
65
  focus?: boolean;
@@ -104,8 +100,7 @@ export interface ChatService {
104
100
 
105
101
  sendRequest(
106
102
  sessionId: string,
107
- request: ChatRequest,
108
- requestedContext?: ChatContextRequest
103
+ request: ChatRequest
109
104
  ): Promise<ChatRequestInvocation | undefined>;
110
105
 
111
106
  deleteChangeSet(sessionId: string): void;
@@ -169,7 +164,7 @@ export class ChatServiceImpl implements ChatService {
169
164
 
170
165
  deleteSession(sessionId: string): void {
171
166
  const sessionIndex = this._sessions.findIndex(candidate => candidate.id === sessionId);
172
- if (~sessionIndex) { return; }
167
+ if (sessionIndex === -1) { return; }
173
168
  const session = this._sessions[sessionIndex];
174
169
  // If the removed session is the active one, set the newest one as active
175
170
  if (session.isActive) {
@@ -189,7 +184,6 @@ export class ChatServiceImpl implements ChatService {
189
184
  async sendRequest(
190
185
  sessionId: string,
191
186
  request: ChatRequest,
192
- requestedContext: ChatContextRequest = { variableRequests: [] }
193
187
  ): Promise<ChatRequestInvocation | undefined> {
194
188
  const session = this.getSession(sessionId);
195
189
  if (!session) {
@@ -211,14 +205,16 @@ export class ChatServiceImpl implements ChatService {
211
205
  };
212
206
  }
213
207
 
214
- const resolvedContext = await this.resolveChatContext(requestedContext, request, session);
208
+ const resolutionContext: ChatSessionContext = { model: session.model };
209
+ const resolvedContext = await this.resolveChatContext(session.model.context.getVariables(), resolutionContext);
215
210
  const requestModel = session.model.addRequest(parsedRequest, agent?.id, resolvedContext);
211
+ resolutionContext.request = requestModel;
216
212
 
217
213
  for (const part of parsedRequest.parts) {
218
214
  if (part instanceof ParsedChatRequestVariablePart) {
219
215
  const resolvedVariable = await this.variableService.resolveVariable(
220
216
  { variable: part.variableName, arg: part.variableArg },
221
- { request, model: session }
217
+ resolutionContext
222
218
  );
223
219
  if (resolvedVariable) {
224
220
  part.resolution = resolvedVariable;
@@ -260,13 +256,12 @@ export class ChatServiceImpl implements ChatService {
260
256
  }
261
257
 
262
258
  protected async resolveChatContext(
263
- requestedContext: ChatContextRequest,
264
- request: ChatRequest,
265
- session: ChatSessionInternal
259
+ resolutionRequests: readonly AIVariableResolutionRequest[],
260
+ context: ChatSessionContext,
266
261
  ): Promise<ChatContext> {
267
262
  const resolvedVariables = await Promise.all(
268
- requestedContext.variableRequests.map(async contextVariable => {
269
- const resolvedVariable = await this.variableService.resolveVariable(contextVariable, { request, model: session });
263
+ resolutionRequests.map(async contextVariable => {
264
+ const resolvedVariable = await this.variableService.resolveVariable(contextVariable, context);
270
265
  if (ResolvedAIContextVariable.is(resolvedVariable)) {
271
266
  return resolvedVariable;
272
267
  }
@@ -317,7 +312,7 @@ export class ChatServiceImpl implements ChatService {
317
312
  }
318
313
 
319
314
  deleteChangeSet(sessionId: string): void {
320
- this.getSession(sessionId)?.model.setChangeSet(undefined);
315
+ this.getSession(sessionId)?.model.removeChangeSet();
321
316
  }
322
317
 
323
318
  deleteChangeSetElement(sessionId: string, index: number): void {
@@ -0,0 +1,23 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH and others.
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
+ export function stringJsonCodeBlock(input: string): string {
18
+ return `\`\`\`json\n${input}\n\`\`\``;
19
+ }
20
+
21
+ export function dataToJsonCodeBlock(input: unknown): string {
22
+ return stringJsonCodeBlock(JSON.stringify(input, undefined, 2));
23
+ }
@@ -0,0 +1,53 @@
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 { MaybePromise, nls } from '@theia/core';
18
+ import { injectable } from '@theia/core/shared/inversify';
19
+ import { AIVariable, ResolvedAIVariable, AIVariableContribution, AIVariableResolver, AIVariableService, AIVariableResolutionRequest, AIVariableContext } from '@theia/ai-core';
20
+ import { dataToJsonCodeBlock } from './chat-string-utils';
21
+ import { ChatSessionContext } from './chat-agents';
22
+ import { CHAT_CONTEXT_DETAILS_VARIABLE_ID } from './context-variables';
23
+
24
+ export const CONTEXT_DETAILS_VARIABLE: AIVariable = {
25
+ id: CHAT_CONTEXT_DETAILS_VARIABLE_ID,
26
+ description: nls.localize('theia/ai/core/contextDetailsVariable/description', 'Provides full text values and descriptions for all context elements.'),
27
+ name: CHAT_CONTEXT_DETAILS_VARIABLE_ID,
28
+ };
29
+
30
+ @injectable()
31
+ export class ContextDetailsVariableContribution implements AIVariableContribution, AIVariableResolver {
32
+ registerVariables(service: AIVariableService): void {
33
+ service.registerResolver(CONTEXT_DETAILS_VARIABLE, this);
34
+ }
35
+
36
+ canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise<number> {
37
+ return request.variable.name === CONTEXT_DETAILS_VARIABLE.name ? 50 : 0;
38
+ }
39
+
40
+ async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
41
+ /** By expecting context.request, we're assuming that this variable will not be resolved until the context has been resolved. */
42
+ if (!ChatSessionContext.is(context) || request.variable.name !== CONTEXT_DETAILS_VARIABLE.name || !context.request) { return undefined; }
43
+ const data = context.request.context.variables.map(variable => ({
44
+ type: variable.variable.name,
45
+ ref: variable.value,
46
+ content: variable.contextValue
47
+ }));
48
+ return {
49
+ variable: CONTEXT_DETAILS_VARIABLE,
50
+ value: dataToJsonCodeBlock(data)
51
+ };
52
+ }
53
+ }
@@ -0,0 +1,53 @@
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 { MaybePromise, nls } from '@theia/core';
18
+ import { injectable } from '@theia/core/shared/inversify';
19
+ import { AIVariable, ResolvedAIVariable, AIVariableContribution, AIVariableResolver, AIVariableService, AIVariableResolutionRequest, AIVariableContext } from '@theia/ai-core';
20
+ import { dataToJsonCodeBlock } from './chat-string-utils';
21
+ import { ChatSessionContext } from './chat-agents';
22
+
23
+ export const CONTEXT_SUMMARY_VARIABLE: AIVariable = {
24
+ id: 'contextSummary',
25
+ description: nls.localize('theia/ai/core/contextSummaryVariable/description', 'Describes files in the context for a given session.'),
26
+ name: 'contextSummary',
27
+ };
28
+
29
+ @injectable()
30
+ export class ContextSummaryVariableContribution implements AIVariableContribution, AIVariableResolver {
31
+ registerVariables(service: AIVariableService): void {
32
+ service.registerResolver(CONTEXT_SUMMARY_VARIABLE, this);
33
+ }
34
+
35
+ canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise<number> {
36
+ return request.variable.name === CONTEXT_SUMMARY_VARIABLE.name ? 50 : 0;
37
+ }
38
+
39
+ async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
40
+ if (!ChatSessionContext.is(context) || request.variable.name !== CONTEXT_SUMMARY_VARIABLE.name) { return undefined; }
41
+ const data = context.model.context.getVariables().filter(variable => variable.variable.isContextVariable)
42
+ .map(variable => ({
43
+ type: variable.variable.name,
44
+ // eslint-disable-next-line no-null/no-null
45
+ instanceData: variable.arg || null,
46
+ contextElementId: variable.variable.id + variable.arg
47
+ }));
48
+ return {
49
+ variable: CONTEXT_SUMMARY_VARIABLE,
50
+ value: dataToJsonCodeBlock(data)
51
+ };
52
+ }
53
+ }
@@ -0,0 +1,19 @@
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
+ export const CHAT_CONTEXT_VARIABLE_ID = 'contextSummary';
18
+ export const CHAT_CONTEXT_DETAILS_VARIABLE_ID = 'contextDetails';
19
+ export const CHANGE_SET_SUMMARY_VARIABLE_ID = 'changeSetSummary';
@@ -24,9 +24,10 @@ export class CustomChatAgent extends AbstractStreamParsingChatAgent {
24
24
  name: string = 'CustomChatAgent';
25
25
  languageModelRequirements: LanguageModelRequirement[] = [{ purpose: 'chat' }];
26
26
  protected defaultLanguageModelPurpose: string = 'chat';
27
- protected override systemPromptId: string = `${this.name}_prompt`;
28
27
 
29
28
  set prompt(prompt: string) {
29
+ // the name is dynamic, so we set the propmptId here
30
+ this.systemPromptId = `${this.name}_prompt`;
30
31
  this.promptTemplates.push({ id: `${this.name}_prompt`, template: prompt });
31
32
  }
32
33
  }
@@ -21,3 +21,4 @@ export * from './chat-request-parser';
21
21
  export * from './chat-service';
22
22
  export * from './custom-chat-agent';
23
23
  export * from './parsed-chat-request';
24
+ export * from './context-variables';