@theia/ai-core 1.74.0-next.9 → 1.74.1

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 (85) hide show
  1. package/lib/browser/ai-configuration-service-impl.d.ts +32 -0
  2. package/lib/browser/ai-configuration-service-impl.d.ts.map +1 -0
  3. package/lib/browser/ai-configuration-service-impl.js +120 -0
  4. package/lib/browser/ai-configuration-service-impl.js.map +1 -0
  5. package/lib/browser/ai-configuration-service-impl.spec.d.ts +2 -0
  6. package/lib/browser/ai-configuration-service-impl.spec.d.ts.map +1 -0
  7. package/lib/browser/ai-configuration-service-impl.spec.js +214 -0
  8. package/lib/browser/ai-configuration-service-impl.spec.js.map +1 -0
  9. package/lib/browser/ai-core-frontend-module.d.ts.map +1 -1
  10. package/lib/browser/ai-core-frontend-module.js +4 -0
  11. package/lib/browser/ai-core-frontend-module.js.map +1 -1
  12. package/lib/browser/ai-settings-service.d.ts +2 -5
  13. package/lib/browser/ai-settings-service.d.ts.map +1 -1
  14. package/lib/browser/ai-settings-service.js +8 -15
  15. package/lib/browser/ai-settings-service.js.map +1 -1
  16. package/lib/browser/frontend-language-model-alias-registry.d.ts +2 -4
  17. package/lib/browser/frontend-language-model-alias-registry.d.ts.map +1 -1
  18. package/lib/browser/frontend-language-model-alias-registry.js +21 -26
  19. package/lib/browser/frontend-language-model-alias-registry.js.map +1 -1
  20. package/lib/browser/frontend-language-model-service.d.ts +2 -3
  21. package/lib/browser/frontend-language-model-service.d.ts.map +1 -1
  22. package/lib/browser/frontend-language-model-service.js +6 -6
  23. package/lib/browser/frontend-language-model-service.js.map +1 -1
  24. package/lib/browser/index.d.ts +1 -0
  25. package/lib/browser/index.d.ts.map +1 -1
  26. package/lib/browser/index.js +1 -0
  27. package/lib/browser/index.js.map +1 -1
  28. package/lib/browser/prompt-variable-contribution.d.ts.map +1 -1
  29. package/lib/browser/prompt-variable-contribution.js +13 -16
  30. package/lib/browser/prompt-variable-contribution.js.map +1 -1
  31. package/lib/browser/prompt-variable-contribution.spec.js +64 -2
  32. package/lib/browser/prompt-variable-contribution.spec.js.map +1 -1
  33. package/lib/browser/trust-aware-preference-reader.d.ts +13 -1
  34. package/lib/browser/trust-aware-preference-reader.d.ts.map +1 -1
  35. package/lib/browser/trust-aware-preference-reader.js +21 -2
  36. package/lib/browser/trust-aware-preference-reader.js.map +1 -1
  37. package/lib/browser/trust-aware-preference-reader.spec.js +26 -0
  38. package/lib/browser/trust-aware-preference-reader.spec.js.map +1 -1
  39. package/lib/common/ai-configuration-service.d.ts +104 -0
  40. package/lib/common/ai-configuration-service.d.ts.map +1 -0
  41. package/lib/common/ai-configuration-service.js +20 -0
  42. package/lib/common/ai-configuration-service.js.map +1 -0
  43. package/lib/common/ai-core-preferences.d.ts +4 -0
  44. package/lib/common/ai-core-preferences.d.ts.map +1 -1
  45. package/lib/common/ai-core-preferences.js +20 -1
  46. package/lib/common/ai-core-preferences.js.map +1 -1
  47. package/lib/common/index.d.ts +1 -0
  48. package/lib/common/index.d.ts.map +1 -1
  49. package/lib/common/index.js +1 -0
  50. package/lib/common/index.js.map +1 -1
  51. package/lib/common/language-model.d.ts +56 -2
  52. package/lib/common/language-model.d.ts.map +1 -1
  53. package/lib/common/language-model.js +53 -3
  54. package/lib/common/language-model.js.map +1 -1
  55. package/lib/common/language-model.spec.js +75 -0
  56. package/lib/common/language-model.spec.js.map +1 -1
  57. package/lib/common/prompt-service.d.ts +13 -0
  58. package/lib/common/prompt-service.d.ts.map +1 -1
  59. package/lib/common/prompt-service.js +8 -0
  60. package/lib/common/prompt-service.js.map +1 -1
  61. package/lib/common/prompt-service.spec.js +46 -0
  62. package/lib/common/prompt-service.spec.js.map +1 -1
  63. package/lib/node/backend-language-model-registry.d.ts.map +1 -1
  64. package/lib/node/backend-language-model-registry.js +1 -0
  65. package/lib/node/backend-language-model-registry.js.map +1 -1
  66. package/package.json +12 -12
  67. package/src/browser/ai-configuration-service-impl.spec.ts +254 -0
  68. package/src/browser/ai-configuration-service-impl.ts +120 -0
  69. package/src/browser/ai-core-frontend-module.ts +7 -1
  70. package/src/browser/ai-settings-service.ts +8 -16
  71. package/src/browser/frontend-language-model-alias-registry.ts +22 -26
  72. package/src/browser/frontend-language-model-service.ts +5 -6
  73. package/src/browser/index.ts +1 -0
  74. package/src/browser/prompt-variable-contribution.spec.ts +88 -2
  75. package/src/browser/prompt-variable-contribution.ts +13 -16
  76. package/src/browser/trust-aware-preference-reader.spec.ts +29 -0
  77. package/src/browser/trust-aware-preference-reader.ts +23 -3
  78. package/src/common/ai-configuration-service.ts +128 -0
  79. package/src/common/ai-core-preferences.ts +23 -1
  80. package/src/common/index.ts +1 -0
  81. package/src/common/language-model.spec.ts +98 -1
  82. package/src/common/language-model.ts +107 -4
  83. package/src/common/prompt-service.spec.ts +56 -0
  84. package/src/common/prompt-service.ts +22 -0
  85. package/src/node/backend-language-model-registry.ts +1 -0
