@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.
Files changed (102) hide show
  1. package/lib/browser/agents-md-frontend-application-contribution.d.ts +65 -0
  2. package/lib/browser/agents-md-frontend-application-contribution.d.ts.map +1 -0
  3. package/lib/browser/agents-md-frontend-application-contribution.js +269 -0
  4. package/lib/browser/agents-md-frontend-application-contribution.js.map +1 -0
  5. package/lib/browser/agents-md-frontend-application-contribution.spec.d.ts +2 -0
  6. package/lib/browser/agents-md-frontend-application-contribution.spec.d.ts.map +1 -0
  7. package/lib/browser/agents-md-frontend-application-contribution.spec.js +225 -0
  8. package/lib/browser/agents-md-frontend-application-contribution.spec.js.map +1 -0
  9. package/lib/browser/agents-md-migration-service.d.ts +78 -0
  10. package/lib/browser/agents-md-migration-service.d.ts.map +1 -0
  11. package/lib/browser/agents-md-migration-service.js +195 -0
  12. package/lib/browser/agents-md-migration-service.js.map +1 -0
  13. package/lib/browser/agents-md-migration-service.spec.d.ts +2 -0
  14. package/lib/browser/agents-md-migration-service.spec.d.ts.map +1 -0
  15. package/lib/browser/agents-md-migration-service.spec.js +278 -0
  16. package/lib/browser/agents-md-migration-service.spec.js.map +1 -0
  17. package/lib/browser/agents-md-prompt-fragment-contribution.d.ts +13 -0
  18. package/lib/browser/agents-md-prompt-fragment-contribution.d.ts.map +1 -0
  19. package/lib/browser/agents-md-prompt-fragment-contribution.js +76 -0
  20. package/lib/browser/agents-md-prompt-fragment-contribution.js.map +1 -0
  21. package/lib/browser/agents-md-service.d.ts +58 -0
  22. package/lib/browser/agents-md-service.d.ts.map +1 -0
  23. package/lib/browser/agents-md-service.js +191 -0
  24. package/lib/browser/agents-md-service.js.map +1 -0
  25. package/lib/browser/agents-md-service.spec.d.ts +2 -0
  26. package/lib/browser/agents-md-service.spec.d.ts.map +1 -0
  27. package/lib/browser/agents-md-service.spec.js +220 -0
  28. package/lib/browser/agents-md-service.spec.js.map +1 -0
  29. package/lib/browser/agents-md-variable-contribution.d.ts +66 -0
  30. package/lib/browser/agents-md-variable-contribution.d.ts.map +1 -0
  31. package/lib/browser/agents-md-variable-contribution.js +169 -0
  32. package/lib/browser/agents-md-variable-contribution.js.map +1 -0
  33. package/lib/browser/agents-md-variable-contribution.spec.d.ts +2 -0
  34. package/lib/browser/agents-md-variable-contribution.spec.d.ts.map +1 -0
  35. package/lib/browser/agents-md-variable-contribution.spec.js +148 -0
  36. package/lib/browser/agents-md-variable-contribution.spec.js.map +1 -0
  37. package/lib/browser/ai-core-frontend-module.d.ts.map +1 -1
  38. package/lib/browser/ai-core-frontend-module.js +15 -0
  39. package/lib/browser/ai-core-frontend-module.js.map +1 -1
  40. package/lib/browser/frontend-prompt-customization-service.d.ts +2 -1
  41. package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
  42. package/lib/browser/frontend-prompt-customization-service.js +10 -0
  43. package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
  44. package/lib/browser/frontend-prompt-customization-service.spec.js +19 -1
  45. package/lib/browser/frontend-prompt-customization-service.spec.js.map +1 -1
  46. package/lib/browser/frontend-variable-service.js +1 -1
  47. package/lib/browser/frontend-variable-service.js.map +1 -1
  48. package/lib/browser/index.d.ts +5 -0
  49. package/lib/browser/index.d.ts.map +1 -1
  50. package/lib/browser/index.js +5 -0
  51. package/lib/browser/index.js.map +1 -1
  52. package/lib/browser/prompt-variable-contribution.js +1 -1
  53. package/lib/browser/prompt-variable-contribution.js.map +1 -1
  54. package/lib/browser/prompt-variable-contribution.spec.js +2 -1
  55. package/lib/browser/prompt-variable-contribution.spec.js.map +1 -1
  56. package/lib/browser/prompttemplate-contribution.js +1 -1
  57. package/lib/browser/prompttemplate-contribution.js.map +1 -1
  58. package/lib/browser/skill-service.spec.js +2 -1
  59. package/lib/browser/skill-service.spec.js.map +1 -1
  60. package/lib/browser/skills-variable-contribution.spec.js +2 -1
  61. package/lib/browser/skills-variable-contribution.spec.js.map +1 -1
  62. package/lib/common/agents-md.d.ts +20 -0
  63. package/lib/common/agents-md.d.ts.map +1 -0
  64. package/lib/common/agents-md.js +38 -0
  65. package/lib/common/agents-md.js.map +1 -0
  66. package/lib/common/ai-variable-resource.js +3 -3
  67. package/lib/common/ai-variable-resource.js.map +1 -1
  68. package/lib/common/capability-variable-contribution.js +1 -1
  69. package/lib/common/capability-variable-contribution.js.map +1 -1
  70. package/lib/common/index.d.ts +1 -0
  71. package/lib/common/index.d.ts.map +1 -1
  72. package/lib/common/index.js +1 -0
  73. package/lib/common/index.js.map +1 -1
  74. package/lib/common/prompt-service.d.ts +12 -0
  75. package/lib/common/prompt-service.d.ts.map +1 -1
  76. package/lib/common/prompt-service.js.map +1 -1
  77. package/lib/common/prompt-service.spec.js +2 -1
  78. package/lib/common/prompt-service.spec.js.map +1 -1
  79. package/lib/common/variable-service.spec.js +2 -1
  80. package/lib/common/variable-service.spec.js.map +1 -1
  81. package/lib/node/model-snapshot-store.js +2 -2
  82. package/lib/node/model-snapshot-store.js.map +1 -1
  83. package/lib/node/proxy-util.js +2 -1
  84. package/lib/node/proxy-util.js.map +1 -1
  85. package/package.json +9 -9
  86. package/src/browser/agents-md-frontend-application-contribution.spec.ts +244 -0
  87. package/src/browser/agents-md-frontend-application-contribution.ts +291 -0
  88. package/src/browser/agents-md-migration-service.spec.ts +338 -0
  89. package/src/browser/agents-md-migration-service.ts +226 -0
  90. package/src/browser/agents-md-prompt-fragment-contribution.ts +73 -0
  91. package/src/browser/agents-md-service.spec.ts +258 -0
  92. package/src/browser/agents-md-service.ts +212 -0
  93. package/src/browser/agents-md-variable-contribution.spec.ts +180 -0
  94. package/src/browser/agents-md-variable-contribution.ts +182 -0
  95. package/src/browser/ai-core-frontend-module.ts +17 -0
  96. package/src/browser/frontend-prompt-customization-service.spec.ts +24 -0
  97. package/src/browser/frontend-prompt-customization-service.ts +13 -1
  98. package/src/browser/index.ts +5 -0
  99. package/src/common/agents-md.ts +37 -0
  100. package/src/common/ai-variable-resource.ts +1 -1
  101. package/src/common/index.ts +1 -0
  102. 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, '&amp;')
177
+ .replace(/</g, '&lt;')
178
+ .replace(/>/g, '&gt;')
179
+ .replace(/"/g, '&quot;')
180
+ .replace(/'/g, '&apos;');
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) {
@@ -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 * as deepEqual from 'fast-deep-equal';
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';
@@ -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