@theia/plugin-ext 1.73.0-next.7 → 1.73.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 (169) hide show
  1. package/lib/common/lm-tool-protocol.d.ts +58 -0
  2. package/lib/common/lm-tool-protocol.d.ts.map +1 -0
  3. package/lib/common/lm-tool-protocol.js +38 -0
  4. package/lib/common/lm-tool-protocol.js.map +1 -0
  5. package/lib/common/lm-tool-protocol.spec.d.ts +2 -0
  6. package/lib/common/lm-tool-protocol.spec.d.ts.map +1 -0
  7. package/lib/common/lm-tool-protocol.spec.js +80 -0
  8. package/lib/common/lm-tool-protocol.spec.js.map +1 -0
  9. package/lib/common/plugin-api-rpc-model.d.ts +4 -0
  10. package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
  11. package/lib/common/plugin-api-rpc-model.js.map +1 -1
  12. package/lib/common/plugin-api-rpc.d.ts +6 -1
  13. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  14. package/lib/common/plugin-api-rpc.js +4 -2
  15. package/lib/common/plugin-api-rpc.js.map +1 -1
  16. package/lib/common/plugin-protocol.d.ts +8 -0
  17. package/lib/common/plugin-protocol.d.ts.map +1 -1
  18. package/lib/common/plugin-protocol.js.map +1 -1
  19. package/lib/hosted/node/hosted-plugin-localization-service.d.ts +2 -1
  20. package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
  21. package/lib/hosted/node/hosted-plugin-localization-service.js +6 -1
  22. package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
  23. package/lib/hosted/node/plugin-deployer-handler-impl.js +4 -3
  24. package/lib/hosted/node/plugin-deployer-handler-impl.js.map +1 -1
  25. package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
  26. package/lib/hosted/node/plugin-host-module.js +2 -0
  27. package/lib/hosted/node/plugin-host-module.js.map +1 -1
  28. package/lib/hosted/node/scanners/scanner-theia.d.ts +2 -0
  29. package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
  30. package/lib/hosted/node/scanners/scanner-theia.js +55 -49
  31. package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
  32. package/lib/main/browser/comments/comment-thread-widget.d.ts +3 -3
  33. package/lib/main/browser/comments/comment-thread-widget.d.ts.map +1 -1
  34. package/lib/main/browser/comments/comment-thread-widget.js.map +1 -1
  35. package/lib/main/browser/custom-editors/custom-editor-opener.d.ts.map +1 -1
  36. package/lib/main/browser/custom-editors/custom-editor-opener.js +1 -0
  37. package/lib/main/browser/custom-editors/custom-editor-opener.js.map +1 -1
  38. package/lib/main/browser/custom-editors/custom-editor-widget.d.ts +1 -0
  39. package/lib/main/browser/custom-editors/custom-editor-widget.d.ts.map +1 -1
  40. package/lib/main/browser/custom-editors/custom-editor-widget.js +3 -0
  41. package/lib/main/browser/custom-editors/custom-editor-widget.js.map +1 -1
  42. package/lib/main/browser/custom-editors/custom-editors-main.d.ts.map +1 -1
  43. package/lib/main/browser/custom-editors/custom-editors-main.js +2 -0
  44. package/lib/main/browser/custom-editors/custom-editors-main.js.map +1 -1
  45. package/lib/main/browser/debug/debug-main.d.ts +1 -11
  46. package/lib/main/browser/debug/debug-main.d.ts.map +1 -1
  47. package/lib/main/browser/debug/debug-main.js +3 -24
  48. package/lib/main/browser/debug/debug-main.js.map +1 -1
  49. package/lib/main/browser/debug/plugin-debug-service.d.ts +2 -1
  50. package/lib/main/browser/debug/plugin-debug-service.d.ts.map +1 -1
  51. package/lib/main/browser/debug/plugin-debug-service.js +8 -3
  52. package/lib/main/browser/debug/plugin-debug-service.js.map +1 -1
  53. package/lib/main/browser/debug/plugin-debug-session-factory.d.ts +7 -44
  54. package/lib/main/browser/debug/plugin-debug-session-factory.d.ts.map +1 -1
  55. package/lib/main/browser/debug/plugin-debug-session-factory.js +29 -35
  56. package/lib/main/browser/debug/plugin-debug-session-factory.js.map +1 -1
  57. package/lib/main/browser/languages-main.d.ts +6 -1
  58. package/lib/main/browser/languages-main.d.ts.map +1 -1
  59. package/lib/main/browser/languages-main.js +39 -2
  60. package/lib/main/browser/languages-main.js.map +1 -1
  61. package/lib/main/browser/lm-main.js +2 -2
  62. package/lib/main/browser/lm-main.js.map +1 -1
  63. package/lib/main/browser/lm-tool-main.d.ts +14 -0
  64. package/lib/main/browser/lm-tool-main.d.ts.map +1 -0
  65. package/lib/main/browser/lm-tool-main.js +101 -0
  66. package/lib/main/browser/lm-tool-main.js.map +1 -0
  67. package/lib/main/browser/lm-tool-main.spec.d.ts +2 -0
  68. package/lib/main/browser/lm-tool-main.spec.d.ts.map +1 -0
  69. package/lib/main/browser/lm-tool-main.spec.js +100 -0
  70. package/lib/main/browser/lm-tool-main.spec.js.map +1 -0
  71. package/lib/main/browser/main-context.d.ts.map +1 -1
  72. package/lib/main/browser/main-context.js +3 -0
  73. package/lib/main/browser/main-context.js.map +1 -1
  74. package/lib/main/browser/main-file-system-event-service.d.ts +21 -4
  75. package/lib/main/browser/main-file-system-event-service.d.ts.map +1 -1
  76. package/lib/main/browser/main-file-system-event-service.js +47 -17
  77. package/lib/main/browser/main-file-system-event-service.js.map +1 -1
  78. package/lib/main/browser/main-file-system-event-service.spec.js +61 -26
  79. package/lib/main/browser/main-file-system-event-service.spec.js.map +1 -1
  80. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts +2 -1
  81. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
  82. package/lib/main/browser/notebooks/renderers/cell-output-webview.js +11 -5
  83. package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
  84. package/lib/main/browser/plugin-contribution-handler.d.ts +2 -0
  85. package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
  86. package/lib/main/browser/plugin-contribution-handler.js +10 -4
  87. package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
  88. package/lib/main/browser/view/plugin-view-registry.d.ts +1 -0
  89. package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
  90. package/lib/main/browser/view/plugin-view-registry.js +28 -3
  91. package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
  92. package/lib/main/browser/view/plugin-view-registry.spec.js +40 -0
  93. package/lib/main/browser/view/plugin-view-registry.spec.js.map +1 -1
  94. package/lib/main/browser/webview/webview-resource-cache.d.ts +2 -0
  95. package/lib/main/browser/webview/webview-resource-cache.d.ts.map +1 -1
  96. package/lib/main/browser/webview/webview-resource-cache.js +7 -1
  97. package/lib/main/browser/webview/webview-resource-cache.js.map +1 -1
  98. package/lib/main/browser/webview/webview.d.ts +2 -1
  99. package/lib/main/browser/webview/webview.d.ts.map +1 -1
  100. package/lib/main/browser/webview/webview.js +7 -1
  101. package/lib/main/browser/webview/webview.js.map +1 -1
  102. package/lib/main/node/plugin-deployer-impl.js +2 -1
  103. package/lib/main/node/plugin-deployer-impl.js.map +1 -1
  104. package/lib/plugin/languages/diagnostics.d.ts +4 -1
  105. package/lib/plugin/languages/diagnostics.d.ts.map +1 -1
  106. package/lib/plugin/languages/diagnostics.js +22 -9
  107. package/lib/plugin/languages/diagnostics.js.map +1 -1
  108. package/lib/plugin/languages/multi-document-highlight.d.ts +14 -0
  109. package/lib/plugin/languages/multi-document-highlight.d.ts.map +1 -0
  110. package/lib/plugin/languages/multi-document-highlight.js +60 -0
  111. package/lib/plugin/languages/multi-document-highlight.js.map +1 -0
  112. package/lib/plugin/languages.d.ts +3 -1
  113. package/lib/plugin/languages.d.ts.map +1 -1
  114. package/lib/plugin/languages.js +11 -0
  115. package/lib/plugin/languages.js.map +1 -1
  116. package/lib/plugin/lm-tool-ext.d.ts +28 -0
  117. package/lib/plugin/lm-tool-ext.d.ts.map +1 -0
  118. package/lib/plugin/lm-tool-ext.js +171 -0
  119. package/lib/plugin/lm-tool-ext.js.map +1 -0
  120. package/lib/plugin/lm-tool-ext.spec.d.ts +2 -0
  121. package/lib/plugin/lm-tool-ext.spec.d.ts.map +1 -0
  122. package/lib/plugin/lm-tool-ext.spec.js +124 -0
  123. package/lib/plugin/lm-tool-ext.spec.js.map +1 -0
  124. package/lib/plugin/plugin-context.d.ts.map +1 -1
  125. package/lib/plugin/plugin-context.js +10 -11
  126. package/lib/plugin/plugin-context.js.map +1 -1
  127. package/lib/plugin/types-impl.d.ts +0 -9
  128. package/lib/plugin/types-impl.d.ts.map +1 -1
  129. package/lib/plugin/types-impl.js +22 -15
  130. package/lib/plugin/types-impl.js.map +1 -1
  131. package/package.json +32 -31
  132. package/src/common/lm-tool-protocol.spec.ts +89 -0
  133. package/src/common/lm-tool-protocol.ts +102 -0
  134. package/src/common/plugin-api-rpc-model.ts +5 -0
  135. package/src/common/plugin-api-rpc.ts +9 -2
  136. package/src/common/plugin-protocol.ts +9 -0
  137. package/src/hosted/node/hosted-plugin-localization-service.ts +6 -3
  138. package/src/hosted/node/plugin-deployer-handler-impl.ts +5 -5
  139. package/src/hosted/node/plugin-host-module.ts +2 -0
  140. package/src/hosted/node/scanners/scanner-theia.ts +54 -50
  141. package/src/main/browser/comments/comment-thread-widget.tsx +5 -5
  142. package/src/main/browser/custom-editors/custom-editor-opener.tsx +1 -0
  143. package/src/main/browser/custom-editors/custom-editor-widget.ts +4 -0
  144. package/src/main/browser/custom-editors/custom-editors-main.ts +2 -0
  145. package/src/main/browser/debug/debug-main.ts +3 -45
  146. package/src/main/browser/debug/plugin-debug-service.ts +7 -5
  147. package/src/main/browser/debug/plugin-debug-session-factory.ts +25 -71
  148. package/src/main/browser/languages-main.ts +49 -4
  149. package/src/main/browser/lm-main.ts +2 -2
  150. package/src/main/browser/lm-tool-main.spec.ts +126 -0
  151. package/src/main/browser/lm-tool-main.ts +116 -0
  152. package/src/main/browser/main-context.ts +4 -0
  153. package/src/main/browser/main-file-system-event-service.spec.ts +76 -29
  154. package/src/main/browser/main-file-system-event-service.ts +46 -16
  155. package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +11 -7
  156. package/src/main/browser/plugin-contribution-handler.ts +8 -4
  157. package/src/main/browser/plugin-ext-widget.tsx +1 -1
  158. package/src/main/browser/view/plugin-view-registry.spec.ts +69 -1
  159. package/src/main/browser/view/plugin-view-registry.ts +30 -3
  160. package/src/main/browser/webview/webview-resource-cache.ts +6 -2
  161. package/src/main/browser/webview/webview.ts +6 -3
  162. package/src/main/node/plugin-deployer-impl.ts +2 -2
  163. package/src/plugin/languages/diagnostics.ts +28 -10
  164. package/src/plugin/languages/multi-document-highlight.ts +75 -0
  165. package/src/plugin/languages.ts +20 -0
  166. package/src/plugin/lm-tool-ext.spec.ts +152 -0
  167. package/src/plugin/lm-tool-ext.ts +197 -0
  168. package/src/plugin/plugin-context.ts +11 -11
  169. package/src/plugin/types-impl.ts +22 -15
