@theia/ai-core 1.74.0-next.0 → 1.74.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/ai-configuration-service-impl.d.ts +32 -0
- package/lib/browser/ai-configuration-service-impl.d.ts.map +1 -0
- package/lib/browser/ai-configuration-service-impl.js +120 -0
- package/lib/browser/ai-configuration-service-impl.js.map +1 -0
- package/lib/browser/ai-configuration-service-impl.spec.d.ts +2 -0
- package/lib/browser/ai-configuration-service-impl.spec.d.ts.map +1 -0
- package/lib/browser/ai-configuration-service-impl.spec.js +214 -0
- package/lib/browser/ai-configuration-service-impl.spec.js.map +1 -0
- package/lib/browser/ai-core-frontend-module.d.ts.map +1 -1
- package/lib/browser/ai-core-frontend-module.js +4 -0
- package/lib/browser/ai-core-frontend-module.js.map +1 -1
- package/lib/browser/ai-settings-service.d.ts +2 -5
- package/lib/browser/ai-settings-service.d.ts.map +1 -1
- package/lib/browser/ai-settings-service.js +8 -15
- package/lib/browser/ai-settings-service.js.map +1 -1
- package/lib/browser/frontend-language-model-alias-registry.d.ts +2 -4
- package/lib/browser/frontend-language-model-alias-registry.d.ts.map +1 -1
- package/lib/browser/frontend-language-model-alias-registry.js +11 -16
- package/lib/browser/frontend-language-model-alias-registry.js.map +1 -1
- package/lib/browser/frontend-language-model-service.d.ts +2 -3
- package/lib/browser/frontend-language-model-service.d.ts.map +1 -1
- package/lib/browser/frontend-language-model-service.js +6 -6
- package/lib/browser/frontend-language-model-service.js.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.d.ts +38 -0
- package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.js +189 -76
- package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.spec.js +103 -0
- package/lib/browser/frontend-prompt-customization-service.spec.js.map +1 -1
- package/lib/browser/index.d.ts +1 -0
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +1 -0
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/trust-aware-preference-reader.d.ts +13 -1
- package/lib/browser/trust-aware-preference-reader.d.ts.map +1 -1
- package/lib/browser/trust-aware-preference-reader.js +21 -2
- package/lib/browser/trust-aware-preference-reader.js.map +1 -1
- package/lib/browser/trust-aware-preference-reader.spec.js +26 -0
- package/lib/browser/trust-aware-preference-reader.spec.js.map +1 -1
- package/lib/common/ai-configuration-service.d.ts +104 -0
- package/lib/common/ai-configuration-service.d.ts.map +1 -0
- package/lib/common/ai-configuration-service.js +20 -0
- package/lib/common/ai-configuration-service.js.map +1 -0
- package/lib/common/ai-core-preferences.d.ts +2 -0
- package/lib/common/ai-core-preferences.d.ts.map +1 -1
- package/lib/common/ai-core-preferences.js +11 -1
- package/lib/common/ai-core-preferences.js.map +1 -1
- package/lib/common/index.d.ts +1 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +1 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/language-model.d.ts +51 -2
- package/lib/common/language-model.d.ts.map +1 -1
- package/lib/common/language-model.js +40 -2
- package/lib/common/language-model.js.map +1 -1
- package/lib/common/language-model.spec.js +34 -0
- package/lib/common/language-model.spec.js.map +1 -1
- package/lib/common/prompt-service.d.ts +13 -3
- package/lib/common/prompt-service.d.ts.map +1 -1
- package/lib/common/prompt-service.js.map +1 -1
- package/lib/node/backend-language-model-registry.d.ts.map +1 -1
- package/lib/node/backend-language-model-registry.js +1 -0
- package/lib/node/backend-language-model-registry.js.map +1 -1
- package/package.json +9 -9
- package/src/browser/ai-configuration-service-impl.spec.ts +254 -0
- package/src/browser/ai-configuration-service-impl.ts +120 -0
- package/src/browser/ai-core-frontend-module.ts +7 -1
- package/src/browser/ai-settings-service.ts +8 -16
- package/src/browser/frontend-language-model-alias-registry.ts +12 -16
- package/src/browser/frontend-language-model-service.ts +5 -6
- package/src/browser/frontend-prompt-customization-service.spec.ts +130 -1
- package/src/browser/frontend-prompt-customization-service.ts +204 -84
- package/src/browser/index.ts +1 -0
- package/src/browser/trust-aware-preference-reader.spec.ts +29 -0
- package/src/browser/trust-aware-preference-reader.ts +23 -3
- package/src/common/ai-configuration-service.ts +128 -0
- package/src/common/ai-core-preferences.ts +12 -0
- package/src/common/index.ts +1 -0
- package/src/common/language-model.spec.ts +45 -1
- package/src/common/language-model.ts +85 -3
- package/src/common/prompt-service.ts +14 -3
- package/src/node/backend-language-model-registry.ts +1 -0
|
@@ -0,0 +1,128 @@
|
|
|
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 { Event } from '@theia/core';
|
|
18
|
+
import { JSONValue } from '@theia/core/shared/@lumino/coreutils';
|
|
19
|
+
import { PreferenceInspection, PreferenceScope } from '@theia/core/lib/common/preferences';
|
|
20
|
+
|
|
21
|
+
export const AiConfigurationService = Symbol('AiConfigurationService');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A {@link PreferenceInspection} augmented with a derived, workspace-trust-aware source scope.
|
|
25
|
+
*/
|
|
26
|
+
export interface AiConfigurationInspection<T extends JSONValue = JSONValue> extends PreferenceInspection<T> {
|
|
27
|
+
/**
|
|
28
|
+
* The narrowest scope (honoring workspace trust) in which a value is explicitly set, i.e. the
|
|
29
|
+
* scope that determines the effective {@link PreferenceInspection.value}. Walked in precedence
|
|
30
|
+
* order `Folder -> Workspace -> User`; `undefined` when only the default value applies. In an
|
|
31
|
+
* untrusted workspace the folder/workspace scopes are ignored, so `sourceScope` is either
|
|
32
|
+
* `User` or `undefined`.
|
|
33
|
+
*/
|
|
34
|
+
sourceScope?: PreferenceScope;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Payload of {@link AiConfigurationService.onDidChange}.
|
|
39
|
+
*/
|
|
40
|
+
export interface AiConfigurationChange {
|
|
41
|
+
/**
|
|
42
|
+
* The preference key that changed. When the change is caused by a workspace-trust transition
|
|
43
|
+
* (which can change the effective value of any trust-gated key) this is `undefined` and
|
|
44
|
+
* {@link affects} returns `true` for every resource. Listeners tracking a specific key should
|
|
45
|
+
* re-query the value when `preferenceName` is `undefined` or equals their key.
|
|
46
|
+
*/
|
|
47
|
+
readonly preferenceName?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Tests whether the given resource is affected by the change.
|
|
50
|
+
* @param resourceUri the uri of the resource to test.
|
|
51
|
+
*/
|
|
52
|
+
affects(resourceUri?: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Tests whether the given preference key is affected by this change, treating a workspace-trust
|
|
55
|
+
* transition (where {@link preferenceName} is `undefined`) as affecting every trust-gated key.
|
|
56
|
+
* Prefer this over comparing {@link preferenceName} directly so listeners do not silently miss
|
|
57
|
+
* trust transitions.
|
|
58
|
+
* @param preferenceName the preference key to test.
|
|
59
|
+
*/
|
|
60
|
+
affectsPreference(preferenceName: string): boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Framework API for reading and writing AI-related (`ai-features.*`) preferences.
|
|
65
|
+
*
|
|
66
|
+
* `AiConfigurationService` wraps the core `PreferenceService` and is the intended extension point
|
|
67
|
+
* for adopters and extensions that configure AI features: prefer it over talking to
|
|
68
|
+
* `PreferenceService` directly for `ai-features.*` keys. Routing all AI configuration through this
|
|
69
|
+
* seam keeps consumers insulated from future changes to how AI preferences are stored.
|
|
70
|
+
*
|
|
71
|
+
* Reads ({@link get}, {@link inspect}) are **workspace-trust-aware**: workspace and folder scope
|
|
72
|
+
* values are suppressed while the workspace is untrusted (failing closed until the trust state
|
|
73
|
+
* resolves), matching how AI features read preferences today. Writes ({@link set}, {@link update})
|
|
74
|
+
* are never gated by trust.
|
|
75
|
+
*/
|
|
76
|
+
export interface AiConfigurationService {
|
|
77
|
+
/**
|
|
78
|
+
* Resolves once the underlying preference service and the initial workspace-trust state are
|
|
79
|
+
* ready. Await this before the first read for a deterministic, trust-aware result.
|
|
80
|
+
*/
|
|
81
|
+
readonly ready: Promise<void>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Retrieves the effective, trust-aware value for the given preference.
|
|
85
|
+
*
|
|
86
|
+
* @param key the preference identifier.
|
|
87
|
+
* @param defaultValue the value to return when no value is stored.
|
|
88
|
+
* @param resourceUri the uri of the resource for which the preference is read.
|
|
89
|
+
*/
|
|
90
|
+
get<T>(key: string, defaultValue?: T, resourceUri?: string): T | undefined;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Writes `value` to the given scope. Maps to `PreferenceService.set`.
|
|
94
|
+
*
|
|
95
|
+
* @param key the preference identifier.
|
|
96
|
+
* @param value the new value (must be JSON-serializable). `undefined` clears the value in the given scope.
|
|
97
|
+
* @param scope the scope to write to. For {@link PreferenceScope.Folder} a `resourceUri` is required.
|
|
98
|
+
* @param resourceUri the uri of the resource for which the preference is stored.
|
|
99
|
+
*/
|
|
100
|
+
set(key: string, value: unknown, scope: PreferenceScope, resourceUri?: string): Promise<void>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* "Smart write": picks the scope so that the effective value becomes `value`. Maps to
|
|
104
|
+
* `PreferenceService.updateValue`.
|
|
105
|
+
*
|
|
106
|
+
* @param key the preference identifier.
|
|
107
|
+
* @param value the value to apply (must be JSON-serializable). `undefined` resets the preference to its default value.
|
|
108
|
+
* @param resourceUri the uri of the resource to which the change applies.
|
|
109
|
+
*/
|
|
110
|
+
update(key: string, value: unknown, resourceUri?: string): Promise<void>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Retrieves the per-scope values for the given preference, enriched with a derived,
|
|
114
|
+
* trust-aware {@link AiConfigurationInspection.sourceScope} and effective
|
|
115
|
+
* {@link PreferenceInspection.value}.
|
|
116
|
+
*
|
|
117
|
+
* @param key the preference identifier.
|
|
118
|
+
* @param resourceUri the uri of the resource for which the preference is inspected.
|
|
119
|
+
*/
|
|
120
|
+
inspect<T extends JSONValue>(key: string, resourceUri?: string): AiConfigurationInspection<T> | undefined;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Fires when a `ai-features.*` preference changes or when the workspace-trust state transitions
|
|
124
|
+
* (which can change the effective value of trust-gated keys). The effective value is not carried
|
|
125
|
+
* on the event; listeners re-query it via {@link get}/{@link inspect}.
|
|
126
|
+
*/
|
|
127
|
+
onDidChange: Event<AiConfigurationChange>;
|
|
128
|
+
}
|
|
@@ -34,11 +34,22 @@ export const PREFERENCE_NAME_REASONING = 'ai-features.reasoning.defaults';
|
|
|
34
34
|
export const PREFERENCE_NAME_MAX_RETRIES = 'ai-features.modelSettings.maxRetries';
|
|
35
35
|
export const PREFERENCE_NAME_DEFAULT_NOTIFICATION_TYPE = 'ai-features.notifications.default';
|
|
36
36
|
export const PREFERENCE_NAME_SKILL_DIRECTORIES = 'ai-features.skills.skillDirectories';
|
|
37
|
+
export const PREFERENCE_NAME_SERVER_SIDE_COMPACTION = 'ai-features.chat.serverSideCompaction';
|
|
37
38
|
|
|
38
39
|
export const LANGUAGE_MODEL_ALIASES_PREFERENCE = 'ai-features.languageModelAliases';
|
|
39
40
|
|
|
40
41
|
export const aiCorePreferenceSchema: PreferenceSchema = {
|
|
41
42
|
properties: {
|
|
43
|
+
[PREFERENCE_NAME_SERVER_SIDE_COMPACTION]: {
|
|
44
|
+
title: AI_CORE_PREFERENCES_TITLE,
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
description: nls.localize('theia/ai/core/serverSideCompaction/description',
|
|
47
|
+
'Enable provider-native server-side compaction by default for chat requests. When on, models that support it ' +
|
|
48
|
+
'may automatically summarize older turns once the conversation grows past their own threshold, letting long ' +
|
|
49
|
+
'sessions continue past the context limit. Each provider offers its own override, and individual chat sessions ' +
|
|
50
|
+
'can override this in their session settings. Models without support ignore it.'),
|
|
51
|
+
default: true,
|
|
52
|
+
},
|
|
42
53
|
[PREFERENCE_NAME_PROMPT_TEMPLATES]: {
|
|
43
54
|
title: AI_CORE_PREFERENCES_TITLE,
|
|
44
55
|
description: nls.localize('theia/ai/core/promptTemplates/description',
|
|
@@ -239,6 +250,7 @@ export interface AICoreConfiguration {
|
|
|
239
250
|
[PREFERENCE_NAME_MAX_RETRIES]: number | undefined;
|
|
240
251
|
[PREFERENCE_NAME_DEFAULT_NOTIFICATION_TYPE]: NotificationType | undefined;
|
|
241
252
|
[PREFERENCE_NAME_SKILL_DIRECTORIES]: string[] | undefined;
|
|
253
|
+
[PREFERENCE_NAME_SERVER_SIDE_COMPACTION]: boolean | undefined;
|
|
242
254
|
}
|
|
243
255
|
|
|
244
256
|
export interface RequestSetting {
|
package/src/common/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ export * from './protocol';
|
|
|
31
31
|
export * from './today-variable-contribution';
|
|
32
32
|
export * from './variable-service';
|
|
33
33
|
export * from './settings-service';
|
|
34
|
+
export * from './ai-configuration-service';
|
|
34
35
|
export * from './language-model-service';
|
|
35
36
|
export * from './token-usage-service';
|
|
36
37
|
export * from './ai-variable-resource';
|
|
@@ -15,7 +15,16 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { expect } from 'chai';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
isCompactionResponsePart,
|
|
20
|
+
isLanguageModelStreamResponsePart,
|
|
21
|
+
isModelMatching,
|
|
22
|
+
isToolCallContent,
|
|
23
|
+
LanguageModel,
|
|
24
|
+
LanguageModelSelector,
|
|
25
|
+
resolveCompactionDefault,
|
|
26
|
+
resolveServerSideCompaction
|
|
27
|
+
} from './language-model';
|
|
19
28
|
|
|
20
29
|
describe('isModelMatching', () => {
|
|
21
30
|
it('returns false with one of two parameter mismatches', () => {
|
|
@@ -163,3 +172,38 @@ describe('isToolCallContent', () => {
|
|
|
163
172
|
});
|
|
164
173
|
|
|
165
174
|
});
|
|
175
|
+
|
|
176
|
+
describe('compaction contract', () => {
|
|
177
|
+
it('recognizes a compaction response part', () => {
|
|
178
|
+
const part = { compaction: { provider: 'anthropic', data: { foo: 1 } } };
|
|
179
|
+
expect(isCompactionResponsePart(part)).to.equal(true);
|
|
180
|
+
expect(isLanguageModelStreamResponsePart(part)).to.equal(true);
|
|
181
|
+
});
|
|
182
|
+
it('rejects a non-compaction part', () => {
|
|
183
|
+
expect(isCompactionResponsePart({ content: 'hi' })).to.equal(false);
|
|
184
|
+
// eslint-disable-next-line no-null/no-null
|
|
185
|
+
expect(isCompactionResponsePart(null)).to.equal(false);
|
|
186
|
+
expect(isCompactionResponsePart(undefined)).to.equal(false);
|
|
187
|
+
// eslint-disable-next-line no-null/no-null
|
|
188
|
+
expect(isCompactionResponsePart({ compaction: null })).to.equal(false);
|
|
189
|
+
expect(isCompactionResponsePart({ compaction: { provider: 42 } })).to.equal(false);
|
|
190
|
+
});
|
|
191
|
+
it('resolves a model default from the global preference and the per-provider override', () => {
|
|
192
|
+
expect(resolveCompactionDefault(true, 'default')).to.equal(true);
|
|
193
|
+
expect(resolveCompactionDefault(false, 'default')).to.equal(false);
|
|
194
|
+
expect(resolveCompactionDefault(false, 'enabled')).to.equal(true);
|
|
195
|
+
expect(resolveCompactionDefault(true, 'disabled')).to.equal(false);
|
|
196
|
+
});
|
|
197
|
+
it('resolves server-side compaction with capability gate and session-wins precedence', () => {
|
|
198
|
+
// capability gate
|
|
199
|
+
expect(resolveServerSideCompaction(false, true, { enabled: true })).to.equal(false);
|
|
200
|
+
expect(resolveServerSideCompaction(undefined, true, undefined)).to.equal(false);
|
|
201
|
+
// no per-session setting -> model default
|
|
202
|
+
expect(resolveServerSideCompaction(true, true, undefined)).to.equal(true);
|
|
203
|
+
expect(resolveServerSideCompaction(true, false, undefined)).to.equal(false);
|
|
204
|
+
expect(resolveServerSideCompaction(true, true, {})).to.equal(true);
|
|
205
|
+
// explicit per-session setting wins over the model default (which already folds in the per-provider override)
|
|
206
|
+
expect(resolveServerSideCompaction(true, false, { enabled: true })).to.equal(true);
|
|
207
|
+
expect(resolveServerSideCompaction(true, true, { enabled: false })).to.equal(false);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -42,7 +42,8 @@ export interface ReasoningSupport {
|
|
|
42
42
|
readonly defaultLevel?: ReasoningLevel;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export type LanguageModelMessage =
|
|
45
|
+
export type LanguageModelMessage =
|
|
46
|
+
TextMessage | ThinkingMessage | ToolUseMessage | ToolResultMessage | ServerToolUseMessage | ImageMessage | CompactionMessage;
|
|
46
47
|
export namespace LanguageModelMessage {
|
|
47
48
|
|
|
48
49
|
export function isTextMessage(obj: LanguageModelMessage): obj is TextMessage {
|
|
@@ -63,6 +64,9 @@ export namespace LanguageModelMessage {
|
|
|
63
64
|
export function isImageMessage(obj: LanguageModelMessage): obj is ImageMessage {
|
|
64
65
|
return obj.type === 'image';
|
|
65
66
|
}
|
|
67
|
+
export function isCompactionMessage(obj: LanguageModelMessage): obj is CompactionMessage {
|
|
68
|
+
return obj.type === 'compaction';
|
|
69
|
+
}
|
|
66
70
|
}
|
|
67
71
|
export interface TextMessage {
|
|
68
72
|
actor: MessageActor;
|
|
@@ -127,6 +131,17 @@ export interface ImageMessage {
|
|
|
127
131
|
image: ImageContent;
|
|
128
132
|
}
|
|
129
133
|
|
|
134
|
+
export interface CompactionMessage {
|
|
135
|
+
actor: 'ai';
|
|
136
|
+
type: 'compaction';
|
|
137
|
+
/** Originating provider tag; a backend replays the payload only when this matches its own provider. */
|
|
138
|
+
provider: string;
|
|
139
|
+
/** Opaque provider payload to replay. */
|
|
140
|
+
data: unknown;
|
|
141
|
+
/** Human-readable summary, when the provider exposes one. */
|
|
142
|
+
summary?: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
130
145
|
export const isLanguageModelRequestMessage = (obj: unknown): obj is LanguageModelMessage =>
|
|
131
146
|
!!(obj && typeof obj === 'object' &&
|
|
132
147
|
'type' in obj &&
|
|
@@ -304,6 +319,50 @@ export namespace ToolRequest {
|
|
|
304
319
|
(!('required' in obj) || (Array.isArray(obj.required) && obj.required.every(prop => typeof prop === 'string')));
|
|
305
320
|
}
|
|
306
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Per-session/per-request server-side compaction settings, carried verbatim from the chat
|
|
324
|
+
* session's common settings to the request. Kept as an object so further parameters can be
|
|
325
|
+
* added later.
|
|
326
|
+
*/
|
|
327
|
+
export interface CompactionSettings {
|
|
328
|
+
/** Explicit enablement for this session; when set it wins over the model's default. `undefined` means "no explicit choice". */
|
|
329
|
+
enabled?: boolean;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Per-provider override for server-side compaction; combined with the global preference by {@link resolveCompactionDefault}. */
|
|
333
|
+
export type ServerSideCompactionSetting = 'default' | 'enabled' | 'disabled';
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Resolves a model's default server-side compaction enablement from the global preference and
|
|
337
|
+
* the per-provider override. `'enabled'`/`'disabled'` force the result; `'default'` defers to
|
|
338
|
+
* the global preference. Intended to be called where the preferences are read (the provider's
|
|
339
|
+
* frontend contribution) and stored on the model.
|
|
340
|
+
*/
|
|
341
|
+
export function resolveCompactionDefault(globalEnabled: boolean, perProviderOverride: ServerSideCompactionSetting): boolean {
|
|
342
|
+
if (perProviderOverride === 'enabled') {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
if (perProviderOverride === 'disabled') {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
return globalEnabled;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Resolves whether server-side compaction is effective for a request: the model must support it
|
|
353
|
+
* (capability), then an explicit per-session setting wins, otherwise the model's resolved default applies.
|
|
354
|
+
*/
|
|
355
|
+
export function resolveServerSideCompaction(
|
|
356
|
+
capability: boolean | undefined,
|
|
357
|
+
enabledByDefault: boolean,
|
|
358
|
+
compaction: CompactionSettings | undefined
|
|
359
|
+
): boolean {
|
|
360
|
+
if (!capability) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
return compaction?.enabled ?? enabledByDefault;
|
|
364
|
+
}
|
|
365
|
+
|
|
307
366
|
export interface LanguageModelRequest {
|
|
308
367
|
messages: LanguageModelMessage[],
|
|
309
368
|
tools?: ToolRequest[];
|
|
@@ -323,6 +382,8 @@ export interface LanguageModelRequest {
|
|
|
323
382
|
clientSettings?: { keepToolCalls: boolean; keepThinking: boolean };
|
|
324
383
|
/** Provider-agnostic reasoning configuration; providers translate it to their native API. */
|
|
325
384
|
reasoning?: ReasoningSettings;
|
|
385
|
+
/** Provider-agnostic server-side compaction settings, copied verbatim from the chat session. Resolved against the model's capability and default in the backend. */
|
|
386
|
+
compaction?: CompactionSettings;
|
|
326
387
|
}
|
|
327
388
|
export interface ResponseFormatJsonSchema {
|
|
328
389
|
type: 'json_schema';
|
|
@@ -378,10 +439,12 @@ export interface LanguageModelTextResponse {
|
|
|
378
439
|
export const isLanguageModelTextResponse = (obj: unknown): obj is LanguageModelTextResponse =>
|
|
379
440
|
!!(obj && typeof obj === 'object' && 'text' in obj && typeof (obj as { text: unknown }).text === 'string');
|
|
380
441
|
|
|
381
|
-
export type LanguageModelStreamResponsePart =
|
|
442
|
+
export type LanguageModelStreamResponsePart =
|
|
443
|
+
TextResponsePart | ToolCallResponsePart | ServerToolCallResponsePart | ThinkingResponsePart | UsageResponsePart | CompactionResponsePart;
|
|
382
444
|
|
|
383
445
|
export const isLanguageModelStreamResponsePart = (part: unknown): part is LanguageModelStreamResponsePart =>
|
|
384
|
-
isUsageResponsePart(part) || isTextResponsePart(part) || isThinkingResponsePart(part) ||
|
|
446
|
+
isUsageResponsePart(part) || isTextResponsePart(part) || isThinkingResponsePart(part) ||
|
|
447
|
+
isToolCallResponsePart(part) || isServerToolCallResponsePart(part) || isCompactionResponsePart(part);
|
|
385
448
|
|
|
386
449
|
export interface UsageResponsePart {
|
|
387
450
|
input_tokens: number;
|
|
@@ -432,6 +495,23 @@ export interface ThinkingResponsePart {
|
|
|
432
495
|
export const isThinkingResponsePart = (part: unknown): part is ThinkingResponsePart =>
|
|
433
496
|
!!(part && typeof part === 'object' && 'thought' in part && typeof part.thought === 'string');
|
|
434
497
|
|
|
498
|
+
export interface CompactionResponsePart {
|
|
499
|
+
compaction: {
|
|
500
|
+
/** Originating provider tag, e.g. 'anthropic' or 'openai-responses'. */
|
|
501
|
+
provider: string;
|
|
502
|
+
/** Opaque provider payload (Anthropic compaction block(s) / OpenAI compaction item). Never interpreted outside the originating backend. */
|
|
503
|
+
data: unknown;
|
|
504
|
+
/** Human-readable summary, when the provider exposes one. */
|
|
505
|
+
summary?: string;
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
export const isCompactionResponsePart = (part: unknown): part is CompactionResponsePart =>
|
|
509
|
+
!!(part && typeof part === 'object' && 'compaction' in part &&
|
|
510
|
+
typeof (part as CompactionResponsePart).compaction === 'object' &&
|
|
511
|
+
(part as CompactionResponsePart).compaction &&
|
|
512
|
+
'provider' in (part as CompactionResponsePart).compaction &&
|
|
513
|
+
typeof (part as CompactionResponsePart).compaction.provider === 'string');
|
|
514
|
+
|
|
435
515
|
export interface ToolCallTextResult { type: 'text', text: string; };
|
|
436
516
|
export interface ToolCallImageResult extends Base64ImageContent { type: 'image' };
|
|
437
517
|
export interface ToolCallAudioResult { type: 'audio', data: string; mimeType: string };
|
|
@@ -530,6 +610,8 @@ export interface LanguageModelMetaData {
|
|
|
530
610
|
* **Note:** If you provide these, you must also provide `vendor` because server tools are vendor-specific.
|
|
531
611
|
*/
|
|
532
612
|
readonly serverTools?: ServerToolDescriptor[];
|
|
613
|
+
/** Whether this model supports provider-native server-side compaction (capability, distinct from whether it is activated). */
|
|
614
|
+
readonly serverSideCompactionSupport?: boolean;
|
|
533
615
|
}
|
|
534
616
|
|
|
535
617
|
export namespace LanguageModelMetaData {
|
|
@@ -385,11 +385,21 @@ export interface PromptFragmentCustomizationService {
|
|
|
385
385
|
createCustomAgentFile(parentDirectory: URI, agent: CustomAgentDescription): Promise<URI>;
|
|
386
386
|
|
|
387
387
|
/**
|
|
388
|
-
*
|
|
388
|
+
* Returns `true` if migration would write anything: a scope still holds a legacy
|
|
389
|
+
* `customAgents.yml`, or a previously migrated scope has `agent.md` files that can be corrected
|
|
390
|
+
* (e.g. headings that were folded by an earlier migration). Read-only; performs no writes and is
|
|
391
|
+
* intended for deciding whether to prompt the user before migrating.
|
|
392
|
+
*/
|
|
393
|
+
hasPendingCustomAgentMigration(): Promise<boolean>;
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Migrates every reachable `customAgents.yml` to the per-agent `agents/<id>/agent.md` layout and
|
|
397
|
+
* corrects already-migrated `agent.md` files whose headings were folded by an earlier migration.
|
|
389
398
|
* The user's original content is never deleted: on success (or on partial failure when no backup
|
|
390
399
|
* exists yet) the YAML is renamed to `customAgents.yml.bak`; if a `.bak` already exists it is not
|
|
391
|
-
* overwritten and the YAML is left in place.
|
|
392
|
-
*
|
|
400
|
+
* overwritten and the YAML is left in place. Corrections only overwrite `agent.md` files the user
|
|
401
|
+
* has not edited since migration.
|
|
402
|
+
* Idempotent — rerunning never overwrites an already up-to-date agent file.
|
|
393
403
|
*/
|
|
394
404
|
migrateCustomAgentsYaml(): Promise<Array<{
|
|
395
405
|
scope: URI;
|
|
@@ -399,6 +409,7 @@ export interface PromptFragmentCustomizationService {
|
|
|
399
409
|
failed: number;
|
|
400
410
|
yamlBackedUp: boolean;
|
|
401
411
|
promptOverridesMigrated: number;
|
|
412
|
+
corrected: number;
|
|
402
413
|
}>>;
|
|
403
414
|
|
|
404
415
|
/**
|
|
@@ -64,6 +64,7 @@ export class BackendLanguageModelRegistryImpl extends DefaultLanguageModelRegist
|
|
|
64
64
|
maxOutputTokens: model.maxOutputTokens,
|
|
65
65
|
reasoningSupport: model.reasoningSupport,
|
|
66
66
|
serverTools: model.serverTools,
|
|
67
|
+
serverSideCompactionSupport: model.serverSideCompactionSupport,
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
}
|