@theia/ai-core 1.77.0-next.49 → 1.77.0-next.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/agents-md-frontend-application-contribution.d.ts +65 -0
- package/lib/browser/agents-md-frontend-application-contribution.d.ts.map +1 -0
- package/lib/browser/agents-md-frontend-application-contribution.js +269 -0
- package/lib/browser/agents-md-frontend-application-contribution.js.map +1 -0
- package/lib/browser/agents-md-frontend-application-contribution.spec.d.ts +2 -0
- package/lib/browser/agents-md-frontend-application-contribution.spec.d.ts.map +1 -0
- package/lib/browser/agents-md-frontend-application-contribution.spec.js +225 -0
- package/lib/browser/agents-md-frontend-application-contribution.spec.js.map +1 -0
- package/lib/browser/agents-md-migration-service.d.ts +78 -0
- package/lib/browser/agents-md-migration-service.d.ts.map +1 -0
- package/lib/browser/agents-md-migration-service.js +195 -0
- package/lib/browser/agents-md-migration-service.js.map +1 -0
- package/lib/browser/agents-md-migration-service.spec.d.ts +2 -0
- package/lib/browser/agents-md-migration-service.spec.d.ts.map +1 -0
- package/lib/browser/agents-md-migration-service.spec.js +278 -0
- package/lib/browser/agents-md-migration-service.spec.js.map +1 -0
- package/lib/browser/agents-md-prompt-fragment-contribution.d.ts +13 -0
- package/lib/browser/agents-md-prompt-fragment-contribution.d.ts.map +1 -0
- package/lib/browser/agents-md-prompt-fragment-contribution.js +76 -0
- package/lib/browser/agents-md-prompt-fragment-contribution.js.map +1 -0
- package/lib/browser/agents-md-service.d.ts +58 -0
- package/lib/browser/agents-md-service.d.ts.map +1 -0
- package/lib/browser/agents-md-service.js +191 -0
- package/lib/browser/agents-md-service.js.map +1 -0
- package/lib/browser/agents-md-service.spec.d.ts +2 -0
- package/lib/browser/agents-md-service.spec.d.ts.map +1 -0
- package/lib/browser/agents-md-service.spec.js +220 -0
- package/lib/browser/agents-md-service.spec.js.map +1 -0
- package/lib/browser/agents-md-variable-contribution.d.ts +66 -0
- package/lib/browser/agents-md-variable-contribution.d.ts.map +1 -0
- package/lib/browser/agents-md-variable-contribution.js +169 -0
- package/lib/browser/agents-md-variable-contribution.js.map +1 -0
- package/lib/browser/agents-md-variable-contribution.spec.d.ts +2 -0
- package/lib/browser/agents-md-variable-contribution.spec.d.ts.map +1 -0
- package/lib/browser/agents-md-variable-contribution.spec.js +148 -0
- package/lib/browser/agents-md-variable-contribution.spec.js.map +1 -0
- package/lib/browser/ai-core-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-core-frontend-module.js +15 -0
- package/lib/browser/ai-core-frontend-module.js.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.d.ts +2 -1
- package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.js +10 -0
- package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.spec.js +19 -1
- package/lib/browser/frontend-prompt-customization-service.spec.js.map +1 -1
- package/lib/browser/frontend-variable-service.js +1 -1
- package/lib/browser/frontend-variable-service.js.map +1 -1
- package/lib/browser/index.d.ts +5 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +5 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/prompt-variable-contribution.js +1 -1
- package/lib/browser/prompt-variable-contribution.js.map +1 -1
- package/lib/browser/prompt-variable-contribution.spec.js +2 -1
- package/lib/browser/prompt-variable-contribution.spec.js.map +1 -1
- package/lib/browser/prompttemplate-contribution.js +1 -1
- package/lib/browser/prompttemplate-contribution.js.map +1 -1
- package/lib/browser/skill-service.spec.js +2 -1
- package/lib/browser/skill-service.spec.js.map +1 -1
- package/lib/browser/skills-variable-contribution.spec.js +2 -1
- package/lib/browser/skills-variable-contribution.spec.js.map +1 -1
- package/lib/common/agents-md.d.ts +20 -0
- package/lib/common/agents-md.d.ts.map +1 -0
- package/lib/common/agents-md.js +38 -0
- package/lib/common/agents-md.js.map +1 -0
- package/lib/common/ai-variable-resource.js +3 -3
- package/lib/common/ai-variable-resource.js.map +1 -1
- package/lib/common/capability-variable-contribution.js +1 -1
- package/lib/common/capability-variable-contribution.js.map +1 -1
- package/lib/common/index.d.ts +1 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +1 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/prompt-service.d.ts +12 -0
- package/lib/common/prompt-service.d.ts.map +1 -1
- package/lib/common/prompt-service.js.map +1 -1
- package/lib/common/prompt-service.spec.js +2 -1
- package/lib/common/prompt-service.spec.js.map +1 -1
- package/lib/common/variable-service.spec.js +2 -1
- package/lib/common/variable-service.spec.js.map +1 -1
- package/lib/node/model-snapshot-store.js +2 -2
- package/lib/node/model-snapshot-store.js.map +1 -1
- package/lib/node/proxy-util.js +2 -1
- package/lib/node/proxy-util.js.map +1 -1
- package/package.json +9 -9
- package/src/browser/agents-md-frontend-application-contribution.spec.ts +244 -0
- package/src/browser/agents-md-frontend-application-contribution.ts +291 -0
- package/src/browser/agents-md-migration-service.spec.ts +338 -0
- package/src/browser/agents-md-migration-service.ts +226 -0
- package/src/browser/agents-md-prompt-fragment-contribution.ts +73 -0
- package/src/browser/agents-md-service.spec.ts +258 -0
- package/src/browser/agents-md-service.ts +212 -0
- package/src/browser/agents-md-variable-contribution.spec.ts +180 -0
- package/src/browser/agents-md-variable-contribution.ts +182 -0
- package/src/browser/ai-core-frontend-module.ts +17 -0
- package/src/browser/frontend-prompt-customization-service.spec.ts +24 -0
- package/src/browser/frontend-prompt-customization-service.ts +13 -1
- package/src/browser/index.ts +5 -0
- package/src/common/agents-md.ts +37 -0
- package/src/common/ai-variable-resource.ts +1 -1
- package/src/common/index.ts +1 -0
- package/src/common/prompt-service.ts +14 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 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 { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { MaybePromise, nls } from '@theia/core';
|
|
19
|
+
import {
|
|
20
|
+
AIVariable, AIVariableContext, AIVariableContribution, AIVariableResolutionRequest,
|
|
21
|
+
AIVariableResolver, AIVariableService, ResolvedAIVariable
|
|
22
|
+
} from '../common/variable-service';
|
|
23
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
24
|
+
import { AGENTS_MD_FILE_NAME } from '../common/agents-md';
|
|
25
|
+
import { AgentsMdFile, AgentsMdService } from './agents-md-service';
|
|
26
|
+
|
|
27
|
+
export const AGENTS_MD_AUTO_LOADED_VARIABLE: AIVariable = {
|
|
28
|
+
id: 'agentsMdAutoLoaded',
|
|
29
|
+
name: 'agentsMdAutoLoaded',
|
|
30
|
+
description: nls.localize('theia/ai/core/agentsMdAutoLoadedVariable/description',
|
|
31
|
+
'Returns the content of the workspace root AGENTS.md when the workspace has a single root, so that it is always in context')
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const AGENTS_MD_VARIABLE: AIVariable = {
|
|
35
|
+
id: 'agentsMd',
|
|
36
|
+
name: 'agentsMd',
|
|
37
|
+
description: nls.localize('theia/ai/core/agentsMdVariable/description',
|
|
38
|
+
'Lists the AGENTS.md files an agent has to read on demand, i.e. the ones that are not already loaded')
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const AGENTS_MD_CONTENT_VARIABLE: AIVariable = {
|
|
42
|
+
id: 'agentsMdContent',
|
|
43
|
+
name: 'agentsMdContent',
|
|
44
|
+
description: nls.localize('theia/ai/core/agentsMdContentVariable/description',
|
|
45
|
+
'Returns the full content of the AGENTS.md files of the workspace')
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the three ways of consuming `AGENTS.md`.
|
|
50
|
+
*
|
|
51
|
+
* A **single-root** workspace has an unambiguous project root, so its `AGENTS.md` is always in
|
|
52
|
+
* context, mirroring how other tools implementing the standard treat the root file. A workspace with
|
|
53
|
+
* several roots has no such file, so nothing is loaded and every discovered file is advertised
|
|
54
|
+
* instead, to be read on demand. {@link AGENTS_MD_AUTO_LOADED_VARIABLE} and
|
|
55
|
+
* {@link AGENTS_MD_VARIABLE} are therefore mutually exclusive by construction: at most one of them is
|
|
56
|
+
* ever non-empty.
|
|
57
|
+
*
|
|
58
|
+
* - {@link AGENTS_MD_AUTO_LOADED_VARIABLE} inlines the single root's file, or nothing.
|
|
59
|
+
* - {@link AGENTS_MD_VARIABLE} advertises files by path so a tool-calling agent can fetch them with
|
|
60
|
+
* `getFileContent`, or nothing.
|
|
61
|
+
* - {@link AGENTS_MD_CONTENT_VARIABLE} always inlines everything discovered, regardless of root
|
|
62
|
+
* count, for consumers such as code completion that have no tool-calling loop and therefore cannot
|
|
63
|
+
* fetch anything on demand.
|
|
64
|
+
*
|
|
65
|
+
* All three resolve to the empty string when the workspace has no `AGENTS.md`.
|
|
66
|
+
*/
|
|
67
|
+
@injectable()
|
|
68
|
+
export class AgentsMdVariableContribution implements AIVariableContribution, AIVariableResolver {
|
|
69
|
+
|
|
70
|
+
@inject(AgentsMdService)
|
|
71
|
+
protected readonly agentsMdService: AgentsMdService;
|
|
72
|
+
|
|
73
|
+
@inject(WorkspaceService)
|
|
74
|
+
protected readonly workspaceService: WorkspaceService;
|
|
75
|
+
|
|
76
|
+
registerVariables(service: AIVariableService): void {
|
|
77
|
+
service.registerResolver(AGENTS_MD_AUTO_LOADED_VARIABLE, this);
|
|
78
|
+
service.registerResolver(AGENTS_MD_VARIABLE, this);
|
|
79
|
+
service.registerResolver(AGENTS_MD_CONTENT_VARIABLE, this);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
canResolve(request: AIVariableResolutionRequest, _context: AIVariableContext): MaybePromise<number> {
|
|
83
|
+
if (request.variable.name === AGENTS_MD_AUTO_LOADED_VARIABLE.name
|
|
84
|
+
|| request.variable.name === AGENTS_MD_VARIABLE.name
|
|
85
|
+
|| request.variable.name === AGENTS_MD_CONTENT_VARIABLE.name) {
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
return -1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async resolve(request: AIVariableResolutionRequest, _context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
|
|
92
|
+
// Without this a request issued before the initial scan completes would silently report that
|
|
93
|
+
// the workspace has no project context.
|
|
94
|
+
await this.agentsMdService.ready;
|
|
95
|
+
const files = this.agentsMdService.getAgentsMdFiles();
|
|
96
|
+
|
|
97
|
+
if (request.variable.name === AGENTS_MD_AUTO_LOADED_VARIABLE.name) {
|
|
98
|
+
return { variable: request.variable, value: this.generateAutoLoaded(files) };
|
|
99
|
+
}
|
|
100
|
+
if (request.variable.name === AGENTS_MD_VARIABLE.name) {
|
|
101
|
+
// Suppressed when the root file is already in context, so the model is never told to read
|
|
102
|
+
// a file it has just been handed.
|
|
103
|
+
return { variable: request.variable, value: this.isAutoLoading() ? '' : this.generateAgentsMdXML(files) };
|
|
104
|
+
}
|
|
105
|
+
if (request.variable.name === AGENTS_MD_CONTENT_VARIABLE.name) {
|
|
106
|
+
return { variable: request.variable, value: this.mergeContent(files) };
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether the workspace root's `AGENTS.md` is put into context rather than advertised. Keyed on
|
|
113
|
+
* the number of workspace **roots**, not the number of discovered files: a workspace with several
|
|
114
|
+
* roots has no single project root whose file could stand for the whole session, even when only
|
|
115
|
+
* one of those roots happens to carry an `AGENTS.md`.
|
|
116
|
+
*/
|
|
117
|
+
protected isAutoLoading(): boolean {
|
|
118
|
+
return this.workspaceService.tryGetRoots().length === 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Inlines the single root's file. The content is raw Markdown and not XML-escaped; only the path,
|
|
123
|
+
* which is an attribute, is.
|
|
124
|
+
*/
|
|
125
|
+
protected generateAutoLoaded(files: AgentsMdFile[]): string {
|
|
126
|
+
if (!this.isAutoLoading() || files.length === 0) {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
const file = files[0];
|
|
130
|
+
return `<project_instructions path="${this.escapeXml(this.toWorkspaceRelativePath(file))}">\n${this.escapeFunctionReferences(file.content)}\n</project_instructions>`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Advertises the files by their `<rootName>/AGENTS.md` path, the path format the workspace tools
|
|
135
|
+
* accept and return, so the model can pass it to `getFileContent` verbatim.
|
|
136
|
+
*/
|
|
137
|
+
protected generateAgentsMdXML(files: AgentsMdFile[]): string {
|
|
138
|
+
if (files.length === 0) {
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
const entries = files.map(file => `<file>${this.escapeXml(this.toWorkspaceRelativePath(file))}</file>`).join('\n');
|
|
142
|
+
return `<agents_md_files>\n${entries}\n</agents_md_files>`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Inlines the content. A single root contributes its file verbatim; only a multi-root workspace
|
|
147
|
+
* gets provenance headings, mirroring how equal-priority prompt fragment sources are merged.
|
|
148
|
+
*/
|
|
149
|
+
protected mergeContent(files: AgentsMdFile[]): string {
|
|
150
|
+
if (files.length === 0) {
|
|
151
|
+
return '';
|
|
152
|
+
}
|
|
153
|
+
if (files.length === 1) {
|
|
154
|
+
return this.escapeFunctionReferences(files[0].content);
|
|
155
|
+
}
|
|
156
|
+
return files.map(file => `### ${file.rootName}\n\n${this.escapeFunctionReferences(file.content)}`).join('\n\n');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Keeps `~{...}` in inlined content literal. Function references are matched on the resolved
|
|
161
|
+
* prompt text, so a `~{someTool}` in a file anyone in the repository can edit would otherwise
|
|
162
|
+
* attach that tool to every agent using the fragment. A zero-width space between `~` and `{`
|
|
163
|
+
* breaks the match without changing what the model reads. `{{...}}` needs no such treatment, as
|
|
164
|
+
* variables are not resolved recursively.
|
|
165
|
+
*/
|
|
166
|
+
protected escapeFunctionReferences(content: string): string {
|
|
167
|
+
return content.replace(/~\{/g, '~\u200B{');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected toWorkspaceRelativePath(file: AgentsMdFile): string {
|
|
171
|
+
return `${file.rootName}/${AGENTS_MD_FILE_NAME}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
protected escapeXml(text: string): string {
|
|
175
|
+
return text
|
|
176
|
+
.replace(/&/g, '&')
|
|
177
|
+
.replace(/</g, '<')
|
|
178
|
+
.replace(/>/g, '>')
|
|
179
|
+
.replace(/"/g, '"')
|
|
180
|
+
.replace(/'/g, ''');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -84,6 +84,11 @@ import { ProductNameVariableContribution } from './product-name-variable-contrib
|
|
|
84
84
|
import { CapabilityVariableContribution } from '../common/capability-variable-contribution';
|
|
85
85
|
import { GenericCapabilitiesVariableContribution } from './generic-capabilities-variable-contribution';
|
|
86
86
|
import { GenericCapabilitiesPromptFragmentContribution } from './generic-capabilities-prompt-fragment-contribution';
|
|
87
|
+
import { AgentsMdService, DefaultAgentsMdService } from './agents-md-service';
|
|
88
|
+
import { AgentsMdVariableContribution } from './agents-md-variable-contribution';
|
|
89
|
+
import { AgentsMdPromptFragmentContribution } from './agents-md-prompt-fragment-contribution';
|
|
90
|
+
import { AgentsMdMigrationService } from './agents-md-migration-service';
|
|
91
|
+
import { AgentsMdFrontendApplicationContribution } from './agents-md-frontend-application-contribution';
|
|
87
92
|
import { LanguageModelService } from '../common/language-model-service';
|
|
88
93
|
import { FrontendLanguageModelServiceImpl } from './frontend-language-model-service';
|
|
89
94
|
import { TokenUsageFrontendService } from './token-usage-frontend-service';
|
|
@@ -142,6 +147,13 @@ export default new ContainerModule(bind => {
|
|
|
142
147
|
bind(SkillPromptCoordinator).toSelf().inSingletonScope();
|
|
143
148
|
bind(FrontendApplicationContribution).toService(SkillPromptCoordinator);
|
|
144
149
|
|
|
150
|
+
bind(DefaultAgentsMdService).toSelf().inSingletonScope();
|
|
151
|
+
bind(AgentsMdService).toService(DefaultAgentsMdService);
|
|
152
|
+
bind(AgentsMdMigrationService).toSelf().inSingletonScope();
|
|
153
|
+
bind(AgentsMdFrontendApplicationContribution).toSelf().inSingletonScope();
|
|
154
|
+
bind(FrontendApplicationContribution).toService(AgentsMdFrontendApplicationContribution);
|
|
155
|
+
bind(CommandContribution).toService(AgentsMdFrontendApplicationContribution);
|
|
156
|
+
|
|
145
157
|
bindRootContributionProvider(bind, SkillDirectoryContribution);
|
|
146
158
|
|
|
147
159
|
bindRootContributionProvider(bind, AIVariableContribution);
|
|
@@ -162,6 +174,8 @@ export default new ContainerModule(bind => {
|
|
|
162
174
|
bind(AIVariableContribution).to(OpenEditorsVariableContribution).inSingletonScope();
|
|
163
175
|
bind(SkillsVariableContribution).toSelf().inSingletonScope();
|
|
164
176
|
bind(AIVariableContribution).toService(SkillsVariableContribution);
|
|
177
|
+
bind(AgentsMdVariableContribution).toSelf().inSingletonScope();
|
|
178
|
+
bind(AIVariableContribution).toService(AgentsMdVariableContribution);
|
|
165
179
|
bind(AIVariableContribution).to(CapabilityVariableContribution).inSingletonScope();
|
|
166
180
|
bind(AIVariableContribution).to(ProductNameVariableContribution).inSingletonScope();
|
|
167
181
|
|
|
@@ -171,6 +185,9 @@ export default new ContainerModule(bind => {
|
|
|
171
185
|
bind(GenericCapabilitiesPromptFragmentContribution).toSelf().inSingletonScope();
|
|
172
186
|
bind(FrontendApplicationContribution).toService(GenericCapabilitiesPromptFragmentContribution);
|
|
173
187
|
|
|
188
|
+
bind(AgentsMdPromptFragmentContribution).toSelf().inSingletonScope();
|
|
189
|
+
bind(FrontendApplicationContribution).toService(AgentsMdPromptFragmentContribution);
|
|
190
|
+
|
|
174
191
|
bind(FrontendApplicationContribution).to(AICoreFrontendApplicationContribution).inSingletonScope();
|
|
175
192
|
|
|
176
193
|
bind(ToolInvocationRegistry).to(ToolInvocationRegistryImpl).inSingletonScope();
|
|
@@ -266,6 +266,10 @@ Template`;
|
|
|
266
266
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
267
267
|
return (this as any).provenanceLabel(sourceUri);
|
|
268
268
|
}
|
|
269
|
+
|
|
270
|
+
public useSourceMaps(active: Map<string, FragmentEntry>, all: Map<string, FragmentEntry>): void {
|
|
271
|
+
Object.assign(this, { activeCustomizations: active, allCustomizations: all });
|
|
272
|
+
}
|
|
269
273
|
}
|
|
270
274
|
|
|
271
275
|
let service: TestableCustomizationService;
|
|
@@ -362,6 +366,26 @@ Template`;
|
|
|
362
366
|
expect(idxB).to.be.lessThan(idxC);
|
|
363
367
|
});
|
|
364
368
|
|
|
369
|
+
it('reports every source and marks all equal-priority merged sources active', () => {
|
|
370
|
+
service.testAddTemplate(activeMap, 'project-info', 'Shared instructions',
|
|
371
|
+
'file:///config/project-info.prompttemplate', allMap, 1, CustomizationSource.CUSTOMIZED);
|
|
372
|
+
service.testAddTemplate(activeMap, 'project-info', 'Root A instructions',
|
|
373
|
+
'file:///rootA/.prompts/project-info.prompttemplate', allMap, 2, CustomizationSource.FOLDER);
|
|
374
|
+
service.testAddTemplate(activeMap, 'project-info', 'Root B instructions',
|
|
375
|
+
'file:///rootB/.prompts/project-info.prompttemplate', allMap, 2, CustomizationSource.FOLDER);
|
|
376
|
+
service.testAddTemplate(activeMap, 'other-fragment', 'Unrelated',
|
|
377
|
+
'file:///config/other.prompttemplate', allMap, 1, CustomizationSource.CUSTOMIZED);
|
|
378
|
+
service.useSourceMaps(activeMap, allMap);
|
|
379
|
+
|
|
380
|
+
expect(service.getPromptFragmentCustomizationSources('project-info').map(source => ({
|
|
381
|
+
path: source.uri.path.toString(), template: source.template, active: source.active
|
|
382
|
+
}))).to.deep.equal([
|
|
383
|
+
{ path: '/config/project-info.prompttemplate', template: 'Shared instructions', active: false },
|
|
384
|
+
{ path: '/rootA/.prompts/project-info.prompttemplate', template: 'Root A instructions', active: true },
|
|
385
|
+
{ path: '/rootB/.prompts/project-info.prompttemplate', template: 'Root B instructions', active: true }
|
|
386
|
+
]);
|
|
387
|
+
});
|
|
388
|
+
|
|
365
389
|
it('provenanceLabel extracts grandparent directory name from URI', () => {
|
|
366
390
|
// For "file:///home/user/my-project/.prompts/foo.prompttemplate"
|
|
367
391
|
// parent is ".prompts", grandparent is "my-project"
|
|
@@ -18,7 +18,8 @@ import { DisposableCollection, URI, Event, Emitter, nls, ILogger } from '@theia/
|
|
|
18
18
|
import { OpenerService } from '@theia/core/lib/browser';
|
|
19
19
|
import { inject, injectable, postConstruct, named } from '@theia/core/shared/inversify';
|
|
20
20
|
import {
|
|
21
|
-
PromptFragmentCustomizationService, CustomAgentDescription, CustomAgentPromptVariant, CustomizedPromptFragment, CommandPromptFragmentMetadata, CustomAgentsLocation
|
|
21
|
+
PromptFragmentCustomizationService, CustomAgentDescription, CustomAgentPromptVariant, CustomizedPromptFragment, CommandPromptFragmentMetadata, CustomAgentsLocation,
|
|
22
|
+
PromptFragmentCustomizationSource
|
|
22
23
|
} from '../common';
|
|
23
24
|
import { ConfigurableInMemoryResources } from '../common/configurable-in-memory-resources';
|
|
24
25
|
import { parseFrontmatter, serializeFrontmatter } from '../common/frontmatter';
|
|
@@ -1166,6 +1167,17 @@ export class DefaultPromptFragmentCustomizationService implements PromptFragment
|
|
|
1166
1167
|
return this.activeCustomizations.has(id);
|
|
1167
1168
|
}
|
|
1168
1169
|
|
|
1170
|
+
getPromptFragmentCustomizationSources(id: string): PromptFragmentCustomizationSource[] {
|
|
1171
|
+
const activeSources = this.activeCustomizations.get(id)?.sourceUris ?? [];
|
|
1172
|
+
return Array.from(this.allCustomizations.values())
|
|
1173
|
+
.filter(entry => entry.id === id)
|
|
1174
|
+
.map(entry => ({
|
|
1175
|
+
uri: new URI(entry.sourceUri),
|
|
1176
|
+
template: entry.template,
|
|
1177
|
+
active: activeSources.includes(entry.sourceUri)
|
|
1178
|
+
}));
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1169
1181
|
getActivePromptFragmentCustomization(id: string): CustomizedPromptFragment | undefined {
|
|
1170
1182
|
const entry = this.activeCustomizations.get(id);
|
|
1171
1183
|
if (!entry) {
|
package/src/browser/index.ts
CHANGED
|
@@ -37,6 +37,11 @@ export * from './theia-variable-contribution';
|
|
|
37
37
|
export * from './open-editors-variable-contribution';
|
|
38
38
|
export * from './skills-variable-contribution';
|
|
39
39
|
export * from './skill-service';
|
|
40
|
+
export * from './agents-md-service';
|
|
41
|
+
export * from './agents-md-variable-contribution';
|
|
42
|
+
export * from './agents-md-prompt-fragment-contribution';
|
|
43
|
+
export * from './agents-md-migration-service';
|
|
44
|
+
export * from './agents-md-frontend-application-contribution';
|
|
40
45
|
export * from './skill-prompt-coordinator';
|
|
41
46
|
export * from './agent-plugin-ui-bridge';
|
|
42
47
|
export * from './skill-registry-ui-bridge';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 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
|
+
/**
|
|
18
|
+
* File name of the [AGENTS.md standard](https://agents.md/), the source of project-specific context
|
|
19
|
+
* for AI agents. Only this exact name is recognized: `AGENT.md`, `CLAUDE.md` and the various
|
|
20
|
+
* vendor-specific instruction files are deliberately not treated as fallbacks.
|
|
21
|
+
*/
|
|
22
|
+
export const AGENTS_MD_FILE_NAME = 'AGENTS.md';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Advertises the workspace's `AGENTS.md` files by path so that a tool-calling agent can read them on
|
|
26
|
+
* demand. Reference it from a system prompt with `{{prompt:agents-md}}`.
|
|
27
|
+
*/
|
|
28
|
+
export const AGENTS_MD_PROMPT_FRAGMENT_ID = 'agents-md';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Inlines the full `AGENTS.md` content.
|
|
32
|
+
*
|
|
33
|
+
* @deprecated Kept so that prompts still referencing `{{prompt:project-info}}` keep working, and for
|
|
34
|
+
* consumers without a tool-calling loop, which cannot fetch anything on demand. Prefer
|
|
35
|
+
* {@link AGENTS_MD_PROMPT_FRAGMENT_ID}, which lets the model load the content only when relevant.
|
|
36
|
+
*/
|
|
37
|
+
export const PROJECT_INFO_PROMPT_FRAGMENT_ID = 'project-info';
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import
|
|
17
|
+
import deepEqual from 'fast-deep-equal';
|
|
18
18
|
import { inject, injectable, postConstruct, named } from '@theia/core/shared/inversify';
|
|
19
19
|
import { Resource, URI, generateUuid, ILogger } from '@theia/core';
|
|
20
20
|
import { AIVariableContext, AIVariableResolutionRequest } from './variable-service';
|
package/src/common/index.ts
CHANGED
|
@@ -41,6 +41,7 @@ export * from './ai-variable-resource';
|
|
|
41
41
|
export * from './configurable-in-memory-resources';
|
|
42
42
|
export * from './notification-types';
|
|
43
43
|
export * from './skill';
|
|
44
|
+
export * from './agents-md';
|
|
44
45
|
export * from './frontmatter';
|
|
45
46
|
export * from './capability-utils';
|
|
46
47
|
export * from './tool-constants';
|
|
@@ -247,6 +247,13 @@ export interface CustomAgentsLocation {
|
|
|
247
247
|
kind: 'agents-dir' | 'legacy-yaml';
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
/** A loaded customization file, including files hidden by a higher-priority override. */
|
|
251
|
+
export interface PromptFragmentCustomizationSource {
|
|
252
|
+
uri: URI;
|
|
253
|
+
template: string;
|
|
254
|
+
active: boolean;
|
|
255
|
+
}
|
|
256
|
+
|
|
250
257
|
/**
|
|
251
258
|
* Service responsible for customizing prompt fragments
|
|
252
259
|
*/
|
|
@@ -269,6 +276,13 @@ export interface PromptFragmentCustomizationService {
|
|
|
269
276
|
*/
|
|
270
277
|
isPromptFragmentCustomized(fragmentId: string): boolean;
|
|
271
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Gets every loaded customization file of a prompt fragment, including the ones hidden by a
|
|
281
|
+
* higher-priority customization, regardless of whether they are active.
|
|
282
|
+
* @param fragmentId The prompt fragment ID
|
|
283
|
+
*/
|
|
284
|
+
getPromptFragmentCustomizationSources?(fragmentId: string): PromptFragmentCustomizationSource[];
|
|
285
|
+
|
|
272
286
|
/**
|
|
273
287
|
* Gets the active customized prompt fragment for a given ID
|
|
274
288
|
* @param fragmentId The prompt fragment ID
|