@theia/ai-copilot 1.76.0-next.4 → 1.76.0-next.42

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 (150) hide show
  1. package/README.md +87 -14
  2. package/lib/browser/copilot-auth-dialog.d.ts.map +1 -1
  3. package/lib/browser/copilot-auth-dialog.js +19 -11
  4. package/lib/browser/copilot-auth-dialog.js.map +1 -1
  5. package/lib/browser/copilot-frontend-application-contribution.d.ts +21 -1
  6. package/lib/browser/copilot-frontend-application-contribution.d.ts.map +1 -1
  7. package/lib/browser/copilot-frontend-application-contribution.js +129 -26
  8. package/lib/browser/copilot-frontend-application-contribution.js.map +1 -1
  9. package/lib/common/copilot-auth-service.d.ts +27 -24
  10. package/lib/common/copilot-auth-service.d.ts.map +1 -1
  11. package/lib/common/copilot-language-models-manager.d.ts +6 -22
  12. package/lib/common/copilot-language-models-manager.d.ts.map +1 -1
  13. package/lib/common/copilot-language-models-manager.js +1 -13
  14. package/lib/common/copilot-language-models-manager.js.map +1 -1
  15. package/lib/common/copilot-preferences.d.ts +1 -0
  16. package/lib/common/copilot-preferences.d.ts.map +1 -1
  17. package/lib/common/copilot-preferences.js +24 -6
  18. package/lib/common/copilot-preferences.js.map +1 -1
  19. package/lib/common/index.d.ts +0 -1
  20. package/lib/common/index.d.ts.map +1 -1
  21. package/lib/common/index.js +0 -1
  22. package/lib/common/index.js.map +1 -1
  23. package/lib/node/copilot-auth-service-impl.d.ts +25 -16
  24. package/lib/node/copilot-auth-service-impl.d.ts.map +1 -1
  25. package/lib/node/copilot-auth-service-impl.js +67 -183
  26. package/lib/node/copilot-auth-service-impl.js.map +1 -1
  27. package/lib/node/copilot-auth-service-impl.spec.js +104 -168
  28. package/lib/node/copilot-auth-service-impl.spec.js.map +1 -1
  29. package/lib/node/copilot-backend-module.d.ts.map +1 -1
  30. package/lib/node/copilot-backend-module.js +10 -2
  31. package/lib/node/copilot-backend-module.js.map +1 -1
  32. package/lib/node/copilot-cli-auth-provider.d.ts +146 -0
  33. package/lib/node/copilot-cli-auth-provider.d.ts.map +1 -0
  34. package/lib/node/copilot-cli-auth-provider.js +430 -0
  35. package/lib/node/copilot-cli-auth-provider.js.map +1 -0
  36. package/lib/node/copilot-cli-auth-provider.spec.d.ts +2 -0
  37. package/lib/node/copilot-cli-auth-provider.spec.d.ts.map +1 -0
  38. package/lib/node/copilot-cli-auth-provider.spec.js +249 -0
  39. package/lib/node/copilot-cli-auth-provider.spec.js.map +1 -0
  40. package/lib/node/copilot-cli-locator.d.ts +120 -0
  41. package/lib/node/copilot-cli-locator.d.ts.map +1 -0
  42. package/lib/node/copilot-cli-locator.js +348 -0
  43. package/lib/node/copilot-cli-locator.js.map +1 -0
  44. package/lib/node/copilot-cli-locator.spec.d.ts +2 -0
  45. package/lib/node/copilot-cli-locator.spec.d.ts.map +1 -0
  46. package/lib/node/copilot-cli-locator.spec.js +327 -0
  47. package/lib/node/copilot-cli-locator.spec.js.map +1 -0
  48. package/lib/node/copilot-credential-store.d.ts +45 -0
  49. package/lib/node/copilot-credential-store.d.ts.map +1 -0
  50. package/lib/node/copilot-credential-store.js +96 -0
  51. package/lib/node/copilot-credential-store.js.map +1 -0
  52. package/lib/node/copilot-credential-store.spec.d.ts +2 -0
  53. package/lib/node/copilot-credential-store.spec.d.ts.map +1 -0
  54. package/lib/node/copilot-credential-store.spec.js +93 -0
  55. package/lib/node/copilot-credential-store.spec.js.map +1 -0
  56. package/lib/node/copilot-language-models-manager-impl.d.ts +44 -20
  57. package/lib/node/copilot-language-models-manager-impl.d.ts.map +1 -1
  58. package/lib/node/copilot-language-models-manager-impl.js +112 -65
  59. package/lib/node/copilot-language-models-manager-impl.js.map +1 -1
  60. package/lib/node/copilot-language-models-manager-impl.spec.d.ts +2 -0
  61. package/lib/node/copilot-language-models-manager-impl.spec.d.ts.map +1 -0
  62. package/lib/node/copilot-language-models-manager-impl.spec.js +162 -0
  63. package/lib/node/copilot-language-models-manager-impl.spec.js.map +1 -0
  64. package/lib/node/copilot-runtime-env.d.ts +20 -0
  65. package/lib/node/copilot-runtime-env.d.ts.map +1 -0
  66. package/lib/node/copilot-runtime-env.js +49 -0
  67. package/lib/node/copilot-runtime-env.js.map +1 -0
  68. package/lib/node/copilot-sdk-client-provider.d.ts +103 -0
  69. package/lib/node/copilot-sdk-client-provider.d.ts.map +1 -0
  70. package/lib/node/copilot-sdk-client-provider.js +247 -0
  71. package/lib/node/copilot-sdk-client-provider.js.map +1 -0
  72. package/lib/node/copilot-sdk-client-provider.spec.d.ts +2 -0
  73. package/lib/node/copilot-sdk-client-provider.spec.d.ts.map +1 -0
  74. package/lib/node/copilot-sdk-client-provider.spec.js +215 -0
  75. package/lib/node/copilot-sdk-client-provider.spec.js.map +1 -0
  76. package/lib/node/copilot-sdk-language-model.d.ts +85 -0
  77. package/lib/node/copilot-sdk-language-model.d.ts.map +1 -0
  78. package/lib/node/copilot-sdk-language-model.js +301 -0
  79. package/lib/node/copilot-sdk-language-model.js.map +1 -0
  80. package/lib/node/copilot-sdk-language-model.spec.d.ts +2 -0
  81. package/lib/node/copilot-sdk-language-model.spec.d.ts.map +1 -0
  82. package/lib/node/copilot-sdk-language-model.spec.js +315 -0
  83. package/lib/node/copilot-sdk-language-model.spec.js.map +1 -0
  84. package/lib/node/copilot-sdk-loader.d.ts +57 -0
  85. package/lib/node/copilot-sdk-loader.d.ts.map +1 -0
  86. package/lib/node/copilot-sdk-loader.js +133 -0
  87. package/lib/node/copilot-sdk-loader.js.map +1 -0
  88. package/lib/node/copilot-sdk-loader.spec.d.ts +2 -0
  89. package/lib/node/copilot-sdk-loader.spec.d.ts.map +1 -0
  90. package/lib/node/copilot-sdk-loader.spec.js +113 -0
  91. package/lib/node/copilot-sdk-loader.spec.js.map +1 -0
  92. package/lib/node/copilot-sdk-mappers.d.ts +54 -0
  93. package/lib/node/copilot-sdk-mappers.d.ts.map +1 -0
  94. package/lib/node/copilot-sdk-mappers.js +158 -0
  95. package/lib/node/copilot-sdk-mappers.js.map +1 -0
  96. package/lib/node/copilot-sdk-mappers.spec.d.ts +2 -0
  97. package/lib/node/copilot-sdk-mappers.spec.d.ts.map +1 -0
  98. package/lib/node/copilot-sdk-mappers.spec.js +168 -0
  99. package/lib/node/copilot-sdk-mappers.spec.js.map +1 -0
  100. package/lib/node/copilot-sdk-types.d.ts +208 -0
  101. package/lib/node/copilot-sdk-types.d.ts.map +1 -0
  102. package/lib/{common/copilot-oauth-config.js → node/copilot-sdk-types.js} +2 -13
  103. package/lib/node/copilot-sdk-types.js.map +1 -0
  104. package/lib/node/index.d.ts +9 -1
  105. package/lib/node/index.d.ts.map +1 -1
  106. package/lib/node/index.js +9 -1
  107. package/lib/node/index.js.map +1 -1
  108. package/package.json +4 -6
  109. package/src/browser/copilot-auth-dialog.tsx +20 -16
  110. package/src/browser/copilot-frontend-application-contribution.ts +144 -31
  111. package/src/common/copilot-auth-service.ts +28 -24
  112. package/src/common/copilot-language-models-manager.ts +6 -29
  113. package/src/common/copilot-preferences.ts +24 -5
  114. package/src/common/index.ts +0 -1
  115. package/src/node/copilot-auth-service-impl.spec.ts +122 -207
  116. package/src/node/copilot-auth-service-impl.ts +63 -218
  117. package/src/node/copilot-backend-module.ts +11 -2
  118. package/src/node/copilot-cli-auth-provider.spec.ts +315 -0
  119. package/src/node/copilot-cli-auth-provider.ts +444 -0
  120. package/src/node/copilot-cli-locator.spec.ts +386 -0
  121. package/src/node/copilot-cli-locator.ts +363 -0
  122. package/src/node/copilot-credential-store.spec.ts +106 -0
  123. package/src/node/copilot-credential-store.ts +99 -0
  124. package/src/node/copilot-language-models-manager-impl.spec.ts +188 -0
  125. package/src/node/copilot-language-models-manager-impl.ts +131 -84
  126. package/src/node/copilot-runtime-env.ts +46 -0
  127. package/src/node/copilot-sdk-client-provider.spec.ts +253 -0
  128. package/src/node/copilot-sdk-client-provider.ts +241 -0
  129. package/src/node/copilot-sdk-language-model.spec.ts +377 -0
  130. package/src/node/copilot-sdk-language-model.ts +330 -0
  131. package/src/node/copilot-sdk-loader.spec.ts +126 -0
  132. package/src/node/copilot-sdk-loader.ts +142 -0
  133. package/src/node/copilot-sdk-mappers.spec.ts +190 -0
  134. package/src/node/copilot-sdk-mappers.ts +172 -0
  135. package/src/node/copilot-sdk-types.ts +290 -0
  136. package/src/node/index.ts +9 -1
  137. package/lib/common/copilot-oauth-config.d.ts +0 -33
  138. package/lib/common/copilot-oauth-config.d.ts.map +0 -1
  139. package/lib/common/copilot-oauth-config.js.map +0 -1
  140. package/lib/node/copilot-language-model.d.ts +0 -34
  141. package/lib/node/copilot-language-model.d.ts.map +0 -1
  142. package/lib/node/copilot-language-model.js +0 -236
  143. package/lib/node/copilot-language-model.js.map +0 -1
  144. package/lib/node/copilot-language-model.spec.d.ts +0 -2
  145. package/lib/node/copilot-language-model.spec.d.ts.map +0 -1
  146. package/lib/node/copilot-language-model.spec.js +0 -89
  147. package/lib/node/copilot-language-model.spec.js.map +0 -1
  148. package/src/common/copilot-oauth-config.ts +0 -55
  149. package/src/node/copilot-language-model.spec.ts +0 -106
  150. package/src/node/copilot-language-model.ts +0 -273