@@ -42,7 +42,8 @@ export interface ReasoningSupport {
42
42
  readonly defaultLevel?: ReasoningLevel;
43
43
  }
44
44
 
45
- export type LanguageModelMessage = TextMessage | ThinkingMessage | ToolUseMessage | ToolResultMessage | ServerToolUseMessage | ImageMessage;
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,69 @@ export namespace ToolRequest {
304
319
  (!('required' in obj) || (Array.isArray(obj.required) && obj.required.every(prop => typeof prop === 'string')));
305
320
  }
306
321
  }
322
+ // Anthropic requires at least 50,000 tokens, so use one conservative minimum for all compaction settings.
323
+ export const SERVER_SIDE_COMPACTION_TOKEN_THRESHOLD_MINIMUM = 50_000;
324
+
325
+ /**
326
+ * Per-session/per-request server-side compaction settings, carried verbatim from the chat
327
+ * session's common settings to the request. Kept as an object so further parameters can be
328
+ * added later.
329
+ */
330
+ export interface CompactionSettings {
331
+ /** Explicit enablement for this session; when set it wins over the model's default. `undefined` means "no explicit choice". */
332
+ enabled?: boolean;
333
+ /** Input-token threshold for this session; when set it wins over the model's default. `undefined` preserves the provider default. */
334
+ tokenThreshold?: number;
335
+ }
336
+
337
+ /** Per-provider override for server-side compaction; combined with the global preference by {@link resolveCompactionDefault}. */
338
+ export type ServerSideCompactionSetting = 'default' | 'enabled' | 'disabled';
339
+
340
+ /**
341
+ * Resolves a model's default server-side compaction enablement from the global preference and
342
+ * the per-provider override. `'enabled'`/`'disabled'` force the result; `'default'` defers to
343
+ * the global preference. Intended to be called where the preferences are read (the provider's
344
+ * frontend contribution) and stored on the model.
345
+ */
346
+ export function resolveCompactionDefault(globalEnabled: boolean, perProviderOverride: ServerSideCompactionSetting): boolean {
347
+ if (perProviderOverride === 'enabled') {
348
+ return true;
349
+ }
350
+ if (perProviderOverride === 'disabled') {
351
+ return false;
352
+ }
353
+ return globalEnabled;
354
+ }
355
+
356
+ export function resolveCompactionTokenThresholdDefault(
357
+ globalThreshold: number | undefined,
358
+ perProviderThreshold: number | undefined
359
+ ): number | undefined {
360
+ return perProviderThreshold ?? globalThreshold;
361
+ }
362
+
363
+ export function resolveCompactionTokenThreshold(
364
+ thresholdByDefault: number | undefined,
365
+ compaction: CompactionSettings | undefined
366
+ ): number | undefined {
367
+ return compaction?.tokenThreshold ?? thresholdByDefault;
368
+ }
369
+
370
+ /**
371
+ * Resolves whether server-side compaction is effective for a request: the model must support it
372
+ * (capability), then an explicit per-session setting wins, otherwise the model's resolved default applies.
373
+ */
374
+ export function resolveServerSideCompaction(
375
+ capability: boolean | undefined,
376
+ enabledByDefault: boolean,
377
+ compaction: CompactionSettings | undefined
378
+ ): boolean {
379
+ if (!capability) {
380
+ return false;
381
+ }
382
+ return compaction?.enabled ?? enabledByDefault;
383
+ }
384
+
307
385
  export interface LanguageModelRequest {
308
386
  messages: LanguageModelMessage[],
309
387
  tools?: ToolRequest[];
@@ -323,6 +401,8 @@ export interface LanguageModelRequest {
323
401
  clientSettings?: { keepToolCalls: boolean; keepThinking: boolean };
324
402
  /** Provider-agnostic reasoning configuration; providers translate it to their native API. */
325
403
  reasoning?: ReasoningSettings;
404
+ /** Provider-agnostic server-side compaction settings, copied verbatim from the chat session. Resolved against the model's capability and default in the backend. */
405
+ compaction?: CompactionSettings;
326
406
  }
327
407
  export interface ResponseFormatJsonSchema {
328
408
  type: 'json_schema';
@@ -378,10 +458,12 @@ export interface LanguageModelTextResponse {
378
458
  export const isLanguageModelTextResponse = (obj: unknown): obj is LanguageModelTextResponse =>
379
459
  !!(obj && typeof obj === 'object' && 'text' in obj && typeof (obj as { text: unknown }).text === 'string');
380
460
 
381
- export type LanguageModelStreamResponsePart = TextResponsePart | ToolCallResponsePart | ServerToolCallResponsePart | ThinkingResponsePart | UsageResponsePart;
461
+ export type LanguageModelStreamResponsePart =
462
+ TextResponsePart | ToolCallResponsePart | ServerToolCallResponsePart | ThinkingResponsePart | UsageResponsePart | CompactionResponsePart;
382
463
 
383
464
  export const isLanguageModelStreamResponsePart = (part: unknown): part is LanguageModelStreamResponsePart =>
384
- isUsageResponsePart(part) || isTextResponsePart(part) || isThinkingResponsePart(part) || isToolCallResponsePart(part) || isServerToolCallResponsePart(part);
465
+ isUsageResponsePart(part) || isTextResponsePart(part) || isThinkingResponsePart(part) ||
466
+ isToolCallResponsePart(part) || isServerToolCallResponsePart(part) || isCompactionResponsePart(part);
385
467
 
386
468
  export interface UsageResponsePart {
387
469
  input_tokens: number;
@@ -432,6 +514,23 @@ export interface ThinkingResponsePart {
432
514
  export const isThinkingResponsePart = (part: unknown): part is ThinkingResponsePart =>
433
515
  !!(part && typeof part === 'object' && 'thought' in part && typeof part.thought === 'string');
434
516
 
517
+ export interface CompactionResponsePart {
518
+ compaction: {
519
+ /** Originating provider tag, e.g. 'anthropic' or 'openai-responses'. */
520
+ provider: string;
521
+ /** Opaque provider payload (Anthropic compaction block(s) / OpenAI compaction item). Never interpreted outside the originating backend. */
522
+ data: unknown;
523
+ /** Human-readable summary, when the provider exposes one. */
524
+ summary?: string;
525
+ };
526
+ }
527
+ export const isCompactionResponsePart = (part: unknown): part is CompactionResponsePart =>
528
+ !!(part && typeof part === 'object' && 'compaction' in part &&
529
+ typeof (part as CompactionResponsePart).compaction === 'object' &&
530
+ (part as CompactionResponsePart).compaction &&
531
+ 'provider' in (part as CompactionResponsePart).compaction &&
532
+ typeof (part as CompactionResponsePart).compaction.provider === 'string');
533
+
435
534
  export interface ToolCallTextResult { type: 'text', text: string; };
436
535
  export interface ToolCallImageResult extends Base64ImageContent { type: 'image' };
437
536
  export interface ToolCallAudioResult { type: 'audio', data: string; mimeType: string };
@@ -530,6 +629,8 @@ export interface LanguageModelMetaData {
530
629
  * **Note:** If you provide these, you must also provide `vendor` because server tools are vendor-specific.
531
630
  */
532
631
  readonly serverTools?: ServerToolDescriptor[];
632
+ /** Whether this model supports provider-native server-side compaction (capability, distinct from whether it is activated). */
633
+ readonly serverSideCompactionSupport?: boolean;
533
634
  }
534
635
 
535
636
  export namespace LanguageModelMetaData {
@@ -644,7 +745,9 @@ export class DefaultLanguageModelRegistryImpl implements LanguageModelRegistry {
644
745
 
645
746
  async getLanguageModels(): Promise<LanguageModel[]> {
646
747
  await this.initialized;
647
- return this.languageModels;
748
+ // Return a fresh array (not the internal, mutated-in-place list) so consumers relying on
749
+ // reference equality - e.g. React memoization in the chat model selector - detect changes.
750
+ return [...this.languageModels];
648
751
  }
649
752
 
650
753
  async getLanguageModel(id: string): Promise<LanguageModel | undefined> {
@@ -562,6 +562,62 @@ describe('PromptService', () => {
562
562
  expect(fragment).to.be.undefined;
563
563
  });
564
564
  });
565
+
566
+ describe('isKnownCommand', () => {
567
+ it('accepts a registered command name', () => {
568
+ promptService.addBuiltInPromptFragment({
569
+ id: 'sample-debug',
570
+ template: 'Help debug: $ARGUMENTS',
571
+ isCommand: true,
572
+ commandName: 'debug'
573
+ });
574
+
575
+ expect(promptService.isKnownCommand('debug')).to.be.true;
576
+ });
577
+
578
+ it('accepts a command fragment referenced by its id', () => {
579
+ promptService.addBuiltInPromptFragment({
580
+ id: 'sample-debug',
581
+ template: 'Help debug: $ARGUMENTS',
582
+ isCommand: true,
583
+ commandName: 'debug'
584
+ });
585
+
586
+ expect(promptService.isKnownCommand('sample-debug')).to.be.true;
587
+ });
588
+
589
+ it('accepts the id of a fragment that is not marked as a command', () => {
590
+ // The `prompt` variable falls back to a plain fragment lookup, so `/normal-fragment`
591
+ // resolves and must therefore be recognized here as well.
592
+ promptService.addBuiltInPromptFragment({
593
+ id: 'normal-fragment',
594
+ template: 'Not a command'
595
+ });
596
+
597
+ expect(promptService.isKnownCommand('normal-fragment')).to.be.true;
598
+ });
599
+
600
+ it('rejects unknown names', () => {
601
+ promptService.addBuiltInPromptFragment({
602
+ id: 'sample-debug',
603
+ template: 'Help debug: $ARGUMENTS',
604
+ isCommand: true,
605
+ commandName: 'debug'
606
+ });
607
+
608
+ expect(promptService.isKnownCommand('home')).to.be.false;
609
+ expect(promptService.isKnownCommand('usr')).to.be.false;
610
+ expect(promptService.isKnownCommand('debugger')).to.be.false;
611
+ });
612
+
613
+ it('rejects the empty name', () => {
614
+ expect(promptService.isKnownCommand('')).to.be.false;
615
+ });
616
+
617
+ it('rejects names when nothing is registered', () => {
618
+ expect(promptService.isKnownCommand('anything')).to.be.false;
619
+ });
620
+ });
565
621
  });
566
622
 
567
623
  describe('CustomAgentDescription', () => {
@@ -467,6 +467,19 @@ export interface PromptService {
467
467
  */
468
468
  getPromptFragmentByCommandName(commandName: string): PromptFragment | undefined;
469
469
 
470
+ /**
471
+ * Checks whether `/name` can be resolved as a slash command.
472
+ *
473
+ * This mirrors the lookup performed when resolving the `prompt` variable: the name may either be
474
+ * the command name of a fragment marked as a command, or the id of any existing prompt fragment.
475
+ * Use this before interpreting a `/name` token as a command, so that unrelated text such as Unix
476
+ * paths is not mistaken for a command.
477
+ *
478
+ * @param name The command name or prompt fragment id
479
+ * @returns `true` if a matching command or prompt fragment exists
480
+ */
481
+ isKnownCommand(name: string): boolean;
482
+
470
483
  /**
471
484
  * Resolves a prompt fragment by replacing variables and function references
472
485
  * @param fragmentId The prompt fragment ID
@@ -758,6 +771,15 @@ export class PromptServiceImpl implements PromptService {
758
771
  );
759
772
  }
760
773
 
774
+ isKnownCommand(name: string): boolean {
775
+ if (!name) {
776
+ return false;
777
+ }
778
+ // Both lookups are needed because the `prompt` variable resolves a command either by its
779
+ // command name or, as a fallback, by prompt fragment id.
780
+ return this.getPromptFragmentByCommandName(name) !== undefined || this.getRawPromptFragment(name) !== undefined;
781
+ }
782
+
761
783
  /**
762
784
  * Strips comments from a template string
763
785
  * @param templateText The template text to process
@@ -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
  }