@@ -0,0 +1,152 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 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 { expect } from 'chai';
18
+ import { LanguageModelToolsExtImpl } from './lm-tool-ext';
19
+ import { ToolResultPartDto, uint8ArrayToBase64 } from '../common/lm-tool-protocol';
20
+
21
+ // Access private methods for testing
22
+ interface LmToolExtInternals {
23
+ convertPartToDto(part: unknown): ToolResultPartDto;
24
+ isTextPart(part: unknown): boolean;
25
+ isDataPart(part: unknown): boolean;
26
+ isPromptTsxPart(part: unknown): boolean;
27
+ }
28
+
29
+ function createInstance(): LmToolExtInternals {
30
+ const impl = Object.create(LanguageModelToolsExtImpl.prototype) as LanguageModelToolsExtImpl;
31
+ return impl as unknown as LmToolExtInternals;
32
+ }
33
+
34
+ describe('LanguageModelToolsExtImpl - Part to DTO conversion', () => {
35
+ let instance: LmToolExtInternals;
36
+
37
+ beforeEach(() => {
38
+ instance = createInstance();
39
+ });
40
+
41
+ describe('isTextPart', () => {
42
+ it('should return true for a text part with string value', () => {
43
+ expect(instance.isTextPart({ value: 'hello' })).to.be.true;
44
+ });
45
+
46
+ it('should return false for a data part (has data and mimeType)', () => {
47
+ expect(instance.isTextPart({ data: new Uint8Array([1, 2]), mimeType: 'text/plain' })).to.be.false;
48
+ });
49
+
50
+ it('should return false for a non-string value', () => {
51
+ expect(instance.isTextPart({ value: { foo: 'bar' } })).to.be.false;
52
+ });
53
+
54
+ it('should return false for undefined', () => {
55
+ expect(instance.isTextPart(undefined)).to.be.false;
56
+ });
57
+ });
58
+
59
+ describe('isDataPart', () => {
60
+ it('should return true for a data part', () => {
61
+ expect(instance.isDataPart({ data: new Uint8Array([1, 2]), mimeType: 'image/png' })).to.be.true;
62
+ });
63
+
64
+ it('should return false for a text part', () => {
65
+ expect(instance.isDataPart({ value: 'hello' })).to.be.false;
66
+ });
67
+
68
+ it('should return false for undefined', () => {
69
+ expect(instance.isDataPart(undefined)).to.be.false;
70
+ });
71
+ });
72
+
73
+ describe('isPromptTsxPart', () => {
74
+ it('should return true for a prompt-tsx part with non-string value', () => {
75
+ expect(instance.isPromptTsxPart({ value: { component: 'div' } })).to.be.true;
76
+ });
77
+
78
+ it('should return false for a text part with string value', () => {
79
+ expect(instance.isPromptTsxPart({ value: 'hello' })).to.be.false;
80
+ });
81
+
82
+ it('should return false for a data part', () => {
83
+ expect(instance.isPromptTsxPart({ data: new Uint8Array(), mimeType: 'text/plain' })).to.be.false;
84
+ });
85
+ });
86
+
87
+ describe('uint8ArrayToBase64', () => {
88
+ it('should encode an empty array', () => {
89
+ const result = uint8ArrayToBase64(new Uint8Array([]));
90
+ expect(result).to.equal('');
91
+ });
92
+
93
+ it('should encode a simple string as Uint8Array', () => {
94
+ const data = new Uint8Array(Buffer.from('hello'));
95
+ const result = uint8ArrayToBase64(data);
96
+ expect(result).to.equal(Buffer.from('hello').toString('base64'));
97
+ });
98
+
99
+ it('should encode binary data', () => {
100
+ const data = new Uint8Array([0, 1, 2, 255]);
101
+ const result = uint8ArrayToBase64(data);
102
+ expect(result).to.equal(Buffer.from([0, 1, 2, 255]).toString('base64'));
103
+ });
104
+ });
105
+
106
+ describe('convertPartToDto', () => {
107
+ it('should convert a LanguageModelTextPart to text DTO', () => {
108
+ const part = { value: 'some text' };
109
+ const result = instance.convertPartToDto(part);
110
+ expect(result).to.deep.equal({ type: 'text', value: 'some text' });
111
+ });
112
+
113
+ it('should convert a LanguageModelDataPart to data DTO', () => {
114
+ const data = new Uint8Array(Buffer.from('binary content'));
115
+ const part = { data, mimeType: 'application/octet-stream' };
116
+ const result = instance.convertPartToDto(part);
117
+ expect(result.type).to.equal('data');
118
+ expect((result as { base64: string }).base64).to.equal(Buffer.from('binary content').toString('base64'));
119
+ expect((result as { mimeType: string }).mimeType).to.equal('application/octet-stream');
120
+ });
121
+
122
+ it('should convert a LanguageModelPromptTsxPart to prompt-tsx DTO', () => {
123
+ const part = { value: { component: 'SomeComponent', props: { x: 1 } } };
124
+ const result = instance.convertPartToDto(part);
125
+ expect(result).to.deep.equal({ type: 'prompt-tsx', value: { component: 'SomeComponent', props: { x: 1 } } });
126
+ });
127
+
128
+ it('should convert an unknown part to unknown DTO', () => {
129
+ const part = { someUnknownField: 42 };
130
+ const result = instance.convertPartToDto(part);
131
+ expect(result).to.deep.equal({ type: 'unknown', json: '{"someUnknownField":42}' });
132
+ });
133
+
134
+ it('should convert a LanguageModelDataPart with image mime type to data DTO', () => {
135
+ const imageData = new Uint8Array([137, 80, 78, 71]); // PNG header bytes
136
+ const part = { data: imageData, mimeType: 'image/png' };
137
+ const result = instance.convertPartToDto(part);
138
+ expect(result.type).to.equal('data');
139
+ expect((result as { base64: string }).base64).to.equal(Buffer.from(imageData).toString('base64'));
140
+ expect((result as { mimeType: string }).mimeType).to.equal('image/png');
141
+ });
142
+
143
+ it('should convert a LanguageModelDataPart with text/x-json to data DTO', () => {
144
+ const jsonStr = '{"key":"value"}';
145
+ const data = new Uint8Array(Buffer.from(jsonStr));
146
+ const part = { data, mimeType: 'text/x-json' };
147
+ const result = instance.convertPartToDto(part);
148
+ expect(result.type).to.equal('data');
149
+ expect((result as { base64: string }).base64).to.equal(Buffer.from(jsonStr).toString('base64'));
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,197 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 EclipseSource.
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 type * as theia from '@theia/plugin';
18
+ import { CancellationToken } from '@theia/core/lib/common/cancellation';
19
+
20
+ import { RPCProtocol } from '../common/rpc-protocol';
21
+ import {
22
+ LanguageModelToolsExt,
23
+ LanguageModelToolsMain,
24
+ LanguageModelToolDto,
25
+ ToolInvocationResult,
26
+ ToolResultPartDto,
27
+ isToolInvocationError,
28
+ uint8ArrayToBase64,
29
+ base64ToUint8Array,
30
+ } from '../common/lm-tool-protocol';
31
+ import { PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc';
32
+ import { PluginPackageLanguageModelToolContribution } from '../common';
33
+ import { PluginLogger } from './logger';
34
+ import { Disposable, LanguageModelTextPart, LanguageModelPromptTsxPart, LanguageModelDataPart, LanguageModelToolResult } from './types-impl';
35
+
36
+ export class LanguageModelToolsExtImpl implements LanguageModelToolsExt {
37
+
38
+ private proxy: LanguageModelToolsMain;
39
+ private logger: PluginLogger;
40
+ private handleCounter = 0;
41
+ private readonly tools = new Map<number, theia.LanguageModelTool<unknown>>();
42
+ private readonly toolNameToHandle = new Map<string, number>();
43
+ private readonly toolContributions = new Map<string, PluginPackageLanguageModelToolContribution>();
44
+ private readonly registeredToolMetadata = new Map<number, LanguageModelToolDto>();
45
+
46
+ constructor(protected readonly rpc: RPCProtocol) {
47
+ this.proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.LM_TOOLS_MAIN);
48
+ this.logger = new PluginLogger(this.rpc, 'lm-tools');
49
+ }
50
+
51
+ registerToolContributions(contributions: PluginPackageLanguageModelToolContribution[]): void {
52
+ for (const contribution of contributions) {
53
+ this.toolContributions.set(contribution.name, contribution);
54
+ }
55
+ }
56
+
57
+ registerTool<T>(name: string, tool: theia.LanguageModelTool<T>, pluginId: string): Disposable {
58
+ const contribution = this.toolContributions.get(name);
59
+ if (!contribution) {
60
+ this.logger.warn(`Tool '${name}' is not declared in package.json contributes.languageModelTools.`);
61
+ }
62
+ if (this.toolNameToHandle.has(name)) {
63
+ this.logger.warn(`Tool '${name}' is already registered. Ignoring duplicate registration.`);
64
+ return Disposable.NULL;
65
+ }
66
+ const handle = this.handleCounter++;
67
+ const metadata: LanguageModelToolDto = {
68
+ name,
69
+ description: contribution?.modelDescription,
70
+ inputSchema: contribution?.inputSchema,
71
+ tags: contribution?.tags,
72
+ };
73
+ this.tools.set(handle, tool);
74
+ this.toolNameToHandle.set(name, handle);
75
+ this.registeredToolMetadata.set(handle, metadata);
76
+ this.proxy.$registerTool(handle, name, metadata, pluginId).catch(err => {
77
+ this.logger.error(`Failed to register tool '${name}': ${err.message || err}`);
78
+ this.tools.delete(handle);
79
+ this.toolNameToHandle.delete(name);
80
+ this.registeredToolMetadata.delete(handle);
81
+ });
82
+
83
+ return new Disposable(() => {
84
+ this.tools.delete(handle);
85
+ this.toolNameToHandle.delete(name);
86
+ this.registeredToolMetadata.delete(handle);
87
+ this.proxy.$unregisterTool(handle);
88
+ });
89
+ }
90
+
91
+ async invokeTool(name: string, options: theia.LanguageModelToolInvocationOptions<object>, token?: CancellationToken): Promise<theia.LanguageModelToolResult> {
92
+ const handle = this.toolNameToHandle.get(name);
93
+ if (handle === undefined) {
94
+ throw new Error(`Tool '${name}' is not registered.`);
95
+ }
96
+ const result = await this.$invokeTool(handle, JSON.stringify(options.input), token);
97
+ if (isToolInvocationError(result)) {
98
+ throw new Error(result.error);
99
+ }
100
+ return new LanguageModelToolResult(result.content.map(part => this.convertDtoToApiPart(part)));
101
+ }
102
+
103
+ getTools(): theia.LanguageModelToolInformation[] {
104
+ const result = new Map<string, theia.LanguageModelToolInformation>();
105
+ // Add declared tool contributions from package.json
106
+ for (const c of this.toolContributions.values()) {
107
+ result.set(c.name, {
108
+ name: c.name,
109
+ description: c.modelDescription ?? '',
110
+ inputSchema: c.inputSchema,
111
+ tags: c.tags ?? [],
112
+ });
113
+ }
114
+ // Add dynamically registered tools not already covered by declarations
115
+ for (const [name, handle] of this.toolNameToHandle.entries()) {
116
+ if (!result.has(name)) {
117
+ const metadata = this.registeredToolMetadata.get(handle);
118
+ if (metadata) {
119
+ result.set(name, {
120
+ name: metadata.name,
121
+ description: metadata.description ?? '',
122
+ inputSchema: metadata.inputSchema,
123
+ tags: metadata.tags ?? [],
124
+ });
125
+ }
126
+ }
127
+ }
128
+ return [...result.values()];
129
+ }
130
+
131
+ async $invokeTool(handle: number, argsString: string, token?: CancellationToken): Promise<ToolInvocationResult> {
132
+ const tool = this.tools.get(handle);
133
+ if (!tool) {
134
+ return { error: `Tool with handle ${handle} not found` };
135
+ }
136
+ try {
137
+ const input = argsString ? JSON.parse(argsString) : {};
138
+ const cancellationToken = token ?? CancellationToken.None;
139
+ const result = await tool.invoke(
140
+ { input, toolInvocationToken: undefined },
141
+ cancellationToken
142
+ );
143
+ if (!result) {
144
+ return { content: [] };
145
+ }
146
+ return { content: result.content.map(part => this.convertPartToDto(part)) };
147
+ } catch (error) {
148
+ const errorMessage = error instanceof Error ? error.message : String(error);
149
+ return { error: errorMessage };
150
+ }
151
+ }
152
+
153
+ private convertPartToDto(part: unknown): ToolResultPartDto {
154
+ if (this.isTextPart(part)) {
155
+ return { type: 'text', value: part.value };
156
+ }
157
+ if (this.isDataPart(part)) {
158
+ const base64 = uint8ArrayToBase64(part.data);
159
+ return { type: 'data', base64, mimeType: part.mimeType };
160
+ }
161
+ if (this.isPromptTsxPart(part)) {
162
+ return { type: 'prompt-tsx', value: part.value };
163
+ }
164
+ return { type: 'unknown', json: JSON.stringify(part) };
165
+ }
166
+
167
+ private isTextPart(part: unknown): part is { value: string } {
168
+ return typeof part === 'object' && part !== undefined
169
+ && 'value' in (part as object) && typeof (part as { value: unknown }).value === 'string'
170
+ && !('data' in (part as object)) && !('mimeType' in (part as object));
171
+ }
172
+
173
+ private isDataPart(part: unknown): part is { data: Uint8Array; mimeType: string } {
174
+ return typeof part === 'object' && part !== undefined
175
+ && 'data' in (part as object) && 'mimeType' in (part as object)
176
+ && typeof (part as { mimeType: unknown }).mimeType === 'string';
177
+ }
178
+
179
+ private isPromptTsxPart(part: unknown): part is { value: unknown } {
180
+ return typeof part === 'object' && part !== undefined
181
+ && 'value' in (part as object) && typeof (part as { value: unknown }).value !== 'string'
182
+ && !('data' in (part as object)) && !('mimeType' in (part as object));
183
+ }
184
+
185
+ private convertDtoToApiPart(part: ToolResultPartDto): theia.LanguageModelTextPart | theia.LanguageModelPromptTsxPart | theia.LanguageModelDataPart {
186
+ switch (part.type) {
187
+ case 'text':
188
+ return new LanguageModelTextPart(part.value);
189
+ case 'data':
190
+ return new LanguageModelDataPart(base64ToUint8Array(part.base64), part.mimeType);
191
+ case 'prompt-tsx':
192
+ return new LanguageModelPromptTsxPart(part.value);
193
+ case 'unknown':
194
+ return new LanguageModelTextPart(part.json);
195
+ }
196
+ }
197
+ }
@@ -297,6 +297,7 @@ import { TestingExtImpl } from './tests';
297
297
  import { UriExtImpl } from './uri-ext';
298
298
  import { PluginLogger } from './logger';
299
299
  import { LmExtImpl } from './lm-ext';
300
+ import { LanguageModelToolsExtImpl } from './lm-tool-ext';
300
301
 
301
302
  export function createAPIObject<T extends Object>(rawObject: T): T {
302
303
  return new Proxy(rawObject, {
@@ -362,6 +363,7 @@ export function createAPIFactory(
362
363
  const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry));
363
364
  const uriExt = rpc.set(MAIN_RPC_CONTEXT.URI_EXT, new UriExtImpl(rpc));
364
365
  const lmExt = rpc.set(MAIN_RPC_CONTEXT.MCP_SERVER_DEFINITION_REGISTRY_EXT, new LmExtImpl(rpc));
366
+ const lmToolExt = rpc.set(MAIN_RPC_CONTEXT.LM_TOOLS_EXT, new LanguageModelToolsExtImpl(rpc));
365
367
  rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt);
366
368
 
367
369
  const commandLogger = new PluginLogger(rpc, 'commands-plugin');
@@ -1061,12 +1063,8 @@ export function createAPIFactory(
1061
1063
  registerDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.DocumentHighlightProvider): theia.Disposable {
1062
1064
  return languagesExt.registerDocumentHighlightProvider(selector, provider, pluginToPluginInfo(plugin));
1063
1065
  },
1064
- /**
1065
- * @stubbed
1066
- * @monaco-uplift: wait until API is available in Monaco (1.85.0+)
1067
- */
1068
1066
  registerMultiDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.MultiDocumentHighlightProvider): theia.Disposable {
1069
- return Disposable.NULL;
1067
+ return languagesExt.registerMultiDocumentHighlightProvider(selector, provider, pluginToPluginInfo(plugin));
1070
1068
  },
1071
1069
  registerWorkspaceSymbolProvider(provider: theia.WorkspaceSymbolProvider): theia.Disposable {
1072
1070
  return languagesExt.registerWorkspaceSymbolProvider(provider, pluginToPluginInfo(plugin));
@@ -1374,6 +1372,9 @@ export function createAPIFactory(
1374
1372
  const mcpContributions = plugin.rawModel.contributes && plugin.rawModel.contributes.mcpServerDefinitionProviders || [];
1375
1373
  lmExt.registerMcpContributions(mcpContributions);
1376
1374
 
1375
+ const toolContributions = plugin.rawModel.contributes && plugin.rawModel.contributes.languageModelTools || [];
1376
+ lmToolExt.registerToolContributions(toolContributions);
1377
+
1377
1378
  const lm: typeof theia.lm = {
1378
1379
  /** @stubbed LanguageModelChat */
1379
1380
  selectChatModels(selector?: theia.LanguageModelChatSelector): Thenable<theia.LanguageModelChat[]> {
@@ -1381,16 +1382,15 @@ export function createAPIFactory(
1381
1382
  },
1382
1383
  /** @stubbed LanguageModelChat */
1383
1384
  onDidChangeChatModels: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables),
1384
- /** @stubbed LanguageModelTool */
1385
1385
  invokeTool(name: string, options: theia.LanguageModelToolInvocationOptions<object>, token?: CancellationToken): Thenable<theia.LanguageModelToolResult> {
1386
- return Promise.resolve({ content: [] });
1386
+ return lmToolExt.invokeTool(name, options, token);
1387
1387
  },
1388
- /** @stubbed LanguageModelTool */
1389
1388
  registerTool<T>(name: string, tool: theia.LanguageModelTool<T>): Disposable {
1390
- return Disposable.NULL;
1389
+ return lmToolExt.registerTool(name, tool, plugin.model.id);
1390
+ },
1391
+ get tools(): readonly theia.LanguageModelToolInformation[] {
1392
+ return lmToolExt.getTools();
1391
1393
  },
1392
- /** @stubbed LanguageModelTool */
1393
- tools: [],
1394
1394
  registerMcpServerDefinitionProvider(id: string, provider: any): theia.Disposable {
1395
1395
  return lmExt.registerMcpServerDefinitionProvider(id, provider);
1396
1396
  },
@@ -4126,7 +4126,11 @@ export class LanguageModelToolCallPart {
4126
4126
  name: string;
4127
4127
  input: object;
4128
4128
 
4129
- constructor(callId: string, name: string, input: object) { }
4129
+ constructor(callId: string, name: string, input: object) {
4130
+ this.callId = callId;
4131
+ this.name = name;
4132
+ this.input = input;
4133
+ }
4130
4134
  }
4131
4135
 
4132
4136
  /**
@@ -4136,24 +4140,25 @@ export class LanguageModelToolResultPart {
4136
4140
  callId: string;
4137
4141
  content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>;
4138
4142
 
4139
- constructor(callId: string, content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) { }
4143
+ constructor(callId: string, content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) {
4144
+ this.callId = callId;
4145
+ this.content = content;
4146
+ }
4140
4147
  }
4141
4148
 
4142
- /**
4143
- * @stubbed
4144
- */
4145
4149
  export class LanguageModelTextPart {
4146
4150
  value: string;
4147
- constructor(value: string) { }
4151
+ constructor(value: string) {
4152
+ this.value = value;
4153
+ }
4148
4154
  }
4149
4155
 
4150
- /**
4151
- * @stubbed
4152
- */
4153
4156
  export class LanguageModelToolResult {
4154
4157
  content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>;
4155
4158
 
4156
- constructor(content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) { }
4159
+ constructor(content: Array<LanguageModelTextPart | LanguageModelPromptTsxPart | LanguageModelDataPart | unknown>) {
4160
+ this.content = content;
4161
+ }
4157
4162
  }
4158
4163
 
4159
4164
  /**
@@ -4162,12 +4167,11 @@ export class LanguageModelToolResult {
4162
4167
  export class LanguageModelPromptTsxPart {
4163
4168
  value: unknown;
4164
4169
 
4165
- constructor(value: unknown) { }
4170
+ constructor(value: unknown) {
4171
+ this.value = value;
4172
+ }
4166
4173
  }
4167
4174
 
4168
- /**
4169
- * @stubbed
4170
- */
4171
4175
  export class LanguageModelDataPart {
4172
4176
  static image(data: Uint8Array, mime: string): LanguageModelDataPart {
4173
4177
  return new LanguageModelDataPart(data, mime);
@@ -4182,7 +4186,10 @@ export class LanguageModelDataPart {
4182
4186
  }
4183
4187
  mimeType: string;
4184
4188
  data: Uint8Array;
4185
- constructor(data: Uint8Array, mimeType: string) { }
4189
+ constructor(data: Uint8Array, mimeType: string) {
4190
+ this.data = data;
4191
+ this.mimeType = mimeType;
4192
+ }
4186
4193
  }
4187
4194
 
4188
4195
  /**