@@ -0,0 +1,190 @@
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 { LanguageModelMessage } from '@theia/ai-core';
19
+ import type { ModelInfo } from './copilot-sdk-types';
20
+ import { selectSdkModelIds, buildSdkPrompt, buildSdkSystemMessage } from './copilot-sdk-mappers';
21
+
22
+ // Minimal ModelInfo factory: selectSdkModelIds only reads `id` and `policy.state`.
23
+ function model(id: string, state?: 'enabled' | 'disabled' | 'unconfigured'): ModelInfo {
24
+ return { id, policy: state ? { state } : undefined } as unknown as ModelInfo;
25
+ }
26
+
27
+ describe('copilot-sdk-mappers - selectSdkModelIds', () => {
28
+ it('keeps enabled and unconfigured models and drops disabled ones', () => {
29
+ const result = selectSdkModelIds([
30
+ model('gpt-4o', 'enabled'),
31
+ model('o1', 'disabled'),
32
+ model('claude', 'unconfigured'),
33
+ model('gpt-5')
34
+ ]);
35
+ expect(result).to.deep.equal(['gpt-4o', 'claude', 'gpt-5']);
36
+ });
37
+
38
+ it('preserves order and removes duplicates', () => {
39
+ const result = selectSdkModelIds([
40
+ model('a', 'enabled'),
41
+ model('b', 'enabled'),
42
+ model('a', 'enabled')
43
+ ]);
44
+ expect(result).to.deep.equal(['a', 'b']);
45
+ });
46
+
47
+ it('keeps a dated release alongside its family, so a release can be pinned', () => {
48
+ const result = selectSdkModelIds([
49
+ model('gpt-5'),
50
+ model('gpt-5-2026-04-17'),
51
+ model('claude-sonnet-5'),
52
+ model('claude-sonnet-5-20260514')
53
+ ]);
54
+ // Which of them the model pickers offer is decided by the favorite models service, which
55
+ // features the families; the releases stay selectable for whoever wants one.
56
+ expect(result).to.deep.equal(['gpt-5', 'gpt-5-2026-04-17', 'claude-sonnet-5', 'claude-sonnet-5-20260514']);
57
+ });
58
+
59
+ it('keeps a dated release that is the only way to select that model', () => {
60
+ const result = selectSdkModelIds([model('gpt-5'), model('o5-preview-2026-03-01')]);
61
+ expect(result).to.deep.equal(['gpt-5', 'o5-preview-2026-03-01']);
62
+ });
63
+
64
+ it('keeps a dated release whose family is disabled by policy', () => {
65
+ const result = selectSdkModelIds([model('gpt-5', 'disabled'), model('gpt-5-2026-04-17')]);
66
+ expect(result).to.deep.equal(['gpt-5-2026-04-17']);
67
+ });
68
+
69
+ it('returns an empty list when given no models', () => {
70
+ expect(selectSdkModelIds([])).to.deep.equal([]);
71
+ });
72
+ });
73
+
74
+ describe('copilot-sdk-mappers - buildSdkPrompt', () => {
75
+ it('forwards a lone user turn verbatim with no system text', () => {
76
+ const messages: LanguageModelMessage[] = [
77
+ { actor: 'user', type: 'text', text: 'hello world' }
78
+ ];
79
+ const result = buildSdkPrompt(messages);
80
+ expect(result.systemText).to.equal('');
81
+ expect(result.prompt).to.equal('hello world');
82
+ });
83
+
84
+ it('extracts and concatenates system messages', () => {
85
+ const messages: LanguageModelMessage[] = [
86
+ { actor: 'system', type: 'text', text: 'You are helpful.' },
87
+ { actor: 'system', type: 'text', text: 'Be concise.' },
88
+ { actor: 'user', type: 'text', text: 'hi' }
89
+ ];
90
+ const result = buildSdkPrompt(messages);
91
+ expect(result.systemText).to.equal('You are helpful.\n\nBe concise.');
92
+ expect(result.prompt).to.equal('hi');
93
+ });
94
+
95
+ it('renders a multi-message history as a role-labelled transcript', () => {
96
+ const messages: LanguageModelMessage[] = [
97
+ { actor: 'user', type: 'text', text: 'first' },
98
+ { actor: 'ai', type: 'text', text: 'reply' },
99
+ { actor: 'user', type: 'text', text: 'second' }
100
+ ];
101
+ const result = buildSdkPrompt(messages);
102
+ expect(result.prompt).to.equal('User: first\n\nAssistant: reply\n\nUser: second');
103
+ });
104
+
105
+ it('drops thinking messages from the conversation body', () => {
106
+ const messages: LanguageModelMessage[] = [
107
+ { actor: 'ai', type: 'thinking', thinking: 'internal', signature: 's' },
108
+ { actor: 'user', type: 'text', text: 'only user' }
109
+ ];
110
+ const result = buildSdkPrompt(messages);
111
+ expect(result.prompt).to.equal('only user');
112
+ });
113
+
114
+ it('summarises tool use and tool result messages in the transcript', () => {
115
+ const messages: LanguageModelMessage[] = [
116
+ { actor: 'user', type: 'text', text: 'run it' },
117
+ { actor: 'ai', type: 'tool_use', id: 'call_1', name: 'foo', input: { x: 1 } },
118
+ { actor: 'user', type: 'tool_result', tool_use_id: 'call_1', name: 'foo', content: 'done' }
119
+ ];
120
+ const result = buildSdkPrompt(messages);
121
+ expect(result.prompt).to.equal(
122
+ 'User: run it\n\nAssistant: [tool call: foo {"x":1}]\n\nUser: [tool result: done]'
123
+ );
124
+ });
125
+
126
+ it('turns a base64 user image into a blob attachment and drops it from the prompt text', () => {
127
+ const messages: LanguageModelMessage[] = [
128
+ { actor: 'user', type: 'text', text: 'what is this?' },
129
+ { actor: 'user', type: 'image', image: { base64data: 'aGk=', mimeType: 'image/png' } }
130
+ ];
131
+ const result = buildSdkPrompt(messages);
132
+ expect(result.prompt).to.equal('what is this?');
133
+ expect(result.attachments).to.deep.equal([{ type: 'blob', data: 'aGk=', mimeType: 'image/png' }]);
134
+ });
135
+
136
+ it('collects multiple base64 images in message order', () => {
137
+ const messages: LanguageModelMessage[] = [
138
+ { actor: 'user', type: 'image', image: { base64data: 'AAA=', mimeType: 'image/png' } },
139
+ { actor: 'user', type: 'image', image: { base64data: 'BBB=', mimeType: 'image/jpeg' } }
140
+ ];
141
+ const result = buildSdkPrompt(messages);
142
+ expect(result.attachments).to.deep.equal([
143
+ { type: 'blob', data: 'AAA=', mimeType: 'image/png' },
144
+ { type: 'blob', data: 'BBB=', mimeType: 'image/jpeg' }
145
+ ]);
146
+ });
147
+
148
+ it('leaves attachments undefined when there are no images', () => {
149
+ const result = buildSdkPrompt([{ actor: 'user', type: 'text', text: 'hi' }]);
150
+ expect(result.attachments).to.be.undefined;
151
+ });
152
+
153
+ it('does not attach a URL image and renders it as an omitted placeholder instead', () => {
154
+ const messages: LanguageModelMessage[] = [
155
+ { actor: 'user', type: 'text', text: 'look' },
156
+ { actor: 'user', type: 'image', image: { url: 'https://example.com/a.png' } }
157
+ ];
158
+ const result = buildSdkPrompt(messages);
159
+ expect(result.attachments).to.be.undefined;
160
+ expect(result.prompt).to.equal('User: look\n\nUser: [image omitted]');
161
+ });
162
+ });
163
+
164
+ describe('copilot-sdk-mappers - buildSdkSystemMessage', () => {
165
+ it('carries the system text as the content of the system message', () => {
166
+ const systemMessage = buildSdkSystemMessage('be helpful');
167
+ expect(systemMessage?.mode).to.equal('customize');
168
+ expect(systemMessage?.content).to.equal('be helpful');
169
+ });
170
+
171
+ it('removes the agent instructions of the CLI that the Theia prompt replaces', () => {
172
+ const systemMessage = buildSdkSystemMessage('be helpful');
173
+ const sections = systemMessage?.mode === 'customize' ? systemMessage.sections : undefined;
174
+ expect(Object.keys(sections ?? {})).to.have.members(['identity', 'tone', 'guidelines', 'code_change_rules']);
175
+ for (const section of Object.values(sections ?? {})) {
176
+ expect(section?.action).to.equal('remove');
177
+ }
178
+ });
179
+
180
+ it('keeps the tool instructions of the CLI, which tool calling relies on', () => {
181
+ const systemMessage = buildSdkSystemMessage('be helpful');
182
+ const sections = systemMessage?.mode === 'customize' ? systemMessage.sections : undefined;
183
+ expect(sections).to.not.have.property('tool_instructions');
184
+ expect(sections).to.not.have.property('safety');
185
+ });
186
+
187
+ it('configures nothing when the request has no system text', () => {
188
+ expect(buildSdkSystemMessage('')).to.be.undefined;
189
+ });
190
+ });
@@ -0,0 +1,172 @@
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 { Base64ImageContent, ImageContent, ImageMessage, LanguageModelMessage } from '@theia/ai-core';
18
+ import type { BlobMessageAttachment, ModelInfo, SystemMessageConfig } from './copilot-sdk-types';
19
+
20
+ /**
21
+ * Selects the model IDs that should be surfaced to Theia from the list returned
22
+ * by `CopilotClient.listModels()`.
23
+ *
24
+ * Models whose policy is explicitly `disabled` are filtered out; `enabled` and
25
+ * `unconfigured` models are kept. Order is preserved and duplicates are removed.
26
+ *
27
+ * The list can name the same model both by its family and by its dated releases, and both are kept:
28
+ * the family is what the model pickers offer, while a dated release stays selectable for anyone who
29
+ * wants to pin one. This mirrors the other providers, where discovery reports every release and only
30
+ * the undated ids are featured.
31
+ */
32
+ export function selectSdkModelIds(models: ModelInfo[]): string[] {
33
+ const result: string[] = [];
34
+ const seen = new Set<string>();
35
+ for (const model of models) {
36
+ if (!model.id || seen.has(model.id)) {
37
+ continue;
38
+ }
39
+ if (model.policy && model.policy.state === 'disabled') {
40
+ continue;
41
+ }
42
+ seen.add(model.id);
43
+ result.push(model.id);
44
+ }
45
+ return result;
46
+ }
47
+
48
+ /**
49
+ * The result of flattening a Theia message history into a single prompt for the
50
+ * agentic Copilot SDK session.
51
+ */
52
+ export interface SdkPrompt {
53
+ /** Concatenated content of all system messages (may be empty). */
54
+ systemText: string;
55
+ /** The user-facing prompt body derived from the non-system messages. */
56
+ prompt: string;
57
+ /** Base64-encoded images pulled out of the user's messages, sent alongside the prompt. */
58
+ attachments?: BlobMessageAttachment[];
59
+ }
60
+
61
+ function roleLabel(message: LanguageModelMessage): string {
62
+ switch (message.actor) {
63
+ case 'ai':
64
+ return 'Assistant';
65
+ case 'system':
66
+ return 'System';
67
+ default:
68
+ return 'User';
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Whether a message is a user image the SDK can be sent as a blob attachment.
74
+ *
75
+ * URL images are not covered: the SDK's attachments carry inline data, not a remote reference,
76
+ * and fetching the URL on the backend to inline it would add a server-side-request-forgery
77
+ * surface for what is, today, not a use case Theia exercises.
78
+ */
79
+ function isAttachableImageMessage(message: LanguageModelMessage): message is ImageMessage & { image: Base64ImageContent } {
80
+ return LanguageModelMessage.isImageMessage(message) && message.actor === 'user' && ImageContent.isBase64(message.image);
81
+ }
82
+
83
+ function messageToText(message: LanguageModelMessage): string {
84
+ if (LanguageModelMessage.isTextMessage(message)) {
85
+ return message.text;
86
+ }
87
+ if (LanguageModelMessage.isToolUseMessage(message)) {
88
+ return `[tool call: ${message.name} ${JSON.stringify(message.input)}]`;
89
+ }
90
+ if (LanguageModelMessage.isToolResultMessage(message)) {
91
+ const content = message.content === undefined
92
+ ? ''
93
+ : (typeof message.content === 'string' ? message.content : JSON.stringify(message.content));
94
+ return `[tool result: ${content}]`;
95
+ }
96
+ if (LanguageModelMessage.isImageMessage(message)) {
97
+ // Reached for URL images and non-user image messages; base64 user images are attachments instead, see buildSdkPrompt.
98
+ return '[image omitted]';
99
+ }
100
+ return '';
101
+ }
102
+
103
+ /**
104
+ * Flattens a Theia message history into an {@link SdkPrompt}.
105
+ *
106
+ * The Copilot SDK session is a stateful agent that accepts a single prompt
107
+ * string per `send()` call, so the full Theia history is collapsed here. System
108
+ * messages are extracted separately and become the system message of the session,
109
+ * see {@link buildSdkSystemMessage}. A lone user turn is forwarded verbatim;
110
+ * richer histories are rendered as a role-labelled transcript.
111
+ *
112
+ * Base64 user images are pulled out of the history and sent as blob attachments instead of
113
+ * being flattened into the prompt text, see {@link isAttachableImageMessage}. This is otherwise
114
+ * a lossy mapping by design, see the limitations documented in the package README, and is
115
+ * intended for single-turn requests.
116
+ */
117
+ export function buildSdkPrompt(messages: LanguageModelMessage[]): SdkPrompt {
118
+ const systemParts: string[] = [];
119
+ const conversation: LanguageModelMessage[] = [];
120
+ const attachments: BlobMessageAttachment[] = [];
121
+ for (const message of messages) {
122
+ if (message.actor === 'system' && LanguageModelMessage.isTextMessage(message)) {
123
+ systemParts.push(message.text);
124
+ } else if (message.type === 'thinking') {
125
+ continue;
126
+ } else if (isAttachableImageMessage(message)) {
127
+ attachments.push({ type: 'blob', data: message.image.base64data, mimeType: message.image.mimeType });
128
+ } else {
129
+ conversation.push(message);
130
+ }
131
+ }
132
+
133
+ const systemText = systemParts.join('\n\n').trim();
134
+
135
+ let prompt: string;
136
+ if (conversation.length === 1 && LanguageModelMessage.isTextMessage(conversation[0]) && conversation[0].actor === 'user') {
137
+ prompt = conversation[0].text;
138
+ } else {
139
+ prompt = conversation
140
+ .map(message => `${roleLabel(message)}: ${messageToText(message)}`)
141
+ .join('\n\n')
142
+ .trim();
143
+ }
144
+
145
+ return { systemText, prompt, attachments: attachments.length > 0 ? attachments : undefined };
146
+ }
147
+
148
+ /**
149
+ * Builds the system message configuration of a session from the system text of a request.
150
+ *
151
+ * The CLI is an agent with a system prompt of its own, while a Theia agent brings a complete one.
152
+ * The sections that describe who the agent is and how it should work are therefore removed, so that
153
+ * they cannot contradict the prompt of the Theia agent, and the prompt is added in their place. The
154
+ * sections that make tool calling work and the ones that carry the safety rules of the CLI are kept:
155
+ * this integration declares Theia's tools to the CLI, so the model still has to be told how to call
156
+ * them. Replacing the message as a whole would drop those as well.
157
+ */
158
+ export function buildSdkSystemMessage(systemText: string): SystemMessageConfig | undefined {
159
+ if (!systemText) {
160
+ return undefined;
161
+ }
162
+ return {
163
+ mode: 'customize',
164
+ sections: {
165
+ identity: { action: 'remove' },
166
+ tone: { action: 'remove' },
167
+ guidelines: { action: 'remove' },
168
+ code_change_rules: { action: 'remove' }
169
+ },
170
+ content: systemText
171
+ };
172
+ }
@@ -0,0 +1,290 @@
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
+ // The declarations in this file mirror the API of the GitHub Copilot SDK '@github/copilot-sdk' (1.0.9)
18
+ // - https://github.com/github/copilot-sdk
19
+ // '@github/copilot-sdk' copyright:
20
+ /*---------------------------------------------------------------------------------------------
21
+ * Copyright GitHub, Inc.
22
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
23
+ *--------------------------------------------------------------------------------------------*/
24
+
25
+ /**
26
+ * The part of the Copilot SDK API this integration uses, mirrored by hand.
27
+ *
28
+ * Mirrored from `@github/copilot-sdk` 1.0.9 (protocol version 3), which the Copilot CLI 1.0.79 ships.
29
+ * The package is deliberately not a dependency: it depends on the CLI, a large proprietary binary that
30
+ * would end up in the tree and the lockfile of every application. The CLI is a prerequisite on the
31
+ * backend host instead, see `CopilotCliLocator`, and the SDK is loaded from it at runtime, see
32
+ * `CopilotSdkLoader`.
33
+ *
34
+ * Only what this integration passes to the SDK or reads from it is declared, so a drift shows up as a
35
+ * compile error here rather than at runtime. `scripts/copilot-sdk-mirror` checks the mirror against a
36
+ * released SDK and runs in CI on a schedule; update the version above along with the declarations.
37
+ */
38
+
39
+ // ============================================================================
40
+ // Models, from `dist/types.d.ts`
41
+ // ============================================================================
42
+
43
+ export interface ModelPolicy {
44
+ state: 'enabled' | 'disabled' | 'unconfigured';
45
+ }
46
+
47
+ /** A model as reported by {@link CopilotClient.listModels}. */
48
+ export interface ModelInfo {
49
+ /** Model identifier, for example `claude-sonnet-4.5`. */
50
+ id: string;
51
+ policy?: ModelPolicy;
52
+ }
53
+
54
+ // ============================================================================
55
+ // System message, from `dist/types.d.ts`
56
+ // ============================================================================
57
+
58
+ /** The sections the runtime assembles its system prompt from. Unknown ids are a silent no-op there. */
59
+ export type SystemMessageSection =
60
+ | 'preamble'
61
+ | 'identity'
62
+ | 'tone'
63
+ | 'tool_efficiency'
64
+ | 'environment_context'
65
+ | 'code_change_rules'
66
+ | 'guidelines'
67
+ | 'safety'
68
+ | 'tool_instructions'
69
+ | 'custom_instructions'
70
+ | 'runtime_instructions'
71
+ | 'last_instructions';
72
+
73
+ export type SectionTransformFn = (currentContent: string) => string | Promise<string>;
74
+
75
+ export type SectionOverrideAction = 'replace' | 'remove' | 'append' | 'prepend' | 'preserve' | SectionTransformFn;
76
+
77
+ export interface SectionOverride {
78
+ action: SectionOverrideAction;
79
+ content?: string;
80
+ }
81
+
82
+ /** The SDK foundation plus the given content. */
83
+ export interface SystemMessageAppendConfig {
84
+ mode?: 'append';
85
+ content?: string;
86
+ }
87
+
88
+ /** The given content as the entire system message. */
89
+ export interface SystemMessageReplaceConfig {
90
+ mode: 'replace';
91
+ content: string;
92
+ }
93
+
94
+ /** The SDK structure with section-level overrides, which is what this integration uses. */
95
+ export interface SystemMessageCustomizeConfig {
96
+ mode: 'customize';
97
+ sections?: Partial<Record<SystemMessageSection, SectionOverride>>;
98
+ content?: string;
99
+ }
100
+
101
+ export type SystemMessageConfig = SystemMessageAppendConfig | SystemMessageReplaceConfig | SystemMessageCustomizeConfig;
102
+
103
+ // ============================================================================
104
+ // Tools, from `dist/types.d.ts`
105
+ // ============================================================================
106
+
107
+ /** What the runtime tells a tool handler about its invocation. */
108
+ export interface ToolInvocation {
109
+ sessionId: string;
110
+ toolCallId: string;
111
+ toolName: string;
112
+ arguments: unknown;
113
+ }
114
+
115
+ export type ToolHandler = (args: unknown, invocation: ToolInvocation) => Promise<unknown> | unknown;
116
+
117
+ /** A tool declared to the runtime. `parameters` also accepts a Zod schema upstream. */
118
+ export interface Tool {
119
+ name: string;
120
+ description?: string;
121
+ parameters?: Record<string, unknown>;
122
+ handler?: ToolHandler;
123
+ /** When true, the tool runs without a permission prompt of the runtime. */
124
+ skipPermission?: boolean;
125
+ }
126
+
127
+ // ============================================================================
128
+ // Permissions, from `dist/types.d.ts` and `dist/generated/rpc.d.ts`
129
+ // ============================================================================
130
+
131
+ /**
132
+ * A permission request of the runtime, upstream a union over the kind of operation with its own fields.
133
+ * Only `managedApprovalRequired` is read, which managed policy sets for a request it reserves for the
134
+ * user.
135
+ */
136
+ export interface PermissionRequest {
137
+ readonly kind: string;
138
+ readonly managedApprovalRequired?: boolean;
139
+ }
140
+
141
+ /** The decisions this integration can return. Upstream also has scopes that outlive a request. */
142
+ export type PermissionRequestResult =
143
+ | { kind: 'approve-once' }
144
+ | { kind: 'reject', feedback?: string }
145
+ /** No user is available to confirm the request. */
146
+ | { kind: 'user-not-available' }
147
+ /** Leaves the request pending, so that another connected client can answer it. */
148
+ | { kind: 'no-result' };
149
+
150
+ export type PermissionHandler = (
151
+ request: PermissionRequest,
152
+ invocation: { sessionId: string, managedSettingsEnabled?: boolean }
153
+ ) => Promise<PermissionRequestResult> | PermissionRequestResult;
154
+
155
+ // ============================================================================
156
+ // Session events, from `dist/generated/session-events.d.ts`
157
+ // ============================================================================
158
+
159
+ /** The envelope every session event comes in. */
160
+ export interface SessionEvent<T extends string, D> {
161
+ readonly type: T;
162
+ readonly data: D;
163
+ }
164
+
165
+ /** The session events this integration listens to, keyed by their type discriminator. */
166
+ export interface CopilotSessionEvents {
167
+ 'assistant.message_delta': SessionEvent<'assistant.message_delta', {
168
+ readonly deltaContent: string
169
+ }>;
170
+ 'assistant.reasoning_delta': SessionEvent<'assistant.reasoning_delta', {
171
+ readonly deltaContent: string
172
+ }>;
173
+ 'assistant.message': SessionEvent<'assistant.message', {
174
+ readonly content: string,
175
+ readonly outputTokens?: number
176
+ }>;
177
+ 'assistant.usage': SessionEvent<'assistant.usage', {
178
+ readonly inputTokens?: number,
179
+ readonly outputTokens?: number
180
+ }>;
181
+ /** Nothing of its payload is read, the event itself is the signal that the turn is over. */
182
+ 'session.idle': SessionEvent<'session.idle', unknown>;
183
+ 'session.error': SessionEvent<'session.error', {
184
+ readonly errorType: string,
185
+ readonly message: string,
186
+ readonly statusCode?: number,
187
+ readonly stack?: string
188
+ }>;
189
+ }
190
+
191
+ export type CopilotSessionEventType = keyof CopilotSessionEvents;
192
+
193
+ // ============================================================================
194
+ // Session, from `dist/session.d.ts` and `dist/types.d.ts`
195
+ // ============================================================================
196
+
197
+ /** The one attachment kind this integration produces: an inline image, from `MessageOptions['attachments']`. */
198
+ export interface BlobMessageAttachment {
199
+ type: 'blob';
200
+ data: string;
201
+ mimeType: string;
202
+ displayName?: string;
203
+ }
204
+
205
+ /** The options this integration passes to {@link CopilotSession.send}. */
206
+ export interface MessageOptions {
207
+ prompt: string;
208
+ attachments?: BlobMessageAttachment[];
209
+ }
210
+
211
+ /** A conversation with the runtime, upstream a class with resume, fork and sharing on top. */
212
+ export interface CopilotSession {
213
+ readonly sessionId: string;
214
+ /**
215
+ * Registers a handler for one type of event and returns the function that removes it again.
216
+ */
217
+ on<K extends CopilotSessionEventType>(eventType: K, handler: (event: CopilotSessionEvents[K]) => void): () => void;
218
+ /** Sends a turn and resolves with the id of the message it created. */
219
+ send(options: MessageOptions): Promise<string>;
220
+ /** Stops the turn in flight. */
221
+ abort(): Promise<void>;
222
+ /** Releases the session in memory, leaving what it persisted in place. */
223
+ disconnect(): Promise<void>;
224
+ }
225
+
226
+ // ============================================================================
227
+ // Client, from `dist/client.d.ts` and `dist/types.d.ts`
228
+ // ============================================================================
229
+
230
+ /** How the SDK reaches the runtime. This integration always spawns the CLI it located. */
231
+ export interface StdioRuntimeConnection {
232
+ readonly kind: 'stdio';
233
+ readonly path?: string;
234
+ readonly args?: readonly string[];
235
+ readonly env?: Record<string, string>;
236
+ }
237
+
238
+ export type RuntimeConnection = StdioRuntimeConnection;
239
+
240
+ /** The `RuntimeConnection` value of the SDK, of which only the stdio factory is used. */
241
+ export interface RuntimeConnectionFactory {
242
+ readonly forStdio: (options?: { path?: string, args?: readonly string[], env?: Record<string, string> }) => StdioRuntimeConnection;
243
+ }
244
+
245
+ /**
246
+ * Selects the defaults of the runtime: `copilot-cli` brings the ambient behaviour of the CLI along,
247
+ * `empty` requires the application to opt into what it needs.
248
+ */
249
+ export type CopilotClientMode = 'empty' | 'copilot-cli';
250
+
251
+ /** The client options this integration sets. */
252
+ export interface CopilotClientOptions {
253
+ connection?: RuntimeConnection;
254
+ mode?: CopilotClientMode;
255
+ /** Copilot home of the runtime, which becomes its `COPILOT_HOME`. Required in `empty` mode. */
256
+ baseDirectory?: string;
257
+ logLevel?: 'none' | 'error' | 'warning' | 'info' | 'debug' | 'all';
258
+ env?: Record<string, string | undefined>;
259
+ gitHubToken?: string;
260
+ /** When false, only an explicit token is used and no sign-in found on the host. */
261
+ useLoggedInUser?: boolean;
262
+ }
263
+
264
+ /** The session configuration this integration passes. */
265
+ export interface SessionConfig {
266
+ model?: string;
267
+ streaming?: boolean;
268
+ tools?: Tool[];
269
+ systemMessage?: SystemMessageConfig;
270
+ /**
271
+ * The tools to enable, as source-qualified patterns such as `custom:*` or `builtin:<name>`.
272
+ * Required in `empty` mode.
273
+ */
274
+ availableTools?: string[];
275
+ onPermissionRequest?: PermissionHandler;
276
+ }
277
+
278
+ /** The runtime as seen by this integration, upstream a class with the full session and status API. */
279
+ export interface CopilotClient {
280
+ /** Starts the runtime and connects to it. */
281
+ start(): Promise<void>;
282
+ /** Stops the runtime and resolves with the errors its cleanup ran into, if any. */
283
+ stop(): Promise<Error[]>;
284
+ listModels(): Promise<ModelInfo[]>;
285
+ createSession(config: SessionConfig): Promise<CopilotSession>;
286
+ /** Removes a session and what it persisted in the Copilot home. */
287
+ deleteSession(sessionId: string): Promise<void>;
288
+ }
289
+
290
+ export type CopilotClientConstructor = new (options?: CopilotClientOptions) => CopilotClient;
package/src/node/index.ts CHANGED
@@ -15,5 +15,13 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  export * from './copilot-auth-service-impl';
18
- export * from './copilot-language-model';
19
18
  export * from './copilot-language-models-manager-impl';
19
+ export * from './copilot-cli-auth-provider';
20
+ export * from './copilot-cli-locator';
21
+ export * from './copilot-credential-store';
22
+ export * from './copilot-runtime-env';
23
+ export * from './copilot-sdk-client-provider';
24
+ export * from './copilot-sdk-loader';
25
+ export * from './copilot-sdk-mappers';
26
+ export * from './copilot-sdk-language-model';
27
+ export * from './copilot-sdk-types';