From 8f293ed91bc0496d851ae1cfc2f544ab606ef207 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 20 Nov 2024 16:10:03 +0100 Subject: [PATCH 01/75] Allows comments in prompt templates (#14470) * Allow comments in prompt templates fixed #14469 --- .../data/prompttemplate.tmLanguage.json | 16 ++++ .../agent-configuration-widget.tsx | 2 +- .../ai-core/src/common/prompt-service.spec.ts | 85 +++++++++++++++++++ packages/ai-core/src/common/prompt-service.ts | 25 +++++- 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/packages/ai-core/data/prompttemplate.tmLanguage.json b/packages/ai-core/data/prompttemplate.tmLanguage.json index b6071f35cb2b0..30a02cb34c4b4 100644 --- a/packages/ai-core/data/prompttemplate.tmLanguage.json +++ b/packages/ai-core/data/prompttemplate.tmLanguage.json @@ -19,6 +19,22 @@ } } }, + { + "name": "comment.block.prompttemplate", + "begin": "\\A{{!--", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin" + } + }, + "end": "--}}", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end" + } + }, + "patterns": [] + }, { "name": "variable.other.prompttemplate.double", "begin": "\\{\\{", diff --git a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx index a81953302dd97..35abb84b328e5 100644 --- a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx +++ b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx @@ -180,7 +180,7 @@ export class AIAgentConfigurationWidget extends ReactWidget { const promptTemplates = agent.promptTemplates; const result: ParsedPrompt = { functions: [], globalVariables: [], agentSpecificVariables: [] }; promptTemplates.forEach(template => { - const storedPrompt = this.promptService.getRawPrompt(template.id); + const storedPrompt = this.promptService.getUnresolvedPrompt(template.id); const prompt = storedPrompt?.template ?? template.template; const variableMatches = matchVariablesRegEx(prompt); diff --git a/packages/ai-core/src/common/prompt-service.spec.ts b/packages/ai-core/src/common/prompt-service.spec.ts index 164925f62a5ee..106ef4fc85df0 100644 --- a/packages/ai-core/src/common/prompt-service.spec.ts +++ b/packages/ai-core/src/common/prompt-service.spec.ts @@ -159,4 +159,89 @@ describe('PromptService', () => { const prompt17 = await promptService.getPrompt('17', { name: 'John' }); expect(prompt17?.text).to.equal('Hi, John! John'); }); + + it('should strip single-line comments at the start of the template', () => { + promptService.storePrompt('comment-basic', '{{!-- Comment --}}Hello, {{name}}!'); + const prompt = promptService.getUnresolvedPrompt('comment-basic'); + expect(prompt?.template).to.equal('Hello, {{name}}!'); + }); + + it('should remove line break after first-line comment', () => { + promptService.storePrompt('comment-line-break', '{{!-- Comment --}}\nHello, {{name}}!'); + const prompt = promptService.getUnresolvedPrompt('comment-line-break'); + expect(prompt?.template).to.equal('Hello, {{name}}!'); + }); + + it('should strip multiline comments at the start of the template', () => { + promptService.storePrompt('comment-multiline', '{{!--\nMultiline comment\n--}}\nGoodbye, {{name}}!'); + const prompt = promptService.getUnresolvedPrompt('comment-multiline'); + expect(prompt?.template).to.equal('Goodbye, {{name}}!'); + }); + + it('should not strip comments not in the first line', () => { + promptService.storePrompt('comment-second-line', 'Hello, {{name}}!\n{{!-- Comment --}}'); + const prompt = promptService.getUnresolvedPrompt('comment-second-line'); + expect(prompt?.template).to.equal('Hello, {{name}}!\n{{!-- Comment --}}'); + }); + + it('should treat unclosed comments as regular text', () => { + promptService.storePrompt('comment-unclosed', '{{!-- Unclosed comment'); + const prompt = promptService.getUnresolvedPrompt('comment-unclosed'); + expect(prompt?.template).to.equal('{{!-- Unclosed comment'); + }); + + it('should treat standalone closing delimiters as regular text', () => { + promptService.storePrompt('comment-standalone', '--}} Hello, {{name}}!'); + const prompt = promptService.getUnresolvedPrompt('comment-standalone'); + expect(prompt?.template).to.equal('--}} Hello, {{name}}!'); + }); + + it('should handle nested comments and stop at the first closing tag', () => { + promptService.storePrompt('nested-comment', '{{!-- {{!-- Nested comment --}} --}}text'); + const prompt = promptService.getUnresolvedPrompt('nested-comment'); + expect(prompt?.template).to.equal('--}}text'); + }); + + it('should handle templates with only comments', () => { + promptService.storePrompt('comment-only', '{{!-- Only comments --}}'); + const prompt = promptService.getUnresolvedPrompt('comment-only'); + expect(prompt?.template).to.equal(''); + }); + + it('should handle mixed delimiters on the same line', () => { + promptService.storePrompt('comment-mixed', '{{!-- Unclosed comment --}}'); + const prompt = promptService.getUnresolvedPrompt('comment-mixed'); + expect(prompt?.template).to.equal(''); + }); + + it('should resolve variables after stripping single-line comments', async () => { + promptService.storePrompt('comment-resolve', '{{!-- Comment --}}Hello, {{name}}!'); + const prompt = await promptService.getPrompt('comment-resolve', { name: 'John' }); + expect(prompt?.text).to.equal('Hello, John!'); + }); + + it('should resolve variables in multiline templates with comments', async () => { + promptService.storePrompt('comment-multiline-vars', '{{!--\nMultiline comment\n--}}\nHello, {{name}}!'); + const prompt = await promptService.getPrompt('comment-multiline-vars', { name: 'John' }); + expect(prompt?.text).to.equal('Hello, John!'); + }); + + it('should resolve variables with standalone closing delimiters', async () => { + promptService.storePrompt('comment-standalone-vars', '--}} Hello, {{name}}!'); + const prompt = await promptService.getPrompt('comment-standalone-vars', { name: 'John' }); + expect(prompt?.text).to.equal('--}} Hello, John!'); + }); + + it('should treat unclosed comments as text and resolve variables', async () => { + promptService.storePrompt('comment-unclosed-vars', '{{!-- Unclosed comment\nHello, {{name}}!'); + const prompt = await promptService.getPrompt('comment-unclosed-vars', { name: 'John' }); + expect(prompt?.text).to.equal('{{!-- Unclosed comment\nHello, John!'); + }); + + it('should handle templates with mixed comments and variables', async () => { + promptService.storePrompt('comment-mixed-vars', '{{!-- Comment --}}Hi, {{name}}! {{!-- Another comment --}}'); + const prompt = await promptService.getPrompt('comment-mixed-vars', { name: 'John' }); + expect(prompt?.text).to.equal('Hi, John! {{!-- Another comment --}}'); + }); + }); diff --git a/packages/ai-core/src/common/prompt-service.ts b/packages/ai-core/src/common/prompt-service.ts index 861a0f309621c..d4bc9aaea0ce5 100644 --- a/packages/ai-core/src/common/prompt-service.ts +++ b/packages/ai-core/src/common/prompt-service.ts @@ -40,10 +40,15 @@ export interface ResolvedPromptTemplate { export const PromptService = Symbol('PromptService'); export interface PromptService { /** - * Retrieve the raw {@link PromptTemplate} object. + * Retrieve the raw {@link PromptTemplate} object (unresolved variables, functions and including comments). * @param id the id of the {@link PromptTemplate} */ getRawPrompt(id: string): PromptTemplate | undefined; + /** + * Retrieve the unresolved {@link PromptTemplate} object (unresolved variables, functions, excluding comments) + * @param id the id of the {@link PromptTemplate} + */ + getUnresolvedPrompt(id: string): PromptTemplate | undefined; /** * Retrieve the default raw {@link PromptTemplate} object. * @param id the id of the {@link PromptTemplate} @@ -182,8 +187,24 @@ export class PromptServiceImpl implements PromptService { return this._prompts[id]; } + getUnresolvedPrompt(id: string): PromptTemplate | undefined { + const rawPrompt = this.getRawPrompt(id); + if (!rawPrompt) { + return undefined; + } + return { + id: rawPrompt.id, + template: this.stripComments(rawPrompt.template) + }; + } + + protected stripComments(template: string): string { + const commentRegex = /^\s*{{!--[\s\S]*?--}}\s*\n?/; + return commentRegex.test(template) ? template.replace(commentRegex, '').trimStart() : template; + } + async getPrompt(id: string, args?: { [key: string]: unknown }): Promise { - const prompt = this.getRawPrompt(id); + const prompt = this.getUnresolvedPrompt(id); if (prompt === undefined) { return undefined; } From 7e3638bcbc6d6a05bea09f200b6153c930c42035 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 21 Nov 2024 01:08:07 +0100 Subject: [PATCH 02/75] Motivate users to contributes prompt templates (#14472) * Add comment to motivate users to contribute prompts fixed #14471 --- .../prompt-template-contribution.md | 66 +++++++++++++++++++ .../ai-chat/src/common/command-chat-agents.ts | 4 +- .../src/common/orchestrator-chat-agent.ts | 4 +- .../src/common/universal-chat-agent.ts | 4 +- .../src/common/code-completion-agent.ts | 4 +- .../src/browser/ai-terminal-agent.ts | 6 +- .../ai-workspace-agent/src/common/template.ts | 4 +- 7 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 .github/DISCUSSION_TEMPLATE/prompt-template-contribution.md diff --git a/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.md b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.md new file mode 100644 index 0000000000000..78e1e573cf8c4 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.md @@ -0,0 +1,66 @@ + + +## What Does This Template Do? + +Provide a brief description of the prompt template: +- What problem does it solve? +- What specific functionality does it enhance and how? + +## The Prompt Template + +Paste your prompt template here as plain text. + +## Tested LLMs + +List the language models this template has been tested with, and describe how well it worked: +- LLM Name & Version, e.g., OpenAI GPT-4, LLaMA 3.2, etc. +- Which LLM providers did you use (OpenAI, llama-file, Ollama, etc.) +- Any notable performance observations or challenges? + +## Example User Requests + +Provide some example user requests that were tested with this template: +1. Example Request 1: [What the user inputted] +2. Example Request 2: [Another example] +3. Example Request 3: [And so on...] + +## Suggestions for Improvements (Optional) + +If you’re looking for feedback or collaboration, feel free to add: +- Known limitations or challenges. +- Ideas for improving the template. + +## Related Discussions or Resources (Optional) + +Link to any relevant discussions, documentation, or resources that could help others understand or improve your template. + +## License Agreement + +By submitting this template, you agree to the following: + +1. Your submission is contributed under the [MIT License](https://opensource.org/licenses/MIT). +2. You certify that your contribution complies with the **Developer Certificate of Origin (DCO) Version 1.1**, outlined below: + +--- + +### Developer Certificate of Origin 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +#### Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I have the right to submit it under the open-source license indicated in the file; or +(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open-source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open-source license (unless I am permitted to submit under a different license), as indicated in the file; or +(c) The contribution was provided directly to me by some other person who certified (a), (b), or (c) and I have not modified it. + +(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my signoff) is maintained indefinitely and may be redistributed consistent with this project or the open-source license(s) involved. + +--- + +- [ ] **I agree to contribute this submission under the MIT License and certify compliance with the Developer Certificate of Origin (DCO) Version 1.1.** + diff --git a/packages/ai-chat/src/common/command-chat-agents.ts b/packages/ai-chat/src/common/command-chat-agents.ts index 7ab6608f049de..be758c06d72f8 100644 --- a/packages/ai-chat/src/common/command-chat-agents.ts +++ b/packages/ai-chat/src/common/command-chat-agents.ts @@ -36,7 +36,9 @@ import { export const commandTemplate: PromptTemplate = { id: 'command-system', - template: `# System Prompt + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +# System Prompt You are a service that helps users find commands to execute in an IDE. You reply with stringified JSON Objects that tell the user which command to execute and its arguments, if any. diff --git a/packages/ai-chat/src/common/orchestrator-chat-agent.ts b/packages/ai-chat/src/common/orchestrator-chat-agent.ts index 94fdd5a9b88ef..a9f3483e70cbe 100644 --- a/packages/ai-chat/src/common/orchestrator-chat-agent.ts +++ b/packages/ai-chat/src/common/orchestrator-chat-agent.ts @@ -27,7 +27,9 @@ import { ChatHistoryEntry } from './chat-history-entry'; export const orchestratorTemplate: PromptTemplate = { id: 'orchestrator-system', - template: `# Instructions + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +# Instructions Your task is to identify which Chat Agent(s) should best reply a given user's message. You consider all messages of the conversation to ensure consistency and avoid agent switches without a clear context change. diff --git a/packages/ai-chat/src/common/universal-chat-agent.ts b/packages/ai-chat/src/common/universal-chat-agent.ts index ab8838d7578e6..4093cec0a6066 100644 --- a/packages/ai-chat/src/common/universal-chat-agent.ts +++ b/packages/ai-chat/src/common/universal-chat-agent.ts @@ -23,7 +23,9 @@ import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } f export const universalTemplate: PromptTemplate = { id: 'universal-system', - template: `# Instructions + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +# Instructions You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by providing concise and accurate answers to programming-related questions. Your role is to enhance the diff --git a/packages/ai-code-completion/src/common/code-completion-agent.ts b/packages/ai-code-completion/src/common/code-completion-agent.ts index 528c888d53d23..694c6f454c31b 100644 --- a/packages/ai-code-completion/src/common/code-completion-agent.ts +++ b/packages/ai-code-completion/src/common/code-completion-agent.ts @@ -134,7 +134,9 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent { promptTemplates: PromptTemplate[] = [ { id: 'code-completion-prompt', - template: `You are a code completion agent. The current file you have to complete is named {{file}}. + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +You are a code completion agent. The current file you have to complete is named {{file}}. The language of the file is {{language}}. Return your result as plain text without markdown formatting. Finish the following code snippet. diff --git a/packages/ai-terminal/src/browser/ai-terminal-agent.ts b/packages/ai-terminal/src/browser/ai-terminal-agent.ts index d6fe2accbe8e9..02ad2b871b519 100644 --- a/packages/ai-terminal/src/browser/ai-terminal-agent.ts +++ b/packages/ai-terminal/src/browser/ai-terminal-agent.ts @@ -55,7 +55,8 @@ export class AiTerminalAgent implements Agent { id: 'terminal-system', name: 'AI Terminal System Prompt', description: 'Prompt for the AI Terminal Assistant', - template: ` + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} # Instructions Generate one or more command suggestions based on the user's request, considering the shell being used, the current working directory, and the recent terminal contents. Provide the best suggestion first, @@ -102,7 +103,8 @@ nothing to commit, working tree clean id: 'terminal-user', name: 'AI Terminal User Prompt', description: 'Prompt that contains the user request', - template: ` + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} user-request: {{userRequest}} shell: {{shell}} cwd: {{cwd}} diff --git a/packages/ai-workspace-agent/src/common/template.ts b/packages/ai-workspace-agent/src/common/template.ts index eb03aeea7eb90..a2a44702b9e5d 100644 --- a/packages/ai-workspace-agent/src/common/template.ts +++ b/packages/ai-workspace-agent/src/common/template.ts @@ -18,7 +18,9 @@ import { GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID, GET_WORK export const workspaceTemplate = { id: 'workspace-system', - template: `# Instructions + template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: +https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +# Instructions You are an AI assistant integrated into Theia IDE, designed to assist software developers with concise answers to programming-related questions. Your goal is to enhance productivity with quick, relevant solutions, explanations, and best practices. Keep responses short, delivering valuable insights and direct solutions. From 31da09eea8754e94c72949cb691c9ddae69bdcfa Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 21 Nov 2024 11:25:00 +0100 Subject: [PATCH 03/75] feat: stub added APIs for LanguageModel in vscode 1.95 (#14446) fix: #14405 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + .../plugin-ext/src/plugin/plugin-context.ts | 24 +- packages/plugin-ext/src/plugin/types-impl.ts | 94 +++- packages/plugin/src/theia.d.ts | 491 +++++++++++++++++- 4 files changed, 588 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c39fb1adc1ebc..5495834f5864b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ +- [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics - [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics ## 1.55.0 - 10/31/2024 diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index be2db8d020a8c..fbebfb18fb4a7 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -226,7 +226,13 @@ import { ChatResultFeedbackKind, LanguageModelChatMessage, LanguageModelChatMessageRole, + LanguageModelChatToolMode, LanguageModelError, + LanguageModelPromptTsxPart, + LanguageModelTextPart, + LanguageModelToolCallPart, + LanguageModelToolResult, + LanguageModelToolResultPart, PortAutoForwardAction, PortAttributes, DebugVisualization, @@ -1325,7 +1331,17 @@ export function createAPIFactory( return Promise.resolve([]); }, /** @stubbed LanguageModelChat */ - onDidChangeChatModels: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables) + onDidChangeChatModels: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables), + /** @stubbed LanguageModelTool */ + invokeTool(name: string, options: theia.LanguageModelToolInvocationOptions, token?: CancellationToken): Thenable { + return Promise.resolve({ content: [] }); + }, + /** @stubbed LanguageModelTool */ + registerTool(name: string, tool: theia.LanguageModelTool): Disposable { + return Disposable.NULL; + }, + /** @stubbed LanguageModelTool */ + tools: [] }; return { @@ -1545,6 +1561,12 @@ export function createAPIFactory( LanguageModelChatMessage, LanguageModelChatMessageRole, LanguageModelError, + LanguageModelChatToolMode, + LanguageModelPromptTsxPart, + LanguageModelTextPart, + LanguageModelToolCallPart, + LanguageModelToolResult, + LanguageModelToolResultPart, PortAutoForwardAction, PortAttributes, DebugVisualization, diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 1bccfbdc39c0a..0e48a1c876e0a 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3868,19 +3868,29 @@ export class TerminalQuickFixOpener { } // #region Chat + +/** + * @stubbed + */ export class ChatRequestTurn { readonly prompt: string; readonly participant: string; readonly command?: string; readonly references: theia.ChatPromptReference[]; - private constructor(prompt: string, command: string | undefined, references: theia.ChatPromptReference[], participant: string) { + readonly toolReferences: readonly theia.ChatLanguageModelToolReference[]; + private constructor(prompt: string, command: string | undefined, references: theia.ChatPromptReference[], participant: string, + toolReferences: theia.ChatLanguageModelToolReference[]) { this.prompt = prompt; this.command = command; this.participant = participant; this.references = references; + this.toolReferences = toolReferences; }; } +/** + * @stubbed + */ export class ChatResponseTurn { readonly command?: string; @@ -3888,6 +3898,9 @@ export class ChatResponseTurn { | theia.ChatResponseCommandButtonPart>, readonly result: theia.ChatResult, readonly participant: string) { } } +/** + * @stubbed + */ export class ChatResponseAnchorPart { value: URI | Location; title?: string; @@ -3895,12 +3908,18 @@ export class ChatResponseAnchorPart { constructor(value: URI | Location, title?: string) { } } +/** + * @stubbed + */ export class ChatResponseProgressPart { value: string; constructor(value: string) { } } +/** + * @stubbed + */ export class ChatResponseReferencePart { value: URI | Location; iconPath?: URI | ThemeIcon | { light: URI; dark: URI; }; @@ -3910,12 +3929,19 @@ export class ChatResponseReferencePart { dark: URI; }) { } } + +/** + * @stubbed + */ export class ChatResponseCommandButtonPart { value: theia.Command; constructor(value: theia.Command) { } } +/** + * @stubbed + */ export class ChatResponseMarkdownPart { value: theia.MarkdownString; @@ -3923,6 +3949,9 @@ export class ChatResponseMarkdownPart { } } +/** + * @stubbed + */ export class ChatResponseFileTreePart { value: theia.ChatResponseFileTree[]; baseUri: URI; @@ -3943,17 +3972,20 @@ export enum LanguageModelChatMessageRole { Assistant = 2 } +/** + * @stubbed + */ export class LanguageModelChatMessage { - - static User(content: string, name?: string): LanguageModelChatMessage { + static User(content: string | (LanguageModelTextPart | LanguageModelToolResultPart)[], name?: string): LanguageModelChatMessage { return new LanguageModelChatMessage(LanguageModelChatMessageRole.User, content, name); } - static Assistant(content: string, name?: string): LanguageModelChatMessage { + static Assistant(content: string | (LanguageModelTextPart | LanguageModelToolResultPart)[], name?: string): LanguageModelChatMessage { return new LanguageModelChatMessage(LanguageModelChatMessageRole.Assistant, content, name); } - constructor(public role: LanguageModelChatMessageRole, public content: string, public name?: string) { } + constructor(public role: LanguageModelChatMessageRole, public content: string | (LanguageModelTextPart | LanguageModelToolResultPart | LanguageModelToolCallPart)[], + public name?: string) { } } export class LanguageModelError extends Error { @@ -3978,6 +4010,58 @@ export class LanguageModelError extends Error { this.code = code ?? ''; } } + +export enum LanguageModelChatToolMode { + Auto = 1, + Required = 2 +} + +/** + * @stubbed + */ +export class LanguageModelToolCallPart { + callId: string; + name: string; + input: object; + + constructor(callId: string, name: string, input: object) { } +} + +/** + * @stubbed + */ +export class LanguageModelToolResultPart { + callId: string; + content: (theia.LanguageModelTextPart | theia.LanguageModelPromptTsxPart | unknown)[]; + + constructor(callId: string, content: (theia.LanguageModelTextPart | theia.LanguageModelPromptTsxPart | unknown)[]) { } +} + +/** + * @stubbed + */ +export class LanguageModelTextPart { + value: string; + constructor(value: string) { } +} + +/** + * @stubbed + */ +export class LanguageModelToolResult { + content: (theia.LanguageModelTextPart | theia.LanguageModelPromptTsxPart | unknown)[]; + + constructor(content: (theia.LanguageModelTextPart | theia.LanguageModelPromptTsxPart)[]) { } +} + +/** + * @stubbed + */ +export class LanguageModelPromptTsxPart { + value: unknown; + + constructor(value: unknown) { } +} // #endregion // #region Port Attributes diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 5a4fe1d8a5722..43006cac4e89f 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -17267,6 +17267,7 @@ export module '@theia/plugin' { /** * Represents a user request in chat history. + * @stubbed */ export class ChatRequestTurn { /** @@ -17294,14 +17295,20 @@ export module '@theia/plugin' { */ readonly references: ChatPromptReference[]; + /** + * The list of tools were attached to this request. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + /** * @hidden */ - private constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string); + private constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string, toolReferences: ChatLanguageModelToolReference[]); } /** * Represents a chat participant's response in chat history. + * @stubbed */ export class ChatResponseTurn { /** @@ -17332,6 +17339,7 @@ export module '@theia/plugin' { /** * Extra context passed to a participant. + * @stubbed */ export interface ChatContext { /** @@ -17342,6 +17350,7 @@ export module '@theia/plugin' { /** * Represents an error result from a chat request. + * @stubbed */ export interface ChatErrorDetails { /** @@ -17357,6 +17366,7 @@ export module '@theia/plugin' { /** * The result of a chat request. + * @stubbed */ export interface ChatResult { /** @@ -17387,6 +17397,7 @@ export module '@theia/plugin' { /** * Represents user feedback for a result. + * @stubbed */ export interface ChatResultFeedback { /** @@ -17403,6 +17414,7 @@ export module '@theia/plugin' { /** * A followup question suggested by the participant. + * @stubbed */ export interface ChatFollowup { /** @@ -17429,11 +17441,13 @@ export module '@theia/plugin' { /** * Will be invoked once after each request to get suggested followup questions to show the user. The user can click the followup to send it to the chat. + * @stubbed */ export interface ChatFollowupProvider { /** * Provide followups for the given result. * @param result This object has the same properties as the result returned from the participant callback, including `metadata`, but is not the same instance. + * @param context Extra context passed to a participant. * @param token A cancellation token. */ provideFollowups(result: ChatResult, context: ChatContext, token: CancellationToken): ProviderResult; @@ -17441,12 +17455,14 @@ export module '@theia/plugin' { /** * A chat request handler is a callback that will be invoked when a request is made to a chat participant. + * @stubbed */ export type ChatRequestHandler = (request: ChatRequest, context: ChatContext, response: ChatResponseStream, token: CancellationToken) => ProviderResult; /** * A chat participant can be invoked by the user in a chat session, using the `@` prefix. When it is invoked, it handles the chat request and is solely * responsible for providing a response to the user. A ChatParticipant is created using {@link chat.createChatParticipant}. + * @stubbed */ export interface ChatParticipant { /** @@ -17495,6 +17511,7 @@ export module '@theia/plugin' { /** * A reference to a value that the user added to their chat request. + * @stubbed */ export interface ChatPromptReference { /** @@ -17523,6 +17540,7 @@ export module '@theia/plugin' { /** * A request to a chat participant. + * @stubbed */ export interface ChatRequest { /** @@ -17550,12 +17568,35 @@ export module '@theia/plugin' { * string-manipulation of the prompt. */ readonly references: readonly ChatPromptReference[]; + + /** + * The list of tools that the user attached to their request. + * + * When a tool reference is present, the chat participant should make a chat request using + * {@link LanguageModelChatToolMode.Required} to force the language model to generate input for the tool. Then, the + * participant can use {@link lm.invokeTool} to use the tool attach the result to its request for the user's prompt. The + * tool may contribute useful extra context for the user's request. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + + /** + * A token that can be passed to {@link lm.invokeTool} when invoking a tool inside the context of handling a chat request. + * This associates the tool invocation to a chat session. + */ + readonly toolInvocationToken: ChatParticipantToolToken; + + /** + * This is the model that is currently selected in the UI. Extensions can use this or use {@link chat.selectChatModels} to + * pick another model. Don't hold onto this past the lifetime of the request. + */ + readonly model: LanguageModelChat; } /** * The ChatResponseStream is how a participant is able to return content to the chat view. It provides several methods for streaming different types of content * which will be rendered in an appropriate way in the chat view. A participant can use the helper method for the type of content it wants to return, or it * can instantiate a {@link ChatResponsePart} and use the generic {@link ChatResponseStream.push} method to return it. + * @stubbed */ export interface ChatResponseStream { /** @@ -17572,7 +17613,7 @@ export module '@theia/plugin' { * `push(new ChatResponseAnchorPart(value, title))`. * An anchor is an inline reference to some type of resource. * - * @param value A uri, location, or symbol information. + * @param value A uri or location. * @param title An optional title that is rendered with value. */ anchor(value: Uri | Location, title?: string): void; @@ -17632,6 +17673,7 @@ export module '@theia/plugin' { /** * Represents a part of a chat response that is formatted as Markdown. + * @stubbed */ export class ChatResponseMarkdownPart { /** @@ -17649,6 +17691,7 @@ export module '@theia/plugin' { /** * Represents a file tree structure in a chat response. + * @stubbed */ export interface ChatResponseFileTree { /** @@ -17664,6 +17707,7 @@ export module '@theia/plugin' { /** * Represents a part of a chat response that is a file tree. + * @stubbed */ export class ChatResponseFileTreePart { /** @@ -17686,6 +17730,7 @@ export module '@theia/plugin' { /** * Represents a part of a chat response that is an anchor, that is rendered as a link to a target. + * @stubbed */ export class ChatResponseAnchorPart { /** @@ -17708,6 +17753,7 @@ export module '@theia/plugin' { /** * Represents a part of a chat response that is a progress message. + * @stubbed */ export class ChatResponseProgressPart { /** @@ -17724,6 +17770,7 @@ export module '@theia/plugin' { /** * Represents a part of a chat response that is a reference, rendered separately from the content. + * @stubbed */ export class ChatResponseReferencePart { /** @@ -17764,6 +17811,7 @@ export module '@theia/plugin' { /** * Represents a part of a chat response that is a button that executes a command. + * @stubbed */ export class ChatResponseCommandButtonPart { /** @@ -17818,6 +17866,7 @@ export module '@theia/plugin' { /** * Represents a message in a chat. Can assume different roles, like user or assistant. + * @stubbed */ export class LanguageModelChatMessage { @@ -17827,7 +17876,7 @@ export module '@theia/plugin' { * @param content The content of the message. * @param name The optional name of a user for the message. */ - static User(content: string, name?: string): LanguageModelChatMessage; + static User(content: string | (LanguageModelTextPart | LanguageModelToolResultPart)[], name?: string): LanguageModelChatMessage; /** * Utility to create a new assistant message. @@ -17835,7 +17884,7 @@ export module '@theia/plugin' { * @param content The content of the message. * @param name The optional name of a user for the message. */ - static Assistant(content: string, name?: string): LanguageModelChatMessage; + static Assistant(content: string | (LanguageModelTextPart | LanguageModelToolCallPart)[], name?: string): LanguageModelChatMessage; /** * The role of this message. @@ -17843,9 +17892,10 @@ export module '@theia/plugin' { role: LanguageModelChatMessageRole; /** - * The content of this message. + * A string or heterogeneous array of things that a message can contain as content. Some parts may be message-type + * specific for some models. */ - content: string; + content: (LanguageModelTextPart | LanguageModelToolResultPart | LanguageModelToolCallPart)[]; /** * The optional name of a user for this message. @@ -17859,31 +17909,40 @@ export module '@theia/plugin' { * @param content The content of the message. * @param name The optional name of a user for the message. */ - constructor(role: LanguageModelChatMessageRole, content: string, name?: string); + constructor(role: LanguageModelChatMessageRole, content: string | (LanguageModelTextPart | LanguageModelToolResultPart | LanguageModelToolCallPart)[], name?: string); } /** * Represents a language model response. * * @see {@link LanguageModelAccess.chatRequest} + * @stubbed */ export interface LanguageModelChatResponse { /** - * An async iterable that is a stream of text chunks forming the overall response. + * An async iterable that is a stream of text and tool-call parts forming the overall response. A + * {@link LanguageModelTextPart} is part of the assistant's response to be shown to the user. A + * {@link LanguageModelToolCallPart} is a request from the language model to call a tool. The latter will + * only be returned if tools were passed in the request via {@link LanguageModelChatRequestOptions.tools}. The + * `unknown`-type is used as a placeholder for future parts, like image data parts. * - * *Note* that this stream will error when during data receiving an error occurs. Consumers of - * the stream should handle the errors accordingly. + * *Note* that this stream will error when during data receiving an error occurs. Consumers of the stream should handle + * the errors accordingly. * - * To cancel the stream, the consumer can {@link CancellationTokenSource.cancel cancel} the token that was used to make the request - * or break from the for-loop. + * To cancel the stream, the consumer can {@link CancellationTokenSource.cancel cancel} the token that was used to make + * the request or break from the for-loop. * * @example * ```ts * try { * // consume stream - * for await (const chunk of response.text) { - * console.log(chunk); + * for await (const chunk of response.stream) { + * if (chunk instanceof LanguageModelTextPart) { + * console.log("TEXT", chunk); + * } else if (chunk instanceof LanguageModelToolCallPart) { + * console.log("TOOL CALL", chunk); + * } * } * * } catch(e) { @@ -17892,6 +17951,13 @@ export module '@theia/plugin' { * } * ``` */ + stream: AsyncIterable; + + /** + * This is equivalent to filtering everything except for text parts from a {@link LanguageModelChatResponse.stream}. + * + * @see {@link LanguageModelChatResponse.stream} + */ text: AsyncIterable; } @@ -17899,6 +17965,7 @@ export module '@theia/plugin' { * Represents a language model for making chat requests. * * @see {@link lm.selectChatModels} + * @stubbed */ export interface LanguageModelChat { @@ -17939,8 +18006,8 @@ export module '@theia/plugin' { * Make a chat request using a language model. * * *Note* that language model use may be subject to access restrictions and user consent. Calling this function - * for the first time (for a extension) will show a consent dialog to the user and because of that this function - * must _only be called in response to a user action!_ Extension can use {@link LanguageModelAccessInformation.canSendRequest} + * for the first time (for an extension) will show a consent dialog to the user and because of that this function + * must _only be called in response to a user action!_ Extensions can use {@link LanguageModelAccessInformation.canSendRequest} * to check if they have the necessary permissions to make a request. * * This function will return a rejected promise if making a request to the language model is not @@ -17951,6 +18018,10 @@ export module '@theia/plugin' { * - quota limits exceeded, see {@link LanguageModelError.Blocked `Blocked`} * - other issues in which case extension must check {@link LanguageModelError.cause `LanguageModelError.cause`} * + * An extension can make use of language model tool calling by passing a set of tools to + * {@link LanguageModelChatRequestOptions.tools}. The language model will return a {@link LanguageModelToolCallPart} and + * the extension can invoke the tool and make another request with the result. + * * @param messages An array of message instances. * @param options Options that control the request. * @param token A cancellation token which controls the request. See {@link CancellationTokenSource} for how to create one. @@ -17971,6 +18042,7 @@ export module '@theia/plugin' { * Describes how to select language models for chat requests. * * @see {@link lm.selectChatModels} + * @stubbed */ export interface LanguageModelChatSelector { @@ -18006,6 +18078,7 @@ export module '@theia/plugin' { * failure causes, like `if(someError.code === vscode.LanguageModelError.NotFound.name) {...}` * for the case of referring to an unknown language model. For unspecified errors the `cause`-property * will contain the actual error. + * @stubbed */ export class LanguageModelError extends Error { @@ -18039,6 +18112,7 @@ export module '@theia/plugin' { * Options for making a chat request using a language model. * * @see {@link LanguageModelChat.sendRequest} + * @stubbed */ export interface LanguageModelChatRequestOptions { @@ -18052,6 +18126,24 @@ export module '@theia/plugin' { * and need to be lookup in the respective documentation. */ modelOptions?: { [name: string]: any }; + + /** + * An optional list of tools that are available to the language model. These could be registered tools available via + * {@link lm.tools}, or private tools that are just implemented within the calling extension. + * + * If the LLM requests to call one of these tools, it will return a {@link LanguageModelToolCallPart} in + * {@link LanguageModelChatResponse.stream}. It's the caller's responsibility to invoke the tool. If it's a tool + * registered in {@link lm.tools}, that means calling {@link lm.invokeTool}. + * + * Then, the tool result can be provided to the LLM by creating an Assistant-type {@link LanguageModelChatMessage} with a + * {@link LanguageModelToolCallPart}, followed by a User-type message with a {@link LanguageModelToolResultPart}. + */ + tools?: LanguageModelChatTool[]; + + /** + * The tool-selecting mode to use. {@link LanguageModelChatToolMode.Auto} by default. + */ + toolMode?: LanguageModelChatToolMode; } /** @@ -18092,10 +18184,56 @@ export module '@theia/plugin' { * @stubbed */ export function selectChatModels(selector?: LanguageModelChatSelector): Thenable; + + /** + * Register a LanguageModelTool. The tool must also be registered in the package.json `languageModelTools` contribution + * point. A registered tool is available in the {@link lm.tools} list for any extension to see. But in order for it to + * be seen by a language model, it must be passed in the list of available tools in {@link LanguageModelChatRequestOptions.tools}. + * @returns A {@link Disposable} that unregisters the tool when disposed. + * @stubbed + */ + export function registerTool(name: string, tool: LanguageModelTool): Disposable; + + /** + * A list of all available tools that were registered by all extensions using {@link lm.registerTool}. They can be called + * with {@link lm.invokeTool} with input that match their declared `inputSchema`. + * @stubbed + */ + export const tools: readonly LanguageModelToolInformation[]; + + /** + * Invoke a tool listed in {@link lm.tools} by name with the given input. The input will be validated against + * the schema declared by the tool + * + * A tool can be invoked by a chat participant, in the context of handling a chat request, or globally by any extension in + * any custom flow. + * + * In the former case, the caller shall pass the + * {@link LanguageModelToolInvocationOptions.toolInvocationToken toolInvocationToken}, which comes with the a + * {@link ChatRequest.toolInvocationToken chat request}. This makes sure the chat UI shows the tool invocation for the + * correct conversation. + * + * A tool {@link LanguageModelToolResult result} is an array of {@link LanguageModelTextPart text-} and + * {@link LanguageModelPromptTsxPart prompt-tsx}-parts. If the tool caller is using `@vscode/prompt-tsx`, it can + * incorporate the response parts into its prompt using a `ToolResult`. If not, the parts can be passed along to the + * {@link LanguageModelChat} via a user message with a {@link LanguageModelToolResultPart}. + * + * If a chat participant wants to preserve tool results for requests across multiple turns, it can store tool results in + * the {@link ChatResult.metadata} returned from the handler and retrieve them on the next turn from + * {@link ChatResponseTurn.result}. + * + * @param name The name of the tool to call. + * @param options The options to use when invoking the tool. + * @param token A cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns The result of the tool invocation. + * @stubbed + */ + export function invokeTool(name: string, options: LanguageModelToolInvocationOptions, token?: CancellationToken): Thenable; } /** * Represents extension specific information about the access to language models. + * @stubbed */ export interface LanguageModelAccessInformation { @@ -18114,6 +18252,327 @@ export module '@theia/plugin' { * model does not exist or consent hasn't been asked for. */ canSendRequest(chat: LanguageModelChat): boolean | undefined; + + } + + /** + * A tool that is available to the language model via {@link LanguageModelChatRequestOptions}. A language model uses all the + * properties of this interface to decide which tool to call, and how to call it. + * @stubbed + */ + export interface LanguageModelChatTool { + /** + * The name of the tool. + */ + name: string; + + /** + * The description of the tool. + */ + description: string; + + /** + * A JSON schema for the input this tool accepts. + */ + inputSchema?: object; + } + + /** + * A tool-calling mode for the language model to use. + */ + export enum LanguageModelChatToolMode { + /** + * The language model can choose to call a tool or generate a message. Is the default. + */ + Auto = 1, + + /** + * The language model must call one of the provided tools. Note- some models only support a single tool when using this + * mode. + */ + Required = 2 + } + + /** + * A language model response part indicating a tool call, returned from a {@link LanguageModelChatResponse}, and also can be + * included as a content part on a {@link LanguageModelChatMessage}, to represent a previous tool call in a chat request. + * @stubbed + */ + export class LanguageModelToolCallPart { + /** + * The ID of the tool call. This is a unique identifier for the tool call within the chat request. + */ + callId: string; + + /** + * The name of the tool to call. + */ + name: string; + + /** + * The input with which to call the tool. + */ + input: object; + + /** + * Create a new LanguageModelToolCallPart. + * + * @param callId The ID of the tool call. + * @param name The name of the tool to call. + * @param input The input with which to call the tool. + */ + constructor(callId: string, name: string, input: object); + } + + /** + * The result of a tool call. This is the counterpart of a {@link LanguageModelToolCallPart tool call} and + * it can only be included in the content of a User message + * @stubbed + */ + export class LanguageModelToolResultPart { + /** + * The ID of the tool call. + * + * *Note* that this should match the {@link LanguageModelToolCallPart.callId callId} of a tool call part. + */ + callId: string; + + /** + * The value of the tool result. + */ + content: (LanguageModelTextPart | LanguageModelPromptTsxPart | unknown)[]; + + /** + * @param callId The ID of the tool call. + * @param content The content of the tool result. + */ + constructor(callId: string, content: (LanguageModelTextPart | LanguageModelPromptTsxPart | unknown)[]); + } + + /** + * A language model response part containing a piece of text, returned from a {@link LanguageModelChatResponse}. + * @stubbed + */ + export class LanguageModelTextPart { + /** + * The text content of the part. + */ + value: string; + + /** + * Construct a text part with the given content. + * @param value The text content of the part. + */ + constructor(value: string); + } + + /** + * A language model response part containing a PromptElementJSON from `@vscode/prompt-tsx`. + * @see {@link LanguageModelToolResult} + * @stubbed + */ + export class LanguageModelPromptTsxPart { + /** + * The value of the part. + */ + value: unknown; + + /** + * Construct a prompt-tsx part with the given content. + * @param value The value of the part, the result of `renderPromptElementJSON` from `@vscode/prompt-tsx`. + */ + constructor(value: unknown); + } + + /** + * A result returned from a tool invocation. If using `@vscode/prompt-tsx`, this result may be rendered using a `ToolResult`. + * @stubbed + */ + export class LanguageModelToolResult { + /** + * A list of tool result content parts. Includes `unknown` becauses this list may be extended with new content types in + * the future. + * @see {@link lm.invokeTool}. + */ + content: (LanguageModelTextPart | LanguageModelPromptTsxPart | unknown)[]; + + /** + * Create a LanguageModelToolResult + * @param content A list of tool result content parts + */ + constructor(content: (LanguageModelTextPart | LanguageModelPromptTsxPart)[]); + } + + /** + * A token that can be passed to {@link lm.invokeTool} when invoking a tool inside the context of handling a chat request. + */ + export type ChatParticipantToolToken = never; + + /** + * Options provided for tool invocation. + * @stubbed + */ + export interface LanguageModelToolInvocationOptions { + /** + * An opaque object that ties a tool invocation to a chat request from a {@link ChatParticipant chat participant}. + * + * The _only_ way to get a valid tool invocation token is using the provided {@link ChatRequest.toolInvocationToken toolInvocationToken} + * from a chat request. In that case, a progress bar will be automatically shown for the tool invocation in the chat response view, and if + * the tool requires user confirmation, it will show up inline in the chat view. + * + * If the tool is being invoked outside of a chat request, `undefined` should be passed instead, and no special UI except for + * confirmations will be shown. + * + * *Note* that a tool that invokes another tool during its invocation, can pass along the `toolInvocationToken` that it received. + */ + toolInvocationToken: ChatParticipantToolToken | undefined; + + /** + * The input with which to invoke the tool. The input must match the schema defined in + * {@link LanguageModelToolInformation.inputSchema} + */ + input: T; + + /** + * Options to hint at how many tokens the tool should return in its response, and enable the tool to count tokens + * accurately. + */ + tokenizationOptions?: LanguageModelToolTokenizationOptions; + } + + /** + * Options related to tokenization for a tool invocation. + * @stubbed + */ + export interface LanguageModelToolTokenizationOptions { + /** + * If known, the maximum number of tokens the tool should emit in its result. + */ + tokenBudget: number; + + /** + * Count the number of tokens in a message using the model specific tokenizer-logic. + * @param text A string. + * @param token Optional cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to the number of tokens. + */ + countTokens(text: string, token?: CancellationToken): Thenable; + } + + /** + * Information about a registered tool available in {@link lm.tools}. + * @stubbed + */ + export interface LanguageModelToolInformation { + /** + * A unique name for the tool. + */ + readonly name: string; + + /** + * A description of this tool that may be passed to a language model. + */ + readonly description: string; + + /** + * A JSON schema for the input this tool accepts. + */ + readonly inputSchema: object | undefined; + + /** + * A set of tags, declared by the tool, that roughly describe the tool's capabilities. A tool user may use these to filter + * the set of tools to just ones that are relevant for the task at hand. + */ + readonly tags: readonly string[]; + } + + /** + * Options for {@link LanguageModelTool.prepareInvocation}. + * @stubbed + */ + export interface LanguageModelToolInvocationPrepareOptions { + /** + * The input that the tool is being invoked with. + */ + input: T; + } + + /** + * A tool that can be invoked by a call to a {@link LanguageModelChat}. + * @stubbed + */ + export interface LanguageModelTool { + /** + * Invoke the tool with the given input and return a result. + * + * The provided {@link LanguageModelToolInvocationOptions.input} has been validated against the declared schema. + */ + invoke(options: LanguageModelToolInvocationOptions, token: CancellationToken): ProviderResult; + + /** + * Called once before a tool is invoked. It's recommended to implement this to customize the progress message that appears + * while the tool is running, and to provide a more useful message with context from the invocation input. Can also + * signal that a tool needs user confirmation before running, if appropriate. + * + * * *Note 1:* Must be free of side-effects. + * * *Note 2:* A call to `prepareInvocation` is not necessarily followed by a call to `invoke`. + */ + prepareInvocation?(options: LanguageModelToolInvocationPrepareOptions, token: CancellationToken): ProviderResult; + } + + /** + * When this is returned in {@link PreparedToolInvocation}, the user will be asked to confirm before running the tool. These + * messages will be shown with buttons that say "Continue" and "Cancel". + * @stubbed + */ + export interface LanguageModelToolConfirmationMessages { + /** + * The title of the confirmation message. + */ + title: string; + + /** + * The body of the confirmation message. + */ + message: string | MarkdownString; + } + + /** + * The result of a call to {@link LanguageModelTool.prepareInvocation}. + * @stubbed + */ + export interface PreparedToolInvocation { + /** + * A customized progress message to show while the tool runs. + */ + invocationMessage?: string; + + /** + * The presence of this property indicates that the user should be asked to confirm before running the tool. The user + * should be asked for confirmation for any tool that has a side-effect or may potentially be dangerous. + */ + confirmationMessages?: LanguageModelToolConfirmationMessages; + } + + /** + * A reference to a tool that the user manually attached to their request, either using the `#`-syntax inline, or as an + * attachment via the paperclip button. + * @stubbed + */ + export interface ChatLanguageModelToolReference { + /** + * The tool name. Refers to a tool listed in {@link lm.tools}. + */ + readonly name: string; + + /** + * The start and end index of the reference in the {@link ChatRequest.prompt prompt}. When undefined, the reference was + * not part of the prompt text. + * + * *Note* that the indices take the leading `#`-character into account which means they can be used to modify the prompt + * as-is. + */ + readonly range?: [start: number, end: number]; } /** From 50f6755642c5b7bba83ed21bf086989b4d9b2078 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 21 Nov 2024 11:59:35 +0100 Subject: [PATCH 04/75] feat: update proposed API MappedEditProviders (#14453) fix: #14451 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5495834f5864b..a747ec97f4e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics - [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics +- [plugin] supported MappedEditProviders proposed API evolution [#14453](https://github.com/eclipse-theia/theia/pull/14453) - Contributed on behalf of STMicroelectronics ## 1.55.0 - 10/31/2024 diff --git a/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts b/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts index 5ed4a121bc80d..cd14e2d9e8b0c 100644 --- a/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts +++ b/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts @@ -35,6 +35,7 @@ export module '@theia/plugin' { export interface ConversationResponse { readonly type: 'response'; readonly message: string; + readonly result?: ChatResult; readonly references?: DocumentContextItem[]; } @@ -69,7 +70,7 @@ export module '@theia/plugin' { } export interface MappedEditsRequest { - readonly codeBlocks: { code: string; resource: Uri }[]; + readonly codeBlocks: { code: string; resource: Uri; markdownBeforeBlock?: string }[]; // for every prior response that contains codeblocks, make sure we pass the code as well as the resources based on the reported codemapper URIs readonly conversation: (ConversationRequest | ConversationResponse)[]; } From 5c10d45b947986de4f48b34fc98533f254ad0da6 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 21 Nov 2024 13:37:37 +0100 Subject: [PATCH 05/75] fix selection and improve active text editor behaviour (#14480) Signed-off-by: Jonah Iden --- .../plugin-ext/src/main/browser/main-context.ts | 2 +- .../notebook-documents-and-editors-main.ts | 9 ++++++++- .../src/main/browser/text-editor-main.ts | 15 ++++++++++----- .../plugin-ext/src/plugin/notebook/notebooks.ts | 14 ++++++++------ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index 6467bf99c2b35..4b3043c858132 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -109,7 +109,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_MAIN, new NotebookRenderersMainImpl(rpc, container)); const notebookEditorsMain = new NotebookEditorsMainImpl(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN, notebookEditorsMain); - rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_AND_EDITORS_MAIN, new NotebooksAndEditorsMain(rpc, container, notebookDocumentsMain, notebookEditorsMain)); + rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_AND_EDITORS_MAIN, new NotebooksAndEditorsMain(rpc, container, tabsMain, notebookDocumentsMain, notebookEditorsMain)); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN, new NotebookKernelsMainImpl(rpc, container)); const editorsMain = new TextEditorsMainImpl(editorsAndDocuments, documentsMain, rpc, container); diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index 06d20cab9b225..400a771755b2e 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -31,6 +31,7 @@ import { NotebookEditorsMainImpl } from './notebook-editors-main'; import { NotebookDocumentsMainImpl } from './notebook-documents-main'; import { diffMaps, diffSets } from '../../../common/collections'; import { Mutex } from 'async-mutex'; +import { TabsMainImpl } from '../tabs/tabs-main'; interface NotebookAndEditorDelta { removedDocuments: UriComponents[]; @@ -96,6 +97,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain constructor( rpc: RPCProtocol, container: interfaces.Container, + tabsMain: TabsMainImpl, protected readonly notebookDocumentsMain: NotebookDocumentsMainImpl, protected readonly notebookEditorsMain: NotebookEditorsMainImpl ) { @@ -113,7 +115,12 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain // this.WidgetManager.onActiveEditorChanged(() => this.updateState(), this, this.disposables); this.notebookEditorService.onDidAddNotebookEditor(async editor => this.handleEditorAdd(editor), this, this.disposables); this.notebookEditorService.onDidRemoveNotebookEditor(async editor => this.handleEditorRemove(editor), this, this.disposables); - this.notebookEditorService.onDidChangeCurrentEditor(async editor => this.updateState(editor), this, this.disposables); + this.notebookEditorService.onDidChangeCurrentEditor(async editor => { + if (editor) { + await tabsMain.waitForWidget(editor); + } + this.updateState(editor); + }, this, this.disposables); } dispose(): void { diff --git a/packages/plugin-ext/src/main/browser/text-editor-main.ts b/packages/plugin-ext/src/main/browser/text-editor-main.ts index f71d93a18d3fe..0ce4f478ed8a7 100644 --- a/packages/plugin-ext/src/main/browser/text-editor-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editor-main.ts @@ -388,11 +388,16 @@ export class TextEditorPropertiesMain { if (editor && editor instanceof MonacoEditor) { result = editor.getControl().getSelections() || undefined; } else if (editor && editor instanceof SimpleMonacoEditor) { - result = editor.getControl().getSelections()?.map(selection => new monaco.Selection( - selection.startLineNumber, - selection.startColumn, - selection.positionLineNumber, - selection.positionColumn)); + result = editor.getControl().getSelections()?.map(selection => { + const monacoSelection = new monaco.Selection( + selection.selectionStartLineNumber, + selection.selectionStartColumn, + selection.positionLineNumber, + selection.positionColumn); + monacoSelection.setStartPosition(selection.startLineNumber, selection.startColumn); + monacoSelection.setEndPosition(selection.endLineNumber, selection.endColumn); + return monacoSelection; + }); } if (!result && prevProperties) { diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 65487fffb55aa..7745f267a2866 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -20,7 +20,7 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core'; import { URI as TheiaURI } from '../types-impl'; -import * as theia from '@theia/plugin'; +import type * as theia from '@theia/plugin'; import { NotebookCellStatusBarListDto, NotebookDataDto, NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin, @@ -337,12 +337,14 @@ export class NotebooksExtImpl implements NotebooksExt { console.error(`FAILED to find active notebook editor ${delta.newActiveEditor}`); } this.activeNotebookEditor = this.editors.get(delta.newActiveEditor); - if (this.textDocumentsAndEditors.activeEditor()?.document.uri.path !== this.activeNotebookEditor?.notebookData.uri.path) { - this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ - newActiveEditor: null - }); - } this.onDidChangeActiveNotebookEditorEmitter.fire(this.activeNotebookEditor?.apiEditor); + + const newActiveCell = this.activeApiNotebookEditor?.notebook.cellAt(this.activeApiNotebookEditor.selection.start); + this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ + newActiveEditor: newActiveCell?.kind === 2 /* code cell */ ? + this.textDocumentsAndEditors.allEditors().find(editor => editor.document.uri.toString() === newActiveCell.document.uri.toString())?.id ?? null : + null + }); } } From 6cebf4ac18ef0e8919a448e092e2544ba21e7fe9 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 21 Nov 2024 13:57:26 +0100 Subject: [PATCH 06/75] add min height to cell outputs (#14488) * add min height to cell outputs * fixed for output collapsing --------- Signed-off-by: Jonah Iden --- .../renderers/output-webview-internal.ts | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 16efe9ab2ef69..745f0968414d9 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -174,7 +174,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { } calcTotalOutputHeight(): number { - return this.outputElements.reduce((acc, output) => acc + output.element.clientHeight, 0) + 5; + return this.outputElements.reduce((acc, output) => acc + output.element.getBoundingClientRect().height, 0) + 5; } createOutputElement(index: number, output: webviewCommunication.Output, items: rendererApi.OutputItem[]): OutputContainer { @@ -216,9 +216,11 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { public outputVisibilityChanged(visible: boolean): void { this.outputElements.forEach(output => { output.element.style.display = visible ? 'initial' : 'none'; + output.containerElement.style.minHeight = visible ? '20px' : '0px'; }); if (visible) { this.element.getElementsByClassName('output-hidden')?.[0].remove(); + window.requestAnimationFrame(() => this.outputElements.forEach(output => sendDidRenderMessage(this, output))); } else { const outputHiddenElement = document.createElement('div'); outputHiddenElement.classList.add('output-hidden'); @@ -290,6 +292,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { private createHtmlElement(): void { this.containerElement = document.createElement('div'); this.containerElement.classList.add('output-container'); + this.containerElement.style.minHeight = '20px'; this.element = document.createElement('div'); this.element.id = this.outputId; this.element.classList.add('output'); @@ -489,26 +492,16 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { img.dataset.waiting = 'true'; // mark to avoid overriding onload a second time return new Promise(resolve => { img.onload = img.onerror = resolve; }); })).then(() => { - this.sendDidRenderMessage(cell, output); - new ResizeObserver(() => this.sendDidRenderMessage(cell, output)).observe(cell.element); + sendDidRenderMessage(cell, output); + new ResizeObserver(() => sendDidRenderMessage(cell, output)).observe(cell.element); }); } else { - this.sendDidRenderMessage(cell, output); - new ResizeObserver(() => this.sendDidRenderMessage(cell, output)).observe(cell.element); + sendDidRenderMessage(cell, output); + new ResizeObserver(() => sendDidRenderMessage(cell, output)).observe(cell.element); } } - private sendDidRenderMessage(cell: OutputCell, output: OutputContainer): void { - theia.postMessage({ - type: 'didRenderOutput', - cellHandle: cell.cellHandle, - outputId: output.outputId, - outputHeight: cell.calcTotalOutputHeight(), - bodyHeight: document.body.clientHeight - }); - } - private async doRender(item: rendererApi.OutputItem, element: HTMLElement, renderer: Renderer, signal: AbortSignal): Promise<{ continue: boolean }> { try { await (await renderer.getOrLoad())?.renderOutputItem(item, element, signal); @@ -565,6 +558,16 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { } }(); + function sendDidRenderMessage(cell: OutputCell, output: OutputContainer): void { + theia.postMessage({ + type: 'didRenderOutput', + cellHandle: cell.cellHandle, + outputId: output.outputId, + outputHeight: cell.calcTotalOutputHeight(), + bodyHeight: document.body.clientHeight + }); + } + const kernelPreloads = new class { private readonly preloads = new Map>(); From 6bf4078fc93081abd84d482bfe209da4a1b7fa21 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 21 Nov 2024 13:59:08 +0100 Subject: [PATCH 07/75] Fix Set Milestone on PR Workflow (#14489) Previously, PRs from forks were not assigned to a milestone due to using the `pull_request` trigger. This trigger runs in the context of the merge commit and cannot access the `GITHUB_TOKEN` for forks. Switching to the `pull_request_target` event allows access from the base context, enabling milestone assignment for forked PRs. For more information, see the [GitHub documentation](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target). --- .github/workflows/set-milestone-on-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/set-milestone-on-pr.yml b/.github/workflows/set-milestone-on-pr.yml index 2a040fdacaf95..ff31f10323050 100644 --- a/.github/workflows/set-milestone-on-pr.yml +++ b/.github/workflows/set-milestone-on-pr.yml @@ -15,7 +15,7 @@ # Set milestone on each pull request by using the next minor version # next version is computed using npm version tool on: - pull_request: + pull_request_target: branches: [master] types: [closed] From 77cba8cc0cdcffb931a768e4527fe888f2825475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 21 Nov 2024 14:51:41 +0100 Subject: [PATCH 08/75] Do not dispose dialogs on close (#14456) Fixes #12093 The about dialog in Theia is reopened and must no be disposed on close. Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 2 ++ .../browser/common-frontend-contribution.ts | 2 +- packages/core/src/browser/dialogs.ts | 20 +++++++++++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a747ec97f4e20..2d93f7c5e7faf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) +[Breaking Changes:](#breaking_changes_1.56.0) +- [core] Do not dispose dialogs on close - [#14456](https://github.com/eclipse-theia/theia/pull/14456) - Contributed on behalf of STMicroelectronics - [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics - [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 64489486e30f0..a1bb4f383ac31 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -1199,7 +1199,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi } protected async openAbout(): Promise { - this.aboutDialog.open(); + this.aboutDialog.open(false); } protected shouldPreventClose = false; diff --git a/packages/core/src/browser/dialogs.ts b/packages/core/src/browser/dialogs.ts index 9076dc49c118f..1cf5bf8eaa786 100644 --- a/packages/core/src/browser/dialogs.ts +++ b/packages/core/src/browser/dialogs.ts @@ -258,13 +258,15 @@ export abstract class AbstractDialog extends BaseWidget { } } - open(): Promise { + open(disposeOnResolve: boolean = true): Promise { if (this.resolve) { return Promise.reject(new Error('The dialog is already opened.')); } this.activeElement = this.node.ownerDocument.activeElement as HTMLElement; return new Promise((resolve, reject) => { - this.resolve = resolve; + this.resolve = value => { + resolve(value); + }; this.reject = reject; this.toDisposeOnDetach.push(Disposable.create(() => { this.resolve = undefined; @@ -273,9 +275,23 @@ export abstract class AbstractDialog extends BaseWidget { Widget.attach(this, this.node.ownerDocument.body); this.activate(); + }).finally(() => { + if (disposeOnResolve) { + this.dispose(); + } }); } + protected override onCloseRequest(msg: Message): void { + // super.onCloseRequest() would automatically dispose the dialog, which we don't want because we're reusing it + if (this.parent) { + // eslint-disable-next-line no-null/no-null + this.parent = null; + } else if (this.isAttached) { + Widget.detach(this); + } + } + override close(): void { if (this.resolve) { if (this.activeElement) { From 7b0bd4f1b96ff92e33996235873bc3e1b9a88ff8 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Thu, 21 Nov 2024 15:50:58 +0100 Subject: [PATCH 09/75] chore(ai): avoid conflicting keybinding for opening chat window ctrlcmd+shift+e is actually used already for the navigator. VS Code uses ctrlcmd+alt+i. So I used that as a keybinding for the chat view instead. --- packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts b/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts index 3c7c25eac7472..053c5f0a4dc29 100644 --- a/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts +++ b/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts @@ -52,7 +52,7 @@ export class AIChatContribution extends AbstractViewContribution rank: 100 }, toggleCommandId: AI_CHAT_TOGGLE_COMMAND_ID, - toggleKeybinding: 'ctrlcmd+shift+e' + toggleKeybinding: 'ctrlcmd+alt+i' }); } From a0a453d88323e295d75979466da7d2f1f76c5413 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 21 Nov 2024 22:14:25 +0100 Subject: [PATCH 10/75] Fix prompt template contribution category and template (#14497) fixed #14496 Signed-off-by: Jonas Helming --- .../prompt-template-contribution.md | 66 ------------- .../prompt-template-contribution.yml | 95 +++++++++++++++++++ .../ai-chat/src/common/command-chat-agents.ts | 2 +- .../src/common/orchestrator-chat-agent.ts | 2 +- .../src/common/universal-chat-agent.ts | 2 +- .../src/common/code-completion-agent.ts | 2 +- .../src/browser/ai-terminal-agent.ts | 4 +- .../ai-workspace-agent/src/common/template.ts | 2 +- 8 files changed, 102 insertions(+), 73 deletions(-) delete mode 100644 .github/DISCUSSION_TEMPLATE/prompt-template-contribution.md create mode 100644 .github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml diff --git a/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.md b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.md deleted file mode 100644 index 78e1e573cf8c4..0000000000000 --- a/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.md +++ /dev/null @@ -1,66 +0,0 @@ - - -## What Does This Template Do? - -Provide a brief description of the prompt template: -- What problem does it solve? -- What specific functionality does it enhance and how? - -## The Prompt Template - -Paste your prompt template here as plain text. - -## Tested LLMs - -List the language models this template has been tested with, and describe how well it worked: -- LLM Name & Version, e.g., OpenAI GPT-4, LLaMA 3.2, etc. -- Which LLM providers did you use (OpenAI, llama-file, Ollama, etc.) -- Any notable performance observations or challenges? - -## Example User Requests - -Provide some example user requests that were tested with this template: -1. Example Request 1: [What the user inputted] -2. Example Request 2: [Another example] -3. Example Request 3: [And so on...] - -## Suggestions for Improvements (Optional) - -If you’re looking for feedback or collaboration, feel free to add: -- Known limitations or challenges. -- Ideas for improving the template. - -## Related Discussions or Resources (Optional) - -Link to any relevant discussions, documentation, or resources that could help others understand or improve your template. - -## License Agreement - -By submitting this template, you agree to the following: - -1. Your submission is contributed under the [MIT License](https://opensource.org/licenses/MIT). -2. You certify that your contribution complies with the **Developer Certificate of Origin (DCO) Version 1.1**, outlined below: - ---- - -### Developer Certificate of Origin 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -660 York Street, Suite 102, San Francisco, CA 94110 USA - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -#### Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I have the right to submit it under the open-source license indicated in the file; or -(b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open-source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open-source license (unless I am permitted to submit under a different license), as indicated in the file; or -(c) The contribution was provided directly to me by some other person who certified (a), (b), or (c) and I have not modified it. - -(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my signoff) is maintained indefinitely and may be redistributed consistent with this project or the open-source license(s) involved. - ---- - -- [ ] **I agree to contribute this submission under the MIT License and certify compliance with the Developer Certificate of Origin (DCO) Version 1.1.** - diff --git a/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml new file mode 100644 index 0000000000000..07062d6a13f6a --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml @@ -0,0 +1,95 @@ +name: Prompt Template Submission +description: Submit a prompt template, detailing its functionality and tested results. +title: Prompt Template for +body: + - type: textarea + id: what_template_does + attributes: + label: What Does This Template Do? + description: Provide a brief description of the prompt template. What problem does it solve? What specific functionality and agent does it enhance and how? + placeholder: Enter a description of the template. + validations: + required: true + + - type: textarea + id: prompt_template + attributes: + label: The Prompt Template + description: Paste your prompt template here as plain text. + placeholder: Enter the prompt template. + validations: + required: true + + - type: textarea + id: tested_llms + attributes: + label: Tested LLMs + description: List the language models this template has been tested with, including versions, providers, and notable performance observations. + placeholder: |- + - LLM Name & Version (e.g., OpenAI GPT-4) + - Provider (e.g., OpenAI, llama-file, Ollama) + - Observations or challenges + validations: + required: true + + - type: textarea + id: example_requests + attributes: + label: Example User Requests + description: Provide some example user requests tested with this template. + placeholder: |- + 1. Example Request 1: [User input] + 2. Example Request 2: [User input] + 3. Example Request 3: [User input] + validations: + required: true + + - type: textarea + id: improvement_suggestions + attributes: + label: Suggestions for Improvements (Optional) + description: Share any known limitations or ideas for improving the template. + placeholder: Describe any challenges or potential enhancements. + validations: + required: false + + - type: textarea + id: related_resources + attributes: + label: Related Discussions or Resources (Optional) + description: Link to any relevant discussions, documentation, or resources that could help others understand or improve your template. + placeholder: Provide URLs or references. + validations: + required: false + + - type: checkboxes + id: license_agreement + attributes: + label: License Agreement + description: By submitting this template, you agree to the following terms + options: + - label: | + By submitting this template, you agree to the following: + + 1. Your submission is contributed under the [MIT License](https://opensource.org/licenses/MIT). + 2. You certify that your contribution complies with the **Developer Certificate of Origin (DCO) Version 1.1**, outlined below: + + ### Developer Certificate of Origin 1.1 + + Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + 660 York Street, Suite 102, San Francisco, CA 94110 USA + + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + + #### Developer's Certificate of Origin 1.1 + + By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I have the right to submit it under the open-source license indicated in the file; or + (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open-source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open-source license (unless I am permitted to submit under a different license), as indicated in the file; or + (c) The contribution was provided directly to me by some other person who certified (a), (b), or (c) and I have not modified it. + + (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my signoff) is maintained indefinitely and may be redistributed consistent with this project or the open-source license(s) involved. + + I agree to contribute this submission under the MIT License and certify compliance with the Developer Certificate of Origin (DCO) Version 1.1. + required: true diff --git a/packages/ai-chat/src/common/command-chat-agents.ts b/packages/ai-chat/src/common/command-chat-agents.ts index be758c06d72f8..d45fa8a75e80c 100644 --- a/packages/ai-chat/src/common/command-chat-agents.ts +++ b/packages/ai-chat/src/common/command-chat-agents.ts @@ -37,7 +37,7 @@ import { export const commandTemplate: PromptTemplate = { id: 'command-system', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} # System Prompt You are a service that helps users find commands to execute in an IDE. diff --git a/packages/ai-chat/src/common/orchestrator-chat-agent.ts b/packages/ai-chat/src/common/orchestrator-chat-agent.ts index a9f3483e70cbe..3ca3420624721 100644 --- a/packages/ai-chat/src/common/orchestrator-chat-agent.ts +++ b/packages/ai-chat/src/common/orchestrator-chat-agent.ts @@ -28,7 +28,7 @@ import { ChatHistoryEntry } from './chat-history-entry'; export const orchestratorTemplate: PromptTemplate = { id: 'orchestrator-system', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} # Instructions Your task is to identify which Chat Agent(s) should best reply a given user's message. diff --git a/packages/ai-chat/src/common/universal-chat-agent.ts b/packages/ai-chat/src/common/universal-chat-agent.ts index 4093cec0a6066..ab70242748795 100644 --- a/packages/ai-chat/src/common/universal-chat-agent.ts +++ b/packages/ai-chat/src/common/universal-chat-agent.ts @@ -24,7 +24,7 @@ import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } f export const universalTemplate: PromptTemplate = { id: 'universal-system', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} # Instructions You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by diff --git a/packages/ai-code-completion/src/common/code-completion-agent.ts b/packages/ai-code-completion/src/common/code-completion-agent.ts index 694c6f454c31b..c47f5b6089957 100644 --- a/packages/ai-code-completion/src/common/code-completion-agent.ts +++ b/packages/ai-code-completion/src/common/code-completion-agent.ts @@ -135,7 +135,7 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent { { id: 'code-completion-prompt', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} You are a code completion agent. The current file you have to complete is named {{file}}. The language of the file is {{language}}. Return your result as plain text without markdown formatting. Finish the following code snippet. diff --git a/packages/ai-terminal/src/browser/ai-terminal-agent.ts b/packages/ai-terminal/src/browser/ai-terminal-agent.ts index 02ad2b871b519..cdb6b8a916e19 100644 --- a/packages/ai-terminal/src/browser/ai-terminal-agent.ts +++ b/packages/ai-terminal/src/browser/ai-terminal-agent.ts @@ -56,7 +56,7 @@ export class AiTerminalAgent implements Agent { name: 'AI Terminal System Prompt', description: 'Prompt for the AI Terminal Assistant', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} # Instructions Generate one or more command suggestions based on the user's request, considering the shell being used, the current working directory, and the recent terminal contents. Provide the best suggestion first, @@ -104,7 +104,7 @@ nothing to commit, working tree clean name: 'AI Terminal User Prompt', description: 'Prompt that contains the user request', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} user-request: {{userRequest}} shell: {{shell}} cwd: {{cwd}} diff --git a/packages/ai-workspace-agent/src/common/template.ts b/packages/ai-workspace-agent/src/common/template.ts index a2a44702b9e5d..b93b62422f2de 100644 --- a/packages/ai-workspace-agent/src/common/template.ts +++ b/packages/ai-workspace-agent/src/common/template.ts @@ -19,7 +19,7 @@ import { GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID, GET_WORK export const workspaceTemplate = { id: 'workspace-system', template: `{{!-- Made improvements or adaptations to this prompt template? We’d love for you to share it with the community! Contribute back here: -https://github.com/eclipse-theia/theia/discussions/new?category=show-and-tell&template=prompt-template-contribution.md --}} +https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}} # Instructions You are an AI assistant integrated into Theia IDE, designed to assist software developers with concise answers to programming-related questions. Your goal is to enhance From 832287da3e45f09842c78dc4fbeefb80772d06d2 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Fri, 22 Nov 2024 09:13:49 +0100 Subject: [PATCH 11/75] Flaky Playwright test - notebook split cell #14450 (#14457) --- .gitignore | 1 + examples/playwright/package.json | 2 +- .../src/tests/theia-notebook-editor.test.ts | 22 ++++++++++----- yarn.lock | 28 +++++++++---------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index aab5678aabd83..40f967eea81da 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ dependency-check-summary.txt* /performance-result.json *.vsix /scripts/native-dependencies-* +allure-results diff --git a/examples/playwright/package.json b/examples/playwright/package.json index 837388a347cd3..8939769f04819 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -31,7 +31,7 @@ "src" ], "dependencies": { - "@playwright/test": "^1.37.1", + "@playwright/test": "^1.47.0", "fs-extra": "^9.0.8" }, "devDependencies": { diff --git a/examples/playwright/src/tests/theia-notebook-editor.test.ts b/examples/playwright/src/tests/theia-notebook-editor.test.ts index 3045d25421b87..152d8c4540741 100644 --- a/examples/playwright/src/tests/theia-notebook-editor.test.ts +++ b/examples/playwright/src/tests/theia-notebook-editor.test.ts @@ -21,6 +21,7 @@ import { TheiaNotebookCell } from '../theia-notebook-cell'; import { TheiaNotebookEditor } from '../theia-notebook-editor'; import { TheiaWorkspace } from '../theia-workspace'; import path = require('path'); +import fs = require('fs'); // See .github/workflows/playwright.yml for preferred python version const preferredKernel = process.env.CI ? 'Python 3.11' : 'Python 3'; @@ -39,7 +40,9 @@ test.describe('Theia Notebook Editor interaction', () => { }); test.afterAll(async () => { - await app.page.close(); + if (app.page) { + await app.page.close(); + } }); test.afterEach(async () => { @@ -110,9 +113,11 @@ test.describe('Theia Notebook Editor interaction', () => { <|>print("Line-2") */ const line = await cell.editor.lineByLineNumber(1); - await line?.waitForElementState('visible'); - await line?.click(); - await line?.press('ArrowRight'); + expect(line, { message: 'Line number 1 should exists' }).toBeDefined(); + const box = await line?.boundingBox(); + console.log(`Split cell test: visible = ${await line?.isVisible()}, box = {${box?.x},${box?.y},${box?.width},${box?.height}}`); + await line!.click(); + await line!.press('ArrowRight'); // split cell await cell.splitCell(); @@ -134,7 +139,9 @@ test.describe('Theia Notebook Cell interaction', () => { }); test.afterAll(async () => { - await app.page.close(); + if (app.page) { + await app.page.close(); + } }); test.beforeEach(async () => { @@ -309,8 +316,9 @@ async function firstCell(editor: TheiaNotebookEditor): Promise { const workingDir = path.resolve(); - // correct WS path. When running from IDE the path is playwright/configs with CLI it's playwright/ - const prefix = workingDir.endsWith('playwright/configs') ? '../' : ''; + // correct WS path. When running from IDE the path is workspace root or playwright/configs, with CLI it's playwright/ + const isWsRoot = fs.existsSync(path.join(workingDir, 'examples', 'playwright')); + const prefix = isWsRoot ? 'examples/playwright/' : (workingDir.endsWith('playwright/configs') ? '../' : ''); const ws = new TheiaWorkspace([prefix + 'src/tests/resources/notebook-files']); const app = await TheiaAppLoader.load(args, ws); // auto-save are disabled using settings.json file diff --git a/yarn.lock b/yarn.lock index 3a312954d3c6a..7ddee1084ff44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1742,12 +1742,12 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@playwright/test@^1.37.1": - version "1.41.2" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.41.2.tgz#bd9db40177f8fd442e16e14e0389d23751cdfc54" - integrity sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg== +"@playwright/test@^1.47.0": + version "1.48.2" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.48.2.tgz#87dd40633f980872283404c8142a65744d3f13d6" + integrity sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw== dependencies: - playwright "1.41.2" + playwright "1.48.2" "@puppeteer/browsers@2.3.1": version "2.3.1" @@ -9961,17 +9961,17 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.41.2: - version "1.41.2" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.41.2.tgz#db22372c708926c697acc261f0ef8406606802d9" - integrity sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA== +playwright-core@1.48.2: + version "1.48.2" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.48.2.tgz#cd76ed8af61690edef5c05c64721c26a8db2f3d7" + integrity sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA== -playwright@1.41.2: - version "1.41.2" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.41.2.tgz#4e760b1c79f33d9129a8c65cc27953be6dd35042" - integrity sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A== +playwright@1.48.2: + version "1.48.2" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.48.2.tgz#fca45ae8abdc34835c715718072aaff7e305167e" + integrity sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ== dependencies: - playwright-core "1.41.2" + playwright-core "1.48.2" optionalDependencies: fsevents "2.3.2" From 0556d2a4ebcf0024e96a1e54ae3450f5e6ea24d6 Mon Sep 17 00:00:00 2001 From: Marc Dumais Date: Fri, 6 Sep 2024 09:19:47 -0400 Subject: [PATCH 12/75] [CI] Disable dash-licenses auto-review mode Related issue: #14127 The Eclipse Foundation Gitlab token, required for dash-licenses to automatically open IP ticket for suspicious license in dependencies. is about to expire. Until it's replaced, we can have the workflow use the basic mode, where dependecies with suspicious licenses are only listed, and have to be handled offline [1]. [1]: To have dash-licenses help with opening IP tickets automatically e.g. after a PR license check workflow failure. Any committer can generate a token from EF Gitlab at the link below and set it in an environment variable, and then use it when running dash-licenses from their laptop. e.g. theia$ git checkout && yarn theia$ export DASH_LICENSES_PAT="" theia$ yarn license:check:review https://gitlab.eclipse.org/-/user_settings/personal_access_tokens Signed-off-by: Marc Dumais --- .github/workflows/license-check.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index b299055bd4415..98f3649133b5e 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -48,6 +48,4 @@ jobs: if: matrix.tests != 'skip' shell: bash run: | - yarn license:check:review || ( sleep 15m && yarn license:check:review ) - env: - DASH_LICENSES_PAT: ${{ secrets.DASH_LICENSES_PAT }} + yarn license:check From 2a0232d74807f861d1cc0df499359e3f2d5362b5 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Sat, 23 Nov 2024 01:09:54 +0100 Subject: [PATCH 13/75] Support prompt variants (#14487) * Support prompt variants fixed #14485 Signed-off-by: Jonas Helming --- .../src/common/universal-chat-agent.ts | 8 +- .../agent-configuration-widget.tsx | 31 ++++- .../template-settings-renderer.tsx | 105 ++++++++++++-- packages/ai-core/src/browser/style/index.css | 38 ++++- packages/ai-core/src/common/agent-service.ts | 2 +- .../ai-core/src/common/prompt-service.spec.ts | 130 +++++++++++++----- packages/ai-core/src/common/prompt-service.ts | 57 ++++++-- .../ai-core/src/common/settings-service.ts | 5 + 8 files changed, 302 insertions(+), 74 deletions(-) diff --git a/packages/ai-chat/src/common/universal-chat-agent.ts b/packages/ai-chat/src/common/universal-chat-agent.ts index ab70242748795..3765b1caa6e7f 100644 --- a/packages/ai-chat/src/common/universal-chat-agent.ts +++ b/packages/ai-chat/src/common/universal-chat-agent.ts @@ -78,6 +78,12 @@ simple solutions. ` }; +export const universalTemplateVariant: PromptTemplate = { + id: 'universal-system-empty', + template: '', + variantOf: universalTemplate.id, +}; + @injectable() export class UniversalChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent { name: string; @@ -98,7 +104,7 @@ export class UniversalChatAgent extends AbstractStreamParsingChatAgent implement + 'questions the user might ask. The universal agent currently does not have any context by default, i.e. it cannot ' + 'access the current user context or the workspace.'; this.variables = []; - this.promptTemplates = [universalTemplate]; + this.promptTemplates = [universalTemplate, universalTemplateVariant]; this.functions = []; this.agentSpecificVariables = []; } diff --git a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx index 35abb84b328e5..8329e25ab5e1f 100644 --- a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx +++ b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx @@ -137,14 +137,31 @@ export class AIAgentConfigurationWidget extends ReactWidget { Enable Agent -
- {agent.promptTemplates?.map(template => - )} +
+ Prompt Templates
+
+ {(() => { + const defaultTemplates = agent.promptTemplates?.filter(template => !template.variantOf) || []; + return defaultTemplates.length > 0 ? ( + defaultTemplates.map(template => ( +
+ +
+ )) + ) : ( +
No default template available
+ ); + })()} +
+
= ({ agentId, template, promptCustomizationService }) => { +export const TemplateRenderer: React.FC = ({ + agentId, + template, + promptCustomizationService, + promptService, + aiSettingsService, +}) => { + const [variantIds, setVariantIds] = React.useState([]); + const [selectedVariant, setSelectedVariant] = React.useState(DEFAULT_VARIANT); + + React.useEffect(() => { + (async () => { + const variants = promptService.getVariantIds(template.id); + setVariantIds([DEFAULT_VARIANT, ...variants]); + + const agentSettings = await aiSettingsService.getAgentSettings(agentId); + const currentVariant = + agentSettings?.selectedVariants?.[template.id] || DEFAULT_VARIANT; + setSelectedVariant(currentVariant); + })(); + }, [template.id, promptService, aiSettingsService, agentId]); + + const handleVariantChange = async (event: React.ChangeEvent) => { + const newVariant = event.target.value; + setSelectedVariant(newVariant); + + const agentSettings = await aiSettingsService.getAgentSettings(agentId); + const selectedVariants = agentSettings?.selectedVariants || {}; + + const updatedVariants = { ...selectedVariants }; + if (newVariant === DEFAULT_VARIANT) { + delete updatedVariants[template.id]; + } else { + updatedVariants[template.id] = newVariant; + } + + await aiSettingsService.updateAgentSettings(agentId, { + selectedVariants: updatedVariants, + }); + }; + const openTemplate = React.useCallback(async () => { - promptCustomizationService.editTemplate(template.id, template.template); - }, [template, promptCustomizationService]); + const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant; + const selectedTemplate = promptService.getRawPrompt(templateId); + promptCustomizationService.editTemplate(templateId, selectedTemplate?.template || ''); + }, [selectedVariant, template.id, promptService, promptCustomizationService]); + const resetTemplate = React.useCallback(async () => { - promptCustomizationService.resetTemplate(template.id); - }, [promptCustomizationService, template]); - - return <> - {template.id} - - - ; + const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant; + promptCustomizationService.resetTemplate(templateId); + }, [selectedVariant, template.id, promptCustomizationService]); + + return ( +
+
+ {template.id} +
+
+ {variantIds.length > 1 && ( + <> + + + + )} + + +
+
+ ); }; diff --git a/packages/ai-core/src/browser/style/index.css b/packages/ai-core/src/browser/style/index.css index 36cdad9c19221..a42e41b29a9a2 100644 --- a/packages/ai-core/src/browser/style/index.css +++ b/packages/ai-core/src/browser/style/index.css @@ -14,14 +14,42 @@ margin-left: var(--theia-ui-padding); } +.theia-settings-container .settings-section-subcategory-title.ai-settings-section-subcategory-title { + padding-left: 0; +} + .ai-templates { - display: grid; - /** Display content in 3 columns */ - grid-template-columns: 1fr auto auto; - /** add a 3px gap between rows */ - row-gap: 3px; + display: flex; + flex-direction: column; + gap: 5px; +} + +.template-renderer { + display: flex; + flex-direction: column; + padding: 10px; +} + +.template-header { + margin-bottom: 8px; } +.template-controls { + display: flex; + align-items: center; + gap: 10px; +} + +.template-select-label { + margin-right: 5px; +} + +.template-variant-selector { + min-width: 120px; +} + + + #ai-variable-configuration-container-widget, #ai-agent-configuration-container-widget { margin-top: 5px; diff --git a/packages/ai-core/src/common/agent-service.ts b/packages/ai-core/src/common/agent-service.ts index 4b351fb93250d..4c8a4ac2e7bdf 100644 --- a/packages/ai-core/src/common/agent-service.ts +++ b/packages/ai-core/src/common/agent-service.ts @@ -99,7 +99,7 @@ export class AgentServiceImpl implements AgentService { registerAgent(agent: Agent): void { this._agents.push(agent); agent.promptTemplates.forEach( - template => this.promptService.storePrompt(template.id, template.template) + template => this.promptService.storePromptTemplate(template) ); this.onDidChangeAgentsEmitter.fire(); } diff --git a/packages/ai-core/src/common/prompt-service.spec.ts b/packages/ai-core/src/common/prompt-service.spec.ts index 106ef4fc85df0..00807e59db11a 100644 --- a/packages/ai-core/src/common/prompt-service.spec.ts +++ b/packages/ai-core/src/common/prompt-service.spec.ts @@ -37,10 +37,10 @@ describe('PromptService', () => { container.bind(AIVariableService).toConstantValue(variableService); promptService = container.get(PromptService); - promptService.storePrompt('1', 'Hello, {{name}}!'); - promptService.storePrompt('2', 'Goodbye, {{name}}!'); - promptService.storePrompt('3', 'Ciao, {{invalid}}!'); - promptService.storePrompt('8', 'Hello, {{{name}}}'); + promptService.storePromptTemplate({ id: '1', template: 'Hello, {{name}}!' }); + promptService.storePromptTemplate({ id: '2', template: 'Goodbye, {{name}}!' }); + promptService.storePromptTemplate({ id: '3', template: 'Ciao, {{invalid}}!' }); + promptService.storePromptTemplate({ id: '8', template: 'Hello, {{{name}}}' }); }); it('should initialize prompts from PromptCollectionService', () => { @@ -62,7 +62,7 @@ describe('PromptService', () => { }); it('should store a new prompt', () => { - promptService.storePrompt('3', 'Welcome, {{name}}!'); + promptService.storePromptTemplate({ id: '3', template: 'Welcome, {{name}}!' }); const newPrompt = promptService.getRawPrompt('3'); expect(newPrompt?.template).to.equal('Welcome, {{name}}!'); }); @@ -88,10 +88,10 @@ describe('PromptService', () => { }); it('should ignore whitespace in variables', async () => { - promptService.storePrompt('4', 'Hello, {{name }}!'); - promptService.storePrompt('5', 'Hello, {{ name}}!'); - promptService.storePrompt('6', 'Hello, {{ name }}!'); - promptService.storePrompt('7', 'Hello, {{ name }}!'); + promptService.storePromptTemplate({ id: '4', template: 'Hello, {{name }}!' }); + promptService.storePromptTemplate({ id: '5', template: 'Hello, {{ name}}!' }); + promptService.storePromptTemplate({ id: '6', template: 'Hello, {{ name }}!' }); + promptService.storePromptTemplate({ id: '7', template: 'Hello, {{ name }}!' }); for (let i = 4; i <= 7; i++) { const prompt = await promptService.getPrompt(`${i}`, { name: 'John' }); expect(prompt?.text).to.equal('Hello, John!'); @@ -109,10 +109,10 @@ describe('PromptService', () => { }); it('should ignore whitespace in variables (three bracket)', async () => { - promptService.storePrompt('9', 'Hello, {{{name }}}'); - promptService.storePrompt('10', 'Hello, {{{ name}}}'); - promptService.storePrompt('11', 'Hello, {{{ name }}}'); - promptService.storePrompt('12', 'Hello, {{{ name }}}'); + promptService.storePromptTemplate({ id: '9', template: 'Hello, {{{name }}}' }); + promptService.storePromptTemplate({ id: '10', template: 'Hello, {{{ name}}}' }); + promptService.storePromptTemplate({ id: '11', template: 'Hello, {{{ name }}}' }); + promptService.storePromptTemplate({ id: '12', template: 'Hello, {{{ name }}}' }); for (let i = 9; i <= 12; i++) { const prompt = await promptService.getPrompt(`${i}`, { name: 'John' }); expect(prompt?.text).to.equal('Hello, John'); @@ -120,26 +120,24 @@ describe('PromptService', () => { }); it('should ignore invalid prompts with unmatched brackets', async () => { - promptService.storePrompt('9', 'Hello, {{name'); - promptService.storePrompt('10', 'Hello, {{{name'); - promptService.storePrompt('11', 'Hello, name}}}}'); + promptService.storePromptTemplate({ id: '9', template: 'Hello, {{name' }); + promptService.storePromptTemplate({ id: '10', template: 'Hello, {{{name' }); + promptService.storePromptTemplate({ id: '11', template: 'Hello, name}}}}' }); const prompt1 = await promptService.getPrompt('9', { name: 'John' }); expect(prompt1?.text).to.equal('Hello, {{name'); // Not matching due to missing closing brackets - const prompt2 = await promptService.getPrompt('10', { name: 'John' }); expect(prompt2?.text).to.equal('Hello, {{{name'); // Matches pattern due to valid three-start-two-end brackets - const prompt3 = await promptService.getPrompt('11', { name: 'John' }); expect(prompt3?.text).to.equal('Hello, name}}}}'); // Extra closing bracket, does not match cleanly }); it('should handle a mixture of two and three brackets correctly', async () => { - promptService.storePrompt('12', 'Hi, {{name}}}'); // (invalid) - promptService.storePrompt('13', 'Hello, {{{name}}'); // (invalid) - promptService.storePrompt('14', 'Greetings, {{{name}}}}'); // (invalid) - promptService.storePrompt('15', 'Bye, {{{{name}}}'); // (invalid) - promptService.storePrompt('16', 'Ciao, {{{{name}}}}'); // (invalid) - promptService.storePrompt('17', 'Hi, {{name}}! {{{name}}}'); // Mixed valid patterns + promptService.storePromptTemplate({ id: '12', template: 'Hi, {{name}}}' }); // (invalid) + promptService.storePromptTemplate({ id: '13', template: 'Hello, {{{name}}' }); // (invalid) + promptService.storePromptTemplate({ id: '14', template: 'Greetings, {{{name}}}}' }); // (invalid) + promptService.storePromptTemplate({ id: '15', template: 'Bye, {{{{name}}}' }); // (invalid) + promptService.storePromptTemplate({ id: '16', template: 'Ciao, {{{{name}}}}' }); // (invalid) + promptService.storePromptTemplate({ id: '17', template: 'Hi, {{name}}! {{{name}}}' }); // Mixed valid patterns const prompt12 = await promptService.getPrompt('12', { name: 'John' }); expect(prompt12?.text).to.equal('Hi, {{name}}}'); @@ -161,87 +159,143 @@ describe('PromptService', () => { }); it('should strip single-line comments at the start of the template', () => { - promptService.storePrompt('comment-basic', '{{!-- Comment --}}Hello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-basic', template: '{{!-- Comment --}}Hello, {{name}}!' }); const prompt = promptService.getUnresolvedPrompt('comment-basic'); expect(prompt?.template).to.equal('Hello, {{name}}!'); }); it('should remove line break after first-line comment', () => { - promptService.storePrompt('comment-line-break', '{{!-- Comment --}}\nHello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-line-break', template: '{{!-- Comment --}}\nHello, {{name}}!' }); const prompt = promptService.getUnresolvedPrompt('comment-line-break'); expect(prompt?.template).to.equal('Hello, {{name}}!'); }); it('should strip multiline comments at the start of the template', () => { - promptService.storePrompt('comment-multiline', '{{!--\nMultiline comment\n--}}\nGoodbye, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-multiline', template: '{{!--\nMultiline comment\n--}}\nGoodbye, {{name}}!' }); const prompt = promptService.getUnresolvedPrompt('comment-multiline'); expect(prompt?.template).to.equal('Goodbye, {{name}}!'); }); it('should not strip comments not in the first line', () => { - promptService.storePrompt('comment-second-line', 'Hello, {{name}}!\n{{!-- Comment --}}'); + promptService.storePromptTemplate({ id: 'comment-second-line', template: 'Hello, {{name}}!\n{{!-- Comment --}}' }); const prompt = promptService.getUnresolvedPrompt('comment-second-line'); expect(prompt?.template).to.equal('Hello, {{name}}!\n{{!-- Comment --}}'); }); it('should treat unclosed comments as regular text', () => { - promptService.storePrompt('comment-unclosed', '{{!-- Unclosed comment'); + promptService.storePromptTemplate({ id: 'comment-unclosed', template: '{{!-- Unclosed comment' }); const prompt = promptService.getUnresolvedPrompt('comment-unclosed'); expect(prompt?.template).to.equal('{{!-- Unclosed comment'); }); it('should treat standalone closing delimiters as regular text', () => { - promptService.storePrompt('comment-standalone', '--}} Hello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-standalone', template: '--}} Hello, {{name}}!' }); const prompt = promptService.getUnresolvedPrompt('comment-standalone'); expect(prompt?.template).to.equal('--}} Hello, {{name}}!'); }); it('should handle nested comments and stop at the first closing tag', () => { - promptService.storePrompt('nested-comment', '{{!-- {{!-- Nested comment --}} --}}text'); + promptService.storePromptTemplate({ id: 'nested-comment', template: '{{!-- {{!-- Nested comment --}} --}}text' }); const prompt = promptService.getUnresolvedPrompt('nested-comment'); expect(prompt?.template).to.equal('--}}text'); }); it('should handle templates with only comments', () => { - promptService.storePrompt('comment-only', '{{!-- Only comments --}}'); + promptService.storePromptTemplate({ id: 'comment-only', template: '{{!-- Only comments --}}' }); const prompt = promptService.getUnresolvedPrompt('comment-only'); expect(prompt?.template).to.equal(''); }); it('should handle mixed delimiters on the same line', () => { - promptService.storePrompt('comment-mixed', '{{!-- Unclosed comment --}}'); + promptService.storePromptTemplate({ id: 'comment-mixed', template: '{{!-- Unclosed comment --}}' }); const prompt = promptService.getUnresolvedPrompt('comment-mixed'); expect(prompt?.template).to.equal(''); }); it('should resolve variables after stripping single-line comments', async () => { - promptService.storePrompt('comment-resolve', '{{!-- Comment --}}Hello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-resolve', template: '{{!-- Comment --}}Hello, {{name}}!' }); const prompt = await promptService.getPrompt('comment-resolve', { name: 'John' }); expect(prompt?.text).to.equal('Hello, John!'); }); it('should resolve variables in multiline templates with comments', async () => { - promptService.storePrompt('comment-multiline-vars', '{{!--\nMultiline comment\n--}}\nHello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-multiline-vars', template: '{{!--\nMultiline comment\n--}}\nHello, {{name}}!' }); const prompt = await promptService.getPrompt('comment-multiline-vars', { name: 'John' }); expect(prompt?.text).to.equal('Hello, John!'); }); it('should resolve variables with standalone closing delimiters', async () => { - promptService.storePrompt('comment-standalone-vars', '--}} Hello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-standalone-vars', template: '--}} Hello, {{name}}!' }); const prompt = await promptService.getPrompt('comment-standalone-vars', { name: 'John' }); expect(prompt?.text).to.equal('--}} Hello, John!'); }); it('should treat unclosed comments as text and resolve variables', async () => { - promptService.storePrompt('comment-unclosed-vars', '{{!-- Unclosed comment\nHello, {{name}}!'); + promptService.storePromptTemplate({ id: 'comment-unclosed-vars', template: '{{!-- Unclosed comment\nHello, {{name}}!' }); const prompt = await promptService.getPrompt('comment-unclosed-vars', { name: 'John' }); expect(prompt?.text).to.equal('{{!-- Unclosed comment\nHello, John!'); }); it('should handle templates with mixed comments and variables', async () => { - promptService.storePrompt('comment-mixed-vars', '{{!-- Comment --}}Hi, {{name}}! {{!-- Another comment --}}'); + promptService.storePromptTemplate({ id: 'comment-mixed-vars', template: '{{!-- Comment --}}Hi, {{name}}! {{!-- Another comment --}}' }); const prompt = await promptService.getPrompt('comment-mixed-vars', { name: 'John' }); expect(prompt?.text).to.equal('Hi, John! {{!-- Another comment --}}'); }); + it('should return all variant IDs of a given prompt', () => { + promptService.storePromptTemplate({ id: 'main', template: 'Main template' }); + + promptService.storePromptTemplate({ + id: 'variant1', + template: 'Variant 1', + variantOf: 'main' + }); + promptService.storePromptTemplate({ + id: 'variant2', + template: 'Variant 2', + variantOf: 'main' + }); + promptService.storePromptTemplate({ + id: 'variant3', + template: 'Variant 3', + variantOf: 'main' + }); + + const variantIds = promptService.getVariantIds('main'); + expect(variantIds).to.deep.equal(['variant1', 'variant2', 'variant3']); + }); + + it('should return an empty array if no variants exist for a given prompt', () => { + promptService.storePromptTemplate({ id: 'main', template: 'Main template' }); + + const variantIds = promptService.getVariantIds('main'); + expect(variantIds).to.deep.equal([]); + }); + + it('should return an empty array if the main prompt ID does not exist', () => { + const variantIds = promptService.getVariantIds('nonExistent'); + expect(variantIds).to.deep.equal([]); + }); + + it('should not influence prompts without variants when other prompts have variants', () => { + promptService.storePromptTemplate({ id: 'mainWithVariants', template: 'Main template with variants' }); + promptService.storePromptTemplate({ id: 'mainWithoutVariants', template: 'Main template without variants' }); + + promptService.storePromptTemplate({ + id: 'variant1', + template: 'Variant 1', + variantOf: 'mainWithVariants' + }); + promptService.storePromptTemplate({ + id: 'variant2', + template: 'Variant 2', + variantOf: 'mainWithVariants' + }); + + const variantsForMainWithVariants = promptService.getVariantIds('mainWithVariants'); + const variantsForMainWithoutVariants = promptService.getVariantIds('mainWithoutVariants'); + + expect(variantsForMainWithVariants).to.deep.equal(['variant1', 'variant2']); + expect(variantsForMainWithoutVariants).to.deep.equal([]); + }); }); diff --git a/packages/ai-core/src/common/prompt-service.ts b/packages/ai-core/src/common/prompt-service.ts index d4bc9aaea0ce5..aef16da8fdc50 100644 --- a/packages/ai-core/src/common/prompt-service.ts +++ b/packages/ai-core/src/common/prompt-service.ts @@ -21,10 +21,16 @@ import { ToolInvocationRegistry } from './tool-invocation-registry'; import { toolRequestToPromptText } from './language-model-util'; import { ToolRequest } from './language-model'; import { matchFunctionsRegEx, matchVariablesRegEx } from './prompt-service-util'; +import { AISettingsService } from './settings-service'; export interface PromptTemplate { id: string; template: string; + /** + * (Optional) The ID of the main template for which this template is a variant. + * If present, this indicates that the current template represents an alternative version of the specified main template. + */ + variantOf?: string; } export interface PromptMap { [id: string]: PromptTemplate } @@ -63,11 +69,10 @@ export interface PromptService { */ getPrompt(id: string, args?: { [key: string]: unknown }): Promise; /** - * Adds a prompt to the list of prompts. - * @param id the id of the prompt - * @param prompt the prompt template to store + * Adds a {@link PromptTemplate} to the list of prompts. + * @param promptTemplate the prompt template to store */ - storePrompt(id: string, prompt: string): void; + storePromptTemplate(promptTemplate: PromptTemplate): void; /** * Removes a prompt from the list of prompts. * @param id the id of the prompt @@ -77,6 +82,20 @@ export interface PromptService { * Return all known prompts as a {@link PromptMap map}. */ getAllPrompts(): PromptMap; + /** + * Retrieve all variant IDs of a given {@link PromptTemplate}. + * @param id the id of the main {@link PromptTemplate} + * @returns an array of string IDs representing the variants of the given template + */ + getVariantIds(id: string): string[]; + /** + * Retrieve the currently selected variant ID for a given main prompt ID. + * If a variant is selected for the main prompt, it will be returned. + * Otherwise, the main prompt ID will be returned. + * @param id the id of the main prompt + * @returns the variant ID if one is selected, or the main prompt ID otherwise + */ + getVariantId(id: string): Promise; } export interface CustomAgentDescription { @@ -163,6 +182,9 @@ export interface PromptCustomizationService { @injectable() export class PromptServiceImpl implements PromptService { + @inject(AISettingsService) @optional() + protected readonly settingsService: AISettingsService | undefined; + @inject(PromptCustomizationService) @optional() protected readonly customizationService: PromptCustomizationService | undefined; @@ -203,8 +225,22 @@ export class PromptServiceImpl implements PromptService { return commentRegex.test(template) ? template.replace(commentRegex, '').trimStart() : template; } + async getVariantId(id: string): Promise { + if (this.settingsService !== undefined) { + const agentSettingsMap = await this.settingsService.getSettings(); + + for (const agentSettings of Object.values(agentSettingsMap)) { + if (agentSettings.selectedVariants && agentSettings.selectedVariants[id]) { + return agentSettings.selectedVariants[id]; + } + } + } + return id; + } + async getPrompt(id: string, args?: { [key: string]: unknown }): Promise { - const prompt = this.getUnresolvedPrompt(id); + const variantId = await this.getVariantId(id); + const prompt = this.getUnresolvedPrompt(variantId); if (prompt === undefined) { return undefined; } @@ -274,10 +310,15 @@ export class PromptServiceImpl implements PromptService { return { ...this._prompts }; } } - storePrompt(id: string, prompt: string): void { - this._prompts[id] = { id, template: prompt }; - } removePrompt(id: string): void { delete this._prompts[id]; } + getVariantIds(id: string): string[] { + return Object.values(this._prompts) + .filter(prompt => prompt.variantOf === id) + .map(variant => variant.id); + } + storePromptTemplate(promptTemplate: PromptTemplate): void { + this._prompts[promptTemplate.id] = promptTemplate; + } } diff --git a/packages/ai-core/src/common/settings-service.ts b/packages/ai-core/src/common/settings-service.ts index 007daec366250..8610a36b92ea4 100644 --- a/packages/ai-core/src/common/settings-service.ts +++ b/packages/ai-core/src/common/settings-service.ts @@ -30,4 +30,9 @@ export type AISettings = Record; export interface AgentSettings { languageModelRequirements?: LanguageModelRequirement[]; enable?: boolean; + /** + * A mapping of main template IDs to their selected variant IDs. + * If a main template is not present in this mapping, it means the main template is used. + */ + selectedVariants?: Record; } From 4b9db2ab58115948bfd48dd524e538964ad36787 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Mon, 25 Nov 2024 09:16:52 +0100 Subject: [PATCH 14/75] Turn automatic inline completion off by default (#14513) fixed #14512 Signed-off-by: Jonas Helming --- .../src/browser/ai-code-completion-preference.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts index 55a22fe001c1d..2a6655958e63f 100644 --- a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts +++ b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts @@ -29,7 +29,7 @@ export const AICodeCompletionPreferencesSchema: PreferenceSchema = { description: 'Automatically trigger AI completions inline within any (Monaco) editor while editing.\ \n\ Alternatively, you can manually trigger the code via the command "Trigger Inline Suggestion" or the default shortcut "SHIFT+Space".', - default: true + default: false }, [PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS]: { title: 'Excluded File Extensions', From 7b2cde93427ed86ddd17e0f57a5fd5b64ce30dc7 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Mon, 25 Nov 2024 09:53:36 +0100 Subject: [PATCH 15/75] Allow canceling llama-file requests (#14515) fixed #14514 Signed-off-by: Jonas Helming --- .../ai-llamafile/src/common/llamafile-language-model.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ai-llamafile/src/common/llamafile-language-model.ts b/packages/ai-llamafile/src/common/llamafile-language-model.ts index 6f1b039013879..78cc443d37e4f 100644 --- a/packages/ai-llamafile/src/common/llamafile-language-model.ts +++ b/packages/ai-llamafile/src/common/llamafile-language-model.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { LanguageModel, LanguageModelRequest, LanguageModelResponse, LanguageModelStreamResponsePart } from '@theia/ai-core'; - +import { CancellationToken } from '@theia/core'; export class LlamafileLanguageModel implements LanguageModel { readonly providerId = 'llamafile'; @@ -28,7 +28,7 @@ export class LlamafileLanguageModel implements LanguageModel { return this.name; } - async request(request: LanguageModelRequest): Promise { + async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { try { let prompt = request.messages.map(message => { switch (message.actor) { @@ -70,6 +70,10 @@ export class LlamafileLanguageModel implements LanguageModel { [Symbol.asyncIterator](): AsyncIterator { return { async next(): Promise> { + if (cancellationToken?.isCancellationRequested) { + reader.cancel(); + return { value: undefined, done: true }; + } const { value, done } = await reader.read(); if (done) { return { value: undefined, done: true }; From 09dfb23caf0be3545dd539a2fa89afb9adcb0fd4 Mon Sep 17 00:00:00 2001 From: Matthew Khouzam Date: Mon, 25 Nov 2024 10:36:41 -0500 Subject: [PATCH 16/75] feat: offer 'disableStreaming' for OpenAI models (#14420) Custom OpenAI models can now be configured with 'disableStreaming: true' to indicate that streaming shall not be used. This is especially useful for models which do not support streaming at all. Co-authored-by: Matthew Khouzam Co-authored-by: Stefan Dirix --- .../openai-frontend-application-contribution.ts | 17 +++++++++++++---- .../ai-openai/src/browser/openai-preferences.ts | 6 ++++++ .../common/openai-language-models-manager.ts | 4 ++++ .../ai-openai/src/node/openai-language-model.ts | 10 ++++++---- .../node/openai-language-models-manager-impl.ts | 5 ++++- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts index b16f80aa0ef90..3f917c77ed1ee 100644 --- a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts +++ b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts @@ -63,8 +63,13 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio const newModels = createCustomModelDescriptionsFromPreferences(event.newValue); const modelsToRemove = oldModels.filter(model => !newModels.some(newModel => newModel.id === model.id)); - const modelsToAddOrUpdate = newModels.filter(newModel => !oldModels.some(model => - model.id === newModel.id && model.model === newModel.model && model.url === newModel.url && model.apiKey === newModel.apiKey)); + const modelsToAddOrUpdate = newModels.filter(newModel => + !oldModels.some(model => + model.id === newModel.id && + model.model === newModel.model && + model.url === newModel.url && + model.apiKey === newModel.apiKey && + model.enableStreaming === newModel.enableStreaming)); this.manager.removeLanguageModels(...modelsToRemove.map(model => model.id)); this.manager.createOrUpdateLanguageModels(...modelsToAddOrUpdate); @@ -74,11 +79,14 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio } } +const openAIModelsWithDisabledStreaming = ['o1-preview']; + function createOpenAIModelDescription(modelId: string): OpenAiModelDescription { return { id: `openai/${modelId}`, model: modelId, - apiKey: true + apiKey: true, + enableStreaming: !openAIModelsWithDisabledStreaming.includes(modelId) }; } @@ -93,7 +101,8 @@ function createCustomModelDescriptionsFromPreferences(preferences: Partial string | undefined, public url: string | undefined) { } + constructor(public readonly id: string, public model: string, public enableStreaming: boolean, public apiKey: () => string | undefined, public url: string | undefined) { } async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { const openai = this.initializeOpenAi(); @@ -152,8 +154,8 @@ export class OpenAiModel implements LanguageModel { }; } - protected isNonStreamingModel(model: string): boolean { - return ['o1-preview'].includes(model); + protected isNonStreamingModel(_model: string): boolean { + return !this.enableStreaming; } protected supportsStructuredOutput(): boolean { diff --git a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts index cfc81ba3b8adb..4ccf77b0cc9f9 100644 --- a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts +++ b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts @@ -57,9 +57,12 @@ export class OpenAiLanguageModelsManagerImpl implements OpenAiLanguageModelsMana } model.url = modelDescription.url; model.model = modelDescription.model; + model.enableStreaming = modelDescription.enableStreaming; model.apiKey = apiKeyProvider; } else { - this.languageModelRegistry.addLanguageModels([new OpenAiModel(modelDescription.id, modelDescription.model, apiKeyProvider, modelDescription.url)]); + this.languageModelRegistry.addLanguageModels([ + new OpenAiModel(modelDescription.id, modelDescription.model, modelDescription.enableStreaming, apiKeyProvider, modelDescription.url) + ]); } } } From 03d8a36e8e9cc43e92cc35a0e1b4c05bf5bba5a5 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Tue, 26 Nov 2024 12:48:15 +0100 Subject: [PATCH 17/75] feat(ai): agents can ask for input and continue (#14486) * Allow custom chat agents to stop completing the response conditionally * Introduce an orthogonal response state called `waitingForInput` * Introduce `show` setting on progress messages to control visibility * 'untilFirstContent': Disappears when first response content appears * 'whileIncomplete': Remains visible while response is incomplete * 'forever': Remains visible forever. * Adds a `QuestionResponseContent` and `QuestionPartRenderer` * Adds an API example agent 'AskAndContinue' that uses these features * Introduces agent-specific content matchers (in contrast to globals) * Adds redundant response completion & recording in `AbstractChatAgent` Co-authored-by: Stefan Dirix Contributed on behalf of STMicroelectronics. --- examples/api-samples/package.json | 2 + .../browser/api-samples-frontend-module.ts | 2 + ...sk-and-continue-chat-agent-contribution.ts | 188 ++++++++++++++++++ examples/api-samples/tsconfig.json | 6 + .../src/browser/ai-chat-ui-frontend-module.ts | 2 + .../question-part-renderer.tsx | 59 ++++++ .../chat-tree-view/chat-view-tree-widget.tsx | 26 ++- .../ai-chat-ui/src/browser/style/index.css | 29 ++- packages/ai-chat/src/common/chat-agents.ts | 42 ++-- .../ai-chat/src/common/chat-model-util.ts | 44 ++++ packages/ai-chat/src/common/chat-model.ts | 89 +++++++++ packages/ai-chat/src/common/index.ts | 1 + .../ai-chat/src/common/parse-contents.spec.ts | 30 +-- packages/ai-chat/src/common/parse-contents.ts | 9 +- .../src/common/response-content-matcher.ts | 7 +- 15 files changed, 493 insertions(+), 43 deletions(-) create mode 100644 examples/api-samples/src/browser/chat/ask-and-continue-chat-agent-contribution.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/question-part-renderer.tsx create mode 100644 packages/ai-chat/src/common/chat-model-util.ts diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 7ff6acaadb5b0..999ac34cd4407 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -4,6 +4,8 @@ "version": "1.55.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { + "@theia/ai-core": "1.55.0", + "@theia/ai-chat": "1.55.0", "@theia/ai-chat-ui": "1.55.0", "@theia/core": "1.55.0", "@theia/file-search": "1.55.0", diff --git a/examples/api-samples/src/browser/api-samples-frontend-module.ts b/examples/api-samples/src/browser/api-samples-frontend-module.ts index fc41efb1bce03..01ae0987bd7ec 100644 --- a/examples/api-samples/src/browser/api-samples-frontend-module.ts +++ b/examples/api-samples/src/browser/api-samples-frontend-module.ts @@ -31,6 +31,7 @@ import { bindSampleAppInfo } from './vsx/sample-frontend-app-info'; import { bindTestSample } from './test/sample-test-contribution'; import { bindSampleFileSystemCapabilitiesCommands } from './file-system/sample-file-system-capabilities'; import { bindChatNodeToolbarActionContribution } from './chat/chat-node-toolbar-action-contribution'; +import { bindAskAndContinueChatAgentContribution } from './chat/ask-and-continue-chat-agent-contribution'; export default new ContainerModule(( bind: interfaces.Bind, @@ -38,6 +39,7 @@ export default new ContainerModule(( isBound: interfaces.IsBound, rebind: interfaces.Rebind, ) => { + bindAskAndContinueChatAgentContribution(bind); bindChatNodeToolbarActionContribution(bind); bindDynamicLabelProvider(bind); bindSampleUnclosableView(bind); diff --git a/examples/api-samples/src/browser/chat/ask-and-continue-chat-agent-contribution.ts b/examples/api-samples/src/browser/chat/ask-and-continue-chat-agent-contribution.ts new file mode 100644 index 0000000000000..c571d22facaf2 --- /dev/null +++ b/examples/api-samples/src/browser/chat/ask-and-continue-chat-agent-contribution.ts @@ -0,0 +1,188 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + AbstractStreamParsingChatAgent, + ChatAgent, + ChatMessage, + ChatModel, + ChatRequestModelImpl, + lastProgressMessage, + QuestionResponseContentImpl, + SystemMessageDescription, + unansweredQuestions +} from '@theia/ai-chat'; +import { Agent, PromptTemplate } from '@theia/ai-core'; +import { injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; + +export function bindAskAndContinueChatAgentContribution(bind: interfaces.Bind): void { + bind(AskAndContinueChatAgent).toSelf().inSingletonScope(); + bind(Agent).toService(AskAndContinueChatAgent); + bind(ChatAgent).toService(AskAndContinueChatAgent); +} + +const systemPrompt: PromptTemplate = { + id: 'askAndContinue-system', + template: ` +You are an agent demonstrating on how to generate questions and continuing the conversation based on the user's answers. + +First answer the user's question or continue their story. +Then come up with an interesting question and 2-3 answers which will be presented to the user as multiple choice. + +Use the following format exactly to define the questions and answers. +Especially add the and tags around the JSON. + + +{ + "question": "YOUR QUESTION HERE", + "options": [ + { + "text": "OPTION 1" + }, + { + "text": "OPTION 2" + } + ] +} + + +Examples: + + +{ + "question": "What is the capital of France?", + "options": [ + { + "text": "Paris" + }, + { + "text": "Lyon" + } + ] +} + + + +{ + "question": "What does the fox say?", + "options": [ + { + "text": "Ring-ding-ding-ding-dingeringeding!" + }, + { + "text": "Wa-pa-pa-pa-pa-pa-pow!" + } + ] +} + + +The user will answer the question and you can continue the conversation. +Once they answered, the question will be replaced with a simple "Question/Answer" pair, for example + +Question: What does the fox say? +Answer: Ring-ding-ding-ding-dingeringeding! + +If the user did not answer the question, it will be marked with "No answer", for example + +Question: What is the capital of France? +No answer + +Do not generate such pairs yourself, instead treat them as a signal for a past question. +Do not ask further questions once the text contains 5 or more "Question/Answer" pairs. +` +}; + +/** + * This is a very simple example agent that asks questions and continues the conversation based on the user's answers. + */ +@injectable() +export class AskAndContinueChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent { + override id = 'AskAndContinue'; + readonly name = 'AskAndContinue'; + override defaultLanguageModelPurpose = 'chat'; + readonly description = 'This chat will ask questions related to the input and continues after that.'; + readonly variables = []; + readonly agentSpecificVariables = []; + readonly functions = []; + + @postConstruct() + addContentMatchers(): void { + this.contentMatchers.push({ + start: /^.*$/m, + end: /^<\/question>$/m, + contentFactory: (content: string, request: ChatRequestModelImpl) => { + const question = content.replace(/^\n|<\/question>$/g, ''); + const parsedQuestion = JSON.parse(question); + return new QuestionResponseContentImpl(parsedQuestion.question, parsedQuestion.options, request, selectedOption => { + this.handleAnswer(selectedOption, request); + }); + } + }); + } + + override languageModelRequirements = [ + { + purpose: 'chat', + identifier: 'openai/gpt-4o', + } + ]; + + readonly promptTemplates = [systemPrompt]; + + protected override async getSystemMessageDescription(): Promise { + const resolvedPrompt = await this.promptService.getPrompt(systemPrompt.id); + return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; + } + + protected override async onResponseComplete(request: ChatRequestModelImpl): Promise { + const unansweredQs = unansweredQuestions(request); + if (unansweredQs.length < 1) { + return super.onResponseComplete(request); + } + request.response.addProgressMessage({ content: 'Waiting for input...', show: 'whileIncomplete' }); + request.response.waitForInput(); + } + + protected handleAnswer(selectedOption: { text: string; value?: string; }, request: ChatRequestModelImpl): void { + const progressMessage = lastProgressMessage(request); + if (progressMessage) { + request.response.updateProgressMessage({ ...progressMessage, show: 'untilFirstContent', status: 'completed' }); + } + request.response.stopWaitingForInput(); + // We're reusing the original request here as a shortcut. In combination with the override of 'getMessages' we continue generating. + // In a real-world scenario, you would likely manually interact with an LLM here to generate and append the next response. + this.invoke(request); + } + + /** + * As the question/answer are handled within the same response, we add an additional user message at the end to indicate to + * the LLM to continue generating. + */ + protected override async getMessages(model: ChatModel): Promise { + const messages = await super.getMessages(model, true); + const requests = model.getRequests(); + if (!requests[requests.length - 1].response.isComplete && requests[requests.length - 1].response.response?.content.length > 0) { + return [...messages, + { + type: 'text', + actor: 'user', + query: 'Continue generating based on the user\'s answer or finish the conversation if 5 or more questions were already answered.' + }]; + } + return messages; + } +} + diff --git a/examples/api-samples/tsconfig.json b/examples/api-samples/tsconfig.json index 551c17de9f91b..000f5e8c524f3 100644 --- a/examples/api-samples/tsconfig.json +++ b/examples/api-samples/tsconfig.json @@ -12,9 +12,15 @@ { "path": "../../dev-packages/ovsx-client" }, + { + "path": "../../packages/ai-chat" + }, { "path": "../../packages/ai-chat-ui" }, + { + "path": "../../packages/ai-core" + }, { "path": "../../packages/core" }, diff --git a/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts b/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts index 285f2cadd42fc..b3c7e70f045f2 100644 --- a/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts +++ b/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts @@ -36,6 +36,7 @@ import { ChatViewLanguageContribution } from './chat-view-language-contribution' import { ChatViewWidget } from './chat-view-widget'; import { ChatViewWidgetToolbarContribution } from './chat-view-widget-toolbar-contribution'; import { EditorPreviewManager } from '@theia/editor-preview/lib/browser/editor-preview-manager'; +import { QuestionPartRenderer } from './chat-response-renderer/question-part-renderer'; export default new ContainerModule((bind, _unbind, _isBound, rebind) => { bindViewContribution(bind, AIChatContribution); @@ -66,6 +67,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(ChatResponsePartRenderer).to(CommandPartRenderer).inSingletonScope(); bind(ChatResponsePartRenderer).to(ToolCallPartRenderer).inSingletonScope(); bind(ChatResponsePartRenderer).to(ErrorPartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(QuestionPartRenderer).inSingletonScope(); [CommandContribution, MenuContribution].forEach(serviceIdentifier => bind(serviceIdentifier).to(ChatViewMenuContribution).inSingletonScope() ); diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/question-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/question-part-renderer.tsx new file mode 100644 index 0000000000000..58d65d3ebb725 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/question-part-renderer.tsx @@ -0,0 +1,59 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ChatResponseContent, QuestionResponseContent } from '@theia/ai-chat'; +import { injectable } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { ReactNode } from '@theia/core/shared/react'; +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { ResponseNode } from '../chat-tree-view'; + +@injectable() +export class QuestionPartRenderer + implements ChatResponsePartRenderer { + + canHandle(response: ChatResponseContent): number { + if (QuestionResponseContent.is(response)) { + return 10; + } + return -1; + } + + render(question: QuestionResponseContent, node: ResponseNode): ReactNode { + return ( +
+
{question.question}
+
+ { + question.options.map((option, index) => ( + + )) + } +
+
+ ); + } + +} diff --git a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx index 668eefef7496d..175eec8547c07 100644 --- a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx +++ b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx @@ -267,6 +267,7 @@ export class ChatViewTreeWidget extends TreeWidget { private renderAgent(node: RequestNode | ResponseNode): React.ReactNode { const inProgress = isResponseNode(node) && !node.response.isComplete && !node.response.isCanceled && !node.response.isError; + const waitingForInput = isResponseNode(node) && node.response.isWaitingForInput; const toolbarContributions = !inProgress ? this.chatNodeToolbarActionContributions.getContributions() .flatMap(c => c.getToolbarActions(node)) @@ -277,7 +278,8 @@ export class ChatViewTreeWidget extends TreeWidget {

{this.getAgentLabel(node)}

- {inProgress && Generating} + {inProgress && !waitingForInput && Generating} + {inProgress && waitingForInput && Waiting for input}
{!inProgress && toolbarContributions.length > 0 && @@ -340,12 +342,28 @@ export class ChatViewTreeWidget extends TreeWidget {
{!node.response.isComplete && node.response.response.content.length === 0 - && node.response.progressMessages.map((c, i) => - - )} + && node.response.progressMessages + .filter(c => c.show === 'untilFirstContent') + .map((c, i) => + + ) + } {node.response.response.content.map((c, i) =>
{this.getChatResponsePartRenderer(c, node)}
)} + {!node.response.isComplete + && node.response.progressMessages + .filter(c => c.show === 'whileIncomplete') + .map((c, i) => + + ) + } + {node.response.progressMessages + .filter(c => c.show === 'forever') + .map((c, i) => + + ) + }
); } diff --git a/packages/ai-chat-ui/src/browser/style/index.css b/packages/ai-chat-ui/src/browser/style/index.css index 4c86cfe272689..bd9dc56775816 100644 --- a/packages/ai-chat-ui/src/browser/style/index.css +++ b/packages/ai-chat-ui/src/browser/style/index.css @@ -231,7 +231,7 @@ div:last-child > .theia-ChatNode { display: flex; flex-direction: column; gap: 2px; - border: 1px solid var(--theia-input-border); + border: var(--theia-border-width) solid var(--theia-input-border); border-radius: 4px; } @@ -265,6 +265,33 @@ div:last-child > .theia-ChatNode { background-color: var(--theia-input-border); } +.theia-QuestionPartRenderer-root { + display: flex; + flex-direction: column; + gap: 8px; + border: var(--theia-border-width) solid + var(--theia-sideBarSectionHeader-border); + padding: 8px 12px 12px; + border-radius: 5px; + margin: 0 0 8px 0; +} +.theia-QuestionPartRenderer-options { + display: flex; + flex-wrap: wrap; + gap: 12px; +} +.theia-QuestionPartRenderer-option { + min-width: 100px; + flex: 1 1 auto; + margin: 0; +} +.theia-QuestionPartRenderer-option.selected:disabled:hover { + background-color: var(--theia-button-disabledBackground); +} +.theia-QuestionPartRenderer-option:disabled:not(.selected) { + background-color: var(--theia-button-secondaryBackground); +} + .theia-toolCall { font-weight: normal; color: var(--theia-descriptionForeground); diff --git a/packages/ai-chat/src/common/chat-agents.ts b/packages/ai-chat/src/common/chat-agents.ts index f5b9f1e735e99..542cbfc462c4b 100644 --- a/packages/ai-chat/src/common/chat-agents.ts +++ b/packages/ai-chat/src/common/chat-agents.ts @@ -144,7 +144,12 @@ export abstract class AbstractChatAgent { @postConstruct() init(): void { - this.contentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers); + this.initializeContentMatchers(); + } + + protected initializeContentMatchers(): void { + const contributedContentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers); + this.contentMatchers.push(...contributedContentMatchers); } async invoke(request: ChatRequestModelImpl): Promise { @@ -195,7 +200,7 @@ export abstract class AbstractChatAgent { cancellationToken.token ); await this.addContentsToResponse(languageModelResponse, request); - request.response.complete(); + await this.onResponseComplete(request); if (this.defaultLogging) { this.recordingService.recordResponse(ChatHistoryEntry.fromResponse(this.id, request)); } @@ -204,9 +209,10 @@ export abstract class AbstractChatAgent { } } - protected parseContents(text: string): ChatResponseContent[] { + protected parseContents(text: string, request: ChatRequestModelImpl): ChatResponseContent[] { return parseContents( text, + request, this.contentMatchers, this.defaultContentFactory?.create.bind(this.defaultContentFactory) ); @@ -290,6 +296,16 @@ export abstract class AbstractChatAgent { return undefined; } + /** + * Invoked after the response by the LLM completed successfully. + * + * The default implementation sets the state of the response to `complete`. + * Subclasses may override this method to perform additional actions or keep the response open for processing further requests. + */ + protected async onResponseComplete(request: ChatRequestModelImpl): Promise { + return request.response.complete(); + } + protected abstract addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise; } @@ -313,20 +329,12 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { protected override async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise { if (isLanguageModelTextResponse(languageModelResponse)) { - const contents = this.parseContents(languageModelResponse.text); + const contents = this.parseContents(languageModelResponse.text, request); request.response.response.addContents(contents); - request.response.complete(); - if (this.defaultLogging) { - this.recordingService.recordResponse(ChatHistoryEntry.fromResponse(this.id, request)); - } return; } if (isLanguageModelStreamResponse(languageModelResponse)) { await this.addStreamResponse(languageModelResponse, request); - request.response.complete(); - if (this.defaultLogging) { - this.recordingService.recordResponse(ChatHistoryEntry.fromResponse(this.id, request)); - } return; } this.logger.error( @@ -341,7 +349,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { protected async addStreamResponse(languageModelResponse: LanguageModelStreamResponse, request: ChatRequestModelImpl): Promise { for await (const token of languageModelResponse.stream) { - const newContents = this.parse(token, request.response.response.content); + const newContents = this.parse(token, request); if (isArray(newContents)) { request.response.response.addContents(newContents); } else { @@ -357,7 +365,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { return; } - const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text) : []; + const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text, request) : []; if (result.length > 0) { request.response.response.addContents(result); } else { @@ -366,11 +374,11 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { } } - protected parse(token: LanguageModelStreamResponsePart, previousContent: ChatResponseContent[]): ChatResponseContent | ChatResponseContent[] { + protected parse(token: LanguageModelStreamResponsePart, request: ChatRequestModelImpl): ChatResponseContent | ChatResponseContent[] { const content = token.content; // eslint-disable-next-line no-null/no-null if (content !== undefined && content !== null) { - return this.defaultContentFactory.create(content); + return this.defaultContentFactory.create(content, request); } const toolCalls = token.tool_calls; if (toolCalls !== undefined) { @@ -378,7 +386,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { new ToolCallChatResponseContentImpl(toolCall.id, toolCall.function?.name, toolCall.function?.arguments, toolCall.finished, toolCall.result)); return toolCallContents; } - return this.defaultContentFactory.create(''); + return this.defaultContentFactory.create('', request); } } diff --git a/packages/ai-chat/src/common/chat-model-util.ts b/packages/ai-chat/src/common/chat-model-util.ts new file mode 100644 index 0000000000000..1bad8b6bad0c6 --- /dev/null +++ b/packages/ai-chat/src/common/chat-model-util.ts @@ -0,0 +1,44 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ChatProgressMessage, ChatRequestModel, ChatResponse, ChatResponseContent, ChatResponseModel, QuestionResponseContent } from './chat-model'; + +export function lastResponseContent(request: ChatRequestModel): ChatResponseContent | undefined { + return lastContentOfResponse(request.response?.response); +} + +export function lastContentOfResponse(response: ChatResponse | undefined): ChatResponseContent | undefined { + const content = response?.content; + return content && content.length > 0 ? content[content.length - 1] : undefined; +} + +export function lastProgressMessage(request: ChatRequestModel): ChatProgressMessage | undefined { + return lastProgressMessageOfResponse(request.response); +} + +export function lastProgressMessageOfResponse(response: ChatResponseModel | undefined): ChatProgressMessage | undefined { + const progressMessages = response?.progressMessages; + return progressMessages && progressMessages.length > 0 ? progressMessages[progressMessages.length - 1] : undefined; +} + +export function unansweredQuestions(request: ChatRequestModel): QuestionResponseContent[] { + const response = request.response; + return unansweredQuestionsOfResponse(response); +} + +function unansweredQuestionsOfResponse(response: ChatResponseModel | undefined): QuestionResponseContent[] { + if (!response || !response.response) { return []; } + return response.response.content.filter((c): c is QuestionResponseContent => QuestionResponseContent.is(c) && c.selectedOption === undefined); +} diff --git a/packages/ai-chat/src/common/chat-model.ts b/packages/ai-chat/src/common/chat-model.ts index 0decfb284da35..9c6cd66af14b4 100644 --- a/packages/ai-chat/src/common/chat-model.ts +++ b/packages/ai-chat/src/common/chat-model.ts @@ -80,6 +80,7 @@ export interface ChatProgressMessage { kind: 'progressMessage'; id: string; status: 'inProgress' | 'completed' | 'failed'; + show: 'untilFirstContent' | 'whileIncomplete' | 'forever'; content: string; } @@ -279,6 +280,42 @@ export namespace ErrorChatResponseContent { } } +export type QuestionResponseHandler = ( + selectedOption: { text: string, value?: string }, +) => void; + +export interface QuestionResponseContent extends ChatResponseContent { + kind: 'question'; + question: string; + options: { text: string, value?: string }[]; + selectedOption?: { text: string, value?: string }; + handler: QuestionResponseHandler; + request: ChatRequestModelImpl; +} + +export namespace QuestionResponseContent { + export function is(obj: unknown): obj is QuestionResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'question' && + 'question' in obj && + typeof (obj as { question: unknown }).question === 'string' && + 'options' in obj && + Array.isArray((obj as { options: unknown }).options) && + (obj as { options: unknown[] }).options.every(option => + typeof option === 'object' && + option && 'text' in option && + typeof (option as { text: unknown }).text === 'string' && + ('value' in option ? typeof (option as { value: unknown }).value === 'string' || typeof (option as { value: unknown }).value === 'undefined' : true) + ) && + 'handler' in obj && + typeof (obj as { handler: unknown }).handler === 'function' && + 'request' in obj && + obj.request instanceof ChatRequestModelImpl + ); + } +} + export interface ChatResponse { readonly content: ChatResponseContent[]; asString(): string; @@ -292,6 +329,7 @@ export interface ChatResponseModel { readonly response: ChatResponse; readonly isComplete: boolean; readonly isCanceled: boolean; + readonly isWaitingForInput: boolean; readonly isError: boolean; readonly agentId?: string readonly errorObject?: Error; @@ -602,6 +640,31 @@ export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayout } } +/** + * Default implementation for the QuestionResponseContent. + */ +export class QuestionResponseContentImpl implements QuestionResponseContent { + readonly kind = 'question'; + protected _selectedOption: { text: string; value?: string } | undefined; + constructor(public question: string, public options: { text: string, value?: string }[], + public request: ChatRequestModelImpl, public handler: QuestionResponseHandler) { + } + set selectedOption(option: { text: string; value?: string; } | undefined) { + this._selectedOption = option; + this.request.response.response.responseContentChanged(); + } + get selectedOption(): { text: string; value?: string; } | undefined { + return this._selectedOption; + } + asString?(): string | undefined { + return `Question: ${this.question} +${this.selectedOption ? `Answer: ${this.selectedOption?.text}` : 'No answer'}`; + } + merge?(): boolean { + return false; + } +} + class ChatResponseImpl implements ChatResponse { protected readonly _onDidChangeEmitter = new Emitter(); onDidChange: Event = this._onDidChangeEmitter.event; @@ -654,6 +717,11 @@ class ChatResponseImpl implements ChatResponse { this._updateResponseRepresentation(); } + responseContentChanged(): void { + this._updateResponseRepresentation(); + this._onDidChangeEmitter.fire(); + } + protected _updateResponseRepresentation(): void { this._responseRepresentation = this._content .map(responseContent => { @@ -688,6 +756,7 @@ class ChatResponseModelImpl implements ChatResponseModel { protected _response: ChatResponseImpl; protected _isComplete: boolean; protected _isCanceled: boolean; + protected _isWaitingForInput: boolean; protected _agentId?: string; protected _isError: boolean; protected _errorObject: Error | undefined; @@ -702,6 +771,7 @@ class ChatResponseModelImpl implements ChatResponseModel { this._response = response; this._isComplete = false; this._isCanceled = false; + this._isWaitingForInput = false; this._agentId = agentId; } @@ -728,6 +798,7 @@ class ChatResponseModelImpl implements ChatResponseModel { kind: 'progressMessage', id, status: message.status ?? 'inProgress', + show: message.show ?? 'untilFirstContent', ...message, }; this._progressMessages.push(newMessage); @@ -759,6 +830,10 @@ class ChatResponseModelImpl implements ChatResponseModel { return this._isCanceled; } + get isWaitingForInput(): boolean { + return this._isWaitingForInput; + } + get agentId(): string | undefined { return this._agentId; } @@ -769,17 +844,31 @@ class ChatResponseModelImpl implements ChatResponseModel { complete(): void { this._isComplete = true; + this._isWaitingForInput = false; this._onDidChangeEmitter.fire(); } cancel(): void { this._isComplete = true; this._isCanceled = true; + this._isWaitingForInput = false; this._onDidChangeEmitter.fire(); } + + waitForInput(): void { + this._isWaitingForInput = true; + this._onDidChangeEmitter.fire(); + } + + stopWaitingForInput(): void { + this._isWaitingForInput = false; + this._onDidChangeEmitter.fire(); + } + error(error: Error): void { this._isComplete = true; this._isCanceled = false; + this._isWaitingForInput = false; this._isError = true; this._errorObject = error; this._onDidChangeEmitter.fire(); diff --git a/packages/ai-chat/src/common/index.ts b/packages/ai-chat/src/common/index.ts index cf160ddcadf10..b0100cff31203 100644 --- a/packages/ai-chat/src/common/index.ts +++ b/packages/ai-chat/src/common/index.ts @@ -16,6 +16,7 @@ export * from './chat-agents'; export * from './chat-agent-service'; export * from './chat-model'; +export * from './chat-model-util'; export * from './chat-request-parser'; export * from './chat-service'; export * from './command-chat-agents'; diff --git a/packages/ai-chat/src/common/parse-contents.spec.ts b/packages/ai-chat/src/common/parse-contents.spec.ts index c0a009f8cb814..cba9fa1b598e6 100644 --- a/packages/ai-chat/src/common/parse-contents.spec.ts +++ b/packages/ai-chat/src/common/parse-contents.spec.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { expect } from 'chai'; -import { ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model'; +import { ChatRequestModelImpl, ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model'; import { parseContents } from './parse-contents'; import { CodeContentMatcher, ResponseContentMatcher } from './response-content-matcher'; @@ -33,22 +33,24 @@ export const CommandContentMatcher: ResponseContentMatcher = { } }; +const fakeRequest = {} as ChatRequestModelImpl; + describe('parseContents', () => { it('should parse code content', () => { const text = '```typescript\nconsole.log("Hello World");\n```'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript')]); }); it('should parse markdown content', () => { const text = 'Hello **World**'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Hello **World**')]); }); it('should parse multiple content blocks', () => { const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), new MarkdownChatResponseContentImpl('\nHello **World**') @@ -57,7 +59,7 @@ describe('parseContents', () => { it('should parse multiple content blocks with different languages', () => { const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")\n```'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), new CodeChatResponseContentImpl('print("Hello World")', 'python') @@ -66,7 +68,7 @@ describe('parseContents', () => { it('should parse multiple content blocks with different languages and markdown', () => { const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**\n```python\nprint("Hello World")\n```'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), new MarkdownChatResponseContentImpl('\nHello **World**\n'), @@ -76,7 +78,7 @@ describe('parseContents', () => { it('should parse content blocks with empty content', () => { const text = '```typescript\n```\nHello **World**\n```python\nprint("Hello World")\n```'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new CodeChatResponseContentImpl('', 'typescript'), new MarkdownChatResponseContentImpl('\nHello **World**\n'), @@ -86,7 +88,7 @@ describe('parseContents', () => { it('should parse content with markdown, code, and markdown', () => { const text = 'Hello **World**\n```typescript\nconsole.log("Hello World");\n```\nGoodbye **World**'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new MarkdownChatResponseContentImpl('Hello **World**\n'), new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), @@ -96,25 +98,25 @@ describe('parseContents', () => { it('should handle text with no special content', () => { const text = 'Just some plain text.'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Just some plain text.')]); }); it('should handle text with only start code block', () => { const text = '```typescript\nconsole.log("Hello World");'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('```typescript\nconsole.log("Hello World");')]); }); it('should handle text with only end code block', () => { const text = 'console.log("Hello World");\n```'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('console.log("Hello World");\n```')]); }); it('should handle text with unmatched code block', () => { const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), new MarkdownChatResponseContentImpl('\n```python\nprint("Hello World")') @@ -123,7 +125,7 @@ describe('parseContents', () => { it('should parse code block without newline after language', () => { const text = '```typescript console.log("Hello World");```'; - const result = parseContents(text); + const result = parseContents(text, fakeRequest); expect(result).to.deep.equal([ new MarkdownChatResponseContentImpl('```typescript console.log("Hello World");```') ]); @@ -131,7 +133,7 @@ describe('parseContents', () => { it('should parse with matches of multiple different matchers and default', () => { const text = '\nMY_SPECIAL_COMMAND\n\nHello **World**\n```python\nprint("Hello World")\n```\n\nMY_SPECIAL_COMMAND2\n'; - const result = parseContents(text, [CodeContentMatcher, CommandContentMatcher]); + const result = parseContents(text, fakeRequest, [CodeContentMatcher, CommandContentMatcher]); expect(result).to.deep.equal([ new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND'), new MarkdownChatResponseContentImpl('\nHello **World**\n'), diff --git a/packages/ai-chat/src/common/parse-contents.ts b/packages/ai-chat/src/common/parse-contents.ts index 16f405495ce20..1dd1afbbe1ee8 100644 --- a/packages/ai-chat/src/common/parse-contents.ts +++ b/packages/ai-chat/src/common/parse-contents.ts @@ -13,7 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 */ -import { ChatResponseContent } from './chat-model'; +import { ChatRequestModelImpl, ChatResponseContent } from './chat-model'; import { CodeContentMatcher, MarkdownContentFactory, ResponseContentFactory, ResponseContentMatcher } from './response-content-matcher'; interface Match { @@ -24,6 +24,7 @@ interface Match { export function parseContents( text: string, + request: ChatRequestModelImpl, contentMatchers: ResponseContentMatcher[] = [CodeContentMatcher], defaultContentFactory: ResponseContentFactory = MarkdownContentFactory ): ChatResponseContent[] { @@ -36,7 +37,7 @@ export function parseContents( if (!match) { // Add the remaining text as default content if (remainingText.length > 0) { - result.push(defaultContentFactory(remainingText)); + result.push(defaultContentFactory(remainingText, request)); } break; } @@ -45,11 +46,11 @@ export function parseContents( if (match.index > 0) { const precedingContent = remainingText.substring(0, match.index); if (precedingContent.trim().length > 0) { - result.push(defaultContentFactory(precedingContent)); + result.push(defaultContentFactory(precedingContent, request)); } } // 2. Add the matched content object - result.push(match.matcher.contentFactory(match.content)); + result.push(match.matcher.contentFactory(match.content, request)); // Update currentIndex to the end of the end of the match // And continue with the search after the end of the match currentIndex += match.index + match.content.length; diff --git a/packages/ai-chat/src/common/response-content-matcher.ts b/packages/ai-chat/src/common/response-content-matcher.ts index 3fb785e603c5f..86aa7e83316cb 100644 --- a/packages/ai-chat/src/common/response-content-matcher.ts +++ b/packages/ai-chat/src/common/response-content-matcher.ts @@ -14,13 +14,14 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 */ import { + ChatRequestModelImpl, ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model'; import { injectable } from '@theia/core/shared/inversify'; -export type ResponseContentFactory = (content: string) => ChatResponseContent; +export type ResponseContentFactory = (content: string, request: ChatRequestModelImpl) => ChatResponseContent; export const MarkdownContentFactory: ResponseContentFactory = (content: string) => new MarkdownChatResponseContentImpl(content); @@ -33,8 +34,8 @@ export const MarkdownContentFactory: ResponseContentFactory = (content: string) */ @injectable() export class DefaultResponseContentFactory { - create(content: string): ChatResponseContent { - return MarkdownContentFactory(content); + create(content: string, request: ChatRequestModelImpl): ChatResponseContent { + return MarkdownContentFactory(content, request); } } From eb9a7e4dfc6225a8ce6947a8717eb65903c44f0f Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 26 Nov 2024 14:14:50 +0100 Subject: [PATCH 18/75] fix: save current background color if not custom (#14491) fix: #14428 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 2 +- packages/core/src/electron-main/electron-main-application.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d93f7c5e7faf..cdaa35128528f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ [Breaking Changes:](#breaking_changes_1.56.0) - [core] Do not dispose dialogs on close - [#14456](https://github.com/eclipse-theia/theia/pull/14456) - Contributed on behalf of STMicroelectronics - +- [core] fixed electron win background color [#14491](https://github.com/eclipse-theia/theia/pull/14491) - Contributed on behalf of STMicroelectronics - [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics - [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics - [plugin] supported MappedEditProviders proposed API evolution [#14453](https://github.com/eclipse-theia/theia/pull/14453) - Contributed on behalf of STMicroelectronics diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index b464c916a84d9..975350414b7ad 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -272,6 +272,7 @@ export class ElectronMainApplication { } setBackgroundColor(webContents: WebContents, backgroundColor: string): void { + BrowserWindow.fromWebContents(webContents)?.setBackgroundColor(backgroundColor); this.customBackgroundColor = backgroundColor; this.saveState(webContents); } @@ -628,7 +629,7 @@ export class ElectronMainApplication { y: bounds.y, frame: this.useNativeWindowFrame, screenLayout: this.getCurrentScreenLayout(), - backgroundColor: this.customBackgroundColor + backgroundColor: this.customBackgroundColor ?? electronWindow.getBackgroundColor() }; this.electronStore.set('windowstate', options); } catch (e) { From 55e29ed4261ff1d1668bc87da9c9bcb8542e2aab Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Tue, 26 Nov 2024 15:48:44 +0100 Subject: [PATCH 19/75] Fix request settings and stop words in HF provider (#14504) fixed #14503 Signed-off-by: Jonas Helming --- .../src/node/huggingface-language-model.ts | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/packages/ai-hugging-face/src/node/huggingface-language-model.ts b/packages/ai-hugging-face/src/node/huggingface-language-model.ts index e54e463f10b74..d5b424aaadf4a 100644 --- a/packages/ai-hugging-face/src/node/huggingface-language-model.ts +++ b/packages/ai-hugging-face/src/node/huggingface-language-model.ts @@ -67,50 +67,75 @@ export class HuggingFaceModel implements LanguageModel { } } + protected getDefaultSettings(): Record { + return { + max_new_tokens: 2024, + stop: ['<|endoftext|>', ''] + }; + } + protected async handleNonStreamingRequest(hfInference: HfInference, request: LanguageModelRequest): Promise { + const settings = request.settings || this.getDefaultSettings(); + const response = await hfInference.textGeneration({ model: this.model, inputs: toHuggingFacePrompt(request.messages), parameters: { - temperature: 0.1, // Controls randomness, 0.1 for consistent outputs - max_new_tokens: 200, // Limits response length - return_full_text: false, // Ensures only the generated part is returned, not the prompt - do_sample: true, // Enables sampling for more varied responses - stop: ['<|endoftext|>'] // Stop generation at this token + ...settings } }); - const cleanText = response.generated_text.replace(/<\|endoftext\|>/g, ''); + const stopWords = Array.isArray(settings.stop) ? settings.stop : []; + let cleanText = response.generated_text; + + stopWords.forEach(stopWord => { + if (cleanText.endsWith(stopWord)) { + cleanText = cleanText.slice(0, -stopWord.length).trim(); + } + }); return { text: cleanText }; } - protected async handleStreamingRequest(hfInference: HfInference, request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { + protected async handleStreamingRequest( + hfInference: HfInference, + request: LanguageModelRequest, + cancellationToken?: CancellationToken + ): Promise { + const settings = request.settings || this.getDefaultSettings(); + const stream = hfInference.textGenerationStream({ model: this.model, inputs: toHuggingFacePrompt(request.messages), parameters: { - temperature: 0.1, - max_new_tokens: 200, - return_full_text: false, - do_sample: true, - stop: ['<|endoftext|>'] + ...settings } }); + const stopWords = Array.isArray(settings.stop) ? settings.stop : []; + const asyncIterator = { async *[Symbol.asyncIterator](): AsyncIterator { for await (const chunk of stream) { - const content = chunk.token.text.replace(/<\|endoftext\|>/g, ''); + let content = chunk.token.text; + + stopWords.forEach(stopWord => { + if (content.endsWith(stopWord)) { + content = content.slice(0, -stopWord.length).trim(); + } + }); + yield { content }; + if (cancellationToken?.isCancellationRequested) { break; } } } }; + return { stream: asyncIterator }; } From a6e788e73a74dd34baf4a8fbb62304c5faefc547 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 27 Nov 2024 09:01:27 +0100 Subject: [PATCH 20/75] Show arguments in tool call response renderer (#14424) * Show arguments in tool call response renderer fixed #14423 Signed-off-by: Jonas Helming --- .../toolcall-part-renderer.tsx | 47 +++++++++++++++---- .../ai-chat-ui/src/browser/style/index.css | 15 ++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx index 4f5bc127f285b..a4915819bdf59 100644 --- a/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx @@ -29,17 +29,46 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer - {response.finished ? -
- Ran {response.name} -
{this.tryPrettyPrintJson(response)}
-
- : Running [{response.name}] - } - ; + return ( +

+ {response.finished ? ( +
+ Ran {response.name} + ({this.renderCollapsibleArguments(response.arguments)}) + +
{this.tryPrettyPrintJson(response)}
+
+ ) : ( + + Running {response.name}({this.renderCollapsibleArguments(response.arguments)}) + + )} +

+ ); + } + protected renderCollapsibleArguments(args: string | undefined): ReactNode { + if (!args || !args.trim() || args.trim() === '{}') { + return undefined; + } + + return ( +
+ ... + {this.prettyPrintArgs(args)} +
+ ); + } + + private prettyPrintArgs(args: string): string { + try { + return JSON.stringify(JSON.parse(args), undefined, 2); + } catch (e) { + // fall through + return args; + } } private tryPrettyPrintJson(response: ToolCallChatResponseContent): string | undefined { diff --git a/packages/ai-chat-ui/src/browser/style/index.css b/packages/ai-chat-ui/src/browser/style/index.css index bd9dc56775816..2236f17e11580 100644 --- a/packages/ai-chat-ui/src/browser/style/index.css +++ b/packages/ai-chat-ui/src/browser/style/index.css @@ -316,6 +316,21 @@ div:last-child > .theia-ChatNode { overflow: auto; } +.collapsible-arguments { + display: inline-block; +} + +.collapsible-arguments .collapsible-arguments-summary { + display: inline-block; + white-space: nowrap; + text-decoration: underline; +} + +details[open].collapsible-arguments, +details[open].collapsible-arguments .collapsible-arguments-summary { + display: unset; +} + .theia-ResponseNode-ProgressMessage { font-weight: normal; color: var(--theia-descriptionForeground); From 3f2f6724b583b363c0a25e0baf3fd5e898c6bdc6 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Wed, 27 Nov 2024 11:00:00 +0100 Subject: [PATCH 21/75] feat(ai): add hovers for agents and variables (#14498) * Highlights the parsed chat request agent and variable parts * Adds hover information for the agent and variable parts * Adds hover information for the agent label in response headers * Add comments explaining the need for string replacements --- .../markdown-part-renderer.tsx | 8 +- .../chat-tree-view/chat-view-tree-widget.tsx | 122 ++++++++++++++++-- .../ai-chat-ui/src/browser/style/index.css | 14 ++ 3 files changed, 132 insertions(+), 12 deletions(-) diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx index 8fba51ad2fcbd..ca41ac0fde417 100644 --- a/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx @@ -61,21 +61,23 @@ const MarkdownRender = ({ response }: { response: MarkdownChatResponseContent | /** * This hook uses markdown-it directly to render markdown. * The reason to use markdown-it directly is that the MarkdownRenderer is - * overriden by theia with a monaco version. This monaco version strips all html + * overridden by theia with a monaco version. This monaco version strips all html * tags from the markdown with empty content. * This leads to unexpected behavior when rendering markdown with html tags. * * @param markdown the string to render as markdown + * @param skipSurroundingParagraph whether to remove a surrounding paragraph element (default: false) * @returns the ref to use in an element to render the markdown */ -export const useMarkdownRendering = (markdown: string | MarkdownString) => { +export const useMarkdownRendering = (markdown: string | MarkdownString, skipSurroundingParagraph: boolean = false) => { // eslint-disable-next-line no-null/no-null const ref = useRef(null); const markdownString = typeof markdown === 'string' ? markdown : markdown.value; useEffect(() => { const markdownIt = markdownit(); const host = document.createElement('div'); - const html = markdownIt.render(markdownString); + // markdownIt always puts the content in a paragraph element, so we remove it if we don't want it + const html = skipSurroundingParagraph ? markdownIt.render(markdownString).replace(/^

|<\/p>|

<\/p>$/g, '') : markdownIt.render(markdownString); host.innerHTML = DOMPurify.sanitize(html, { ALLOW_UNKNOWN_PROTOCOLS: true // DOMPurify usually strips non http(s) links from hrefs }); diff --git a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx index 175eec8547c07..280be41ad590a 100644 --- a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx +++ b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx @@ -14,12 +14,15 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { + ChatAgent, ChatAgentService, ChatModel, ChatProgressMessage, ChatRequestModel, ChatResponseContent, ChatResponseModel, + ParsedChatRequestAgentPart, + ParsedChatRequestVariablePart, } from '@theia/ai-chat'; import { CommandRegistry, ContributionProvider } from '@theia/core'; import { @@ -27,6 +30,7 @@ import { CommonCommands, CompositeTreeNode, ContextMenuRenderer, + HoverService, Key, KeyCode, NodeProps, @@ -46,6 +50,7 @@ import * as React from '@theia/core/shared/react'; import { ChatNodeToolbarActionContribution } from '../chat-node-toolbar-action-contribution'; import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; import { useMarkdownRendering } from '../chat-response-renderer/markdown-part-renderer'; +import { AIVariableService } from '@theia/ai-core'; // TODO Instead of directly operating on the ChatRequestModel we could use an intermediate view model export interface RequestNode extends TreeNode { @@ -77,9 +82,15 @@ export class ChatViewTreeWidget extends TreeWidget { @inject(ChatAgentService) protected chatAgentService: ChatAgentService; + @inject(AIVariableService) + protected readonly variableService: AIVariableService; + @inject(CommandRegistry) private commandRegistry: CommandRegistry; + @inject(HoverService) + private hoverService: HoverService; + protected _shouldScrollToEnd = true; protected isEnabled = false; @@ -274,11 +285,25 @@ export class ChatViewTreeWidget extends TreeWidget { .filter(action => this.commandRegistry.isEnabled(action.commandId, node)) .sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0)) : []; + const agentLabel = React.createRef(); + const agentDescription = this.getAgent(node)?.description; return

-

{this.getAgentLabel(node)}

- {inProgress && !waitingForInput && Generating} +

{ + if (agentDescription) { + this.hoverService.requestHover({ + content: agentDescription, + target: agentLabel.current!, + position: 'right' + }); + } + }}> + {this.getAgentLabel(node)} +

+ {inProgress && Generating} {inProgress && waitingForInput && Waiting for input}
{!inProgress && @@ -311,8 +336,14 @@ export class ChatViewTreeWidget extends TreeWidget { // TODO find user name return 'You'; } - const agent = node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined; - return agent?.name ?? 'AI'; + return this.getAgent(node)?.name ?? 'AI'; + } + + private getAgent(node: RequestNode | ResponseNode): ChatAgent | undefined { + if (isRequestNode(node)) { + return undefined; + } + return node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined; } private getAgentIconClassName(node: RequestNode | ResponseNode): string | undefined { @@ -334,7 +365,12 @@ export class ChatViewTreeWidget extends TreeWidget { } private renderChatRequest(node: RequestNode): React.ReactNode { - return ; + return ; } private renderChatResponse(node: ResponseNode): React.ReactNode { @@ -394,11 +430,79 @@ export class ChatViewTreeWidget extends TreeWidget { } } -const ChatRequestRender = ({ node }: { node: RequestNode }) => { - const text = node.request.request.displayText ?? node.request.request.text; - const ref = useMarkdownRendering(text); +const ChatRequestRender = ( + { + node, hoverService, chatAgentService, variableService + }: { + node: RequestNode, + hoverService: HoverService, + chatAgentService: ChatAgentService, + variableService: AIVariableService + }) => { + const parts = node.request.message.parts; + return ( +
+

+ {parts.map((part, index) => { + if (part instanceof ParsedChatRequestAgentPart || part instanceof ParsedChatRequestVariablePart) { + let description = undefined; + let className = ''; + if (part instanceof ParsedChatRequestAgentPart) { + description = chatAgentService.getAgent(part.agentId)?.description; + className = 'theia-RequestNode-AgentLabel'; + } else if (part instanceof ParsedChatRequestVariablePart) { + description = variableService.getVariable(part.variableName)?.description; + className = 'theia-RequestNode-VariableLabel'; + } + return ( + + ); + } else { + // maintain the leading and trailing spaces with explicit ` `, otherwise they would get trimmed by the markdown renderer + const ref = useMarkdownRendering(part.text.replace(/^\s|\s$/g, ' '), true); + return ( + + ); + } + })} +

+
+ ); +}; - return
; +const HoverableLabel = ( + { + text, description, hoverService, className + }: { + text: string, + description?: string, + hoverService: HoverService, + className: string + }) => { + const spanRef = React.createRef(); + return ( + { + if (description) { + hoverService.requestHover({ + content: description, + target: spanRef.current!, + position: 'right' + }); + } + }} + > + {text} + + ); }; const ProgressMessage = (c: ChatProgressMessage) => ( diff --git a/packages/ai-chat-ui/src/browser/style/index.css b/packages/ai-chat-ui/src/browser/style/index.css index 2236f17e11580..faf9ada7849e9 100644 --- a/packages/ai-chat-ui/src/browser/style/index.css +++ b/packages/ai-chat-ui/src/browser/style/index.css @@ -132,6 +132,20 @@ div:last-child > .theia-ChatNode { font-size: var(--theia-code-font-size); } +.theia-RequestNode > p div { + display: inline; +} + +.theia-RequestNode .theia-RequestNode-AgentLabel, +.theia-RequestNode .theia-RequestNode-VariableLabel { + padding: calc(var(--theia-ui-padding) * 2 / 3); + padding-top: 0px; + padding-bottom: 0px; + border-radius: calc(var(--theia-ui-padding) * 2 / 3); + background: var(--theia-badge-background); + color: var(--theia-badge-foreground); +} + .chat-input-widget { align-items: flex-end; display: flex; From 41ba18e5276dc60eaf6db1fffab73e2e81ec3bd2 Mon Sep 17 00:00:00 2001 From: Luke-zhang-mchp Date: Wed, 27 Nov 2024 07:17:10 -0500 Subject: [PATCH 22/75] Fix calculation of `SelectComponent` dropdown bottom (#14381) Signed-off-by: Luke Zhang --- packages/core/src/browser/widgets/select-component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/browser/widgets/select-component.tsx b/packages/core/src/browser/widgets/select-component.tsx index 0e038e5a8043e..ae0a3845902a7 100644 --- a/packages/core/src/browser/widgets/select-component.tsx +++ b/packages/core/src/browser/widgets/select-component.tsx @@ -325,7 +325,7 @@ export class SelectComponent extends React.Component Date: Wed, 13 Nov 2024 11:53:28 +0100 Subject: [PATCH 23/75] dev: add eslint rule for annotation checks Inversify >=6.1 requires to annotate all constructor parameters of injectable classes. To avoid runtime errors we add a new eslint rule to check for this at lint time. --- configs/errors.eslintrc.json | 1 + dev-packages/private-eslint-plugin/README.md | 5 + dev-packages/private-eslint-plugin/index.js | 1 + .../rules/annotation-check.js | 103 ++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 dev-packages/private-eslint-plugin/rules/annotation-check.js diff --git a/configs/errors.eslintrc.json b/configs/errors.eslintrc.json index 13f2ea5250dda..e7eb84276e76e 100644 --- a/configs/errors.eslintrc.json +++ b/configs/errors.eslintrc.json @@ -135,6 +135,7 @@ } } ], + "@theia/annotation-check": "error", "@theia/localization-check": "error", "@theia/no-src-import": "error", "@theia/runtime-import-check": "error", diff --git a/dev-packages/private-eslint-plugin/README.md b/dev-packages/private-eslint-plugin/README.md index cca75688e1af9..e8cd91259b622 100644 --- a/dev-packages/private-eslint-plugin/README.md +++ b/dev-packages/private-eslint-plugin/README.md @@ -17,6 +17,11 @@ The plugin helps identify problems during development through static analysis in ## Rules +### `annotation-check`: + +Inversify >=6.1 requires to annotate all constructor parameters of injectable classes as otherwise runtime errors are thrown. +The rule checks that all constructor parameters of injectable classes are annotated with `@inject`, `@unmanaged` or `@multiInject`. + ### `localization-check`: The rule prevents the following localization related issues: diff --git a/dev-packages/private-eslint-plugin/index.js b/dev-packages/private-eslint-plugin/index.js index aa082e196ca5b..ea3c8d7470a09 100644 --- a/dev-packages/private-eslint-plugin/index.js +++ b/dev-packages/private-eslint-plugin/index.js @@ -17,6 +17,7 @@ /** @type {{[ruleId: string]: import('eslint').Rule.RuleModule}} */ exports.rules = { + "annotation-check": require('./rules/annotation-check'), "localization-check": require('./rules/localization-check'), "no-src-import": require('./rules/no-src-import'), "runtime-import-check": require('./rules/runtime-import-check'), diff --git a/dev-packages/private-eslint-plugin/rules/annotation-check.js b/dev-packages/private-eslint-plugin/rules/annotation-check.js new file mode 100644 index 0000000000000..c1da1cca9d75a --- /dev/null +++ b/dev-packages/private-eslint-plugin/rules/annotation-check.js @@ -0,0 +1,103 @@ +// @ts-check +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/** + * @typedef {import('@typescript-eslint/utils').TSESTree.ClassDeclaration} ClassDeclaration + * @typedef {import('@typescript-eslint/utils').TSESTree.ClassElement} ClassElement + * @typedef {import('@typescript-eslint/utils').TSESTree.Decorator} Decorator + * @typedef {import('@typescript-eslint/utils').TSESTree.MethodDefinition} MethodDefinition + * @typedef {import('@typescript-eslint/utils').TSESTree.Parameter} Parameter + * @typedef {import('estree').Node} Node + * @typedef {import('eslint').Rule.RuleModule} RuleModule + */ + +/** + * Type guard to check if a ClassElement is a MethodDefinition. + * @param {ClassElement} element + * @returns {element is MethodDefinition} + */ +function isMethodDefinition(element) { + return element.type === 'MethodDefinition'; +} + +/** @type {RuleModule} */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: + 'Ensure @injectable classes have annotated constructor parameters', + }, + messages: { + missingAnnotation: 'Constructor parameters in an @injectable class must be annotated with @inject, @unmanaged or @multiInject', + }, + }, + create(context) { + return { + /** + * @param {ClassDeclaration} node + */ + ClassDeclaration(node) { + // Check if the class has a decorator named `injectable` + const hasInjectableDecorator = node.decorators?.some( + (/** @type {Decorator} */ decorator) => + decorator.expression.type === 'CallExpression' && + decorator.expression.callee.type === 'Identifier' && + decorator.expression.callee.name === 'injectable' + ); + + if (hasInjectableDecorator) { + // Find the constructor method within the class body + const constructor = node.body.body.find( + member => + isMethodDefinition(member) && + member.kind === 'constructor' + ); + + if ( + constructor && + // We need to re-apply 'isMethodDefinition' here because the type guard is not properly preserved + isMethodDefinition(constructor) && + constructor.value && + constructor.value.params.length > 0 + ) { + constructor.value.params.forEach( + /** @type {Parameter} */ param => { + // Check if each constructor parameter has a decorator + const hasAnnotation = param.decorators?.some( + (/** @type {Decorator} */ decorator) => + decorator.expression.type === 'CallExpression' && + decorator.expression.callee.type === 'Identifier' && + (decorator.expression.callee.name === 'inject' || + decorator.expression.callee.name === 'unmanaged' || + decorator.expression.callee.name === 'multiInject') + ); + + if (!hasAnnotation) { + context.report({ + node: /** @type Node */ (param), + messageId: 'missingAnnotation', + }); + } + } + ); + } + } + }, + }; + }, +}; From edd2fa8fecdb8a4a40a95df0caefa34d4b006634 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Wed, 27 Nov 2024 13:52:16 +0100 Subject: [PATCH 24/75] Bump API version to 1.95.3 (#14541) fixes #14540 Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 7 +++++-- dev-packages/application-package/src/api.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdaa35128528f..6e5c55230677b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,16 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -[Breaking Changes:](#breaking_changes_1.56.0) -- [core] Do not dispose dialogs on close - [#14456](https://github.com/eclipse-theia/theia/pull/14456) - Contributed on behalf of STMicroelectronics +- [application-package] bumped API version to 1.95.3 [#14541](https://github.com/eclipse-theia/theia/pull/14541) - Contributed on behalf of STMicroelectronics - [core] fixed electron win background color [#14491](https://github.com/eclipse-theia/theia/pull/14491) - Contributed on behalf of STMicroelectronics - [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics - [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics - [plugin] supported MappedEditProviders proposed API evolution [#14453](https://github.com/eclipse-theia/theia/pull/14453) - Contributed on behalf of STMicroelectronics +[Breaking Changes:](#breaking_changes_1.56.0) + +- [core] Do not dispose dialogs on close - [#14456](https://github.com/eclipse-theia/theia/pull/14456) - Contributed on behalf of STMicroelectronics + ## 1.55.0 - 10/31/2024 - [ai] added logic to allow to order and clear AI History view [#14233](https://github.com/eclipse-theia/theia/pull/14233) diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index db3a312ab4505..7807f2774cccc 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.94.2'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.95.3'; From 8ba447d148c816a3f222033eb997d2434eb129c6 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 27 Nov 2024 14:45:51 +0100 Subject: [PATCH 25/75] Show progress while calculating AI code completion (#14537) fixed #14536 Signed-off-by: Jonas Helming --- .../src/common/code-completion-agent.ts | 156 ++++++++++-------- 1 file changed, 83 insertions(+), 73 deletions(-) diff --git a/packages/ai-code-completion/src/common/code-completion-agent.ts b/packages/ai-code-completion/src/common/code-completion-agent.ts index c47f5b6089957..e081835d2ff5e 100644 --- a/packages/ai-code-completion/src/common/code-completion-agent.ts +++ b/packages/ai-code-completion/src/common/code-completion-agent.ts @@ -18,7 +18,7 @@ import { Agent, AgentSpecificVariables, CommunicationRecordingService, getTextOfResponse, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequirement, PromptService, PromptTemplate } from '@theia/ai-core/lib/common'; -import { generateUuid, ILogger } from '@theia/core'; +import { generateUuid, ILogger, ProgressService } from '@theia/core'; import { inject, injectable, named } from '@theia/core/shared/inversify'; import * as monaco from '@theia/monaco-editor-core'; @@ -36,82 +36,89 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent { context: monaco.languages.InlineCompletionContext, token: monaco.CancellationToken ): Promise { - const languageModel = - await this.languageModelRegistry.selectLanguageModel({ - agent: this.id, - ...this.languageModelRequirements[0], + const progress = await this.progressService.showProgress( + { text: 'Calculating AI code completion...', options: { location: 'window' } } + ); + try { + const languageModel = + await this.languageModelRegistry.selectLanguageModel({ + agent: this.id, + ...this.languageModelRequirements[0], + }); + if (!languageModel) { + this.logger.error( + 'No language model found for code-completion-agent' + ); + return undefined; + } + + // Get text until the given position + const prefix = model.getValueInRange({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column, }); - if (!languageModel) { - this.logger.error( - 'No language model found for code-completion-agent' - ); - return undefined; - } - // Get text until the given position - const prefix = model.getValueInRange({ - startLineNumber: 1, - startColumn: 1, - endLineNumber: position.lineNumber, - endColumn: position.column, - }); - - // Get text after the given position - const suffix = model.getValueInRange({ - startLineNumber: position.lineNumber, - startColumn: position.column, - endLineNumber: model.getLineCount(), - endColumn: model.getLineMaxColumn(model.getLineCount()), - }); - - const file = model.uri.toString(false); - const language = model.getLanguageId(); - - if (token.isCancellationRequested) { - return undefined; - } - const prompt = await this.promptService - .getPrompt('code-completion-prompt', { prefix, suffix, file, language }) - .then(p => p?.text); - if (!prompt) { - this.logger.error('No prompt found for code-completion-agent'); - return undefined; - } + // Get text after the given position + const suffix = model.getValueInRange({ + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: model.getLineCount(), + endColumn: model.getLineMaxColumn(model.getLineCount()), + }); - // since we do not actually hold complete conversions, the request/response pair is considered a session - const sessionId = generateUuid(); - const requestId = generateUuid(); - const request: LanguageModelRequest = { - messages: [{ type: 'text', actor: 'user', query: prompt }], - }; - if (token.isCancellationRequested) { - return undefined; - } - this.recordingService.recordRequest({ - agentId: this.id, - sessionId, - requestId, - request: prompt, - }); - const response = await languageModel.request(request, token); - if (token.isCancellationRequested) { - return undefined; - } - const completionText = await getTextOfResponse(response); - if (token.isCancellationRequested) { - return undefined; + const file = model.uri.toString(false); + const language = model.getLanguageId(); + + if (token.isCancellationRequested) { + return undefined; + } + const prompt = await this.promptService + .getPrompt('code-completion-prompt', { prefix, suffix, file, language }) + .then(p => p?.text); + if (!prompt) { + this.logger.error('No prompt found for code-completion-agent'); + return undefined; + } + + // since we do not actually hold complete conversions, the request/response pair is considered a session + const sessionId = generateUuid(); + const requestId = generateUuid(); + const request: LanguageModelRequest = { + messages: [{ type: 'text', actor: 'user', query: prompt }], + }; + if (token.isCancellationRequested) { + return undefined; + } + this.recordingService.recordRequest({ + agentId: this.id, + sessionId, + requestId, + request: prompt, + }); + const response = await languageModel.request(request, token); + if (token.isCancellationRequested) { + return undefined; + } + const completionText = await getTextOfResponse(response); + if (token.isCancellationRequested) { + return undefined; + } + this.recordingService.recordResponse({ + agentId: this.id, + sessionId, + requestId, + response: completionText, + }); + + return { + items: [{ insertText: completionText }], + enableForwardStability: true, + }; + } finally { + progress.cancel(); } - this.recordingService.recordResponse({ - agentId: this.id, - sessionId, - requestId, - response: completionText, - }); - - return { - items: [{ insertText: completionText }], - enableForwardStability: true, - }; } @inject(ILogger) @@ -127,6 +134,9 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent { @inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService; + @inject(ProgressService) + protected progressService: ProgressService; + id = 'Code Completion'; name = 'Code Completion'; description = From 99f24f5a0970f1dbcb1e7d2d218841e9165bd53b Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 27 Nov 2024 14:53:48 +0100 Subject: [PATCH 26/75] fixed container stopping on disconnect and application close (#14542) Signed-off-by: Jonah Iden --- .../remote-container-connection-provider.ts | 10 +++++++--- .../electron-browser/remote-frontend-contribution.ts | 11 +++++++---- .../src/electron-common/remote-status-service.ts | 4 +++- .../src/electron-node/remote-connection-service.ts | 6 +++++- .../remote/src/electron-node/remote-status-service.ts | 7 +++++++ packages/remote/src/electron-node/remote-types.ts | 5 +++++ 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 1efa6abd60613..a72e4e6c05fa2 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -31,7 +31,7 @@ import { DockerContainerService } from './docker-container-service'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { WriteStream } from 'tty'; import { PassThrough } from 'stream'; -import { exec } from 'child_process'; +import { exec, execSync } from 'child_process'; import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; @@ -303,9 +303,13 @@ export class RemoteDockerContainerConnection implements RemoteConnection { return deferred.promise; } - async dispose(): Promise { + disposeSync(): void { // cant use dockerrode here since this needs to happen on one tick - exec(`docker stop ${this.container.id}`); + execSync(`docker stop ${this.container.id}`); + } + + async dispose(): Promise { + return this.container.stop(); } } diff --git a/packages/remote/src/electron-browser/remote-frontend-contribution.ts b/packages/remote/src/electron-browser/remote-frontend-contribution.ts index 8e8d2f13d2fde..c0a02f3cb2249 100644 --- a/packages/remote/src/electron-browser/remote-frontend-contribution.ts +++ b/packages/remote/src/electron-browser/remote-frontend-contribution.ts @@ -105,10 +105,13 @@ export class RemoteFrontendContribution implements CommandContribution, Frontend }); } - protected disconnectRemote(): void { - const port = new URLSearchParams(location.search).get('localPort'); - if (port) { - this.windowService.reload({ search: { port } }); + protected async disconnectRemote(): Promise { + const searchParams = new URLSearchParams(location.search); + const localPort = searchParams.get('localPort'); + if (localPort) { + const currentPort = searchParams.get('port'); + this.remoteStatusService.connectionClosed(parseInt(currentPort ?? '0')); + this.windowService.reload({ search: { port: localPort } }); } } diff --git a/packages/remote/src/electron-common/remote-status-service.ts b/packages/remote/src/electron-common/remote-status-service.ts index a4ccec7376156..f63cd83c74418 100644 --- a/packages/remote/src/electron-common/remote-status-service.ts +++ b/packages/remote/src/electron-common/remote-status-service.ts @@ -31,5 +31,7 @@ export const RemoteStatusServicePath = '/remote/status'; export const RemoteStatusService = Symbol('RemoteStatusService'); export interface RemoteStatusService { - getStatus(localPort: number): Promise + getStatus(localPort: number): Promise; + + connectionClosed(localPort: number): Promise; } diff --git a/packages/remote/src/electron-node/remote-connection-service.ts b/packages/remote/src/electron-node/remote-connection-service.ts index e86612ee60257..b97c26f9c786a 100644 --- a/packages/remote/src/electron-node/remote-connection-service.ts +++ b/packages/remote/src/electron-node/remote-connection-service.ts @@ -50,7 +50,11 @@ export class RemoteConnectionService implements BackendApplicationContribution { onStop(): void { for (const connection of this.connections.values()) { - connection.dispose(); + if (connection.disposeSync) { + connection.disposeSync(); + } else { + connection.dispose(); + }; } } } diff --git a/packages/remote/src/electron-node/remote-status-service.ts b/packages/remote/src/electron-node/remote-status-service.ts index a88f75c8862b0..14d85e3bb66c6 100644 --- a/packages/remote/src/electron-node/remote-status-service.ts +++ b/packages/remote/src/electron-node/remote-status-service.ts @@ -38,4 +38,11 @@ export class RemoteStatusServiceImpl implements RemoteStatusService { }; } } + + async connectionClosed(localPort: number): Promise { + const connection = this.remoteConnectionService.getConnectionFromPort(localPort); + if (connection) { + connection.dispose(); + } + } } diff --git a/packages/remote/src/electron-node/remote-types.ts b/packages/remote/src/electron-node/remote-types.ts index c8ab798b1a15a..900ceaa827475 100644 --- a/packages/remote/src/electron-node/remote-types.ts +++ b/packages/remote/src/electron-node/remote-types.ts @@ -61,4 +61,9 @@ export interface RemoteConnection extends Disposable { * copy files from local to remote */ copy(localPath: string | Buffer | NodeJS.ReadableStream, remotePath: string): Promise; + + /** + * used for disposing when theia is shutting down + */ + disposeSync?(): void; } From ab928008c01e502063886610377043099d39ec5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 28 Nov 2024 09:45:22 +0100 Subject: [PATCH 27/75] Update parcel watcher to 2.5.0 (#14545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should fix #14529 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/README.md | 2 +- packages/core/package.json | 2 +- yarn.lock | 165 ++++++++++++++++++++++--------------- 3 files changed, 100 insertions(+), 69 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 1bda346a92a57..7317f67960a02 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -98,7 +98,7 @@ export class SomeClass { - `react-virtuoso` (from [`react-virtuoso@^2.17.0`](https://www.npmjs.com/package/react-virtuoso)) - `vscode-languageserver-protocol` (from [`vscode-languageserver-protocol@^3.17.2`](https://www.npmjs.com/package/vscode-languageserver-protocol)) - `vscode-uri` (from [`vscode-uri@^2.1.1`](https://www.npmjs.com/package/vscode-uri)) - - `@parcel/watcher` (from [`@parcel/watcher@^2.4.1`](https://www.npmjs.com/package/@parcel/watcher)) + - `@parcel/watcher` (from [`@parcel/watcher@^2.5.0`](https://www.npmjs.com/package/@parcel/watcher)) - `dompurify` (from [`dompurify@^2.2.9`](https://www.npmjs.com/package/dompurify)) - `express` (from [`express@^4.21.0`](https://www.npmjs.com/package/express)) - `lodash.debounce` (from [`lodash.debounce@^4.0.8`](https://www.npmjs.com/package/lodash.debounce)) diff --git a/packages/core/package.json b/packages/core/package.json index 4f59aa602c1b3..5d10471361293 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -6,7 +6,7 @@ "typings": "lib/common/index.d.ts", "dependencies": { "@babel/runtime": "^7.10.0", - "@parcel/watcher": "^2.4.1", + "@parcel/watcher": "^2.5.0", "@phosphor/algorithm": "1", "@phosphor/commands": "1", "@phosphor/coreutils": "1", diff --git a/yarn.lock b/yarn.lock index 7ddee1084ff44..b41f28d988304 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1547,65 +1547,70 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@parcel/watcher-android-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" - integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== +"@parcel/watcher-android-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a" + integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ== -"@parcel/watcher-darwin-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" - integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== +"@parcel/watcher-darwin-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f" + integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== -"@parcel/watcher-darwin-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" - integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== +"@parcel/watcher-darwin-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb" + integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA== -"@parcel/watcher-freebsd-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" - integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== +"@parcel/watcher-freebsd-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82" + integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw== -"@parcel/watcher-linux-arm-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" - integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== +"@parcel/watcher-linux-arm-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42" + integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA== -"@parcel/watcher-linux-arm64-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" - integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== +"@parcel/watcher-linux-arm-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4" + integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA== -"@parcel/watcher-linux-arm64-musl@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" - integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== +"@parcel/watcher-linux-arm64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03" + integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA== -"@parcel/watcher-linux-x64-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" - integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== +"@parcel/watcher-linux-arm64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732" + integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q== -"@parcel/watcher-linux-x64-musl@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" - integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== +"@parcel/watcher-linux-x64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d" + integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== -"@parcel/watcher-win32-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" - integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== +"@parcel/watcher-linux-x64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef" + integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== -"@parcel/watcher-win32-ia32@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" - integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== +"@parcel/watcher-win32-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154" + integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig== -"@parcel/watcher-win32-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" - integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== +"@parcel/watcher-win32-ia32@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220" + integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA== + +"@parcel/watcher-win32-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7" + integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw== "@parcel/watcher@2.0.4": version "2.0.4" @@ -1615,28 +1620,29 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@parcel/watcher@^2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" - integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== +"@parcel/watcher@^2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10" + integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ== dependencies: detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" node-addon-api "^7.0.0" optionalDependencies: - "@parcel/watcher-android-arm64" "2.4.1" - "@parcel/watcher-darwin-arm64" "2.4.1" - "@parcel/watcher-darwin-x64" "2.4.1" - "@parcel/watcher-freebsd-x64" "2.4.1" - "@parcel/watcher-linux-arm-glibc" "2.4.1" - "@parcel/watcher-linux-arm64-glibc" "2.4.1" - "@parcel/watcher-linux-arm64-musl" "2.4.1" - "@parcel/watcher-linux-x64-glibc" "2.4.1" - "@parcel/watcher-linux-x64-musl" "2.4.1" - "@parcel/watcher-win32-arm64" "2.4.1" - "@parcel/watcher-win32-ia32" "2.4.1" - "@parcel/watcher-win32-x64" "2.4.1" + "@parcel/watcher-android-arm64" "2.5.0" + "@parcel/watcher-darwin-arm64" "2.5.0" + "@parcel/watcher-darwin-x64" "2.5.0" + "@parcel/watcher-freebsd-x64" "2.5.0" + "@parcel/watcher-linux-arm-glibc" "2.5.0" + "@parcel/watcher-linux-arm-musl" "2.5.0" + "@parcel/watcher-linux-arm64-glibc" "2.5.0" + "@parcel/watcher-linux-arm64-musl" "2.5.0" + "@parcel/watcher-linux-x64-glibc" "2.5.0" + "@parcel/watcher-linux-x64-musl" "2.5.0" + "@parcel/watcher-win32-arm64" "2.5.0" + "@parcel/watcher-win32-ia32" "2.5.0" + "@parcel/watcher-win32-x64" "2.5.0" "@phosphor/algorithm@1", "@phosphor/algorithm@^1.2.0": version "1.2.0" @@ -11515,7 +11521,7 @@ string-argv@^0.1.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11533,6 +11539,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11598,7 +11613,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11619,6 +11634,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12871,7 +12893,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12889,6 +12911,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 8ad410c5e00ca2069f664287f16e334d8c885d3f Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 28 Nov 2024 09:52:14 +0100 Subject: [PATCH 28/75] Allow specifying max lines used for AI code completion context (#14539) * Allow specifying max lines used for AI code completion context fixed #14538 Signed-off-by: Jonas Helming --- .../ai-code-completion-frontend-module.ts | 2 +- .../browser/ai-code-completion-preference.ts | 9 ++++ .../ai-code-inline-completion-provider.ts | 2 +- .../code-completion-agent.ts | 41 ++++++++++++++++--- 4 files changed, 47 insertions(+), 7 deletions(-) rename packages/ai-code-completion/src/{common => browser}/code-completion-agent.ts (80%) diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts b/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts index 449a37f15ec52..5b72e254bf8cd 100644 --- a/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts +++ b/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts @@ -16,7 +16,7 @@ import { ILogger } from '@theia/core'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { CodeCompletionAgent, CodeCompletionAgentImpl } from '../common/code-completion-agent'; +import { CodeCompletionAgent, CodeCompletionAgentImpl } from './code-completion-agent'; import { AIFrontendApplicationContribution } from './ai-code-frontend-application-contribution'; import { FrontendApplicationContribution, KeybindingContribution, PreferenceContribution } from '@theia/core/lib/browser'; import { Agent } from '@theia/ai-core'; diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts index 2a6655958e63f..549284d76154d 100644 --- a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts +++ b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts @@ -19,6 +19,7 @@ import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-pr export const PREF_AI_INLINE_COMPLETION_AUTOMATIC_ENABLE = 'ai-features.codeCompletion.automaticCodeCompletion'; export const PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS = 'ai-features.codeCompletion.excludedFileExtensions'; +export const PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES = 'ai-features.codeCompletion.maxContextLines'; export const AICodeCompletionPreferencesSchema: PreferenceSchema = { type: 'object', @@ -39,6 +40,14 @@ export const AICodeCompletionPreferencesSchema: PreferenceSchema = { type: 'string' }, default: [] + }, + [PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES]: { + title: 'Maximum Context Lines', + type: 'number', + description: 'The maximum number of lines used as context, distributed among the lines before and after the cursor position (prefix and suffix).\ + Set this to -1 to use the full file as context without any line limit and 0 to only use the current line.', + default: -1, + minimum: -1 } } }; diff --git a/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts b/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts index b2bb35af85533..48c0c6c959632 100644 --- a/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts +++ b/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts @@ -17,7 +17,7 @@ import * as monaco from '@theia/monaco-editor-core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { CodeCompletionAgent } from '../common/code-completion-agent'; +import { CodeCompletionAgent } from './code-completion-agent'; import { AgentService } from '@theia/ai-core'; @injectable() diff --git a/packages/ai-code-completion/src/common/code-completion-agent.ts b/packages/ai-code-completion/src/browser/code-completion-agent.ts similarity index 80% rename from packages/ai-code-completion/src/common/code-completion-agent.ts rename to packages/ai-code-completion/src/browser/code-completion-agent.ts index e081835d2ff5e..abb550cbb3711 100644 --- a/packages/ai-code-completion/src/common/code-completion-agent.ts +++ b/packages/ai-code-completion/src/browser/code-completion-agent.ts @@ -21,6 +21,8 @@ import { import { generateUuid, ILogger, ProgressService } from '@theia/core'; import { inject, injectable, named } from '@theia/core/shared/inversify'; import * as monaco from '@theia/monaco-editor-core'; +import { PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES } from './ai-code-completion-preference'; +import { PreferenceService } from '@theia/core/lib/browser'; export const CodeCompletionAgent = Symbol('CodeCompletionAgent'); export interface CodeCompletionAgent extends Agent { @@ -52,20 +54,46 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent { return undefined; } - // Get text until the given position + const maxContextLines = this.preferences.get(PREF_AI_INLINE_COMPLETION_MAX_CONTEXT_LINES, -1); + + let prefixStartLine = 1; + let suffixEndLine = model.getLineCount(); + // if maxContextLines is -1, use the full file as context without any line limit + + if (maxContextLines === 0) { + // Only the cursor line + prefixStartLine = position.lineNumber; + suffixEndLine = position.lineNumber; + } else if (maxContextLines > 0) { + const linesBeforeCursor = position.lineNumber - 1; + const linesAfterCursor = model.getLineCount() - position.lineNumber; + + // Allocate one more line to the prefix in case of an odd maxContextLines + const prefixLines = Math.min( + Math.ceil(maxContextLines / 2), + linesBeforeCursor + ); + const suffixLines = Math.min( + Math.floor(maxContextLines / 2), + linesAfterCursor + ); + + prefixStartLine = Math.max(1, position.lineNumber - prefixLines); + suffixEndLine = Math.min(model.getLineCount(), position.lineNumber + suffixLines); + } + const prefix = model.getValueInRange({ - startLineNumber: 1, + startLineNumber: prefixStartLine, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column, }); - // Get text after the given position const suffix = model.getValueInRange({ startLineNumber: position.lineNumber, startColumn: position.column, - endLineNumber: model.getLineCount(), - endColumn: model.getLineMaxColumn(model.getLineCount()), + endLineNumber: suffixEndLine, + endColumn: model.getLineMaxColumn(suffixEndLine), }); const file = model.uri.toString(false); @@ -137,6 +165,9 @@ export class CodeCompletionAgentImpl implements CodeCompletionAgent { @inject(ProgressService) protected progressService: ProgressService; + @inject(PreferenceService) + protected preferences: PreferenceService; + id = 'Code Completion'; name = 'Code Completion'; description = From 4e99bcd6895f2d1a887a32f0b8428f68737e315a Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 28 Nov 2024 10:01:36 +0100 Subject: [PATCH 29/75] Allow adding variant via prompttemplate files (#14509) * Allow adding variant via prompttemplate files fixed #14507 --- .../template-settings-renderer.tsx | 40 ++++++++++++------- .../frontend-prompt-customization-service.ts | 4 ++ packages/ai-core/src/browser/style/index.css | 6 ++- packages/ai-core/src/common/prompt-service.ts | 12 +++++- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx b/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx index 3465e68e7286a..c72b25a852da5 100644 --- a/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx +++ b/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx @@ -34,20 +34,19 @@ export const TemplateRenderer: React.FC = ({ promptService, aiSettingsService, }) => { - const [variantIds, setVariantIds] = React.useState([]); + const variantIds = [DEFAULT_VARIANT, ...promptService.getVariantIds(template.id)]; const [selectedVariant, setSelectedVariant] = React.useState(DEFAULT_VARIANT); React.useEffect(() => { (async () => { - const variants = promptService.getVariantIds(template.id); - setVariantIds([DEFAULT_VARIANT, ...variants]); - const agentSettings = await aiSettingsService.getAgentSettings(agentId); const currentVariant = agentSettings?.selectedVariants?.[template.id] || DEFAULT_VARIANT; setSelectedVariant(currentVariant); })(); - }, [template.id, promptService, aiSettingsService, agentId]); + }, [template.id, aiSettingsService, agentId]); + + const isInvalidVariant = !variantIds.includes(selectedVariant); const handleVariantChange = async (event: React.ChangeEvent) => { const newVariant = event.target.value; @@ -68,16 +67,16 @@ export const TemplateRenderer: React.FC = ({ }); }; - const openTemplate = React.useCallback(async () => { + const openTemplate = () => { const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant; const selectedTemplate = promptService.getRawPrompt(templateId); promptCustomizationService.editTemplate(templateId, selectedTemplate?.template || ''); - }, [selectedVariant, template.id, promptService, promptCustomizationService]); + }; - const resetTemplate = React.useCallback(async () => { + const resetTemplate = () => { const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant; promptCustomizationService.resetTemplate(templateId); - }, [selectedVariant, template.id, promptCustomizationService]); + }; return (
@@ -85,17 +84,22 @@ export const TemplateRenderer: React.FC = ({ {template.id}
- {variantIds.length > 1 && ( + {(variantIds.length > 1 || isInvalidVariant) && ( <> )} - -
diff --git a/packages/ai-core/src/browser/frontend-prompt-customization-service.ts b/packages/ai-core/src/browser/frontend-prompt-customization-service.ts index 6ffe3969df378..7a2691d4206a5 100644 --- a/packages/ai-core/src/browser/frontend-prompt-customization-service.ts +++ b/packages/ai-core/src/browser/frontend-prompt-customization-service.ts @@ -169,6 +169,10 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati return this.templates.get(id); } + getCustomPromptTemplateIDs(): string[] { + return Array.from(this.templates.keys()); + } + async editTemplate(id: string, defaultContent?: string): Promise { const editorUri = await this.getTemplateURI(id); if (! await this.fileService.exists(editorUri)) { diff --git a/packages/ai-core/src/browser/style/index.css b/packages/ai-core/src/browser/style/index.css index a42e41b29a9a2..bcd7437046a30 100644 --- a/packages/ai-core/src/browser/style/index.css +++ b/packages/ai-core/src/browser/style/index.css @@ -48,7 +48,11 @@ min-width: 120px; } - +.template-variant-selector.error { + border-color: var(--theia-errorForeground); + background-color: var(--theia-errorBackground, rgba(255, 0, 0, 0.1)); + color: var(--theia-errorForeground); +} #ai-variable-configuration-container-widget, #ai-agent-configuration-container-widget { diff --git a/packages/ai-core/src/common/prompt-service.ts b/packages/ai-core/src/common/prompt-service.ts index aef16da8fdc50..8d89c6274e8e4 100644 --- a/packages/ai-core/src/common/prompt-service.ts +++ b/packages/ai-core/src/common/prompt-service.ts @@ -136,6 +136,7 @@ export interface PromptCustomizationService { */ getCustomizedPromptTemplate(id: string): string | undefined + getCustomPromptTemplateIDs(): string[]; /** * Edit the template. If the content is specified, is will be * used to customize the template. Otherwise, the behavior depends @@ -314,9 +315,18 @@ export class PromptServiceImpl implements PromptService { delete this._prompts[id]; } getVariantIds(id: string): string[] { - return Object.values(this._prompts) + const allCustomPromptTemplateIds = this.customizationService?.getCustomPromptTemplateIDs() || []; + const knownPromptIds = Object.keys(this._prompts); + + // We filter out known IDs from the custom prompt template IDs, these are no variants, but customizations. Then we retain IDs that start with the main ID + const customVariantIds = allCustomPromptTemplateIds.filter(customId => + !knownPromptIds.includes(customId) && customId.startsWith(id) + ); + const variantIds = Object.values(this._prompts) .filter(prompt => prompt.variantOf === id) .map(variant => variant.id); + + return [...variantIds, ...customVariantIds]; } storePromptTemplate(promptTemplate: PromptTemplate): void { this._prompts[promptTemplate.id] = promptTemplate; From 48283ca80814e6aac8a41a5c9b609940becfa708 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 28 Nov 2024 11:28:24 +0100 Subject: [PATCH 30/75] Update PR template (#14490) Removed the changelog requirement from the review guidelines. Changelog entries will be created in the release process, with the exception of breaking changes. For breaking changes the message needs to be added within the PR. Adjusted the PR template with sections to indicate if breaking changes are present. Added hint that if there are breaking changes a changelog message is needed. Streamlined the pull request guide to link directly to the existing PR template. Added attribution field to PR template to make it easier to see during the release. --- .github/PULL_REQUEST_TEMPLATE.md | 8 +++++++ doc/pull-requests.md | 40 +++++++------------------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6f7d691b60110..232e1cec91c18 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,6 +21,14 @@ vulnerabilities. +#### Breaking changes + +- [ ] This PR introduces breaking changes and requires careful review. If yes, the breaking changes section in the [changelog](https://github.com/eclipse-theia/theia/blob/master/CHANGELOG.md) has been updated. + +#### Attribution + + + #### Review checklist - [ ] As an author, I have thoroughly tested my changes and carefully followed [the review guidelines](https://github.com/theia-ide/theia/blob/master/doc/pull-requests.md#requesting-a-review) diff --git a/doc/pull-requests.md b/doc/pull-requests.md index 2fd80ad524cc6..17d98da5062bc 100644 --- a/doc/pull-requests.md +++ b/doc/pull-requests.md @@ -15,30 +15,8 @@ If a rule causes distress during discussions itself, it has to be reviewed on [t ## Opening a Pull Request -- [1.](#pr-template) Each PR description has to follow the following template: - ```md - - - #### What it does - - - #### How to test - - - #### Review checklist - - - [ ] As an author, I have thoroughly tested my changes and carefully followed [the review guidelines](https://github.com/eclipse-theia/theia/blob/master/doc/pull-requests.md#requesting-a-review) - - #### Reminder for reviewers - - - As a reviewer, I agree to review in accordance with [the review guidelines](https://github.com/eclipse-theia/theia/blob/master/doc/pull-requests.md#reviewing) - ``` +- [1.](#pr-template) Each PR description has to follow the [PR template](https://github.com/eclipse-theia/theia/blob/master/.github/PULL_REQUEST_TEMPLATE.md) - [2.](#design-review) A PR can be opened early for the design review before going into the detailed implementation. @@ -67,24 +45,22 @@ If a rule causes distress during discussions itself, it has to be reviewed on [t - [1.](#checklist-build-and-test) The new code is built and tested according to the `How to test` section of a PR description. - [2.](#checklist-project-org) The new code is aligned with the [project organization](code-organization.md) and [coding conventions](coding-guidelines.md). - -- [3.](#checklist-changelog) [Changelog](https://github.com/eclipse-theia/theia/blob/master/CHANGELOG.md) is updated. -- [4.](#checklist-breaking-changes) Breaking changes are justified and recorded in the [changelog](https://github.com/eclipse-theia/theia/blob/master/CHANGELOG.md). +- [3.](#checklist-breaking-changes) Breaking changes are justified and recorded in the [changelog](https://github.com/eclipse-theia/theia/blob/master/CHANGELOG.md). -- [5.](#checklist-dependencies) New dependencies are justified and [verified](https://github.com/eclipse-theia/theia/wiki/Registering-CQs#wip---new-ecd-theia-intellectual-property-clearance-approach-experimental). +- [4.](#checklist-dependencies) New dependencies are justified and [verified](https://github.com/eclipse-theia/theia/wiki/Registering-CQs#wip---new-ecd-theia-intellectual-property-clearance-approach-experimental). -- [6.](#checklist-copied-code) Copied code is justified and [approved via a CQ](https://github.com/eclipse-theia/theia/wiki/Registering-CQs#case-3rd-party-project-code-copiedforked-from-another-project-into-eclipse-theia-maintained-by-us). +- [5.](#checklist-copied-code) Copied code is justified and [approved via a CQ](https://github.com/eclipse-theia/theia/wiki/Registering-CQs#case-3rd-party-project-code-copiedforked-from-another-project-into-eclipse-theia-maintained-by-us). - Look closely at the GitHub actions running for your PR: the 3pp/dash license check should be green. - If red: it most likely mean you need to create a CQ. -- [7.](#checklist-copyright) Each new file has proper copyright with the current year and the name of contributing entity (individual or company). +- [6.](#checklist-copyright) Each new file has proper copyright with the current year and the name of contributing entity (individual or company). -- [8.](#checklist-sign-off) Commits are signed-off: https://github.com/eclipse-theia/theia/blob/master/CONTRIBUTING.md#sign-your-work. +- [7.](#checklist-sign-off) Commits are signed-off: https://github.com/eclipse-theia/theia/blob/master/CONTRIBUTING.md#sign-your-work. -- [9.](#checklist-meaningful-commit) Each commit has meaningful title and a body that explains what it does. One can take inspiration from the `What it does` section from the PR. +- [8.](#checklist-meaningful-commit) Each commit has meaningful title and a body that explains what it does. One can take inspiration from the `What it does` section from the PR. -- [10.](#checklist-commit-history) Commit history is rebased on master and contains only meaningful commits and changes (less are usually better). +- [9.](#checklist-commit-history) Commit history is rebased on master and contains only meaningful commits and changes (less are usually better). - For example, use `git pull -r` or `git fetch && git rebase` to pick up changes from the master. ## Reviewing From 1301b45c9a11f4b6ee481de2ad2d5a1363b5505a Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 28 Nov 2024 11:56:04 +0100 Subject: [PATCH 31/75] Add support for users to specify custom request settings, model and optionally provider specific (#14535) * Fix request settings and stop words in HF provider fixed #14503 Signed-off-by: Jonas Helming Co-authored-by: Stefan Dirix --- .../src/browser/ai-core-preferences.ts | 41 ++++- packages/ai-core/src/common/language-model.ts | 5 + ...gface-frontend-application-contribution.ts | 61 +++++-- .../huggingface-language-models-manager.ts | 4 + .../src/node/huggingface-language-model.ts | 30 +++- ...uggingface-language-models-manager-impl.ts | 16 +- .../browser/llamafile-command-contribution.ts | 3 +- ...afile-frontend-application-contribution.ts | 95 +++++++++-- .../src/common/llamafile-language-model.ts | 32 +++- .../src/common/llamafile-manager.ts | 32 ++-- .../src/node/llamafile-manager-impl.ts | 30 +++- ...llama-frontend-application-contribution.ts | 54 ++++-- .../common/ollama-language-models-manager.ts | 21 ++- .../src/node/ollama-language-model.ts | 38 +++-- .../ollama-language-models-manager-impl.ts | 33 ++-- ...penai-frontend-application-contribution.ts | 155 ++++++++++++------ .../common/openai-language-models-manager.ts | 4 + .../src/node/openai-language-model.ts | 29 +++- .../openai-language-models-manager-impl.ts | 20 ++- 19 files changed, 528 insertions(+), 175 deletions(-) diff --git a/packages/ai-core/src/browser/ai-core-preferences.ts b/packages/ai-core/src/browser/ai-core-preferences.ts index 970b1c485368f..2250931338951 100644 --- a/packages/ai-core/src/browser/ai-core-preferences.ts +++ b/packages/ai-core/src/browser/ai-core-preferences.ts @@ -21,6 +21,7 @@ import { interfaces } from '@theia/core/shared/inversify'; export const AI_CORE_PREFERENCES_TITLE = '✨ AI Features [Experimental]'; export const PREFERENCE_NAME_ENABLE_EXPERIMENTAL = 'ai-features.AiEnable.enableAI'; export const PREFERENCE_NAME_PROMPT_TEMPLATES = 'ai-features.promptTemplates.promptTemplatesFolder'; +export const PREFERENCE_NAME_REQUEST_SETTINGS = 'ai-features.modelSettings.requestSettings'; export const aiCorePreferenceSchema: PreferenceSchema = { type: 'object', @@ -55,13 +56,51 @@ export const aiCorePreferenceSchema: PreferenceSchema = { canSelectMany: false } }, - + }, + [PREFERENCE_NAME_REQUEST_SETTINGS]: { + title: 'Custom Request Settings', + markdownDescription: 'Allows specifying custom request settings for multiple models.\n\ + Each object represents the configuration for a specific model. The `modelId` field specifies the model ID, `requestSettings` defines model-specific settings.\n\ + The `providerId` field is optional and allows you to apply the settings to a specific provider. If not set, the settings will be applied to all providers.\n\ + Example providerIds: huggingface, openai, ollama, llamafile.\n\ + Refer to [our documentation](https://theia-ide.org/docs/user_ai/#custom-request-settings) for more information.', + type: 'array', + items: { + type: 'object', + properties: { + modelId: { + type: 'string', + description: 'The model id' + }, + requestSettings: { + type: 'object', + additionalProperties: true, + description: 'Settings for the specific model ID.', + }, + providerId: { + type: 'string', + description: 'The (optional) provider id to apply the settings to. If not set, the settings will be applied to all providers.', + }, + }, + }, + default: [], } } }; export interface AICoreConfiguration { [PREFERENCE_NAME_ENABLE_EXPERIMENTAL]: boolean | undefined; [PREFERENCE_NAME_PROMPT_TEMPLATES]: string | undefined; + [PREFERENCE_NAME_REQUEST_SETTINGS]: Array<{ + modelId: string; + requestSettings?: { [key: string]: unknown }; + providerId?: string; + }> | undefined; +} + +export interface RequestSetting { + modelId: string; + requestSettings?: { [key: string]: unknown }; + providerId?: string; } export const AICorePreferences = Symbol('AICorePreferences'); diff --git a/packages/ai-core/src/common/language-model.ts b/packages/ai-core/src/common/language-model.ts index 2945e9ff0d05a..4c507b74bdfcc 100644 --- a/packages/ai-core/src/common/language-model.ts +++ b/packages/ai-core/src/common/language-model.ts @@ -107,6 +107,11 @@ export interface LanguageModelMetaData { readonly family?: string; readonly maxInputTokens?: number; readonly maxOutputTokens?: number; + /** + * Default request settings for the language model. These settings can be set by a user preferences. + * Settings in a request will override these default settings. + */ + readonly defaultRequestSettings?: { [key: string]: unknown }; } export namespace LanguageModelMetaData { diff --git a/packages/ai-hugging-face/src/browser/huggingface-frontend-application-contribution.ts b/packages/ai-hugging-face/src/browser/huggingface-frontend-application-contribution.ts index 8c595133b4464..ad9cb783d01dc 100644 --- a/packages/ai-hugging-face/src/browser/huggingface-frontend-application-contribution.ts +++ b/packages/ai-hugging-face/src/browser/huggingface-frontend-application-contribution.ts @@ -18,7 +18,9 @@ import { FrontendApplicationContribution, PreferenceService } from '@theia/core/ import { inject, injectable } from '@theia/core/shared/inversify'; import { HuggingFaceLanguageModelsManager, HuggingFaceModelDescription } from '../common'; import { API_KEY_PREF, MODELS_PREF } from './huggingface-preferences'; +import { PREFERENCE_NAME_REQUEST_SETTINGS, RequestSetting } from '@theia/ai-core/lib/browser/ai-core-preferences'; +const HUGGINGFACE_PROVIDER_ID = 'huggingface'; @injectable() export class HuggingFaceFrontendApplicationContribution implements FrontendApplicationContribution { @@ -36,31 +38,58 @@ export class HuggingFaceFrontendApplicationContribution implements FrontendAppli this.manager.setApiKey(apiKey); const models = this.preferenceService.get(MODELS_PREF, []); - this.manager.createOrUpdateLanguageModels(...models.map(createHuggingFaceModelDescription)); + const requestSettings = this.preferenceService.get(PREFERENCE_NAME_REQUEST_SETTINGS, []); + this.manager.createOrUpdateLanguageModels(...models.map(modelId => this.createHuggingFaceModelDescription(modelId, requestSettings))); this.prevModels = [...models]; this.preferenceService.onPreferenceChanged(event => { if (event.preferenceName === API_KEY_PREF) { this.manager.setApiKey(event.newValue); } else if (event.preferenceName === MODELS_PREF) { - const oldModels = new Set(this.prevModels); - const newModels = new Set(event.newValue as string[]); - - const modelsToRemove = [...oldModels].filter(model => !newModels.has(model)); - const modelsToAdd = [...newModels].filter(model => !oldModels.has(model)); - - this.manager.removeLanguageModels(...modelsToRemove.map(model => `huggingface/${model}`)); - this.manager.createOrUpdateLanguageModels(...modelsToAdd.map(createHuggingFaceModelDescription)); - this.prevModels = [...event.newValue]; + this.handleModelChanges(event.newValue as string[]); + } else if (event.preferenceName === PREFERENCE_NAME_REQUEST_SETTINGS) { + this.handleRequestSettingsChanges(event.newValue as RequestSetting[]); } }); }); } -} -function createHuggingFaceModelDescription(modelId: string): HuggingFaceModelDescription { - return { - id: `huggingface/${modelId}`, - model: modelId - }; + protected handleModelChanges(newModels: string[]): void { + const oldModels = new Set(this.prevModels); + const updatedModels = new Set(newModels); + + const modelsToRemove = [...oldModels].filter(model => !updatedModels.has(model)); + const modelsToAdd = [...updatedModels].filter(model => !oldModels.has(model)); + + this.manager.removeLanguageModels(...modelsToRemove.map(model => `${HUGGINGFACE_PROVIDER_ID}/${model}`)); + const requestSettings = this.preferenceService.get(PREFERENCE_NAME_REQUEST_SETTINGS, []); + this.manager.createOrUpdateLanguageModels(...modelsToAdd.map(modelId => this.createHuggingFaceModelDescription(modelId, requestSettings))); + this.prevModels = newModels; + } + + protected handleRequestSettingsChanges(newSettings: RequestSetting[]): void { + const models = this.preferenceService.get(MODELS_PREF, []); + this.manager.createOrUpdateLanguageModels(...models.map(modelId => this.createHuggingFaceModelDescription(modelId, newSettings))); + } + + protected createHuggingFaceModelDescription( + modelId: string, + requestSettings: RequestSetting[] + ): HuggingFaceModelDescription { + const id = `${HUGGINGFACE_PROVIDER_ID}/${modelId}`; + const matchingSettings = requestSettings.filter( + setting => (!setting.providerId || setting.providerId === HUGGINGFACE_PROVIDER_ID) && setting.modelId === modelId + ); + if (matchingSettings.length > 1) { + console.warn( + `Multiple entries found for modelId "${modelId}". Using the first match and ignoring the rest.` + ); + } + const modelRequestSetting = matchingSettings[0]; + return { + id: id, + model: modelId, + defaultRequestSettings: modelRequestSetting?.requestSettings + }; + } } diff --git a/packages/ai-hugging-face/src/common/huggingface-language-models-manager.ts b/packages/ai-hugging-face/src/common/huggingface-language-models-manager.ts index a19937301e241..21a3c85550e78 100644 --- a/packages/ai-hugging-face/src/common/huggingface-language-models-manager.ts +++ b/packages/ai-hugging-face/src/common/huggingface-language-models-manager.ts @@ -26,6 +26,10 @@ export interface HuggingFaceModelDescription { * The model ID as used by the Hugging Face API. */ model: string; + /** + * Default request settings for the Hugging Face model. + */ + defaultRequestSettings?: { [key: string]: unknown }; } export interface HuggingFaceLanguageModelsManager { diff --git a/packages/ai-hugging-face/src/node/huggingface-language-model.ts b/packages/ai-hugging-face/src/node/huggingface-language-model.ts index d5b424aaadf4a..2758f588bca52 100644 --- a/packages/ai-hugging-face/src/node/huggingface-language-model.ts +++ b/packages/ai-hugging-face/src/node/huggingface-language-model.ts @@ -55,8 +55,18 @@ export class HuggingFaceModel implements LanguageModel { * @param model the model id as it is used by the Hugging Face API * @param apiKey function to retrieve the API key for Hugging Face */ - constructor(public readonly id: string, public model: string, public apiKey: () => string | undefined) { - } + constructor( + public readonly id: string, + public model: string, + public apiKey: () => string | undefined, + public readonly name?: string, + public readonly vendor?: string, + public readonly version?: string, + public readonly family?: string, + public readonly maxInputTokens?: number, + public readonly maxOutputTokens?: number, + public defaultRequestSettings?: Record + ) { } async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { const hfInference = this.initializeHfInference(); @@ -67,15 +77,16 @@ export class HuggingFaceModel implements LanguageModel { } } - protected getDefaultSettings(): Record { - return { - max_new_tokens: 2024, - stop: ['<|endoftext|>', ''] - }; + protected getSettings(request: LanguageModelRequest): Record { + const settings = request.settings ? request.settings : this.defaultRequestSettings; + if (!settings) { + return {}; + } + return settings; } protected async handleNonStreamingRequest(hfInference: HfInference, request: LanguageModelRequest): Promise { - const settings = request.settings || this.getDefaultSettings(); + const settings = this.getSettings(request); const response = await hfInference.textGeneration({ model: this.model, @@ -104,7 +115,8 @@ export class HuggingFaceModel implements LanguageModel { request: LanguageModelRequest, cancellationToken?: CancellationToken ): Promise { - const settings = request.settings || this.getDefaultSettings(); + + const settings = this.getSettings(request); const stream = hfInference.textGenerationStream({ model: this.model, diff --git a/packages/ai-hugging-face/src/node/huggingface-language-models-manager-impl.ts b/packages/ai-hugging-face/src/node/huggingface-language-models-manager-impl.ts index 85073968a628b..e0c180cd7c961 100644 --- a/packages/ai-hugging-face/src/node/huggingface-language-models-manager-impl.ts +++ b/packages/ai-hugging-face/src/node/huggingface-language-models-manager-impl.ts @@ -43,8 +43,22 @@ export class HuggingFaceLanguageModelsManagerImpl implements HuggingFaceLanguage } model.model = modelDescription.model; model.apiKey = apiKeyProvider; + model.defaultRequestSettings = modelDescription.defaultRequestSettings; } else { - this.languageModelRegistry.addLanguageModels([new HuggingFaceModel(modelDescription.id, modelDescription.model, apiKeyProvider)]); + this.languageModelRegistry.addLanguageModels([ + new HuggingFaceModel( + modelDescription.id, + modelDescription.model, + apiKeyProvider, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + modelDescription.defaultRequestSettings + ) + ]); } } } diff --git a/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts b/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts index eae616cfd94bd..cc7eff04d060a 100644 --- a/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts +++ b/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts @@ -17,8 +17,9 @@ import { AICommandHandlerFactory } from '@theia/ai-core/lib/browser/ai-command-h import { CommandContribution, CommandRegistry, MessageService } from '@theia/core'; import { PreferenceService, QuickInputService } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { LlamafileEntry, LlamafileManager } from '../common/llamafile-manager'; +import { LlamafileManager } from '../common/llamafile-manager'; import { PREFERENCE_LLAMAFILE } from './llamafile-preferences'; +import { LlamafileEntry } from './llamafile-frontend-application-contribution'; export const StartLlamafileCommand = { id: 'llamafile.start', diff --git a/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts b/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts index c202ca12fe54e..e1f144acc2979 100644 --- a/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts +++ b/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts @@ -16,9 +16,11 @@ import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { LlamafileEntry, LlamafileManager } from '../common/llamafile-manager'; +import { LlamafileManager, LlamafileModelDescription } from '../common/llamafile-manager'; import { PREFERENCE_LLAMAFILE } from './llamafile-preferences'; +import { PREFERENCE_NAME_REQUEST_SETTINGS, RequestSetting } from '@theia/ai-core/lib/browser/ai-core-preferences'; +const LLAMAFILE_PROVIDER_ID = 'llamafile'; @injectable() export class LlamafileFrontendApplicationContribution implements FrontendApplicationContribution { @@ -33,27 +35,92 @@ export class LlamafileFrontendApplicationContribution implements FrontendApplica onStart(): void { this.preferenceService.ready.then(() => { const llamafiles = this.preferenceService.get(PREFERENCE_LLAMAFILE, []); - this.llamafileManager.addLanguageModels(llamafiles); - llamafiles.forEach(model => this._knownLlamaFiles.set(model.name, model)); + const validLlamafiles = llamafiles.filter(LlamafileEntry.is); + + const LlamafileModelDescriptions = this.getLLamaFileModelDescriptions(validLlamafiles); + + this.llamafileManager.addLanguageModels(LlamafileModelDescriptions); + validLlamafiles.forEach(model => this._knownLlamaFiles.set(model.name, model)); this.preferenceService.onPreferenceChanged(event => { if (event.preferenceName === PREFERENCE_LLAMAFILE) { - // only new models which are actual LLamaFileEntries const newModels = event.newValue.filter((llamafileEntry: unknown) => LlamafileEntry.is(llamafileEntry)) as LlamafileEntry[]; + this.handleLlamaFilePreferenceChange(newModels); + } else if (event.preferenceName === PREFERENCE_NAME_REQUEST_SETTINGS) { + this.handleRequestSettingsChange(event.newValue as RequestSetting[]); + } + }); + }); + } - const llamafilesToAdd = newModels.filter(llamafile => - !this._knownLlamaFiles.has(llamafile.name) || !LlamafileEntry.equals(this._knownLlamaFiles.get(llamafile.name)!, llamafile)); + protected getLLamaFileModelDescriptions(llamafiles: LlamafileEntry[]): LlamafileModelDescription[] { + const requestSettings = this.preferenceService.get(PREFERENCE_NAME_REQUEST_SETTINGS, []); + return llamafiles.map(llamafile => { + const matchingSettings = requestSettings.filter( + setting => + (!setting.providerId || setting.providerId === LLAMAFILE_PROVIDER_ID) && + setting.modelId === llamafile.name + ); + if (matchingSettings.length > 1) { + console.warn(`Multiple entries found for model "${llamafile.name}". Using the first match.`); + } + return { + name: llamafile.name, + uri: llamafile.uri, + port: llamafile.port, + defaultRequestSettings: matchingSettings[0]?.requestSettings + }; + }); + } - const llamafileIdsToRemove = [...this._knownLlamaFiles.values()].filter(llamafile => - !newModels.find(a => LlamafileEntry.equals(a, llamafile))).map(a => a.name); + protected handleLlamaFilePreferenceChange(newModels: LlamafileEntry[]): void { + const llamafilesToAdd = newModels.filter(llamafile => + !this._knownLlamaFiles.has(llamafile.name) || + !LlamafileEntry.equals(this._knownLlamaFiles.get(llamafile.name)!, llamafile)); - this.llamafileManager.removeLanguageModels(llamafileIdsToRemove); - llamafileIdsToRemove.forEach(model => this._knownLlamaFiles.delete(model)); + const llamafileIdsToRemove = [...this._knownLlamaFiles.values()].filter(llamafile => + !newModels.find(newModel => LlamafileEntry.equals(newModel, llamafile))) + .map(llamafile => llamafile.name); - this.llamafileManager.addLanguageModels(llamafilesToAdd); - llamafilesToAdd.forEach(model => this._knownLlamaFiles.set(model.name, model)); - } - }); + this.llamafileManager.removeLanguageModels(llamafileIdsToRemove); + llamafileIdsToRemove.forEach(id => this._knownLlamaFiles.delete(id)); + + this.llamafileManager.addLanguageModels(this.getLLamaFileModelDescriptions(llamafilesToAdd)); + llamafilesToAdd.forEach(model => this._knownLlamaFiles.set(model.name, model)); + } + + protected handleRequestSettingsChange(newSettings: RequestSetting[]): void { + const llamafiles = Array.from(this._knownLlamaFiles.values()); + const llamafileModelDescriptions = this.getLLamaFileModelDescriptions(llamafiles); + llamafileModelDescriptions.forEach(llamafileModelDescription => { + this.llamafileManager.updateRequestSettings(llamafileModelDescription.name, llamafileModelDescription.defaultRequestSettings); }); } } + +export interface LlamafileEntry { + name: string; + uri: string; + port: number; +} + +namespace LlamafileEntry { + export function equals(a: LlamafileEntry, b: LlamafileEntry): boolean { + return ( + a.name === b.name && + a.uri === b.uri && + a.port === b.port + ); + } + + export function is(entry: unknown): entry is LlamafileEntry { + return ( + typeof entry === 'object' && + // eslint-disable-next-line no-null/no-null + entry !== null && + 'name' in entry && typeof (entry as LlamafileEntry).name === 'string' && + 'uri' in entry && typeof (entry as LlamafileEntry).uri === 'string' && + 'port' in entry && typeof (entry as LlamafileEntry).port === 'number' + ); + } +} diff --git a/packages/ai-llamafile/src/common/llamafile-language-model.ts b/packages/ai-llamafile/src/common/llamafile-language-model.ts index 78cc443d37e4f..34968377045b8 100644 --- a/packages/ai-llamafile/src/common/llamafile-language-model.ts +++ b/packages/ai-llamafile/src/common/llamafile-language-model.ts @@ -21,14 +21,37 @@ export class LlamafileLanguageModel implements LanguageModel { readonly providerId = 'llamafile'; readonly vendor: string = 'Mozilla'; - constructor(readonly name: string, readonly uri: string, readonly port: number) { - } + /** + * @param name the unique name for this language model. It will be used to identify the model in the UI. + * @param uri the URI pointing to the Llamafile model location. + * @param port the port on which the Llamafile model server operates. + * @param defaultRequestSettings optional default settings for requests made using this model. + */ + constructor( + public readonly name: string, + public readonly uri: string, + public readonly port: number, + public defaultRequestSettings?: { [key: string]: unknown } + ) { } get id(): string { return this.name; } + protected getSettings(request: LanguageModelRequest): Record { + const settings = request.settings ? request.settings : this.defaultRequestSettings; + if (!settings) { + return { + n_predict: 200, + stream: true, + stop: ['', 'Llama:', 'User:', '<|eot_id|>'], + cache_prompt: true, + }; + } + return settings; + } async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { + const settings = this.getSettings(request); try { let prompt = request.messages.map(message => { switch (message.actor) { @@ -48,10 +71,7 @@ export class LlamafileLanguageModel implements LanguageModel { }, body: JSON.stringify({ prompt: prompt, - n_predict: 200, - stream: true, - stop: ['', 'Llama:', 'User:', '<|eot_id|>'], - cache_prompt: true, + ...settings }), }); diff --git a/packages/ai-llamafile/src/common/llamafile-manager.ts b/packages/ai-llamafile/src/common/llamafile-manager.ts index 561f3acdee95b..b6ffb786b0f44 100644 --- a/packages/ai-llamafile/src/common/llamafile-manager.ts +++ b/packages/ai-llamafile/src/common/llamafile-manager.ts @@ -17,34 +17,26 @@ export const LlamafileManager = Symbol('LlamafileManager'); export const LlamafileManagerPath = '/services/llamafilemanager'; +export interface LlamafileModelDescription { + name: string; + uri: string; + port: number; + /** + * Default request settings for the Llama model. + */ + defaultRequestSettings?: { [key: string]: unknown }; +} + export interface LlamafileManager { startServer(name: string): Promise; stopServer(name: string): void; getStartedLlamafiles(): Promise; setClient(client: LlamafileServerManagerClient): void; - addLanguageModels(llamaFiles: LlamafileEntry[]): Promise; + addLanguageModels(llamaFiles: LlamafileModelDescription[]): Promise; removeLanguageModels(modelIds: string[]): void; + updateRequestSettings(modelId: string, requestSettings?: { [key: string]: unknown }): void; } export interface LlamafileServerManagerClient { log(llamafileName: string, message: string): void; error(llamafileName: string, message: string): void; } - -export interface LlamafileEntry { - name: string; - uri: string; - port: number; -} - -export namespace LlamafileEntry { - export function equals(a: LlamafileEntry, b: LlamafileEntry): boolean { - return a.name === b.name && a.uri === b.uri && a.port === b.port; - } - export function is(entry: unknown): entry is LlamafileEntry { - // eslint-disable-next-line no-null/no-null - return typeof entry === 'object' && entry !== null - && 'name' in entry && typeof entry.name === 'string' - && 'uri' in entry && typeof entry.uri === 'string' - && 'port' in entry && typeof entry.port === 'number'; - } -} diff --git a/packages/ai-llamafile/src/node/llamafile-manager-impl.ts b/packages/ai-llamafile/src/node/llamafile-manager-impl.ts index 3d726457f9faa..e5d5f601e97bc 100644 --- a/packages/ai-llamafile/src/node/llamafile-manager-impl.ts +++ b/packages/ai-llamafile/src/node/llamafile-manager-impl.ts @@ -19,7 +19,7 @@ import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; import { basename, dirname } from 'path'; import { fileURLToPath } from 'url'; import { LlamafileLanguageModel } from '../common/llamafile-language-model'; -import { LlamafileEntry, LlamafileManager, LlamafileServerManagerClient } from '../common/llamafile-manager'; +import { LlamafileManager, LlamafileModelDescription, LlamafileServerManagerClient } from '../common/llamafile-manager'; @injectable() export class LlamafileManagerImpl implements LlamafileManager { @@ -30,22 +30,42 @@ export class LlamafileManagerImpl implements LlamafileManager { private processMap: Map = new Map(); private client: LlamafileServerManagerClient; - async addLanguageModels(llamaFiles: LlamafileEntry[]): Promise { - for (const llamafile of llamaFiles) { + async addLanguageModels(LlamafileModelDescriptions: LlamafileModelDescription[]): Promise { + for (const llamafile of LlamafileModelDescriptions) { const model = await this.languageModelRegistry.getLanguageModel(llamafile.name); if (model) { if (!(model instanceof LlamafileLanguageModel)) { - console.warn(`Llamafile: model ${model.id} is not an LLamafile model`); + console.warn(`Llamafile: model ${model.id} is not a Llamafile model`); continue; } else { // This can happen during the initializing of more than one frontends, changes are handled in the frontend console.info(`Llamafile: skip creating or updating model ${llamafile.name} because it already exists.`); } } else { - this.languageModelRegistry.addLanguageModels([new LlamafileLanguageModel(llamafile.name, llamafile.uri, llamafile.port)]); + this.languageModelRegistry.addLanguageModels([ + new LlamafileLanguageModel( + llamafile.name, + llamafile.uri, + llamafile.port, + llamafile.defaultRequestSettings + ) + ]); } } } + + async updateRequestSettings(modelId: string, requestSettings?: { [key: string]: unknown; }): Promise { + const model = await this.languageModelRegistry.getLanguageModel(modelId); + if (model) { + if (!(model instanceof LlamafileLanguageModel)) { + console.warn(`Llamafile: model ${model.id} is not a Llamafile model`); + return; + } else { + model.defaultRequestSettings = requestSettings; + } + } + } + removeLanguageModels(modelIds: string[]): void { modelIds.filter(modelId => this.isStarted(modelId)).forEach(modelId => this.stopServer(modelId)); this.languageModelRegistry.removeLanguageModels(modelIds); diff --git a/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts b/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts index e08680563cd72..dc21ae55a3cfa 100644 --- a/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts +++ b/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts @@ -16,9 +16,11 @@ import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { OllamaLanguageModelsManager } from '../common'; +import { OllamaLanguageModelsManager, OllamaModelDescription } from '../common'; import { HOST_PREF, MODELS_PREF } from './ollama-preferences'; +import { PREFERENCE_NAME_REQUEST_SETTINGS, RequestSetting } from '@theia/ai-core/lib/browser/ai-core-preferences'; +const OLLAMA_PROVIDER_ID = 'ollama'; @injectable() export class OllamaFrontendApplicationContribution implements FrontendApplicationContribution { @@ -36,24 +38,54 @@ export class OllamaFrontendApplicationContribution implements FrontendApplicatio this.manager.setHost(host); const models = this.preferenceService.get(MODELS_PREF, []); - this.manager.createLanguageModels(...models); + const requestSettings = this.preferenceService.get(PREFERENCE_NAME_REQUEST_SETTINGS, []); + this.manager.createOrUpdateLanguageModels(...models.map(modelId => this.createOllamaModelDescription(modelId, requestSettings))); this.prevModels = [...models]; this.preferenceService.onPreferenceChanged(event => { if (event.preferenceName === HOST_PREF) { this.manager.setHost(event.newValue); } else if (event.preferenceName === MODELS_PREF) { - const oldModels = new Set(this.prevModels); - const newModels = new Set(event.newValue as string[]); - - const modelsToRemove = [...oldModels].filter(model => !newModels.has(model)); - const modelsToAdd = [...newModels].filter(model => !oldModels.has(model)); - - this.manager.removeLanguageModels(...modelsToRemove); - this.manager.createLanguageModels(...modelsToAdd); - this.prevModels = [...event.newValue]; + this.handleModelChanges(event.newValue as string[]); + } else if (event.preferenceName === PREFERENCE_NAME_REQUEST_SETTINGS) { + this.handleRequestSettingsChange(event.newValue as RequestSetting[]); } }); }); } + + protected handleModelChanges(newModels: string[]): void { + const oldModels = new Set(this.prevModels); + const updatedModels = new Set(newModels); + + const modelsToRemove = [...oldModels].filter(model => !updatedModels.has(model)); + const modelsToAdd = [...updatedModels].filter(model => !oldModels.has(model)); + + this.manager.removeLanguageModels(...modelsToRemove); + const requestSettings = this.preferenceService.get(PREFERENCE_NAME_REQUEST_SETTINGS, []); + this.manager.createOrUpdateLanguageModels(...modelsToAdd.map(modelId => this.createOllamaModelDescription(modelId, requestSettings))); + this.prevModels = newModels; + } + + protected handleRequestSettingsChange(newSettings: RequestSetting[]): void { + const models = this.preferenceService.get(MODELS_PREF, []); + this.manager.createOrUpdateLanguageModels(...models.map(modelId => this.createOllamaModelDescription(modelId, newSettings))); + } + + protected createOllamaModelDescription(modelId: string, requestSettings: RequestSetting[]): OllamaModelDescription { + const id = `${OLLAMA_PROVIDER_ID}/${modelId}`; + const matchingSettings = requestSettings.filter( + setting => (!setting.providerId || setting.providerId === OLLAMA_PROVIDER_ID) && setting.modelId === modelId + ); + if (matchingSettings.length > 1) { + console.warn(`Multiple entries found for modelId "${modelId}". Using the first match and ignoring the rest.`); + } + + const modelRequestSetting = matchingSettings[0]; + return { + id: id, + model: modelId, + defaultRequestSettings: modelRequestSetting?.requestSettings + }; + } } diff --git a/packages/ai-ollama/src/common/ollama-language-models-manager.ts b/packages/ai-ollama/src/common/ollama-language-models-manager.ts index 2714ef3a7757a..e37cc0f6a50c5 100644 --- a/packages/ai-ollama/src/common/ollama-language-models-manager.ts +++ b/packages/ai-ollama/src/common/ollama-language-models-manager.ts @@ -13,11 +13,28 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** + export const OLLAMA_LANGUAGE_MODELS_MANAGER_PATH = '/services/ollama/language-model-manager'; export const OllamaLanguageModelsManager = Symbol('OllamaLanguageModelsManager'); + +export interface OllamaModelDescription { + /** + * The identifier of the model which will be shown in the UI. + */ + id: string; + /** + * The name or ID of the model in the Ollama environment. + */ + model: string; + /** + * Default request settings for the Ollama model. + */ + defaultRequestSettings?: { [key: string]: unknown }; +} + export interface OllamaLanguageModelsManager { host: string | undefined; setHost(host: string | undefined): void; - createLanguageModels(...modelIds: string[]): Promise; - removeLanguageModels(...modelIds: string[]): void + createOrUpdateLanguageModels(...models: OllamaModelDescription[]): Promise; + removeLanguageModels(...modelIds: string[]): void; } diff --git a/packages/ai-ollama/src/node/ollama-language-model.ts b/packages/ai-ollama/src/node/ollama-language-model.ts index 899305831f7b5..8c90ac29dcf1b 100644 --- a/packages/ai-ollama/src/node/ollama-language-model.ts +++ b/packages/ai-ollama/src/node/ollama-language-model.ts @@ -24,7 +24,7 @@ import { ToolRequest } from '@theia/ai-core'; import { CancellationToken } from '@theia/core'; -import { ChatRequest, ChatResponse, Message, Ollama, Tool } from 'ollama'; +import { ChatRequest, ChatResponse, Message, Ollama, Options, Tool } from 'ollama'; export const OllamaModelIdentifier = Symbol('OllamaModelIdentifier'); @@ -37,30 +37,40 @@ export class OllamaModel implements LanguageModel { readonly providerId = 'ollama'; readonly vendor: string = 'Ollama'; - constructor(protected readonly model: string, protected host: () => string | undefined) { - } - - get id(): string { - return this.providerId + '/' + this.model; - } - - get name(): string { - return this.model; + /** + * @param id the unique id for this language model. It will be used to identify the model in the UI. + * @param model the unique model name as used in the Ollama environment. + * @param hostProvider a function to provide the host URL for the Ollama server. + * @param defaultRequestSettings optional default settings for requests made using this model. + */ + constructor( + public readonly id: string, + protected readonly model: string, + protected host: () => string | undefined, + public defaultRequestSettings?: { [key: string]: unknown } + ) { } + + protected getSettings(request: LanguageModelRequest): Partial { + const settings = request.settings ?? this.defaultRequestSettings ?? {}; + return { + options: settings as Partial + }; } async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { + const settings = this.getSettings(request); const ollama = this.initializeOllama(); if (request.response_format?.type === 'json_schema') { return this.handleStructuredOutputRequest(ollama, request); } const response = await ollama.chat({ - ...this.DEFAULT_REQUEST_SETTINGS, model: this.model, + ...this.DEFAULT_REQUEST_SETTINGS, + ...settings, messages: request.messages.map(this.toOllamaMessage), stream: true, tools: request.tools?.map(this.toOllamaTool), - ...request.settings }); cancellationToken?.onCancellationRequested(() => { @@ -77,12 +87,14 @@ export class OllamaModel implements LanguageModel { } protected async handleStructuredOutputRequest(ollama: Ollama, request: LanguageModelRequest): Promise { + const settings = this.getSettings(request); const result = await ollama.chat({ + ...settings, ...this.DEFAULT_REQUEST_SETTINGS, model: this.model, messages: request.messages.map(this.toOllamaMessage), format: 'json', - ...request.settings + stream: false, }); try { return { diff --git a/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts b/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts index 1fbd1f520c3c8..db7bae2c9f640 100644 --- a/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts +++ b/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts @@ -17,7 +17,7 @@ import { LanguageModelRegistry } from '@theia/ai-core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { OllamaModel } from './ollama-language-model'; -import { OllamaLanguageModelsManager } from '../common'; +import { OllamaLanguageModelsManager, OllamaModelDescription } from '../common'; @injectable() export class OllamaLanguageModelsManagerImpl implements OllamaLanguageModelsManager { @@ -33,13 +33,26 @@ export class OllamaLanguageModelsManagerImpl implements OllamaLanguageModelsMana // Triggered from frontend. In case you want to use the models on the backend // without a frontend then call this yourself - async createLanguageModels(...modelIds: string[]): Promise { - for (const id of modelIds) { - // TODO check that the model exists in Ollama using `list`. Ask and trigger download if not. - if (!(await this.languageModelRegistry.getLanguageModel(`ollama/${id}`))) { - this.languageModelRegistry.addLanguageModels([new OllamaModel(id, () => this.host)]); + async createOrUpdateLanguageModels(...models: OllamaModelDescription[]): Promise { + for (const modelDescription of models) { + const existingModel = await this.languageModelRegistry.getLanguageModel(modelDescription.id); + const hostProvider = () => this.host; + + if (existingModel) { + if (!(existingModel instanceof OllamaModel)) { + console.warn(`Ollama: model ${modelDescription.id} is not an Ollama model`); + continue; + } + existingModel.defaultRequestSettings = modelDescription.defaultRequestSettings; } else { - console.info(`Ollama: skip creating model ${id} because it already exists`); + this.languageModelRegistry.addLanguageModels([ + new OllamaModel( + modelDescription.id, + modelDescription.model, + hostProvider, + modelDescription.defaultRequestSettings + ) + ]); } } } @@ -49,10 +62,6 @@ export class OllamaLanguageModelsManagerImpl implements OllamaLanguageModelsMana } setHost(host: string | undefined): void { - if (host) { - this._host = host; - } else { - this._host = undefined; - } + this._host = host || undefined; } } diff --git a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts index 3f917c77ed1ee..689278ebfcc45 100644 --- a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts +++ b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts @@ -18,6 +18,9 @@ import { FrontendApplicationContribution, PreferenceService } from '@theia/core/ import { inject, injectable } from '@theia/core/shared/inversify'; import { OpenAiLanguageModelsManager, OpenAiModelDescription } from '../common'; import { API_KEY_PREF, CUSTOM_ENDPOINTS_PREF, MODELS_PREF } from './openai-preferences'; +import { PREFERENCE_NAME_REQUEST_SETTINGS, RequestSetting } from '@theia/ai-core/lib/browser/ai-core-preferences'; + +const OPENAI_PROVIDER_ID = 'openai'; @injectable() export class OpenAiFrontendApplicationContribution implements FrontendApplicationContribution { @@ -28,7 +31,6 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio @inject(OpenAiLanguageModelsManager) protected manager: OpenAiLanguageModelsManager; - // The preferenceChange.oldValue is always undefined for some reason protected prevModels: string[] = []; protected prevCustomModels: Partial[] = []; @@ -38,72 +40,123 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio this.manager.setApiKey(apiKey); const models = this.preferenceService.get(MODELS_PREF, []); - this.manager.createOrUpdateLanguageModels(...models.map(createOpenAIModelDescription)); + const requestSettings = this.getRequestSettingsPref(); + this.manager.createOrUpdateLanguageModels(...models.map(modelId => this.createOpenAIModelDescription(modelId, requestSettings))); this.prevModels = [...models]; const customModels = this.preferenceService.get[]>(CUSTOM_ENDPOINTS_PREF, []); - this.manager.createOrUpdateLanguageModels(...createCustomModelDescriptionsFromPreferences(customModels)); + this.manager.createOrUpdateLanguageModels(...this.createCustomModelDescriptionsFromPreferences(customModels, this.getRequestSettingsPref())); this.prevCustomModels = [...customModels]; this.preferenceService.onPreferenceChanged(event => { if (event.preferenceName === API_KEY_PREF) { this.manager.setApiKey(event.newValue); } else if (event.preferenceName === MODELS_PREF) { - const oldModels = new Set(this.prevModels); - const newModels = new Set(event.newValue as string[]); - - const modelsToRemove = [...oldModels].filter(model => !newModels.has(model)); - const modelsToAdd = [...newModels].filter(model => !oldModels.has(model)); - - this.manager.removeLanguageModels(...modelsToRemove.map(model => `openai/${model}`)); - this.manager.createOrUpdateLanguageModels(...modelsToAdd.map(createOpenAIModelDescription)); - this.prevModels = [...event.newValue]; + this.handleModelChanges(event.newValue as string[]); } else if (event.preferenceName === CUSTOM_ENDPOINTS_PREF) { - const oldModels = createCustomModelDescriptionsFromPreferences(this.prevCustomModels); - const newModels = createCustomModelDescriptionsFromPreferences(event.newValue); - - const modelsToRemove = oldModels.filter(model => !newModels.some(newModel => newModel.id === model.id)); - const modelsToAddOrUpdate = newModels.filter(newModel => - !oldModels.some(model => - model.id === newModel.id && - model.model === newModel.model && - model.url === newModel.url && - model.apiKey === newModel.apiKey && - model.enableStreaming === newModel.enableStreaming)); - - this.manager.removeLanguageModels(...modelsToRemove.map(model => model.id)); - this.manager.createOrUpdateLanguageModels(...modelsToAddOrUpdate); + this.handleCustomModelChanges(event.newValue as Partial[]); + } else if (event.preferenceName === PREFERENCE_NAME_REQUEST_SETTINGS) { + this.handleRequestSettingsChanges(event.newValue as RequestSetting[]); } }); }); } -} -const openAIModelsWithDisabledStreaming = ['o1-preview']; + protected handleModelChanges(newModels: string[]): void { + const oldModels = new Set(this.prevModels); + const updatedModels = new Set(newModels); -function createOpenAIModelDescription(modelId: string): OpenAiModelDescription { - return { - id: `openai/${modelId}`, - model: modelId, - apiKey: true, - enableStreaming: !openAIModelsWithDisabledStreaming.includes(modelId) - }; -} + const modelsToRemove = [...oldModels].filter(model => !updatedModels.has(model)); + const modelsToAdd = [...updatedModels].filter(model => !oldModels.has(model)); -function createCustomModelDescriptionsFromPreferences(preferences: Partial[]): OpenAiModelDescription[] { - return preferences.reduce((acc, pref) => { - if (!pref.model || !pref.url || typeof pref.model !== 'string' || typeof pref.url !== 'string') { - return acc; - } - return [ - ...acc, - { - id: pref.id && typeof pref.id === 'string' ? pref.id : pref.model, - model: pref.model, - url: pref.url, - apiKey: typeof pref.apiKey === 'string' || pref.apiKey === true ? pref.apiKey : undefined, - enableStreaming: pref.enableStreaming ?? true + this.manager.removeLanguageModels(...modelsToRemove.map(model => `openai/${model}`)); + const requestSettings = this.getRequestSettingsPref(); + this.manager.createOrUpdateLanguageModels(...modelsToAdd.map(modelId => this.createOpenAIModelDescription(modelId, requestSettings))); + this.prevModels = newModels; + } + + private getRequestSettingsPref(): RequestSetting[] { + return this.preferenceService.get(PREFERENCE_NAME_REQUEST_SETTINGS, []); + } + + protected handleCustomModelChanges(newCustomModels: Partial[]): void { + const requestSettings = this.getRequestSettingsPref(); + const oldModels = this.createCustomModelDescriptionsFromPreferences(this.prevCustomModels, requestSettings); + const newModels = this.createCustomModelDescriptionsFromPreferences(newCustomModels, requestSettings); + + const modelsToRemove = oldModels.filter(model => !newModels.some(newModel => newModel.id === model.id)); + const modelsToAddOrUpdate = newModels.filter(newModel => + !oldModels.some(model => + model.id === newModel.id && + model.model === newModel.model && + model.url === newModel.url && + model.apiKey === newModel.apiKey && + model.enableStreaming === newModel.enableStreaming)); + + this.manager.removeLanguageModels(...modelsToRemove.map(model => model.id)); + this.manager.createOrUpdateLanguageModels(...modelsToAddOrUpdate); + this.prevCustomModels = [...newCustomModels]; + } + + protected handleRequestSettingsChanges(newSettings: RequestSetting[]): void { + const models = this.preferenceService.get(MODELS_PREF, []); + this.manager.createOrUpdateLanguageModels(...models.map(modelId => this.createOpenAIModelDescription(modelId, newSettings))); + + const customModels = this.preferenceService.get[]>(CUSTOM_ENDPOINTS_PREF, []); + this.manager.createOrUpdateLanguageModels(...this.createCustomModelDescriptionsFromPreferences(customModels, newSettings)); + } + + protected createOpenAIModelDescription(modelId: string, requestSettings: RequestSetting[]): OpenAiModelDescription { + const id = `${OPENAI_PROVIDER_ID}/${modelId}`; + const modelRequestSetting = this.getMatchingRequestSetting(modelId, OPENAI_PROVIDER_ID, requestSettings); + return { + id: id, + model: modelId, + apiKey: true, + enableStreaming: !openAIModelsWithDisabledStreaming.includes(modelId), + defaultRequestSettings: modelRequestSetting?.requestSettings + }; + } + + protected createCustomModelDescriptionsFromPreferences( + preferences: Partial[], + requestSettings: RequestSetting[] + ): OpenAiModelDescription[] { + return preferences.reduce((acc, pref) => { + if (!pref.model || !pref.url || typeof pref.model !== 'string' || typeof pref.url !== 'string') { + return acc; } - ]; - }, []); + + const modelRequestSetting = this.getMatchingRequestSetting(pref.model, OPENAI_PROVIDER_ID, requestSettings); + + return [ + ...acc, + { + id: pref.id && typeof pref.id === 'string' ? pref.id : pref.model, + model: pref.model, + url: pref.url, + apiKey: typeof pref.apiKey === 'string' || pref.apiKey === true ? pref.apiKey : undefined, + enableStreaming: pref.enableStreaming ?? true, + defaultRequestSettings: modelRequestSetting?.requestSettings + } + ]; + }, []); + } + protected getMatchingRequestSetting( + modelId: string, + providerId: string, + requestSettings: RequestSetting[] + ): RequestSetting | undefined { + const matchingSettings = requestSettings.filter( + setting => (!setting.providerId || setting.providerId === providerId) && setting.modelId === modelId + ); + if (matchingSettings.length > 1) { + console.warn( + `Multiple entries found for provider "${providerId}" and model "${modelId}". Using the first match.` + ); + } + return matchingSettings[0]; + } } + +const openAIModelsWithDisabledStreaming = ['o1-preview']; diff --git a/packages/ai-openai/src/common/openai-language-models-manager.ts b/packages/ai-openai/src/common/openai-language-models-manager.ts index 4ce75b3779464..368c3d0875fa2 100644 --- a/packages/ai-openai/src/common/openai-language-models-manager.ts +++ b/packages/ai-openai/src/common/openai-language-models-manager.ts @@ -36,6 +36,10 @@ export interface OpenAiModelDescription { * Indicate whether the streaming API shall be used. */ enableStreaming: boolean; + /** + * Default request settings for the OpenAI model. + */ + defaultRequestSettings?: { [key: string]: unknown }; } export interface OpenAiLanguageModelsManager { apiKey: string | undefined; diff --git a/packages/ai-openai/src/node/openai-language-model.ts b/packages/ai-openai/src/node/openai-language-model.ts index 289fef89ebaaa..d43e3f4fd97c0 100644 --- a/packages/ai-openai/src/node/openai-language-model.ts +++ b/packages/ai-openai/src/node/openai-language-model.ts @@ -57,10 +57,27 @@ export class OpenAiModel implements LanguageModel { * @param enableStreaming whether the streaming API shall be used * @param apiKey a function that returns the API key to use for this model, called on each request * @param url the OpenAI API compatible endpoint where the model is hosted. If not provided the default OpenAI endpoint will be used. + * @param defaultRequestSettings optional default settings for requests made using this model. */ - constructor(public readonly id: string, public model: string, public enableStreaming: boolean, public apiKey: () => string | undefined, public url: string | undefined) { } + constructor( + public readonly id: string, + public model: string, + public enableStreaming: boolean, + public apiKey: () => string | undefined, + public url: string | undefined, + public defaultRequestSettings?: { [key: string]: unknown } + ) { } + + protected getSettings(request: LanguageModelRequest): Record { + const settings = request.settings ? request.settings : this.defaultRequestSettings; + if (!settings) { + return {}; + } + return settings; + } async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { + const settings = this.getSettings(request); const openai = this.initializeOpenAi(); if (this.isNonStreamingModel(this.model)) { @@ -80,14 +97,14 @@ export class OpenAiModel implements LanguageModel { stream: true, tools: tools, tool_choice: 'auto', - ...request.settings + ...settings }); } else { runner = openai.beta.chat.completions.stream({ model: this.model, messages: request.messages.map(toOpenAIMessage), stream: true, - ...request.settings + ...settings }); } cancellationToken?.onCancellationRequested(() => { @@ -141,10 +158,11 @@ export class OpenAiModel implements LanguageModel { } protected async handleNonStreamingRequest(openai: OpenAI, request: LanguageModelRequest): Promise { + const settings = this.getSettings(request); const response = await openai.chat.completions.create({ model: this.model, messages: request.messages.map(toOpenAIMessage), - ...request.settings + ...settings }); const message = response.choices[0].message; @@ -168,12 +186,13 @@ export class OpenAiModel implements LanguageModel { } protected async handleStructuredOutputRequest(openai: OpenAI, request: LanguageModelRequest): Promise { + const settings = this.getSettings(request); // TODO implement tool support for structured output (parse() seems to require different tool format) const result = await openai.beta.chat.completions.parse({ model: this.model, messages: request.messages.map(toOpenAIMessage), response_format: request.response_format, - ...request.settings + ...settings }); const message = result.choices[0].message; if (message.refusal || message.parsed === undefined) { diff --git a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts index 4ccf77b0cc9f9..90d3c65e1fec6 100644 --- a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts +++ b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts @@ -45,23 +45,27 @@ export class OpenAiLanguageModelsManagerImpl implements OpenAiLanguageModelsMana } return undefined; }; + if (model) { if (!(model instanceof OpenAiModel)) { - console.warn(`Open AI: model ${modelDescription.id} is not an OpenAI model`); - continue; - } - if (!modelDescription.url) { - // This seems to be an official model, but it was already created. This can happen during the initializing of more than one frontend. - console.info(`Open AI: skip creating model ${modelDescription.id} because it already exists`); + console.warn(`OpenAI: model ${modelDescription.id} is not an OpenAI model`); continue; } - model.url = modelDescription.url; model.model = modelDescription.model; model.enableStreaming = modelDescription.enableStreaming; + model.url = modelDescription.url; model.apiKey = apiKeyProvider; + model.defaultRequestSettings = modelDescription.defaultRequestSettings; } else { this.languageModelRegistry.addLanguageModels([ - new OpenAiModel(modelDescription.id, modelDescription.model, modelDescription.enableStreaming, apiKeyProvider, modelDescription.url) + new OpenAiModel( + modelDescription.id, + modelDescription.model, + modelDescription.enableStreaming, + apiKeyProvider, + modelDescription.url, + modelDescription.defaultRequestSettings + ) ]); } } From ecedc9bbf6f03ceae8df8c2c9b2d0186731eb099 Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Thu, 28 Nov 2024 11:57:17 +0100 Subject: [PATCH 32/75] feat: add support for files.insertFinalNewline during formatOnSave (#13751) When the `files.insertFinalNewline` property is set to true, formatOnSave() will ensure that the file ends with a newline character instead of always removing a final empty line. Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich --- .../src/browser/filesystem-preferences.ts | 7 ++++ .../src/browser/monaco-editor-provider.ts | 36 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/filesystem/src/browser/filesystem-preferences.ts b/packages/filesystem/src/browser/filesystem-preferences.ts index 27a35de410054..c8ab034d3b7a5 100644 --- a/packages/filesystem/src/browser/filesystem-preferences.ts +++ b/packages/filesystem/src/browser/filesystem-preferences.ts @@ -95,6 +95,12 @@ export const filesystemPreferenceSchema: PreferenceSchema = { description: nls.localizeByDefault('When enabled, will trim trailing whitespace when saving a file.'), scope: 'language-overridable' }, + 'files.insertFinalNewline': { + type: 'boolean', + default: false, + description: nls.localizeByDefault('When enabled, insert a final new line at the end of the file when saving it.'), + scope: 'language-overridable' + }, 'files.maxConcurrentUploads': { type: 'integer', default: 1, @@ -116,6 +122,7 @@ export interface FileSystemConfiguration { 'files.participants.timeout': number 'files.maxFileSizeMB': number 'files.trimTrailingWhitespace': boolean + 'files.insertFinalNewline': boolean 'files.maxConcurrentUploads': number } diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 75fa4dbcb049a..32fc859201e4b 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -254,6 +254,7 @@ export class MonacoEditorProvider { if (!this.shouldFormat(editor, event)) { return []; } + const edits: monaco.editor.IIdentifiedSingleEditOperation[] = []; const overrideIdentifier = editor.document.languageId; const uri = editor.uri.toString(); const formatOnSave = this.editorPreferences.get({ preferenceName: 'editor.formatOnSave', overrideIdentifier }, undefined, uri); @@ -268,7 +269,11 @@ export class MonacoEditorProvider { if (shouldRemoveWhiteSpace) { await editor.runAction('editor.action.trimTrailingWhitespace'); } - return []; + const insertFinalNewline = this.filePreferences.get({ preferenceName: 'files.insertFinalNewline', overrideIdentifier }, undefined, uri); + if (insertFinalNewline) { + edits.push(...this.insertFinalNewline(editor)); + } + return edits; } protected get diffPreferencePrefixes(): string[] { @@ -452,4 +457,33 @@ export class MonacoEditorProvider { ) ) as MonacoDiffEditor; } + + protected insertFinalNewline(editor: MonacoEditor): monaco.editor.IIdentifiedSingleEditOperation[] { + const model = editor.document && editor.document.textEditorModel; + if (!model) { + return []; + } + + const lines = model?.getLineCount(); + if (lines === 0) { + return []; + } + + const lastLine = model?.getLineContent(lines); + if (lastLine.trim() === '') { + return []; + } + + const lastLineMaxColumn = model?.getLineMaxColumn(lines); + const range = { + startLineNumber: lines, + startColumn: lastLineMaxColumn, + endLineNumber: lines, + endColumn: lastLineMaxColumn + }; + return [{ + range, + text: model?.getEOL() + }]; + } } From 717c06390e466cf8f0900b01d2cf7b60e2594ed1 Mon Sep 17 00:00:00 2001 From: Robert Jandow <38583713+robertjndw@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:15:58 +0100 Subject: [PATCH 33/75] feat: migrate from BrowserFS to OPFS (#14263) Replaces BrowserFS with the browser native Origin Private File System (OPFS) for file handling within the browser-only environment. --- CHANGELOG.md | 2 + configs/base.tsconfig.json | 3 +- configs/build.eslintrc.json | 9 +- .../api-samples-frontend-only-module.ts | 4 +- .../example-filesystem-initialization.ts | 41 +- packages/filesystem/package.json | 1 - ...browser-only-filesystem-frontend-module.ts | 14 +- .../browserfs-filesystem-initialization.ts | 61 --- .../browserfs-filesystem-provider.ts | 462 ------------------ .../opfs-filesystem-initialization.ts | 36 ++ .../browser-only/opfs-filesystem-provider.ts | 347 +++++++++++++ yarn.lock | 15 +- 12 files changed, 425 insertions(+), 570 deletions(-) delete mode 100644 packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts delete mode 100644 packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts create mode 100644 packages/filesystem/src/browser-only/opfs-filesystem-initialization.ts create mode 100644 packages/filesystem/src/browser-only/opfs-filesystem-provider.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5c55230677b..c627bbae735e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,14 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) + - [application-package] bumped API version to 1.95.3 [#14541](https://github.com/eclipse-theia/theia/pull/14541) - Contributed on behalf of STMicroelectronics - [core] fixed electron win background color [#14491](https://github.com/eclipse-theia/theia/pull/14491) - Contributed on behalf of STMicroelectronics - [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics - [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics - [plugin] supported MappedEditProviders proposed API evolution [#14453](https://github.com/eclipse-theia/theia/pull/14453) - Contributed on behalf of STMicroelectronics +- [browser-only] remove browserfs dependency (replaced by OPFS) [#14263](https://github.com/eclipse-theia/theia/pull/14263) [Breaking Changes:](#breaking_changes_1.56.0) diff --git a/configs/base.tsconfig.json b/configs/base.tsconfig.json index 731dcdfd7106f..794060005aafd 100644 --- a/configs/base.tsconfig.json +++ b/configs/base.tsconfig.json @@ -22,7 +22,8 @@ "lib": [ "ES2019", "ES2020.Promise", - "DOM" + "DOM", + "DOM.AsyncIterable" ], "sourceMap": true } diff --git a/configs/build.eslintrc.json b/configs/build.eslintrc.json index c0a2c52c0f081..7e8ee574ecab0 100644 --- a/configs/build.eslintrc.json +++ b/configs/build.eslintrc.json @@ -2,5 +2,12 @@ "extends": [ "./base.eslintrc.json", "./errors.eslintrc.json" - ] + ], + "parserOptions": { + "lib": [ + "ES2019", + "ES2020.Promise", + "DOM" + ] + } } diff --git a/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts b/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts index 2fe1703ffa8b1..6de6d07280007 100644 --- a/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts +++ b/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; -import { bindBrowserFSInitialization } from './filesystem/example-filesystem-initialization'; +import { bindOPFSInitialization } from './filesystem/example-filesystem-initialization'; export default new ContainerModule(( bind: interfaces.Bind, @@ -23,5 +23,5 @@ export default new ContainerModule(( _isBound: interfaces.IsBound, rebind: interfaces.Rebind, ) => { - bindBrowserFSInitialization(bind, rebind); + bindOPFSInitialization(bind, rebind); }); diff --git a/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts b/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts index f048231f977a5..9105dc4766b35 100644 --- a/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts +++ b/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts @@ -17,35 +17,34 @@ import { URI } from '@theia/core'; import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; import { EncodingService } from '@theia/core/lib/common/encoding-service'; -import { BrowserFSInitialization, DefaultBrowserFSInitialization } from '@theia/filesystem/lib/browser-only/browserfs-filesystem-initialization'; -import { BrowserFSFileSystemProvider } from '@theia/filesystem/lib/browser-only/browserfs-filesystem-provider'; -import type { FSModule } from 'browserfs/dist/node/core/FS'; +import { OPFSInitialization, DefaultOPFSInitialization } from '@theia/filesystem/lib/browser-only/opfs-filesystem-initialization'; +import { OPFSFileSystemProvider } from '@theia/filesystem/lib/browser-only/opfs-filesystem-provider'; @injectable() -export class ExampleBrowserFSInitialization extends DefaultBrowserFSInitialization { +export class ExampleOPFSInitialization extends DefaultOPFSInitialization { @inject(EncodingService) protected encodingService: EncodingService; - override async initializeFS(fs: FSModule, provider: BrowserFSFileSystemProvider): Promise { - try { - if (!fs.existsSync('/home/workspace')) { - await provider.mkdir(new URI('/home/workspace')); - await provider.writeFile(new URI('/home/workspace/my-file.txt'), this.encodingService.encode('foo').buffer, { create: true, overwrite: false }); - await provider.writeFile(new URI('/home/workspace/my-file2.txt'), this.encodingService.encode('bar').buffer, { create: true, overwrite: false }); - } - if (!fs.existsSync('/home/workspace2')) { - await provider.mkdir(new URI('/home/workspace2')); - await provider.writeFile(new URI('/home/workspace2/my-file.json'), this.encodingService.encode('{ foo: true }').buffer, { create: true, overwrite: false }); - await provider.writeFile(new URI('/home/workspace2/my-file2.json'), this.encodingService.encode('{ bar: false }').buffer, { create: true, overwrite: false }); - } - } catch (e) { - console.error('An error occurred while initializing the demo workspaces', e); + override async initializeFS(provider: OPFSFileSystemProvider): Promise { + // Check whether the directory exists + if (await provider.exists(new URI('/home/workspace'))) { + await provider.readdir(new URI('/home/workspace')); + } else { + await provider.mkdir(new URI('/home/workspace')); + await provider.writeFile(new URI('/home/workspace/my-file.txt'), this.encodingService.encode('foo').buffer, { create: true, overwrite: false }); + } + + if (await provider.exists(new URI('/home/workspace2'))) { + await provider.readdir(new URI('/home/workspace2')); + } else { + await provider.mkdir(new URI('/home/workspace2')); + await provider.writeFile(new URI('/home/workspace2/my-file.json'), this.encodingService.encode('{ foo: true }').buffer, { create: true, overwrite: false }); } } } -export const bindBrowserFSInitialization = (bind: interfaces.Bind, rebind: interfaces.Rebind): void => { - bind(ExampleBrowserFSInitialization).toSelf(); - rebind(BrowserFSInitialization).toService(ExampleBrowserFSInitialization); +export const bindOPFSInitialization = (bind: interfaces.Bind, rebind: interfaces.Rebind): void => { + bind(ExampleOPFSInitialization).toSelf(); + rebind(OPFSInitialization).toService(ExampleOPFSInitialization); }; diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index 462cc604d1419..69d2cf50ef3b9 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -9,7 +9,6 @@ "@types/tar-fs": "^1.16.1", "async-mutex": "^0.3.1", "body-parser": "^1.18.3", - "browserfs": "^1.4.3", "http-status-codes": "^1.3.0", "minimatch": "^5.1.0", "multer": "1.4.4-lts.1", diff --git a/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts b/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts index eae27435b6d90..bc3ff5b51808b 100644 --- a/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts +++ b/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts @@ -16,19 +16,19 @@ import { ContainerModule } from '@theia/core/shared/inversify'; import { FileSystemProvider } from '../common/files'; -import { BrowserFSFileSystemProvider } from './browserfs-filesystem-provider'; +import { OPFSFileSystemProvider } from './opfs-filesystem-provider'; import { RemoteFileSystemProvider, RemoteFileSystemServer } from '../common/remote-file-system-provider'; -import { BrowserFSInitialization, DefaultBrowserFSInitialization } from './browserfs-filesystem-initialization'; +import { OPFSInitialization, DefaultOPFSInitialization } from './opfs-filesystem-initialization'; import { BrowserOnlyFileSystemProviderServer } from './browser-only-filesystem-provider-server'; export default new ContainerModule((bind, _unbind, isBound, rebind) => { - bind(DefaultBrowserFSInitialization).toSelf(); - bind(BrowserFSFileSystemProvider).toSelf(); - bind(BrowserFSInitialization).toService(DefaultBrowserFSInitialization); + bind(DefaultOPFSInitialization).toSelf(); + bind(OPFSFileSystemProvider).toSelf(); + bind(OPFSInitialization).toService(DefaultOPFSInitialization); if (isBound(FileSystemProvider)) { - rebind(FileSystemProvider).to(BrowserFSFileSystemProvider).inSingletonScope(); + rebind(FileSystemProvider).to(OPFSFileSystemProvider).inSingletonScope(); } else { - bind(FileSystemProvider).to(BrowserFSFileSystemProvider).inSingletonScope(); + bind(FileSystemProvider).to(OPFSFileSystemProvider).inSingletonScope(); } if (isBound(RemoteFileSystemProvider)) { rebind(RemoteFileSystemServer).to(BrowserOnlyFileSystemProviderServer).inSingletonScope(); diff --git a/packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts b/packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts deleted file mode 100644 index 0b17d3076a468..0000000000000 --- a/packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts +++ /dev/null @@ -1,61 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2023 EclipseSource and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -import type { FSModule } from 'browserfs/dist/node/core/FS'; -import type { BrowserFSFileSystemProvider } from './browserfs-filesystem-provider'; -import { injectable } from '@theia/core/shared/inversify'; -import { FileSystem, initialize } from 'browserfs'; -import MountableFileSystem from 'browserfs/dist/node/backend/MountableFileSystem'; - -export const BrowserFSInitialization = Symbol('BrowserFSInitialization'); -export interface BrowserFSInitialization { - createMountableFileSystem(): Promise - initializeFS: (fs: FSModule, provider: BrowserFSFileSystemProvider) => Promise; -} - -@injectable() -export class DefaultBrowserFSInitialization implements BrowserFSInitialization { - - createMountableFileSystem(): Promise { - return new Promise(resolve => { - FileSystem.IndexedDB.Create({}, (e, persistedFS) => { - if (e) { - throw e; - } - if (!persistedFS) { - throw Error('Could not create filesystem'); - } - FileSystem.MountableFileSystem.Create({ - '/home': persistedFS - - }, (error, mountableFS) => { - if (error) { - throw error; - } - if (!mountableFS) { - throw Error('Could not create filesystem'); - } - initialize(mountableFS); - resolve(mountableFS); - }); - }); - }); - } - - async initializeFS(fs: FSModule, provider: BrowserFSFileSystemProvider): Promise { - - } -} diff --git a/packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts b/packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts deleted file mode 100644 index ca833d9536313..0000000000000 --- a/packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts +++ /dev/null @@ -1,462 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2023 EclipseSource and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -// based on https://github.com/microsoft/vscode/blob/04c36be045a94fee58e5f8992d3e3fd980294a84/src/vs/platform/files/node/diskFileSystemProvider.ts - -/* eslint-disable no-null/no-null */ - -import { inject, injectable } from '@theia/core/shared/inversify'; -import { - FileChange, FileDeleteOptions, FileOpenOptions, - FileOverwriteOptions, FileReadStreamOptions, FileSystemProviderCapabilities, - FileSystemProviderError, - FileSystemProviderErrorCode, - FileSystemProviderWithFileReadWriteCapability, - FileType, FileUpdateOptions, FileUpdateResult, FileWriteOptions, Stat, WatchOptions, createFileSystemProviderError -} from '../common/files'; -import { Event, URI, Disposable, CancellationToken } from '@theia/core'; -import { TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol'; -import { ReadableStreamEvents } from '@theia/core/lib/common/stream'; -import { BFSRequire } from 'browserfs'; -import type { FSModule } from 'browserfs/dist/node/core/FS'; -import type { FileSystem } from 'browserfs/dist/node/core/file_system'; -import MountableFileSystem from 'browserfs/dist/node/backend/MountableFileSystem'; -import { basename, dirname, normalize } from 'path'; -import Stats from 'browserfs/dist/node/core/node_fs_stats'; -import { retry } from '@theia/core/lib/common/promise-util'; -import { BrowserFSInitialization } from './browserfs-filesystem-initialization'; - -// adapted from DiskFileSystemProvider -@injectable() -export class BrowserFSFileSystemProvider implements FileSystemProviderWithFileReadWriteCapability { - capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; - onDidChangeCapabilities: Event = Event.None; - onDidChangeFile: Event = Event.None; - onFileWatchError: Event = Event.None; - private mapHandleToPos: Map = new Map(); - private writeHandles: Set = new Set(); - private canFlush: boolean = true; - - private fs: FSModule; - private mountableFS: MountableFileSystem; - private initialized: Promise; - - constructor(@inject(BrowserFSInitialization) readonly initialization: BrowserFSInitialization) { - const init = async (): Promise => { - this.mountableFS = await initialization.createMountableFileSystem(); - this.fs = BFSRequire('fs'); - await initialization.initializeFS(this.fs, new Proxy(this, { - get(target, prop, receiver): unknown { - if (prop === 'initialized') { - return Promise.resolve(true); - } - return Reflect.get(target, prop, receiver); - } - })); - return true; - }; - this.initialized = init(); - } - - async mount(mountPoint: string, fs: FileSystem): Promise { - await this.initialized; - this.mountableFS.mount(mountPoint, fs); - }; - - watch(_resource: URI, _opts: WatchOptions): Disposable { - return Disposable.NULL; - } - async stat(resource: URI): Promise { - await this.initialized; - const path = this.toFilePath(resource); - - let stats: Stats; - try { - stats = await this.promisify(this.fs.stat)(path) as Stats; - } catch (error) { - throw this.toFileSystemProviderError(error); - } - if (stats === undefined) { - throw new Error(`Could not read file stat for resource '${path}'`); - } - return { - type: this.toType(stats, /* symbolicLink */undefined), // FIXME: missing symbolicLink - ctime: stats.birthtime.getTime(), // intentionally not using ctime here, we want the creation time - mtime: stats.mtime.getTime(), - size: stats.size, - // FIXME: missing mode, permissions - }; - - } - async mkdir(resource: URI): Promise { - await this.initialized; - try { - await this.promisify(this.fs.mkdir)(this.toFilePath(resource)); - } catch (error) { - throw this.toFileSystemProviderError(error); - } - } - async readdir(resource: URI): Promise<[string, FileType][]> { - await this.initialized; - try { - - const children = await this.promisify(this.fs.readdir)(this.toFilePath(resource)) as string[]; - const result: [string, FileType][] = []; - await Promise.all(children.map(async child => { - try { - const stat = await this.stat(resource.resolve(child)); - result.push([child, stat.type]); - } catch (error) { - console.trace(error); // ignore errors for individual entries that can arise from permission denied - } - })); - - return result; - } catch (error) { - throw this.toFileSystemProviderError(error); - } - } - async delete(resource: URI, _opts: FileDeleteOptions): Promise { - await this.initialized; - // FIXME use options - try { - await this.promisify(this.fs.unlink)(this.toFilePath(resource)); - } catch (error) { - throw this.toFileSystemProviderError(error); - } - } - async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - await this.initialized; - const fromFilePath = this.toFilePath(from); - const toFilePath = this.toFilePath(to); - if (fromFilePath === toFilePath) { - return; // simulate node.js behaviour here and do a no-op if paths match - } - try { - // assume FS is path case sensitive - correct? - const targetExists = await this.promisify(this.fs.exists)(toFilePath); - if (targetExists) { - throw Error(`File '${toFilePath}' already exists.`); - } - if (fromFilePath === toFilePath) { - return Promise.resolve(); - } - - await this.promisify(this.fs.rename)(fromFilePath, toFilePath); - - const stat = await this.promisify(this.fs.lstat)(toFilePath) as Stats; - if (stat.isDirectory() || stat.isSymbolicLink()) { - return Promise.resolve(); // only for files - } - const fd = await this.promisify(open)(toFilePath, 'a'); - try { - await this.promisify(this.fs.futimes)(fd, stat.atime, new Date()); - } catch (error) { - // ignore - } - - this.promisify(this.fs.close)(fd); - } catch (error) { - // rewrite some typical errors that can happen especially around symlinks - // to something the user can better understand - if (error.code === 'EINVAL' || error.code === 'EBUSY' || error.code === 'ENAMETOOLONG') { - error = new Error(`Unable to move '${basename(fromFilePath)}' into '${basename(dirname(toFilePath))}' (${error.toString()}).`); - } - - throw this.toFileSystemProviderError(error); - } - } - async copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise { - await this.initialized; - throw new Error('Method not implemented.'); - } - async readFile(resource: URI): Promise { - await this.initialized; - try { - const filePath = this.toFilePath(resource); - return await this.promisify(this.fs.readFile)(filePath) as Uint8Array; - } catch (error) { - throw this.toFileSystemProviderError(error); - } - } - async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { - await this.initialized; - let handle: number | undefined = undefined; - try { - const filePath = this.toFilePath(resource); - - // Validate target unless { create: true, overwrite: true } - if (!opts.create || !opts.overwrite) { - const fileExists = await this.promisify(this.fs.exists)(filePath); - if (fileExists) { - if (!opts.overwrite) { - throw createFileSystemProviderError('File already exists', FileSystemProviderErrorCode.FileExists); - } - } else { - if (!opts.create) { - throw createFileSystemProviderError('File does not exist', FileSystemProviderErrorCode.FileNotFound); - } - } - } - - // Open - handle = await this.open(resource, { create: true }); - - // Write content at once - await this.write(handle, 0, content, 0, content.byteLength); - } catch (error) { - throw this.toFileSystemProviderError(error); - } finally { - if (typeof handle === 'number') { - await this.close(handle); - } - } - } - readFileStream?(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { - throw new Error('Method not implemented.'); - } - async open(resource: URI, opts: FileOpenOptions): Promise { - await this.initialized; - try { - const filePath = this.toFilePath(resource); - - let flags: string | undefined = undefined; - if (opts.create) { - // we take opts.create as a hint that the file is opened for writing - // as such we use 'w' to truncate an existing or create the - // file otherwise. we do not allow reading. - if (!flags) { - flags = 'w'; - } - } else { - // otherwise we assume the file is opened for reading - // as such we use 'r' to neither truncate, nor create - // the file. - flags = 'r'; - } - - const handle = await this.promisify(this.fs.open)(filePath, flags) as number; - - // remember this handle to track file position of the handle - // we init the position to 0 since the file descriptor was - // just created and the position was not moved so far (see - // also http://man7.org/linux/man-pages/man2/open.2.html - - // "The file offset is set to the beginning of the file.") - this.mapHandleToPos.set(handle, 0); - - // remember that this handle was used for writing - if (opts.create) { - this.writeHandles.add(handle); - } - - return handle; - } catch (error) { - throw this.toFileSystemProviderError(error); - } - } - async close(fd: number): Promise { - await this.initialized; - // remove this handle from map of positions - this.mapHandleToPos.delete(fd); - - // if a handle is closed that was used for writing, ensure - // to flush the contents to disk if possible. - if (this.writeHandles.delete(fd) && this.canFlush) { - try { - await this.promisify(this.fs.fdatasync)(fd); - } catch (error) { - // In some exotic setups it is well possible that node fails to sync - // In that case we disable flushing and log the error to our logger - this.canFlush = false; - console.error(error); - } - } - - await this.promisify(this.fs.close)(fd); - } - async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { - await this.initialized; - const normalizedPos = this.normalizePos(fd, pos); - - let bytesRead: number | null = null; - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const result: { bytesRead: number, buffer: Uint8Array } | number = (await this.promisify(this.fs.read)(fd, data, offset, length, normalizedPos)) as any; - - if (typeof result === 'number') { - bytesRead = result; // node.d.ts fail - } else { - bytesRead = result.bytesRead; - } - - return bytesRead; - } catch (error) { - throw this.toFileSystemProviderError(error); - } finally { - this.updatePos(fd, normalizedPos, bytesRead); - } - } - async write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { - await this.initialized; - // we know at this point that the file to write to is truncated and thus empty - // if the write now fails, the file remains empty. as such we really try hard - // to ensure the write succeeds by retrying up to three times. - return retry(() => this.doWrite(fd, pos, data, offset, length), 100 /* ms delay */, 3 /* retries */); - - } - private async doWrite(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { - await this.initialized; - const normalizedPos = this.normalizePos(fd, pos); - - let bytesWritten: number | null = null; - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const result: { bytesWritten: number, buffer: Uint8Array } | number = (await this.promisify(this.fs.write)(fd, data, offset, length, normalizedPos)) as any; - - if (typeof result === 'number') { - bytesWritten = result; // node.d.ts fail - } else { - bytesWritten = result.bytesWritten; - } - - return bytesWritten; - } catch (error) { - throw this.toFileSystemProviderError(error); - } finally { - this.updatePos(fd, normalizedPos, bytesWritten); - } - } - private normalizePos(fd: number, pos: number): number | null { - - // when calling fs.read/write we try to avoid passing in the "pos" argument and - // rather prefer to pass in "null" because this avoids an extra seek(pos) - // call that in some cases can even fail (e.g. when opening a file over FTP - - // see https://github.com/microsoft/vscode/issues/73884). - // - // as such, we compare the passed in position argument with our last known - // position for the file descriptor and use "null" if they match. - if (pos === this.mapHandleToPos.get(fd)) { - return null; - } - - return pos; - } - private updatePos(fd: number, pos: number | null, bytesLength: number | null): void { - const lastKnownPos = this.mapHandleToPos.get(fd); - if (typeof lastKnownPos === 'number') { - - // pos !== null signals that previously a position was used that is - // not null. node.js documentation explains, that in this case - // the internal file pointer is not moving and as such we do not move - // our position pointer. - // - // Docs: "If position is null, data will be read from the current file position, - // and the file position will be updated. If position is an integer, the file position - // will remain unchanged." - if (typeof pos === 'number') { - // do not modify the position - } else if (typeof bytesLength === 'number') { - this.mapHandleToPos.set(fd, lastKnownPos + bytesLength); - } else { - this.mapHandleToPos.delete(fd); - } - } - } - async access?(resource: URI, mode?: number | undefined): Promise { - await this.initialized; - throw new Error('Method not implemented.'); - } - async fsPath?(resource: URI): Promise { - await this.initialized; - throw new Error('Method not implemented.'); - } - async updateFile?(resource: URI, changes: TextDocumentContentChangeEvent[], opts: FileUpdateOptions): Promise { - await this.initialized; - throw new Error('Method not implemented.'); - } - - private toFilePath(resource: URI): string { - return normalize(resource.path.toString()); - } - - private toType(entry: Stats, symbolicLink?: { dangling: boolean }): FileType { - // Signal file type by checking for file / directory, except: - // - symbolic links pointing to non-existing files are FileType.Unknown - // - files that are neither file nor directory are FileType.Unknown - let type: FileType; - if (symbolicLink?.dangling) { - type = FileType.Unknown; - } else if (entry.isFile()) { - type = FileType.File; - } else if (entry.isDirectory()) { - type = FileType.Directory; - } else { - type = FileType.Unknown; - } - - // Always signal symbolic link as file type additionally - if (symbolicLink) { - type |= FileType.SymbolicLink; - } - - return type; - } - - // FIXME typing - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private promisify(f: Function): (...args: any[]) => Promise { - // eslint-disable-next-line @typescript-eslint/tslint/config, @typescript-eslint/no-explicit-any - return function (...args: any[]) { - return new Promise((resolve, reject) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - f(...args, (err: Error, result: T) => err ? reject(err) : resolve(result)); - }); - }; - } - - private toFileSystemProviderError(error: NodeJS.ErrnoException): FileSystemProviderError { - if (error instanceof FileSystemProviderError) { - return error; // avoid double conversion - } - - let code: FileSystemProviderErrorCode; - switch (error.code) { - case 'ENOENT': - code = FileSystemProviderErrorCode.FileNotFound; - break; - case 'EISDIR': - code = FileSystemProviderErrorCode.FileIsADirectory; - break; - case 'ENOTDIR': - code = FileSystemProviderErrorCode.FileNotADirectory; - break; - case 'EEXIST': - code = FileSystemProviderErrorCode.FileExists; - break; - case 'EPERM': - case 'EACCES': - code = FileSystemProviderErrorCode.NoPermissions; - break; - default: - code = FileSystemProviderErrorCode.Unknown; - } - - return createFileSystemProviderError(error, code); - } -} diff --git a/packages/filesystem/src/browser-only/opfs-filesystem-initialization.ts b/packages/filesystem/src/browser-only/opfs-filesystem-initialization.ts new file mode 100644 index 0000000000000..d7e6e072e6da5 --- /dev/null +++ b/packages/filesystem/src/browser-only/opfs-filesystem-initialization.ts @@ -0,0 +1,36 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import type { OPFSFileSystemProvider } from './opfs-filesystem-provider'; +import { injectable } from '@theia/core/shared/inversify'; + +export const OPFSInitialization = Symbol('OPFSInitialization'); +export interface OPFSInitialization { + getRootDirectory(): Promise + initializeFS(provider: OPFSFileSystemProvider): Promise; +} + +@injectable() +export class DefaultOPFSInitialization implements OPFSInitialization { + + getRootDirectory(): Promise { + return navigator.storage.getDirectory(); + } + + async initializeFS(provider: OPFSFileSystemProvider): Promise { + + } +} diff --git a/packages/filesystem/src/browser-only/opfs-filesystem-provider.ts b/packages/filesystem/src/browser-only/opfs-filesystem-provider.ts new file mode 100644 index 0000000000000..51556154e53a5 --- /dev/null +++ b/packages/filesystem/src/browser-only/opfs-filesystem-provider.ts @@ -0,0 +1,347 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { + FileChange, FileChangeType, FileDeleteOptions, + FileOverwriteOptions, FileSystemProviderCapabilities, + FileSystemProviderError, + FileSystemProviderErrorCode, + FileSystemProviderWithFileReadWriteCapability, + FileType, FileWriteOptions, Stat, WatchOptions, createFileSystemProviderError +} from '../common/files'; +import { Emitter, Event, URI, Disposable, Path } from '@theia/core'; +import { OPFSInitialization } from './opfs-filesystem-initialization'; + +/** Options to be used when traversing the file system handles */ +interface CreateFileSystemHandleOptions { + isDirectory?: boolean; + create?: boolean; +} + +@injectable() +export class OPFSFileSystemProvider implements FileSystemProviderWithFileReadWriteCapability { + capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + onDidChangeCapabilities: Event = Event.None; + + private readonly onDidChangeFileEmitter = new Emitter(); + readonly onDidChangeFile = this.onDidChangeFileEmitter.event; + onFileWatchError: Event = Event.None; + + @inject(OPFSInitialization) + protected readonly initialization: OPFSInitialization; + + private directoryHandle: FileSystemDirectoryHandle; + private initialized: Promise; + + @postConstruct() + protected init(): void { + const setup = async (): Promise => { + this.directoryHandle = await this.initialization.getRootDirectory(); + await this.initialization.initializeFS(new Proxy(this, { + get(target, prop, receiver): unknown { + if (prop === 'initialized') { + return Promise.resolve(true); + } + return Reflect.get(target, prop, receiver); + } + })); + return true; + }; + this.initialized = setup(); + } + + watch(_resource: URI, _opts: WatchOptions): Disposable { + return Disposable.NULL; + } + + async exists(resource: URI): Promise { + try { + await this.initialized; + await this.toFileSystemHandle(resource); + return true; + } catch (error) { + return false; + } + } + + async stat(resource: URI): Promise { + try { + await this.initialized; + + const handle = await this.toFileSystemHandle(resource); + + if (handle.kind === 'file') { + const fileHandle = handle as FileSystemFileHandle; + const file = await fileHandle.getFile(); + return { + type: FileType.File, + ctime: file.lastModified, + mtime: file.lastModified, + size: file.size + }; + } else if (handle.kind === 'directory') { + return { + type: FileType.Directory, + ctime: 0, + mtime: 0, + size: 0 + }; + } + + throw createFileSystemProviderError('Unknown file handle error', FileSystemProviderErrorCode.Unknown); + + } catch (error) { + throw createFileSystemProviderError(`Error while accessing resource ${resource.toString()}`, FileSystemProviderErrorCode.Unknown); + } + } + + async mkdir(resource: URI): Promise { + await this.initialized; + try { + await this.toFileSystemHandle(resource, { create: true, isDirectory: true }); + this.onDidChangeFileEmitter.fire([{ resource, type: FileChangeType.ADDED }]); + } catch (error) { + throw toFileSystemProviderError(error, true); + } + } + + async readdir(resource: URI): Promise<[string, FileType][]> { + await this.initialized; + + try { + // Get the directory handle from the directoryHandle + const directoryHandle = await this.toFileSystemHandle(resource, { create: false, isDirectory: true }) as FileSystemDirectoryHandle; + + const result: [string, FileType][] = []; + + // Iterate through the entries in the directory (files and subdirectories) + for await (const [name, handle] of directoryHandle.entries()) { + // Determine the type of the entry (file or directory) + if (handle.kind === 'file') { + result.push([name, FileType.File]); + } else if (handle.kind === 'directory') { + result.push([name, FileType.Directory]); + } + } + + return result; + } catch (error) { + throw toFileSystemProviderError(error, true); + } + } + + async delete(resource: URI, _opts: FileDeleteOptions): Promise { + await this.initialized; + try { + const parentURI = resource.parent; + const parentHandle = await this.toFileSystemHandle(parentURI, { create: false, isDirectory: true }); + if (parentHandle.kind !== 'directory') { + throw createFileSystemProviderError(new Error('Parent is not a directory'), FileSystemProviderErrorCode.FileNotADirectory); + } + const name = resource.path.base; + return (parentHandle as FileSystemDirectoryHandle).removeEntry(name, { recursive: _opts.recursive }); + } catch (error) { + throw toFileSystemProviderError(error); + } finally { + this.onDidChangeFileEmitter.fire([{ resource, type: FileChangeType.DELETED }]); + } + } + + async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + await this.initialized; + + try { + const fromHandle = await this.toFileSystemHandle(from); + // Check whether the source is a file or directory + if (fromHandle.kind === 'directory') { + // Create the new directory and get the handle + await this.mkdir(to); + const toHandle = await this.toFileSystemHandle(to) as FileSystemDirectoryHandle; + await copyDirectoryContents(fromHandle as FileSystemDirectoryHandle, toHandle); + + // Delete the old directory + await this.delete(from, { recursive: true, useTrash: false }); + } else { + const content = await this.readFile(from); + await this.writeFile(to, content, { create: true, overwrite: opts.overwrite }); + await this.delete(from, { recursive: true, useTrash: false }); + } + + this.onDidChangeFileEmitter.fire([{ resource: to, type: FileChangeType.ADDED }]); + } catch (error) { + throw toFileSystemProviderError(error); + } + } + + async readFile(resource: URI): Promise { + await this.initialized; + + try { + // Get the file handle from the directoryHandle + const fileHandle = await this.toFileSystemHandle(resource, { create: false, isDirectory: false }) as FileSystemFileHandle; + + // Get the file itself (which includes the content) + const file = await fileHandle.getFile(); + + // Read the file as an ArrayBuffer and convert it to Uint8Array + const arrayBuffer = await file.arrayBuffer(); + return new Uint8Array(arrayBuffer); + } catch (error) { + throw toFileSystemProviderError(error, false); + } + } + + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + await this.initialized; + let writeableHandle: FileSystemWritableFileStream | undefined = undefined; + try { + // Validate target unless { create: true, overwrite: true } + if (!opts.create || !opts.overwrite) { + const fileExists = await this.stat(resource).then(() => true, () => false); + if (fileExists) { + if (!opts.overwrite) { + throw createFileSystemProviderError('File already exists', FileSystemProviderErrorCode.FileExists); + } + } else { + if (!opts.create) { + throw createFileSystemProviderError('File does not exist', FileSystemProviderErrorCode.FileNotFound); + } + } + } + + const handle = await this.toFileSystemHandle(resource, { create: true, isDirectory: false }) as FileSystemFileHandle; + + // Open + writeableHandle = await handle?.createWritable(); + + // Write content at once + await writeableHandle?.write(content); + + this.onDidChangeFileEmitter.fire([{ resource: resource, type: FileChangeType.UPDATED }]); + } catch (error) { + throw toFileSystemProviderError(error, false); + } finally { + if (typeof writeableHandle !== 'undefined') { + await writeableHandle.close(); + } + } + } + + /** + * Returns the FileSystemHandle for the given resource given by a URI. + * @param resource URI/path of the resource + * @param options Options for the creation of the handle while traversing the path + * @returns FileSystemHandle for the given resource + */ + private async toFileSystemHandle(resource: URI, options?: CreateFileSystemHandleOptions): Promise { + const pathParts = resource.path.toString().split(Path.separator).filter(Boolean); + + return recursiveFileSystemHandle(this.directoryHandle, pathParts, options); + } +} + +// #region Helper functions +async function recursiveFileSystemHandle(handle: FileSystemHandle, pathParts: string[], options?: CreateFileSystemHandleOptions): Promise { + // We reached the end of the path, this happens only when not creating + if (pathParts.length === 0) { + return handle; + } + // If there are parts left, the handle must be a directory + if (handle.kind !== 'directory') { + throw FileSystemProviderErrorCode.FileNotADirectory; + } + const dirHandle = handle as FileSystemDirectoryHandle; + // We need to create it and thus we need to stop early to create the file or directory + if (pathParts.length === 1 && options?.create) { + if (options?.isDirectory) { + return dirHandle.getDirectoryHandle(pathParts[0], { create: options.create }); + } else { + return dirHandle.getFileHandle(pathParts[0], { create: options.create }); + } + } + + // Continue to resolve the path + const part = pathParts.shift()!; + for await (const entry of dirHandle.entries()) { + // Check the entry name in the current directory + if (entry[0] === part) { + return recursiveFileSystemHandle(entry[1], pathParts, options); + } + } + + // If we haven't found the part, we need to create it along the way + if (options?.create) { + const newHandle = await dirHandle.getDirectoryHandle(part, { create: true }); + return recursiveFileSystemHandle(newHandle, pathParts, options); + } + + throw FileSystemProviderErrorCode.FileNotFound; +} + +// Function to copy directory contents recursively +async function copyDirectoryContents(sourceHandle: FileSystemDirectoryHandle, destinationHandle: FileSystemDirectoryHandle): Promise { + for await (const [name, handle] of sourceHandle.entries()) { + if (handle.kind === 'file') { + const file = await (handle as FileSystemFileHandle).getFile(); + const newFileHandle = await destinationHandle.getFileHandle(name, { create: true }); + const writable = await newFileHandle.createWritable(); + try { + await writable.write(await file.arrayBuffer()); + } finally { + await writable.close(); + } + } else if (handle.kind === 'directory') { + const newSubDirHandle = await destinationHandle.getDirectoryHandle(name, { create: true }); + await copyDirectoryContents(handle as FileSystemDirectoryHandle, newSubDirHandle); + } + } +} + +function toFileSystemProviderError(error: DOMException, is_dir?: boolean): FileSystemProviderError { + if (error instanceof FileSystemProviderError) { + return error; // avoid double conversion + } + + let code: FileSystemProviderErrorCode; + switch (error.name) { + case 'NotFoundError': + code = FileSystemProviderErrorCode.FileNotFound; + break; + case 'InvalidModificationError': + code = FileSystemProviderErrorCode.FileExists; + break; + case 'NotAllowedError': + code = FileSystemProviderErrorCode.NoPermissions; + break; + case 'TypeMismatchError': + if (!is_dir) { + code = FileSystemProviderErrorCode.FileIsADirectory; + } else { + code = FileSystemProviderErrorCode.FileNotADirectory; + } + + break; + case 'QuotaExceededError': + code = FileSystemProviderErrorCode.FileTooLarge; + break; + default: + code = FileSystemProviderErrorCode.Unknown; + } + + return createFileSystemProviderError(error, code); +} +// #endregion diff --git a/yarn.lock b/yarn.lock index b41f28d988304..21d2692e9f2aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3399,7 +3399,7 @@ async-mutex@^0.4.0: dependencies: tslib "^2.4.0" -async@^2.1.4, async@^2.6.4: +async@^2.6.4: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== @@ -3737,14 +3737,6 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserfs@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/browserfs/-/browserfs-1.4.3.tgz#92ffc6063967612daccdb8566d3fc03f521205fb" - integrity sha512-tz8HClVrzTJshcyIu8frE15cjqjcBIu15Bezxsvl/i+6f59iNCN3kznlWjz0FEb3DlnDx3gW5szxeT6D1x0s0w== - dependencies: - async "^2.1.4" - pako "^1.0.4" - browserslist@^4.21.10, browserslist@^4.22.2, browserslist@^4.22.3: version "4.23.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" @@ -9707,11 +9699,6 @@ pacote@^15.2.0: ssri "^10.0.0" tar "^6.1.11" -pako@^1.0.4: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" From 22e7bdc9667e639bdc21db9434c4a7039da3d58b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:06:19 +0100 Subject: [PATCH 34/75] Translation update for version 1.56.0 (#14549) Co-authored-by: sgraband --- packages/core/i18n/nls.cs.json | 1 - packages/core/i18n/nls.de.json | 1 - packages/core/i18n/nls.es.json | 1 - packages/core/i18n/nls.fr.json | 1 - packages/core/i18n/nls.hu.json | 1 - packages/core/i18n/nls.it.json | 1 - packages/core/i18n/nls.ja.json | 1 - packages/core/i18n/nls.json | 1 - packages/core/i18n/nls.ko.json | 1 - packages/core/i18n/nls.pl.json | 1 - packages/core/i18n/nls.pt-br.json | 1 - packages/core/i18n/nls.ru.json | 1 - packages/core/i18n/nls.tr.json | 1 - packages/core/i18n/nls.zh-cn.json | 1 - packages/core/i18n/nls.zh-tw.json | 1 - 15 files changed, 15 deletions(-) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index b9bd97a5241bd..43f9eb437fdb7 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Předpokládejme, že není připojena čtečka obrazovky", "editor.bracketPairColorization.enabled": "Ovládá, zda je obarvení dvojice závorek povoleno, nebo ne. Pro přepsání barev zvýraznění závorek použijte `#workbench.colorCustomizations#`.", "editor.codeActionWidget.includeNearbyQuickfixes": "Povolení/zakázání zobrazení nejbližší opravy v rámci řádku, pokud se zrovna neprovádí diagnostika.", - "editor.cursorSurroundingLinesStyle": "Řídí, kdy se má vynutit `#cursorSurroundingLines#`.", "editor.detectIndentation": "Řídí, zda se při otevření souboru automaticky zjistí `#editor.tabSize#` a `#editor.insertSpaces#` na základě obsahu souboru.", "editor.dropIntoEditor.enabled": "Ovládá, zda můžete soubor přetáhnout do textového editoru podržením klávesy `shift` (namísto otevření souboru v editoru).", "editor.formatOnSaveMode.modificationsIfAvailable": "Pokusí se formátovat pouze změny (vyžaduje kontrolu zdrojů). Pokud nelze použít kontrolu zdrojů, bude formátován celý soubor.", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index fc49e00acb4f5..601b976b4a64c 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Angenommen, ein Bildschirmlesegerät ist nicht angeschlossen", "editor.bracketPairColorization.enabled": "Steuert, ob die Einfärbung von Klammerpaaren aktiviert ist oder nicht. Verwenden Sie `#workbench.colorCustomizations#`, um die Farben der Klammerhervorhebung zu überschreiben.", "editor.codeActionWidget.includeNearbyQuickfixes": "Aktivieren/Deaktivieren der Anzeige des nächstgelegenen Quickfix innerhalb einer Zeile, wenn keine Diagnose durchgeführt wird.", - "editor.cursorSurroundingLinesStyle": "Steuert, wann `#cursorSurroundingLines#` erzwungen werden soll.", "editor.detectIndentation": "Steuert, ob `#editor.tabSize#` und `#editor.insertSpaces#` automatisch erkannt werden, wenn eine Datei geöffnet wird, basierend auf dem Inhalt der Datei.", "editor.dropIntoEditor.enabled": "Steuert, ob Sie eine Datei durch Ziehen und Ablegen in einen Texteditor ziehen können, indem Sie die \"Umschalttaste\" gedrückt halten (anstatt die Datei in einem Editor zu öffnen).", "editor.formatOnSaveMode.modificationsIfAvailable": "Es wird versucht, nur Änderungen zu formatieren (erfordert Quellensicherung). Wenn die Versionskontrolle nicht verwendet werden kann, wird die gesamte Datei formatiert.", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index 28a27e008492b..d5ebb1da302e2 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Supongamos que no hay un lector de pantalla conectado", "editor.bracketPairColorization.enabled": "Controla si la coloración del par de corchetes está activada o no. Utilice `#workbench.colorCustomizations#` para anular los colores de resaltado de los corchetes.", "editor.codeActionWidget.includeNearbyQuickfixes": "Activar/desactivar la visualización del quickfix más cercano dentro de una línea cuando no se está actualmente en un diagnóstico.", - "editor.cursorSurroundingLinesStyle": "Controla cuándo debe aplicarse `#cursorSurroundingLines#`.", "editor.detectIndentation": "Controla si `#editor.tabSize#` y `#editor.insertSpaces#` se detectarán automáticamente al abrir un archivo en función de su contenido.", "editor.dropIntoEditor.enabled": "Controla si puedes arrastrar y soltar un archivo en un editor de texto manteniendo pulsada la tecla `shift` (en lugar de abrir el archivo en un editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Intentará formatear sólo las modificaciones (requiere control de origen). Si no se puede utilizar el control de origen, se formateará todo el archivo.", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index 64e75ba0daa36..0a88327d0c807 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Supposons qu'il n'y ait pas de lecteur d'écran", "editor.bracketPairColorization.enabled": "Contrôle si la colorisation des paires de crochets est activée ou non. Utilisez `#workbench.colorCustomizations#` pour surcharger les couleurs de mise en évidence des crochets.", "editor.codeActionWidget.includeNearbyQuickfixes": "Activer/désactiver l'affichage de la réparation rapide la plus proche dans une ligne lorsqu'il n'y a pas de diagnostic en cours.", - "editor.cursorSurroundingLinesStyle": "Contrôle quand `#cursorSurroundingLines#` doit être appliqué.", "editor.detectIndentation": "Contrôle si `#editor.tabSize#` et `#editor.insertSpaces#` seront automatiquement détectés lors de l'ouverture d'un fichier en fonction de son contenu.", "editor.dropIntoEditor.enabled": "Contrôle si vous pouvez glisser-déposer un fichier dans un éditeur de texte en maintenant la touche \"Maj\" enfoncée (au lieu d'ouvrir le fichier dans un éditeur).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tentera de formater uniquement les modifications (nécessite le contrôle de la source). Si le contrôle de la source ne peut pas être utilisé, alors le fichier entier sera formaté.", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index b86422d98516d..ffacaeb65e89b 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Feltételezzük, hogy a képernyőolvasó nincs csatlakoztatva", "editor.bracketPairColorization.enabled": "Szabályozza, hogy a zárójelpár színezése engedélyezve legyen-e vagy sem. Használja a `#workbench.colorCustomizations#` parancsot a zárójelek kiemelési színeinek felülbírálásához.", "editor.codeActionWidget.includeNearbyQuickfixes": "A legközelebbi gyorsjavítás megjelenítésének engedélyezése/letiltása egy soron belül, ha éppen nem diagnosztikán van.", - "editor.cursorSurroundingLinesStyle": "Szabályozza, hogy mikor legyen érvényes a `#cursorSurroundingLines#`.", "editor.detectIndentation": "Szabályozza, hogy a `#editor.tabSize#` és a `#editor.insertSpaces#` automatikusan felismerésre kerüljön-e egy fájl megnyitásakor a fájl tartalma alapján.", "editor.dropIntoEditor.enabled": "Azt szabályozza, hogy a \"shift\" lenyomva tartásával húzhat-e egy fájlt egy szövegszerkesztőbe (ahelyett, hogy megnyitná a fájlt egy szerkesztőprogramban).", "editor.formatOnSaveMode.modificationsIfAvailable": "Csak a módosítások formázására tesz kísérletet (forrásellenőrzés szükséges). Ha a forrásellenőrzés nem használható, akkor a teljes fájl lesz formázva.", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index a4f0d61b54de8..cdd24c0598e1f 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Supponiamo che un lettore di schermo non sia collegato", "editor.bracketPairColorization.enabled": "Controlla se la colorazione delle coppie di parentesi è abilitata o meno. Usare `#workbench.colorCustomizations#` per sovrascrivere i colori di evidenziazione delle parentesi.", "editor.codeActionWidget.includeNearbyQuickfixes": "Abilita/disabilita la visualizzazione della soluzione rapida più vicina all'interno di una linea quando non è in corso una diagnostica.", - "editor.cursorSurroundingLinesStyle": "Controlla quando `#cursorSurroundingLines#` deve essere applicato.", "editor.detectIndentation": "Controlla se `#editor.tabSize#` e `#editor.insertSpaces#` saranno rilevati automaticamente all'apertura di un file, in base al suo contenuto.", "editor.dropIntoEditor.enabled": "Controlla se è possibile trascinare e rilasciare un file in un editor di testo tenendo premuto `shift` (invece di aprire il file in un editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tenterà di formattare solo le modifiche (richiede il controllo della fonte). Se il controllo della fonte non può essere usato, allora l'intero file sarà formattato.", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index ae43eb7ceccc3..ca894be855cde 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "スクリーンリーダーが添付されていないと仮定する", "editor.bracketPairColorization.enabled": "ブラケットペアのカラー化を有効にするかどうかを制御する。ブラケットのハイライト色を上書きするには `#workbench.colorCustomizations#` を使用してください。", "editor.codeActionWidget.includeNearbyQuickfixes": "現在診断中でない場合に、行内に最も近いクイックフィックスを表示するかどうかを設定します。", - "editor.cursorSurroundingLinesStyle": "cursorSurroundingLines#`を実行するタイミングを制御する。", "editor.detectIndentation": "ファイルを開いたときに、ファイルの内容に基づいて `#editor.tabSize#` と `#editor.insertSpaces#` を自動的に検出するかどうかを制御する。", "editor.dropIntoEditor.enabled": "shift`を押しながらファイルをテキストエディタにドラッグ&ドロップできるかどうかをコントロールします(エディタでファイルを開く代わりに)。", "editor.formatOnSaveMode.modificationsIfAvailable": "変更点のみをフォーマットしようとします(ソースコントロールが必要です)。ソースコントロールが使用できない場合は、ファイル全体がフォーマットされます。", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index 0260ec913a9a8..4bb702ec73d63 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Assume a screen reader is not attached", "editor.bracketPairColorization.enabled": "Controls whether bracket pair colorization is enabled or not. Use `#workbench.colorCustomizations#` to override the bracket highlight colors.", "editor.codeActionWidget.includeNearbyQuickfixes": "Enable/disable showing nearest quickfix within a line when not currently on a diagnostic.", - "editor.cursorSurroundingLinesStyle": "Controls when `#cursorSurroundingLines#` should be enforced.", "editor.detectIndentation": "Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents.", "editor.dropIntoEditor.enabled": "Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Will attempt to format modifications only (requires source control). If source control can't be used, then the whole file will be formatted.", diff --git a/packages/core/i18n/nls.ko.json b/packages/core/i18n/nls.ko.json index aafce95dfb254..7a4d9ef04b893 100644 --- a/packages/core/i18n/nls.ko.json +++ b/packages/core/i18n/nls.ko.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "화면 리더가 연결되어 있지 않다고 가정합니다.", "editor.bracketPairColorization.enabled": "대괄호 쌍 색상화 사용 여부를 제어합니다. 대괄호 하이라이트 색상을 재정의하려면 `#workbench.colorCustomizations#`를 사용합니다.", "editor.codeActionWidget.includeNearbyQuickfixes": "현재 진단 중이 아닐 때 줄 내에서 가장 가까운 퀵픽스 표시를 사용/사용 안 함으로 설정합니다.", - "editor.cursorSurroundingLinesStyle": "커서 서라운드 라인 적용 시기를 제어합니다.", "editor.detectIndentation": "파일 내용을 기반으로 파일을 열 때 `#편집기.탭 크기#` 및 `#편집기.삽입 공백#`을 자동으로 감지할지 여부를 제어합니다.", "editor.dropIntoEditor.enabled": "편집기에서 파일을 여는 대신 'Shift' 키를 누른 채로 파일을 텍스트 편집기로 끌어다 놓을 수 있는지 여부를 제어합니다.", "editor.formatOnSaveMode.modificationsIfAvailable": "수정 사항만 포맷하려고 시도합니다(소스 제어 필요). 소스 제어를 사용할 수 없는 경우 전체 파일이 포맷됩니다.", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 4889392473dfb..d92a97f907e16 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Załóżmy, że czytnik ekranu nie jest podłączony", "editor.bracketPairColorization.enabled": "Kontroluje, czy kolorowanie par nawiasów jest włączone, czy nie. Użyj `#workbench.colorCustomizations#`, aby zastąpić kolory podświetlenia nawiasów.", "editor.codeActionWidget.includeNearbyQuickfixes": "Włączenie/wyłączenie wyświetlania najbliższego quickfixa w linii, gdy nie jest on aktualnie w diagnostyce.", - "editor.cursorSurroundingLinesStyle": "Kontroluje, kiedy `#cursorSurroundingLines#` powinien być wymuszany.", "editor.detectIndentation": "Kontroluje, czy `#editor.tabSize#` i `#editor.insertSpaces#` będą automatycznie wykrywane podczas otwierania pliku na podstawie jego zawartości.", "editor.dropIntoEditor.enabled": "Kontroluje, czy można przeciągnąć i upuścić plik do edytora tekstu, przytrzymując `shift` (zamiast otwierać plik w edytorze).", "editor.formatOnSaveMode.modificationsIfAvailable": "Spowoduje próbę sformatowania tylko modyfikacji (wymaga kontroli źródła). Jeśli kontrola źródła nie może być użyta, sformatowany zostanie cały plik.", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index 7fb28b8ba55a4..c06ac9f9eb7e3 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Suponha que um leitor de tela não esteja conectado", "editor.bracketPairColorization.enabled": "Controla se a colorização de pares de colchetes está ativada ou não. Use `#workbench.colorCustomizations#` para substituir as cores de destaque dos colchetes.", "editor.codeActionWidget.includeNearbyQuickfixes": "Ativar/desativar a exibição do reparo rápido mais próximo em uma linha quando não estiver em um diagnóstico no momento.", - "editor.cursorSurroundingLinesStyle": "Controla quando o `#cursorSurroundingLines#` deve ser aplicado.", "editor.detectIndentation": "Controla se `#editor.tabSize#` e `#editor.insertSpaces#` serão detectados automaticamente quando um arquivo for aberto com base no conteúdo do arquivo.", "editor.dropIntoEditor.enabled": "Controla se você pode arrastar e soltar um arquivo em um editor de texto mantendo pressionada a tecla `shift` (em vez de abrir o arquivo em um editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tentará formatar modificações apenas (requer controle de fonte). Se o controle da fonte não puder ser usado, então o arquivo inteiro será formatado.", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index 15ffc3b23ce89..089b1e32623da 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Предположим, что устройство чтения с экрана не подключено", "editor.bracketPairColorization.enabled": "Управляет тем, включена или нет раскраска пар скобок. Используйте `#workbench.colorCustomizations#`, чтобы переопределить цвета выделения скобок.", "editor.codeActionWidget.includeNearbyQuickfixes": "Включить/выключить показ ближайшего быстрого исправления в линии, если в данный момент нет диагностики.", - "editor.cursorSurroundingLinesStyle": "Контролирует, когда `#cursorSurroundingLines#` должен быть применен.", "editor.detectIndentation": "Определяет, будут ли `#editor.tabSize#` и `#editor.insertSpaces#` автоматически определяться при открытии файла на основе его содержимого.", "editor.dropIntoEditor.enabled": "Позволяет перетащить файл в текстовый редактор, удерживая клавишу `shift` (вместо открытия файла в редакторе).", "editor.formatOnSaveMode.modificationsIfAvailable": "Попытается отформатировать только модификации (требуется контроль исходного текста). Если контроль источника не может быть использован, то будет отформатирован весь файл.", diff --git a/packages/core/i18n/nls.tr.json b/packages/core/i18n/nls.tr.json index 60865450e135d..08aaa4c466168 100644 --- a/packages/core/i18n/nls.tr.json +++ b/packages/core/i18n/nls.tr.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "Bir ekran okuyucunun bağlı olmadığını varsayalım", "editor.bracketPairColorization.enabled": "Ayraç çifti renklendirmesinin etkin olup olmadığını kontrol eder. Ayraç vurgu renklerini geçersiz kılmak için `#workbench.colorCustomizations#` kullanın.", "editor.codeActionWidget.includeNearbyQuickfixes": "O anda bir tanılama üzerinde değilken bir hat içindeki en yakın hızlı düzeltmeyi göstermeyi etkinleştirin/devre dışı bırakın.", - "editor.cursorSurroundingLinesStyle": "Ne zaman `#cursorSurroundingLines#` uygulanacağını kontrol eder.", "editor.detectIndentation": "Dosya içeriğine bağlı olarak bir dosya açıldığında `#editor.tabSize#` ve `#editor.insertSpaces#` öğelerinin otomatik olarak algılanıp algılanmayacağını kontrol eder.", "editor.dropIntoEditor.enabled": "Bir dosyayı `shift` tuşunu basılı tutarak bir metin düzenleyicisine sürükleyip bırakıp bırakamayacağınızı kontrol eder (dosyayı bir düzenleyicide açmak yerine).", "editor.formatOnSaveMode.modificationsIfAvailable": "Yalnızca değişiklikleri biçimlendirmeye çalışır (kaynak kontrolü gerektirir). Kaynak kontrolü kullanılamazsa, tüm dosya biçimlendirilecektir.", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 78c9e99deaf87..c7e0ee53ddb0e 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "假设未连接屏幕阅读器", "editor.bracketPairColorization.enabled": "控制是否启用括号对着色。使用 `#workbench.colorCustomizations#` 覆盖括号高亮颜色。", "editor.codeActionWidget.includeNearbyQuickfixes": "启用/禁用在当前未进行诊断时显示行内最近的快速修复。", - "editor.cursorSurroundingLinesStyle": "控制何时执行 `#cursorSurroundingLines#` 。", "editor.detectIndentation": "控制打开文件时是否根据文件内容自动检测 `#editor.tabSize#` 和 `#editor.insertSpaces#`。", "editor.dropIntoEditor.enabled": "控制是否可以按住 `shift` 将文件拖放到文本编辑器中(而不是在编辑器中打开文件)。", "editor.formatOnSaveMode.modificationsIfAvailable": "将尝试只对修改部分进行格式化(需要源代码控制)。如果不能使用源码控制,那么整个文件将被格式化。", diff --git a/packages/core/i18n/nls.zh-tw.json b/packages/core/i18n/nls.zh-tw.json index d8a444b8127c4..781208e4fd393 100644 --- a/packages/core/i18n/nls.zh-tw.json +++ b/packages/core/i18n/nls.zh-tw.json @@ -144,7 +144,6 @@ "editor.accessibilitySupport2": "假設沒有連接螢幕閱讀器", "editor.bracketPairColorization.enabled": "控制是否啟用括號對著色。使用 `#workbench.colorCustomizations#` 來覆寫括弧高亮顏色。", "editor.codeActionWidget.includeNearbyQuickfixes": "當目前未進行診斷時,啟用/停用在行內顯示最近的快速修復。", - "editor.cursorSurroundingLinesStyle": "控制何時執行 `#cursorSurroundingLines#`。", "editor.detectIndentation": "控制在開啟檔案時,是否會根據檔案內容自動偵測 `#editor.tabSize#` 和 `#editor.insertSpaces#`。", "editor.dropIntoEditor.enabled": "控制是否可以按住 `shift` 將檔案拖放到文字編輯器中 (而不是在編輯器中開啟檔案)。", "editor.formatOnSaveMode.modificationsIfAvailable": "僅嘗試格式化修改 (需要來源控制)。如果無法使用原始碼控制,則會格式化整個檔案。", From b00db52f9693249b810ad006a9028a5428b5a978 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 28 Nov 2024 11:04:30 +0100 Subject: [PATCH 35/75] docs: update changelog for 1.56.0 --- CHANGELOG.md | 72 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c627bbae735e7..d9bfb996c16bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,18 +4,60 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) - - +## 1.56.0 - 11/28/2024 + +- [ai] added support for users to specify custom request settings, model, and optionally provider-specific [#14535](https://github.com/eclipse-theia/theia/pull/14535) +- [ai] allowed specifying max lines used for AI code completion context [#14539](https://github.com/eclipse-theia/theia/pull/14539) +- [ai] added hovers for agents and variables [#14498](https://github.com/eclipse-theia/theia/pull/14498) +- [ai] allowed comments in prompt templates [#14470](https://github.com/eclipse-theia/theia/pull/14470) +- [ai] added local models to non-streaming accept list [#14420](https://github.com/eclipse-theia/theia/pull/14420) +- [ai] allowed canceling llama-file requests [#14515](https://github.com/eclipse-theia/theia/pull/14515) +- [ai] showed arguments in tool call response renderer [#14424](https://github.com/eclipse-theia/theia/pull/14424) +- [ai] supported prompt variants [#14487](https://github.com/eclipse-theia/theia/pull/14487) +- [ai] turned automatic inline completion off by default [#14513](https://github.com/eclipse-theia/theia/pull/14513) +- [ai] fixed request settings and stop words in HF provider [#14504](https://github.com/eclipse-theia/theia/pull/14504) +- [ai] added preference to ignore files in workspace functions [#14449](https://github.com/eclipse-theia/theia/pull/14449) +- [ai] fixed prompt template contribution category and template [#14497](https://github.com/eclipse-theia/theia/pull/14497) +- [ai] chore: avoided conflicting keybinding for opening chat window [#14495](https://github.com/eclipse-theia/theia/pull/14495) +- [ai] supported agents asking for input and continuing [#14486](https://github.com/eclipse-theia/theia/pull/14486) - Contributed on behalf of STMicroelectronics +- [ai] showed progress while calculating AI code completion [#14537](https://github.com/eclipse-theia/theia/pull/14537) +- [ai] fixed AI history view crashing on first use [#14443](https://github.com/eclipse-theia/theia/pull/14443) +- [ai] fixed: added React keys in chat views [#14444](https://github.com/eclipse-theia/theia/pull/14444) +- [ai] sorted models in LLM selection dialogue in AI Configuration View [#14442](https://github.com/eclipse-theia/theia/pull/14442) +- [ai] added support for Hugging Face [#14412](https://github.com/eclipse-theia/theia/pull/14412) +- [ai] added manual AI code completion [#14393](https://github.com/eclipse-theia/theia/pull/14393) +- [ai] improved workspace agent functions and prompt [#14426](https://github.com/eclipse-theia/theia/pull/14426) +- [ai] improved agent history recording [#14378](https://github.com/eclipse-theia/theia/pull/14378) +- [ai] allowed reopening AI history widget [#14422](https://github.com/eclipse-theia/theia/pull/14422) +- [ai] avoided prompt template directory error [#14421](https://github.com/eclipse-theia/theia/pull/14421) +- [ai] adjusted chat input height dynamically [#14432](https://github.com/eclipse-theia/theia/pull/14432) +- [ai] fixed: allowed all three brackets for variables [#14465](https://github.com/eclipse-theia/theia/pull/14465) +- [ai] allowed adding variants via prompt template files [#14509](https://github.com/eclipse-theia/theia/pull/14509) - [application-package] bumped API version to 1.95.3 [#14541](https://github.com/eclipse-theia/theia/pull/14541) - Contributed on behalf of STMicroelectronics +- [browser-only] removed browserfs dependency (replaced by OPFS) [#14263](https://github.com/eclipse-theia/theia/pull/14263) +- [core] updated Inversify to latest [#14435](https://github.com/eclipse-theia/theia/pull/14435) +- [core] updated parcel watcher to 2.5.0 [#14545](https://github.com/eclipse-theia/theia/pull/14545) +- [core] fixed calculation of SelectComponent dropdown bottom [#14381](https://github.com/eclipse-theia/theia/pull/14381) - [core] fixed electron win background color [#14491](https://github.com/eclipse-theia/theia/pull/14491) - Contributed on behalf of STMicroelectronics -- [plugin] added stubs for the additional LanguageModel APIs [#14446](https://github.com/eclipse-theia/theia/pull/14446) - Contributed on behalf of STMicroelectronics -- [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics +- [core] fixed: kept closable state through pin/unpin [#14377](https://github.com/eclipse-theia/theia/pull/14377) - Contributed on behalf of STMicroelectronics +- [core] fixed alignment of viewWelcome to VS Code [#14391](https://github.com/eclipse-theia/theia/pull/14391) +- [core] fixed uppermost context menu item sometimes not clickable in Electron [#14401](https://github.com/eclipse-theia/theia/pull/14401) +- [debug] disabled editing of read-only variables [#14440](https://github.com/eclipse-theia/theia/pull/14440) +- [dev-container] fixed container stopping on disconnect and application close [#14542](https://github.com/eclipse-theia/theia/pull/14542) +- [electron] pinned Electron version to 30.1.2 [#14407](https://github.com/eclipse-theia/theia/pull/14407) - Contributed on behalf of STMicroelectronics +- [filesystem] added support for files.insertFinalNewline during formatOnSave [#13751](https://github.com/eclipse-theia/theia/pull/13751) - Contributed on behalf of STMicroelectronics +- [plugin] added min height to cell outputs [#14488](https://github.com/eclipse-theia/theia/pull/14488) +- [plugin] fixed selection and improved active text editor behavior [#14480](https://github.com/eclipse-theia/theia/pull/14480) - [plugin] supported MappedEditProviders proposed API evolution [#14453](https://github.com/eclipse-theia/theia/pull/14453) - Contributed on behalf of STMicroelectronics -- [browser-only] remove browserfs dependency (replaced by OPFS) [#14263](https://github.com/eclipse-theia/theia/pull/14263) +- [plugin] added support for ThemeColor property id [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics +- [plugin] added support for property ThemeColor ID [#14437](https://github.com/eclipse-theia/theia/pull/14437) - Contributed on behalf of STMicroelectronics +- [plugin-ext] fixed overlapping outputs when creating a cell at the top [#14417](https://github.com/eclipse-theia/theia/pull/14417) +- [plugin-ext] fixed active notebook editor staying active as long as the editor is active [#14419](https://github.com/eclipse-theia/theia/pull/14419) +- [metrics] fixed MeasurementNotificationService binding [#14439](https://github.com/eclipse-theia/theia/pull/14439) [Breaking Changes:](#breaking_changes_1.56.0) -- [core] Do not dispose dialogs on close - [#14456](https://github.com/eclipse-theia/theia/pull/14456) - Contributed on behalf of STMicroelectronics +- [core] fixed disposing of dialogs on close - [#14456](https://github.com/eclipse-theia/theia/pull/14456) - Contributed on behalf of STMicroelectronics ## 1.55.0 - 10/31/2024 @@ -262,7 +304,7 @@ [Breaking Changes:](#breaking_changes_1.50.0) - [core] Classes implementing the `Saveable` interface no longer need to implement the `autoSave` field. However, a new `onContentChanged` event has been added instead. -- [navigator] The `Open With...` command now uses a dedicated `OpenWithHandler` to populate the quick pick. +- [navigator] The `Open With...` command now uses a dedicated `OpenWithHandler` to populate the quick pick. Adopters contributing an open handler need to explicitly add the handler to the `OpenWithHandler` ([#13573](https://github.com/eclipse-theia/theia/pull/13573)). ## v1.49.0 - 04/29/2024 @@ -356,6 +398,7 @@ - [terminal] added terminal observer API [#13402](https://github.com/eclipse-theia/theia/pull/13402) [Breaking Changes:](#breaking_changes_1.48.0) + - [core] Add secondary windows support for text editors. [#13493](https://github.com/eclipse-theia/theia/pull/13493 ). The changes in require more extensive patches for our dependencies than before. For this purpose, we are using the `patch-package` library. However, this change requires adopters to add the line `"postinstall": "theia-patch"` to the `package.json` at the root of their monorepo (where the `node_modules` folder is located). - contributed on behalf of STMicroelectronics ## v1.47.0 - 02/29/2024 @@ -408,6 +451,7 @@ file comment in `monaco-init.ts`. ## v1.46.0 - 01/25/2024 + - [plugin] Add prefix to contributed view container ids [#13362](https://github.com/eclipse-theia/theia/pull/13362) - contributed on behalf of STMicroelectronics - [application-manager] updated message for missing Electron main entries [#13242](https://github.com/eclipse-theia/theia/pull/13242) - [application-package] bumped the default supported API from `1.84.2` to `1.85.1` [#13276](https://github.com/eclipse-theia/theia/pull/13276) - contributed on behalf of STMicroelectronics @@ -663,9 +707,9 @@ [Breaking Changes:](#breaking_changes_1.40.0) - [preferences] changed the `window.tabbar.enhancedPreview` preference from boolean to enum: [#12648](https://github.com/eclipse-theia/theia/pull/12648) - Contributed on behalf of STMicroelectronics - - `classic`: Display a simple preview of the tab with basic information. - - `enhanced`: Display an enhanced preview of the tab with additional information. (The behavior introduced in [#12350](https://github.com/eclipse-theia/theia/pull/12350)) - - `visual`: Display a visual preview of the tab. (The preview support was added with this PR) + - `classic`: Display a simple preview of the tab with basic information. + - `enhanced`: Display an enhanced preview of the tab with additional information. (The behavior introduced in [#12350](https://github.com/eclipse-theia/theia/pull/12350)) + - `visual`: Display a visual preview of the tab. (The preview support was added with this PR) - [repo] updated GitHub workflow to stop publishing `next` versions [#12699](https://github.com/eclipse-theia/theia/pull/12699) - [workspace] split `CommonWorkspaceUtils` into `WorkspaceFileService` and `UntitledWorkspaceService` [#12420](https://github.com/eclipse-theia/theia/pull/12420) - [plugin] Removed synchronous `fs` calls from the backend application and plugins. The plugin scanner, directory and file handlers, and the plugin deploy entry has async API now. Internal `protected` APIs have been affected. [#12798](https://github.com/eclipse-theia/theia/pull/12798) @@ -845,11 +889,11 @@ - [core] changed default icon theme from `none` to `theia-file-icons` [#11028](https://github.com/eclipse-theia/theia/pull/12346) - [plugin] renamed `TreeViewExtImpl#toTreeItem()` to `TreeViewExtImpl#toTreeElement()` - [scm] fixed `scm` inline toolbar commands, the changes introduces the following breakage: [#12295](https://github.com/eclipse-theia/theia/pull/12295) - - Interface `ScmInlineAction` removes `commands: CommandRegistry` - - Interface `ScmInlineActions` removes `commands: CommandRegistry` - - Interface `ScmTreeWidget.Props` removes `commands: CommandRegistry` + - Interface `ScmInlineAction` removes `commands: CommandRegistry` + - Interface `ScmInlineActions` removes `commands: CommandRegistry` + - Interface `ScmTreeWidget.Props` removes `commands: CommandRegistry` - [terminal] removed `openTerminalFromProfile` method from `TerminalFrontendContribution` [#12322](https://github.com/eclipse-theia/theia/pull/12322) -- [electron] enabled context isolation and disabled node integration in Electron renderer (https://github.com/eclipse-theia/theia/issues/2018) +- [electron] enabled context isolation and disabled node integration in Electron renderer () ## v1.35.0 - 02/23/2023 From f5b4066b40fecb09d88b26abfaa149c7bc5e940b Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 28 Nov 2024 13:11:45 +0100 Subject: [PATCH 36/75] core: update re-exports for 1.56.0 --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 7317f67960a02..2f598c6e398a9 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.55.0`](https://www.npmjs.com/package/@theia/application-package/v/1.55.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.55.0`](https://www.npmjs.com/package/@theia/application-package/v/1.55.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.55.0`](https://www.npmjs.com/package/@theia/application-package/v/1.55.0)) - - `@theia/request` (from [`@theia/request@1.55.0`](https://www.npmjs.com/package/@theia/request/v/1.55.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.55.0`](https://www.npmjs.com/package/@theia/request/v/1.55.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.55.0`](https://www.npmjs.com/package/@theia/request/v/1.55.0)) + - `@theia/application-package` (from [`@theia/application-package@1.56.0`](https://www.npmjs.com/package/@theia/application-package/v/1.56.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.56.0`](https://www.npmjs.com/package/@theia/application-package/v/1.56.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.56.0`](https://www.npmjs.com/package/@theia/application-package/v/1.56.0)) + - `@theia/request` (from [`@theia/request@1.56.0`](https://www.npmjs.com/package/@theia/request/v/1.56.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.56.0`](https://www.npmjs.com/package/@theia/request/v/1.56.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.56.0`](https://www.npmjs.com/package/@theia/request/v/1.56.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.1.3`](https://www.npmjs.com/package/inversify)) From c8ed4981bef563c4a783e9e2dbcd55587b526748 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 28 Nov 2024 13:14:25 +0100 Subject: [PATCH 37/75] v1.56.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 32 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 106 +++++++-------- examples/browser/package.json | 124 +++++++++--------- examples/electron/package.json | 120 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/ai-chat-ui/package.json | 20 +-- packages/ai-chat/package.json | 14 +- packages/ai-code-completion/package.json | 14 +- packages/ai-core/package.json | 18 +-- packages/ai-history/package.json | 14 +- packages/ai-hugging-face/package.json | 8 +- packages/ai-llamafile/package.json | 10 +- packages/ai-ollama/package.json | 12 +- packages/ai-openai/package.json | 12 +- packages/ai-terminal/package.json | 12 +- packages/ai-workspace-agent/package.json | 20 +-- packages/bulk-edit/package.json | 14 +- packages/callhierarchy/package.json | 8 +- packages/collaboration/package.json | 14 +- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 30 ++--- packages/dev-container/package.json | 12 +- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 +-- packages/keymaps/package.json | 12 +- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 14 +- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 30 ++--- packages/plugin-ext/package.json | 56 ++++---- packages/plugin-metrics/package.json | 12 +- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +- packages/scm/package.json | 12 +- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++-- packages/terminal/package.json | 18 +-- packages/test/package.json | 14 +- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 +-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 20 +-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 4 +- 84 files changed, 633 insertions(+), 633 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 2b2369070bdfa..94284238bad2a 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.55.0", - "@theia/ffmpeg": "1.55.0", - "@theia/native-webpack-plugin": "1.55.0", + "@theia/application-package": "1.56.0", + "@theia/ffmpeg": "1.56.0", + "@theia/native-webpack-plugin": "1.56.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index 4df9b8ae01919..c04ac8b723aae 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.55.0", + "@theia/request": "1.56.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -43,7 +43,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index e65feb63fc7be..3b0ef4f195091 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -32,12 +32,12 @@ "clean": "theiaext clean" }, "dependencies": { - "@theia/application-manager": "1.55.0", - "@theia/application-package": "1.55.0", - "@theia/ffmpeg": "1.55.0", - "@theia/localization-manager": "1.55.0", - "@theia/ovsx-client": "1.55.0", - "@theia/request": "1.55.0", + "@theia/application-manager": "1.56.0", + "@theia/application-package": "1.56.0", + "@theia/ffmpeg": "1.56.0", + "@theia/localization-manager": "1.56.0", + "@theia/ovsx-client": "1.56.0", + "@theia/request": "1.56.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index 7fbb464af9c17..4ea719d7a414d 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index a1ecfaa1ed013..6ccd241bf06ec 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "typescript": "~5.4.5" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index ddb5b7710c0a3..316bde9a6dcba 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.55.0", + "version": "1.56.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index fd16e3e0ecb08..9a5eab86c17af 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.55.0", + "@theia/request": "1.56.0", "limiter": "^2.1.0", "semver": "^7.5.4", "tslib": "^2.6.2" diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index 50742b922d3bb..44c9a426e5d95 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.55.0", + "version": "1.56.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.55.0", - "@theia/ext-scripts": "1.55.0", - "@theia/re-exports": "1.55.0", + "@theia/core": "1.56.0", + "@theia/ext-scripts": "1.56.0", + "@theia/re-exports": "1.56.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index a8e44cb2c15bb..6db5d5540c8ed 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.55.0", + "version": "1.56.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index b2c4ebded7aa7..df70f3daa6a38 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index ae1975c3d683e..99c9adbba4010 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index 91521ad61bbdf..bb44838ee7d0a 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/plugin-ext-headless": "1.55.0" + "@theia/core": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/plugin-ext-headless": "1.56.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 999ac34cd4407..ca95bc4a82f52 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,24 +1,24 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/ai-core": "1.55.0", - "@theia/ai-chat": "1.55.0", - "@theia/ai-chat-ui": "1.55.0", - "@theia/core": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/ai-chat": "1.56.0", + "@theia/ai-chat-ui": "1.56.0", + "@theia/ai-core": "1.56.0", + "@theia/core": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.55.0", - "@theia/ovsx-client": "1.55.0", - "@theia/search-in-workspace": "1.55.0", - "@theia/test": "1.55.0", - "@theia/toolbar": "1.55.0", - "@theia/vsx-registry": "1.55.0", - "@theia/workspace": "1.55.0" + "@theia/output": "1.56.0", + "@theia/ovsx-client": "1.56.0", + "@theia/search-in-workspace": "1.56.0", + "@theia/test": "1.56.0", + "@theia/toolbar": "1.56.0", + "@theia/vsx-registry": "1.56.0", + "@theia/workspace": "1.56.0" }, "theiaExtensions": [ { @@ -60,6 +60,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 43a1502b44ef1..2729d91a566e7 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.55.0" + "@theia/core": "1.56.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index 3f268a6aecf40..c04f8e6bacc72 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.55.0", + "version": "1.56.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,57 +15,57 @@ } }, "dependencies": { - "@theia/ai-chat": "1.55.0", - "@theia/ai-chat-ui": "1.55.0", - "@theia/ai-code-completion": "1.55.0", - "@theia/ai-core": "1.55.0", - "@theia/ai-history": "1.55.0", - "@theia/ai-ollama": "1.55.0", - "@theia/ai-openai": "1.55.0", - "@theia/api-samples": "1.55.0", - "@theia/bulk-edit": "1.55.0", - "@theia/callhierarchy": "1.55.0", - "@theia/collaboration": "1.55.0", - "@theia/console": "1.55.0", - "@theia/core": "1.55.0", - "@theia/debug": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/editor-preview": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/getting-started": "1.55.0", - "@theia/git": "1.55.0", - "@theia/keymaps": "1.55.0", - "@theia/markers": "1.55.0", - "@theia/memory-inspector": "1.55.0", - "@theia/messages": "1.55.0", - "@theia/metrics": "1.55.0", - "@theia/mini-browser": "1.55.0", - "@theia/monaco": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/outline-view": "1.55.0", - "@theia/output": "1.55.0", - "@theia/plugin-dev": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/plugin-ext-vscode": "1.55.0", - "@theia/plugin-metrics": "1.55.0", - "@theia/preferences": "1.55.0", - "@theia/preview": "1.55.0", - "@theia/process": "1.55.0", - "@theia/property-view": "1.55.0", - "@theia/scm": "1.55.0", - "@theia/scm-extra": "1.55.0", - "@theia/search-in-workspace": "1.55.0", - "@theia/secondary-window": "1.55.0", - "@theia/task": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/timeline": "1.55.0", - "@theia/toolbar": "1.55.0", - "@theia/typehierarchy": "1.55.0", - "@theia/userstorage": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/vsx-registry": "1.55.0", - "@theia/workspace": "1.55.0" + "@theia/ai-chat": "1.56.0", + "@theia/ai-chat-ui": "1.56.0", + "@theia/ai-code-completion": "1.56.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-history": "1.56.0", + "@theia/ai-ollama": "1.56.0", + "@theia/ai-openai": "1.56.0", + "@theia/api-samples": "1.56.0", + "@theia/bulk-edit": "1.56.0", + "@theia/callhierarchy": "1.56.0", + "@theia/collaboration": "1.56.0", + "@theia/console": "1.56.0", + "@theia/core": "1.56.0", + "@theia/debug": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/editor-preview": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/getting-started": "1.56.0", + "@theia/git": "1.56.0", + "@theia/keymaps": "1.56.0", + "@theia/markers": "1.56.0", + "@theia/memory-inspector": "1.56.0", + "@theia/messages": "1.56.0", + "@theia/metrics": "1.56.0", + "@theia/mini-browser": "1.56.0", + "@theia/monaco": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/outline-view": "1.56.0", + "@theia/output": "1.56.0", + "@theia/plugin-dev": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/plugin-ext-vscode": "1.56.0", + "@theia/plugin-metrics": "1.56.0", + "@theia/preferences": "1.56.0", + "@theia/preview": "1.56.0", + "@theia/process": "1.56.0", + "@theia/property-view": "1.56.0", + "@theia/scm": "1.56.0", + "@theia/scm-extra": "1.56.0", + "@theia/search-in-workspace": "1.56.0", + "@theia/secondary-window": "1.56.0", + "@theia/task": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/timeline": "1.56.0", + "@theia/toolbar": "1.56.0", + "@theia/typehierarchy": "1.56.0", + "@theia/userstorage": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/vsx-registry": "1.56.0", + "@theia/workspace": "1.56.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -81,6 +81,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.55.0" + "@theia/cli": "1.56.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index eff0f7fd39538..2fdea15ac3e78 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.55.0", + "version": "1.56.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -22,66 +22,66 @@ }, "theiaPluginsDir": "../../plugins", "dependencies": { - "@theia/ai-chat": "1.55.0", - "@theia/ai-chat-ui": "1.55.0", - "@theia/ai-code-completion": "1.55.0", - "@theia/ai-core": "1.55.0", - "@theia/ai-history": "1.55.0", - "@theia/ai-huggingface": "1.55.0", - "@theia/ai-llamafile": "1.55.0", - "@theia/ai-ollama": "1.55.0", - "@theia/ai-openai": "1.55.0", - "@theia/ai-terminal": "1.55.0", - "@theia/ai-workspace-agent": "1.55.0", - "@theia/api-provider-sample": "1.55.0", - "@theia/api-samples": "1.55.0", - "@theia/bulk-edit": "1.55.0", - "@theia/callhierarchy": "1.55.0", - "@theia/collaboration": "1.55.0", - "@theia/console": "1.55.0", - "@theia/core": "1.55.0", - "@theia/debug": "1.55.0", - "@theia/dev-container": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/editor-preview": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/getting-started": "1.55.0", - "@theia/keymaps": "1.55.0", - "@theia/markers": "1.55.0", - "@theia/memory-inspector": "1.55.0", - "@theia/messages": "1.55.0", - "@theia/metrics": "1.55.0", - "@theia/mini-browser": "1.55.0", - "@theia/monaco": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/notebook": "1.55.0", - "@theia/outline-view": "1.55.0", - "@theia/output": "1.55.0", - "@theia/plugin-dev": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/plugin-ext-headless": "1.55.0", - "@theia/plugin-ext-vscode": "1.55.0", - "@theia/plugin-metrics": "1.55.0", - "@theia/preferences": "1.55.0", - "@theia/preview": "1.55.0", - "@theia/process": "1.55.0", - "@theia/property-view": "1.55.0", - "@theia/remote": "1.55.0", - "@theia/scm": "1.55.0", - "@theia/scm-extra": "1.55.0", - "@theia/search-in-workspace": "1.55.0", - "@theia/secondary-window": "1.55.0", - "@theia/task": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/test": "1.55.0", - "@theia/timeline": "1.55.0", - "@theia/toolbar": "1.55.0", - "@theia/typehierarchy": "1.55.0", - "@theia/userstorage": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/vsx-registry": "1.55.0", - "@theia/workspace": "1.55.0" + "@theia/ai-chat": "1.56.0", + "@theia/ai-chat-ui": "1.56.0", + "@theia/ai-code-completion": "1.56.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-history": "1.56.0", + "@theia/ai-huggingface": "1.56.0", + "@theia/ai-llamafile": "1.56.0", + "@theia/ai-ollama": "1.56.0", + "@theia/ai-openai": "1.56.0", + "@theia/ai-terminal": "1.56.0", + "@theia/ai-workspace-agent": "1.56.0", + "@theia/api-provider-sample": "1.56.0", + "@theia/api-samples": "1.56.0", + "@theia/bulk-edit": "1.56.0", + "@theia/callhierarchy": "1.56.0", + "@theia/collaboration": "1.56.0", + "@theia/console": "1.56.0", + "@theia/core": "1.56.0", + "@theia/debug": "1.56.0", + "@theia/dev-container": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/editor-preview": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/getting-started": "1.56.0", + "@theia/keymaps": "1.56.0", + "@theia/markers": "1.56.0", + "@theia/memory-inspector": "1.56.0", + "@theia/messages": "1.56.0", + "@theia/metrics": "1.56.0", + "@theia/mini-browser": "1.56.0", + "@theia/monaco": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/notebook": "1.56.0", + "@theia/outline-view": "1.56.0", + "@theia/output": "1.56.0", + "@theia/plugin-dev": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/plugin-ext-headless": "1.56.0", + "@theia/plugin-ext-vscode": "1.56.0", + "@theia/plugin-metrics": "1.56.0", + "@theia/preferences": "1.56.0", + "@theia/preview": "1.56.0", + "@theia/process": "1.56.0", + "@theia/property-view": "1.56.0", + "@theia/remote": "1.56.0", + "@theia/scm": "1.56.0", + "@theia/scm-extra": "1.56.0", + "@theia/search-in-workspace": "1.56.0", + "@theia/secondary-window": "1.56.0", + "@theia/task": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/test": "1.56.0", + "@theia/timeline": "1.56.0", + "@theia/toolbar": "1.56.0", + "@theia/typehierarchy": "1.56.0", + "@theia/userstorage": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/vsx-registry": "1.56.0", + "@theia/workspace": "1.56.0" }, "scripts": { "clean": "theia clean", @@ -104,6 +104,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.55.0" + "@theia/cli": "1.56.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 90ae2f6cf9481..9d22b602f5f90 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.55.0", + "version": "1.56.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -26,64 +26,64 @@ } }, "dependencies": { - "@theia/ai-chat": "1.55.0", - "@theia/ai-chat-ui": "1.55.0", - "@theia/ai-code-completion": "1.55.0", - "@theia/ai-core": "1.55.0", - "@theia/ai-history": "1.55.0", - "@theia/ai-llamafile": "1.55.0", - "@theia/ai-ollama": "1.55.0", - "@theia/ai-openai": "1.55.0", - "@theia/ai-terminal": "1.55.0", - "@theia/ai-workspace-agent": "1.55.0", - "@theia/api-provider-sample": "1.55.0", - "@theia/api-samples": "1.55.0", - "@theia/bulk-edit": "1.55.0", - "@theia/callhierarchy": "1.55.0", - "@theia/collaboration": "1.55.0", - "@theia/console": "1.55.0", - "@theia/core": "1.55.0", - "@theia/debug": "1.55.0", - "@theia/dev-container": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/editor-preview": "1.55.0", - "@theia/electron": "1.55.0", - "@theia/external-terminal": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/getting-started": "1.55.0", - "@theia/keymaps": "1.55.0", - "@theia/markers": "1.55.0", - "@theia/memory-inspector": "1.55.0", - "@theia/messages": "1.55.0", - "@theia/metrics": "1.55.0", - "@theia/mini-browser": "1.55.0", - "@theia/monaco": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/outline-view": "1.55.0", - "@theia/output": "1.55.0", - "@theia/plugin-dev": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/plugin-ext-headless": "1.55.0", - "@theia/plugin-ext-vscode": "1.55.0", - "@theia/preferences": "1.55.0", - "@theia/preview": "1.55.0", - "@theia/process": "1.55.0", - "@theia/property-view": "1.55.0", - "@theia/remote": "1.55.0", - "@theia/scm": "1.55.0", - "@theia/scm-extra": "1.55.0", - "@theia/search-in-workspace": "1.55.0", - "@theia/secondary-window": "1.55.0", - "@theia/task": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/timeline": "1.55.0", - "@theia/toolbar": "1.55.0", - "@theia/typehierarchy": "1.55.0", - "@theia/userstorage": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/vsx-registry": "1.55.0", - "@theia/workspace": "1.55.0" + "@theia/ai-chat": "1.56.0", + "@theia/ai-chat-ui": "1.56.0", + "@theia/ai-code-completion": "1.56.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-history": "1.56.0", + "@theia/ai-llamafile": "1.56.0", + "@theia/ai-ollama": "1.56.0", + "@theia/ai-openai": "1.56.0", + "@theia/ai-terminal": "1.56.0", + "@theia/ai-workspace-agent": "1.56.0", + "@theia/api-provider-sample": "1.56.0", + "@theia/api-samples": "1.56.0", + "@theia/bulk-edit": "1.56.0", + "@theia/callhierarchy": "1.56.0", + "@theia/collaboration": "1.56.0", + "@theia/console": "1.56.0", + "@theia/core": "1.56.0", + "@theia/debug": "1.56.0", + "@theia/dev-container": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/editor-preview": "1.56.0", + "@theia/electron": "1.56.0", + "@theia/external-terminal": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/getting-started": "1.56.0", + "@theia/keymaps": "1.56.0", + "@theia/markers": "1.56.0", + "@theia/memory-inspector": "1.56.0", + "@theia/messages": "1.56.0", + "@theia/metrics": "1.56.0", + "@theia/mini-browser": "1.56.0", + "@theia/monaco": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/outline-view": "1.56.0", + "@theia/output": "1.56.0", + "@theia/plugin-dev": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/plugin-ext-headless": "1.56.0", + "@theia/plugin-ext-vscode": "1.56.0", + "@theia/preferences": "1.56.0", + "@theia/preview": "1.56.0", + "@theia/process": "1.56.0", + "@theia/property-view": "1.56.0", + "@theia/remote": "1.56.0", + "@theia/scm": "1.56.0", + "@theia/scm-extra": "1.56.0", + "@theia/search-in-workspace": "1.56.0", + "@theia/secondary-window": "1.56.0", + "@theia/task": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/timeline": "1.56.0", + "@theia/toolbar": "1.56.0", + "@theia/typehierarchy": "1.56.0", + "@theia/userstorage": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/vsx-registry": "1.56.0", + "@theia/workspace": "1.56.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -101,7 +101,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.55.0", + "@theia/cli": "1.56.0", "electron": "30.1.2" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index 8939769f04819..15a03c158f567 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.55.0", + "version": "1.56.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index 9bbf44caf0c8b..f05077aa15b47 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.55.0", + "version": "1.56.0", "command": { "run": { "stream": true diff --git a/packages/ai-chat-ui/package.json b/packages/ai-chat-ui/package.json index f37f61622e422..e74bf246a6c43 100644 --- a/packages/ai-chat-ui/package.json +++ b/packages/ai-chat-ui/package.json @@ -1,17 +1,17 @@ { "name": "@theia/ai-chat-ui", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - AI Chat UI Extension", "dependencies": { - "@theia/ai-core": "1.55.0", - "@theia/ai-chat": "1.55.0", - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-chat": "1.56.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/editor-preview": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/editor-preview": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2", "uuid": "^9.0.1" @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-chat/package.json b/packages/ai-chat/package.json index f2979e70f98b9..e5e06cb7a207a 100644 --- a/packages/ai-chat/package.json +++ b/packages/ai-chat/package.json @@ -1,13 +1,13 @@ { "name": "@theia/ai-chat", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - AI Chat Extension", "dependencies": { - "@theia/ai-core": "1.55.0", - "@theia/ai-history": "1.55.0", - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-history": "1.56.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-code-completion/package.json b/packages/ai-code-completion/package.json index 57cff0974ba08..f2b290ee99710 100644 --- a/packages/ai-code-completion/package.json +++ b/packages/ai-code-completion/package.json @@ -1,14 +1,14 @@ { "name": "@theia/ai-code-completion", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - AI Core", "dependencies": { - "@theia/ai-core": "1.55.0", - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", + "@theia/ai-core": "1.56.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/output": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-core/package.json b/packages/ai-core/package.json index 64578eed298cc..c66ae07b9b7a6 100644 --- a/packages/ai-core/package.json +++ b/packages/ai-core/package.json @@ -1,16 +1,16 @@ { "name": "@theia/ai-core", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - AI Core", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/output": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/workspace": "1.56.0", "@types/js-yaml": "^4.0.9", "js-yaml": "^4.1.0", "minimatch": "^5.1.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-history/package.json b/packages/ai-history/package.json index 73c3b71fd62a3..060df4537cac1 100644 --- a/packages/ai-history/package.json +++ b/packages/ai-history/package.json @@ -1,13 +1,13 @@ { "name": "@theia/ai-history", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - AI communication history", "dependencies": { - "@theia/ai-core": "1.55.0", - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/output": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/ai-core": "1.56.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/output": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-hugging-face/package.json b/packages/ai-hugging-face/package.json index 8036bd1b8c562..169991655f57c 100644 --- a/packages/ai-hugging-face/package.json +++ b/packages/ai-hugging-face/package.json @@ -1,11 +1,11 @@ { "name": "@theia/ai-huggingface", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Hugging Face Integration", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "@huggingface/inference": "^2.0.0", - "@theia/ai-core": "1.55.0" + "@theia/ai-core": "1.56.0" }, "publishConfig": { "access": "public" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-llamafile/package.json b/packages/ai-llamafile/package.json index ef0932604b3d8..a7f0e49a30d8f 100644 --- a/packages/ai-llamafile/package.json +++ b/packages/ai-llamafile/package.json @@ -1,11 +1,11 @@ { "name": "@theia/ai-llamafile", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Llamafile Integration", "dependencies": { - "@theia/ai-core": "1.55.0", - "@theia/core": "1.55.0", - "@theia/output": "1.55.0", + "@theia/ai-core": "1.56.0", + "@theia/core": "1.56.0", + "@theia/output": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-ollama/package.json b/packages/ai-ollama/package.json index 5a493709b9c86..f5679f76f46ee 100644 --- a/packages/ai-ollama/package.json +++ b/packages/ai-ollama/package.json @@ -1,15 +1,15 @@ { "name": "@theia/ai-ollama", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Ollama Integration", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2", "ollama": "^0.5.8", - "@theia/ai-core": "1.55.0" + "@theia/ai-core": "1.56.0" }, "publishConfig": { "access": "public" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-openai/package.json b/packages/ai-openai/package.json index cc427d0dc1a6f..c5786ebaa0cf9 100644 --- a/packages/ai-openai/package.json +++ b/packages/ai-openai/package.json @@ -1,15 +1,15 @@ { "name": "@theia/ai-openai", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - OpenAI Integration", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2", "openai": "^4.55.7", - "@theia/ai-core": "1.55.0" + "@theia/ai-core": "1.56.0" }, "publishConfig": { "access": "public" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-terminal/package.json b/packages/ai-terminal/package.json index 8ff2bfbbdd79f..68a48a010ac3a 100644 --- a/packages/ai-terminal/package.json +++ b/packages/ai-terminal/package.json @@ -1,12 +1,12 @@ { "name": "@theia/ai-terminal", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - AI Terminal Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/ai-core": "1.55.0", - "@theia/ai-chat": "1.55.0", - "@theia/terminal": "1.55.0", + "@theia/core": "1.56.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-chat": "1.56.0", + "@theia/terminal": "1.56.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.2" }, @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/ai-workspace-agent/package.json b/packages/ai-workspace-agent/package.json index ce14788d25f64..3846e2d174ae4 100644 --- a/packages/ai-workspace-agent/package.json +++ b/packages/ai-workspace-agent/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ai-workspace-agent", - "version": "1.55.0", + "version": "1.56.0", "description": "AI Workspace Agent Extension", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { @@ -15,13 +15,13 @@ "theia-extension" ], "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/workspace": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/ai-core": "1.55.0", - "@theia/ai-chat": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/workspace": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/ai-core": "1.56.0", + "@theia/ai-chat": "1.56.0", "ignore": "^6.0.0", "minimatch": "^9.0.0" }, @@ -29,8 +29,8 @@ "access": "public" }, "devDependencies": { - "@theia/cli": "1.55.0", - "@theia/test": "1.55.0" + "@theia/cli": "1.56.0", + "@theia/test": "1.56.0" }, "theiaExtensions": [ { diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index 8a6c2b424629c..073152262490c 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.55.0", + "@theia/workspace": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index f446cfc2fd284..8ecb0a4f5b55c 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/collaboration/package.json b/packages/collaboration/package.json index e8937e1ad4a13..75a535974cef1 100644 --- a/packages/collaboration/package.json +++ b/packages/collaboration/package.json @@ -1,14 +1,14 @@ { "name": "@theia/collaboration", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Collaboration Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.55.0", + "@theia/workspace": "1.56.0", "open-collaboration-protocol": "0.2.0", "open-collaboration-yjs": "0.2.0", "socket.io-client": "^4.5.3", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index 0fc9b7bea2ea2..d1d6b1baaab8e 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1", "tslib": "^2.6.2" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index 5d10471361293..bb20d499acf0c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -17,8 +17,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.55.0", - "@theia/request": "1.55.0", + "@theia/application-package": "1.56.0", + "@theia/request": "1.56.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -210,8 +210,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", - "@theia/re-exports": "1.55.0", + "@theia/ext-scripts": "1.56.0", + "@theia/re-exports": "1.56.0", "minimist": "^1.2.0", "nodejs-file-downloader": "4.13.0" }, diff --git a/packages/debug/package.json b/packages/debug/package.json index 6a9893971ffbb..e839b19cca650 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,22 +1,22 @@ { "name": "@theia/debug", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.55.0", - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/markers": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/console": "1.56.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/markers": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.55.0", - "@theia/process": "1.55.0", - "@theia/task": "1.55.0", - "@theia/test": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/output": "1.56.0", + "@theia/process": "1.56.0", + "@theia/task": "1.56.0", + "@theia/test": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/workspace": "1.56.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -59,7 +59,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/dev-container/package.json b/packages/dev-container/package.json index 44574d491d55a..53426aea0c29a 100644 --- a/packages/dev-container/package.json +++ b/packages/dev-container/package.json @@ -1,12 +1,12 @@ { "name": "@theia/dev-container", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/output": "1.55.0", - "@theia/remote": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/output": "1.56.0", + "@theia/remote": "1.56.0", + "@theia/workspace": "1.56.0", "dockerode": "^4.0.2", "jsonc-parser": "^2.2.0", "uuid": "^8.0.0" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/dockerode": "^3.3.23" }, "nyc": { diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index c04256e37352a..bb9764ad1a75c 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/navigator": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/navigator": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index 77983d93d897b..37bf55bfbd91d 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/variable-resolver": "1.55.0", + "@theia/core": "1.56.0", + "@theia/variable-resolver": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index 14e16813040bf..ad70efeadd23c 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", - "@theia/re-exports": "1.55.0" + "@theia/ext-scripts": "1.56.0", + "@theia/re-exports": "1.56.0" }, "peerDependencies": { "electron": "30.1.2" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index 5968a3850150e..54f0de3d892b7 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/workspace": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 3859705d6b988..0f241e4458e9e 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/process": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/process": "1.56.0", + "@theia/workspace": "1.56.0", "@vscode/ripgrep": "^1.14.2", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index 69d2cf50ef3b9..9078849fcd202 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/tar-fs": "^1.16.1", @@ -71,7 +71,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index 4eebef73af117..d70d5d564a17e 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/keymaps": "1.55.0", - "@theia/preview": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/keymaps": "1.56.0", + "@theia/preview": "1.56.0", + "@theia/workspace": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index 6a63e8a747b2f..96c9e36d80f1f 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.55.0", - "@theia/scm": "1.55.0", - "@theia/scm-extra": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/navigator": "1.56.0", + "@theia/scm": "1.56.0", + "@theia/scm-extra": "1.56.0", + "@theia/workspace": "1.56.0", "@types/diff": "^5.2.1", "@types/p-queue": "^2.3.1", "diff": "^5.2.0", @@ -67,7 +67,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index c4db592886fa1..3f65a633f0a05 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,18 +1,18 @@ { "name": "@theia/keymaps", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/preferences": "1.55.0", - "@theia/userstorage": "1.55.0", + "@theia/preferences": "1.56.0", + "@theia/userstorage": "1.56.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index d6454f785faef..b4f6b42fb140d 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/workspace": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index 2c324ed170869..25ef3efcd065d 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.55.0", - "@theia/debug": "1.55.0", + "@theia/core": "1.56.0", + "@theia/debug": "1.56.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0", "tslib": "^2.6.2" diff --git a/packages/messages/package.json b/packages/messages/package.json index e8a205220507a..a90a274c485ad 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2", "tslib": "^2.6.2" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index c44aaf47ebbf0..358a0606b0e65 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "prom-client": "^10.2.0", "tslib": "^2.6.2" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index ea926b1cccf56..3282f27448aa8 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index e70cdd87cf0be..8a36f2ca4d2a5 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/markers": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/markers": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/outline-view": "1.56.0", + "@theia/workspace": "1.56.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index f3c3a9ee9064a..5f62b9e16f8fe 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/workspace": "1.56.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 1f32ae058ea98..58cb2d7ac7db9 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,14 +1,14 @@ { "name": "@theia/notebook", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.55.0", + "@theia/outline-view": "1.56.0", "advanced-mark.js": "^2.6.0", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/markdown-it": "^12.2.3", "@types/vscode-notebook-renderer": "^1.72.0" }, diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index 3dae0f9029ae3..e0d0cd0e706ad 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index 49e616f0de67d..03aed3820decc 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2", @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index d6736b6a5fb1a..e8b26614998a1 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.55.0", - "@theia/debug": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/output": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/debug": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/output": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/workspace": "1.56.0", "ps-tree": "^1.2.0", "tslib": "^2.6.2" }, @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index 59dbd17fcc6e5..acdb1854e0932 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/terminal": "1.55.0", + "@theia/core": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/terminal": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 9681a3c9ec0bc..4af01a0107368 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,22 +1,22 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.55.0", - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/callhierarchy": "1.56.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.55.0", - "@theia/outline-view": "1.55.0", - "@theia/plugin": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/typehierarchy": "1.55.0", - "@theia/userstorage": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/navigator": "1.56.0", + "@theia/outline-view": "1.56.0", + "@theia/plugin": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/typehierarchy": "1.56.0", + "@theia/userstorage": "1.56.0", + "@theia/workspace": "1.56.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 78b9fe4ff45fa..df9859aa87945 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.55.0", - "@theia/callhierarchy": "1.55.0", - "@theia/console": "1.55.0", - "@theia/core": "1.55.0", - "@theia/debug": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/editor-preview": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/markers": "1.55.0", - "@theia/messages": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/bulk-edit": "1.56.0", + "@theia/callhierarchy": "1.56.0", + "@theia/console": "1.56.0", + "@theia/core": "1.56.0", + "@theia/debug": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/editor-preview": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/markers": "1.56.0", + "@theia/messages": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.55.0", - "@theia/notebook": "1.55.0", - "@theia/output": "1.55.0", - "@theia/plugin": "1.55.0", - "@theia/preferences": "1.55.0", - "@theia/scm": "1.55.0", - "@theia/search-in-workspace": "1.55.0", - "@theia/task": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/test": "1.55.0", - "@theia/timeline": "1.55.0", - "@theia/typehierarchy": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/navigator": "1.56.0", + "@theia/notebook": "1.56.0", + "@theia/output": "1.56.0", + "@theia/plugin": "1.56.0", + "@theia/preferences": "1.56.0", + "@theia/scm": "1.56.0", + "@theia/search-in-workspace": "1.56.0", + "@theia/task": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/test": "1.56.0", + "@theia/timeline": "1.56.0", + "@theia/typehierarchy": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/workspace": "1.56.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index 7e5156f1cfb47..7603421d2bf56 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.55.0", - "@theia/metrics": "1.55.0", + "@theia/core": "1.56.0", + "@theia/metrics": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/plugin": "1.55.0", - "@theia/plugin-ext": "1.55.0", + "@theia/plugin": "1.56.0", + "@theia/plugin-ext": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 4194d3abc3c05..0bcc4b385a655 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index b98b411078f33..0ad2645b9d8c9 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/userstorage": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/userstorage": "1.56.0", + "@theia/workspace": "1.56.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index 9f66b317c7983..be29a323af9f4 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/mini-browser": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/mini-browser": "1.56.0", + "@theia/monaco": "1.56.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index b47f034693422..a45e4791fe73e 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "node-pty": "0.11.0-beta24", "string-argv": "^0.1.1", "tslib": "^2.6.2" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index 5917023fe81ad..e5fdc8568df45 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index a73ecfc6bd16a..2a2bf887eeb8c 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.6", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index 302d7e2976b4e..ab2c08b29a822 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/scm": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/scm": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index e38940b3b0f4d..4037dfef1d04a 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,12 +1,12 @@ { "name": "@theia/scm", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", "@types/diff": "^5.2.1", "diff": "^5.2.0", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 5a03618e59949..e5afc0d075f68 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/process": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/process": "1.56.0", + "@theia/workspace": "1.56.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0", @@ -48,6 +48,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index f371f23ed0940..294013685ce0a 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,6 +39,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index c53b640b78f64..1a0c7cd069393 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/markers": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/markers": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/process": "1.55.0", - "@theia/terminal": "1.55.0", - "@theia/userstorage": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/process": "1.56.0", + "@theia/terminal": "1.56.0", + "@theia/userstorage": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/workspace": "1.56.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 483238f0619d9..413a3e50ad50a 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,15 +1,15 @@ { "name": "@theia/terminal", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/process": "1.55.0", - "@theia/variable-resolver": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/process": "1.56.0", + "@theia/variable-resolver": "1.56.0", + "@theia/workspace": "1.56.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index d79f8df3c1f6e..fa6648aa78b35 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/terminal": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/terminal": "1.56.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index 680a87c5189a3..5b9f614b528fd 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/navigator": "1.55.0", + "@theia/core": "1.56.0", + "@theia/navigator": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index 98085f8c5b77c..82adb84ac1cdb 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", - "@theia/file-search": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/monaco": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", + "@theia/file-search": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/monaco": "1.56.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/search-in-workspace": "1.55.0", - "@theia/userstorage": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/search-in-workspace": "1.56.0", + "@theia/userstorage": "1.56.0", + "@theia/workspace": "1.56.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0", diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 7f5a22f506670..824b8b162af28 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/editor": "1.55.0", + "@theia/core": "1.56.0", + "@theia/editor": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index 8c5a2e0522c13..71b9a75fde981 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index 3f328ce2ce88d..713eca103245c 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.55.0", + "@theia/core": "1.56.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 86564f58d722d..3ec6fbf66c5da 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,16 +1,16 @@ { "name": "@theia/vsx-registry", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/navigator": "1.55.0", - "@theia/ovsx-client": "1.55.0", - "@theia/plugin-ext": "1.55.0", - "@theia/plugin-ext-vscode": "1.55.0", - "@theia/preferences": "1.55.0", - "@theia/workspace": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/navigator": "1.56.0", + "@theia/ovsx-client": "1.56.0", + "@theia/plugin-ext": "1.56.0", + "@theia/plugin-ext-vscode": "1.56.0", + "@theia/preferences": "1.56.0", + "@theia/workspace": "1.56.0", "limiter": "^2.1.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0", + "@theia/ext-scripts": "1.56.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 0acc10966a7d6..cdd46ff882786 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.55.0", + "version": "1.56.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.55.0", - "@theia/filesystem": "1.55.0", - "@theia/variable-resolver": "1.55.0", + "@theia/core": "1.56.0", + "@theia/filesystem": "1.56.0", + "@theia/variable-resolver": "1.56.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2", "valid-filename": "^2.0.1" @@ -47,7 +47,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.55.0" + "@theia/ext-scripts": "1.56.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 1f00826244e81..24e26725246bc 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.55.0", + "version": "1.56.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index 4a64bdad38b2c..9ca46ee01aa2a 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.55.0", + "version": "1.56.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index 39c18050c2bd1..03e95e96bf03a 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.55.0", + "version": "1.56.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "1.55.0" + "@theia/api-provider-sample": "1.56.0" }, "scripts": { "prepare": "yarn -s package", From 49770cade5f4a894802d2cd9f6ff4b1660788125 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Fri, 29 Nov 2024 09:05:40 +0100 Subject: [PATCH 38/75] Fix prompt contribution template (#14561) fixed #14560 Signed-off-by: Jonas Helming --- .../prompt-template-contribution.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml index 07062d6a13f6a..c2fff0730c4e7 100644 --- a/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml +++ b/.github/DISCUSSION_TEMPLATE/prompt-template-contribution.yml @@ -1,12 +1,10 @@ -name: Prompt Template Submission -description: Submit a prompt template, detailing its functionality and tested results. -title: Prompt Template for +title: "Prompt Template for " body: - type: textarea id: what_template_does attributes: label: What Does This Template Do? - description: Provide a brief description of the prompt template. What problem does it solve? What specific functionality and agent does it enhance and how? + description: Provide a brief description of the prompt template. What problem does it solve? What specific functionality and agent does it enhance and how? placeholder: Enter a description of the template. validations: required: true @@ -25,7 +23,7 @@ body: attributes: label: Tested LLMs description: List the language models this template has been tested with, including versions, providers, and notable performance observations. - placeholder: |- + placeholder: |- - LLM Name & Version (e.g., OpenAI GPT-4) - Provider (e.g., OpenAI, llama-file, Ollama) - Observations or challenges @@ -37,7 +35,7 @@ body: attributes: label: Example User Requests description: Provide some example user requests tested with this template. - placeholder: |- + placeholder: |- 1. Example Request 1: [User input] 2. Example Request 2: [User input] 3. Example Request 3: [User input] @@ -76,7 +74,7 @@ body: ### Developer Certificate of Origin 1.1 - Copyright (C) 2004, 2006 The Linux Foundation and its contributors. + Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -85,9 +83,9 @@ body: By making a contribution to this project, I certify that: - (a) The contribution was created in whole or in part by me and I have the right to submit it under the open-source license indicated in the file; or - (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open-source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open-source license (unless I am permitted to submit under a different license), as indicated in the file; or - (c) The contribution was provided directly to me by some other person who certified (a), (b), or (c) and I have not modified it. + (a) The contribution was created in whole or in part by me and I have the right to submit it under the open-source license indicated in the file; or + (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open-source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open-source license (unless I am permitted to submit under a different license), as indicated in the file; or + (c) The contribution was provided directly to me by some other person who certified (a), (b), or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my signoff) is maintained indefinitely and may be redistributed consistent with this project or the open-source license(s) involved. From fc69a852e9d2aaf962c35f1993e29c570b58cc27 Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Thu, 28 Nov 2024 17:06:36 +0100 Subject: [PATCH 39/75] Dev Container uses the right workspace now When there are more than one workspaces open the config json is picked from the right workspace. Solves #14307 --- .../container-connection-contribution.ts | 6 +++++- .../remote-container-connection-provider.ts | 2 +- .../src/electron-node/dev-container-file-service.ts | 9 ++------- .../remote-container-connection-provider.ts | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/dev-container/src/electron-browser/container-connection-contribution.ts b/packages/dev-container/src/electron-browser/container-connection-contribution.ts index 415ada831f733..b5b64f590f7ea 100644 --- a/packages/dev-container/src/electron-browser/container-connection-contribution.ts +++ b/packages/dev-container/src/electron-browser/container-connection-contribution.ts @@ -84,7 +84,11 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr } async getOrSelectDevcontainerFile(): Promise { - const devcontainerFiles = await this.connectionProvider.getDevContainerFiles(); + const workspace = await this.workspaceService.workspace; + if(!workspace) { + return; + } + const devcontainerFiles = await this.connectionProvider.getDevContainerFiles(workspace.resource.path.toString()); if (devcontainerFiles.length === 1) { return devcontainerFiles[0].path; diff --git a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts index c6674f91371b9..b6f61ac59a14d 100644 --- a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts @@ -46,6 +46,6 @@ export interface DevContainerFile { export interface RemoteContainerConnectionProvider extends RpcServer { connectToContainer(options: ContainerConnectionOptions): Promise; - getDevContainerFiles(): Promise; + getDevContainerFiles(workspacePath: string): Promise; getCurrentContainerInfo(port: number): Promise; } diff --git a/packages/dev-container/src/electron-node/dev-container-file-service.ts b/packages/dev-container/src/electron-node/dev-container-file-service.ts index 00f59186f7710..1eca1a0e48d28 100644 --- a/packages/dev-container/src/electron-node/dev-container-file-service.ts +++ b/packages/dev-container/src/electron-node/dev-container-file-service.ts @@ -38,12 +38,7 @@ export class DevContainerFileService { return configuration; } - async getAvailableFiles(): Promise { - const workspace = await this.workspaceServer.getMostRecentlyUsedWorkspace(); - if (!workspace) { - return []; - } - + async getAvailableFiles(workspace: string): Promise { const devcontainerPath = new URI(workspace).path.join('.devcontainer').fsPath(); return (await this.searchForDevontainerJsonFiles(devcontainerPath, 1)).map(file => ({ @@ -54,7 +49,7 @@ export class DevContainerFileService { } protected async searchForDevontainerJsonFiles(directory: string, depth: number): Promise { - if (depth < 0) { + if (depth < 0 || !fs.existsSync(directory)) { return []; } const filesPaths = (await fs.readdir(directory)).map(file => new Path(directory).join(file).fsPath()); diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index a72e4e6c05fa2..07d9afd4659ae 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -121,8 +121,8 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection } } - getDevContainerFiles(): Promise { - return this.devContainerFileService.getAvailableFiles(); + getDevContainerFiles(workspacePath: string): Promise { + return this.devContainerFileService.getAvailableFiles(workspacePath); } async createContainerConnection(container: Docker.Container, docker: Docker, name?: string): Promise { From 689bd5152acaaa01e2ab550de971d4dc740372e2 Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Fri, 29 Nov 2024 10:55:27 +0100 Subject: [PATCH 40/75] Fixed linting --- .../src/electron-browser/container-connection-contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev-container/src/electron-browser/container-connection-contribution.ts b/packages/dev-container/src/electron-browser/container-connection-contribution.ts index b5b64f590f7ea..ad021109dce54 100644 --- a/packages/dev-container/src/electron-browser/container-connection-contribution.ts +++ b/packages/dev-container/src/electron-browser/container-connection-contribution.ts @@ -84,8 +84,8 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr } async getOrSelectDevcontainerFile(): Promise { - const workspace = await this.workspaceService.workspace; - if(!workspace) { + const workspace = this.workspaceService.workspace; + if (!workspace) { return; } const devcontainerFiles = await this.connectionProvider.getDevContainerFiles(workspace.resource.path.toString()); From 2078cdfdec1be23a264371b3a030896b966d2d4e Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Thu, 28 Nov 2024 13:44:14 +0100 Subject: [PATCH 41/75] On darwin/arm64 force to use amd64 image --- .../main-container-creation-contributions.ts | 5 ++++- .../remote/src/electron-node/setup/remote-setup-service.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts index ad0d0d4d265de..8d724cc537847 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts @@ -37,7 +37,10 @@ export class ImageFileContribution implements ContainerCreationContribution { async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: ImageContainer, api: Docker, outputprovider: ContainerOutputProvider): Promise { if (containerConfig.image) { - await new Promise((res, rej) => api.pull(containerConfig.image, {}, (err, stream) => { + const platform = process.platform; + const arch = process.arch; + const options = platform === 'darwin' && arch === 'arm64' ? { platform: 'amd64' } : {}; + await new Promise((res, rej) => api.pull(containerConfig.image, options, (err, stream) => { if (err) { rej(err); } else { diff --git a/packages/remote/src/electron-node/setup/remote-setup-service.ts b/packages/remote/src/electron-node/setup/remote-setup-service.ts index 9ef150807d985..2fe643c26d843 100644 --- a/packages/remote/src/electron-node/setup/remote-setup-service.ts +++ b/packages/remote/src/electron-node/setup/remote-setup-service.ts @@ -167,6 +167,8 @@ export class RemoteSetupService { arch = 'x64'; } else if (archResult.match(/i\d83/)) { // i386, i483, i683 arch = 'x86'; + } else if (archResult.includes('aarch64')) { + arch = 'arm64'; } else { arch = archResult.trim(); } From 15e9e26b9c47340367a306b37d64d2b3273c8445 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Tue, 3 Dec 2024 12:29:39 +0100 Subject: [PATCH 42/75] Add Hugging Face Provider to Electron example (#14570) * Add Hugging Face Provider to Electron example fixed #14569 Signed-off-by: Jonas Helming Co-authored-by: Simon Graband --- examples/electron/package.json | 1 + examples/electron/tsconfig.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/examples/electron/package.json b/examples/electron/package.json index 9d22b602f5f90..84b09461c94a9 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -31,6 +31,7 @@ "@theia/ai-code-completion": "1.56.0", "@theia/ai-core": "1.56.0", "@theia/ai-history": "1.56.0", + "@theia/ai-huggingface": "1.56.0", "@theia/ai-llamafile": "1.56.0", "@theia/ai-ollama": "1.56.0", "@theia/ai-openai": "1.56.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index 9199410a96828..f73f79901de16 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../../packages/ai-history" }, + { + "path": "../../packages/ai-hugging-face" + }, { "path": "../../packages/ai-llamafile" }, From 66c1b9d1b3468aefc487247d3214e7e60fea4585 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 3 Dec 2024 15:45:22 +0100 Subject: [PATCH 43/75] Remote: still use settings and configuration from the local user dir (#14548) * Remote: still use settings and configuration from the local user dir --------- Signed-off-by: Jonah Iden --- CHANGELOG.md | 4 ++ .../electron-local-ws-connection-source.ts | 5 ++ .../container-info-contribution.ts | 3 +- packages/remote/package.json | 1 + .../local-backend-services.ts | 31 +++++++++ .../port-forwarding-service.ts | 3 +- .../remote-frontend-contribution.ts | 7 +- .../remote-frontend-module.ts | 19 +++++- .../remote-user-storage-provider.ts | 64 +++++++++++++++++++ packages/remote/tsconfig.json | 3 + .../src/browser/user-storage-contribution.ts | 13 +++- 11 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 packages/remote/src/electron-browser/local-backend-services.ts create mode 100644 packages/remote/src/electron-browser/remote-user-storage-provider.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d9bfb996c16bc..da78c0c18aef7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) +[Breaking Changes:](#breaking_changes_1.57.0) + +- [remote] use local settings and configuration while connected to remote (rebinds UserStorageProvider) [#14548]https://github.com/eclipse-theia/theia/pull/14548/ + ## 1.56.0 - 11/28/2024 - [ai] added support for users to specify custom request settings, model, and optionally provider-specific [#14535](https://github.com/eclipse-theia/theia/pull/14535) diff --git a/packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts b/packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts index f62e740a02d48..82c19a40aa598 100644 --- a/packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts +++ b/packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts @@ -23,6 +23,11 @@ export function getLocalPort(): string | undefined { return params.get('localPort') ?? undefined; } +export function getCurrentPort(): string | undefined { + const params = new URLSearchParams(location.search); + return params.get('port') ?? undefined; +} + @injectable() export class ElectronLocalWebSocketConnectionSource extends WebSocketConnectionSource { diff --git a/packages/dev-container/src/electron-browser/container-info-contribution.ts b/packages/dev-container/src/electron-browser/container-info-contribution.ts index 06a39417f5a6b..40532f2d7a675 100644 --- a/packages/dev-container/src/electron-browser/container-info-contribution.ts +++ b/packages/dev-container/src/electron-browser/container-info-contribution.ts @@ -19,6 +19,7 @@ import { FrontendApplicationContribution } from '@theia/core/lib/browser'; import type { ContainerInspectInfo } from 'dockerode'; import { RemoteContainerConnectionProvider } from '../electron-common/remote-container-connection-provider'; import { PortForwardingService } from '@theia/remote/lib/electron-browser/port-forwarding/port-forwarding-service'; +import { getCurrentPort } from '@theia/core/lib/electron-browser/messaging/electron-local-ws-connection-source'; @injectable() export class ContainerInfoContribution implements FrontendApplicationContribution { @@ -32,7 +33,7 @@ export class ContainerInfoContribution implements FrontendApplicationContributio containerInfo: ContainerInspectInfo | undefined; async onStart(): Promise { - this.containerInfo = await this.connectionProvider.getCurrentContainerInfo(parseInt(new URLSearchParams(location.search).get('port') ?? '0')); + this.containerInfo = await this.connectionProvider.getCurrentContainerInfo(parseInt(getCurrentPort() ?? '0')); this.portForwardingService.forwardedPorts = Object.entries(this.containerInfo?.NetworkSettings.Ports ?? {}).flatMap(([_, ports]) => ( ports.map(port => ({ diff --git a/packages/remote/package.json b/packages/remote/package.json index 2a2bf887eeb8c..357f3a8e54411 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -5,6 +5,7 @@ "dependencies": { "@theia/core": "1.56.0", "@theia/filesystem": "1.56.0", + "@theia/userstorage": "1.56.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", diff --git a/packages/remote/src/electron-browser/local-backend-services.ts b/packages/remote/src/electron-browser/local-backend-services.ts new file mode 100644 index 0000000000000..61697304e2fcf --- /dev/null +++ b/packages/remote/src/electron-browser/local-backend-services.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { RpcProxy } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { RemoteFileSystemProvider, RemoteFileSystemServer } from '@theia/filesystem/lib/common/remote-file-system-provider'; + +export const LocalEnvVariablesServer = Symbol('LocalEnviromentVariableServer'); +export const LocalRemoteFileSytemServer = Symbol('LocalRemoteFileSytemServer'); + +/** + * provide file access to local files while connected to a remote workspace or dev container. + */ +@injectable() +export class LocalRemoteFileSystemProvider extends RemoteFileSystemProvider { + @inject(LocalRemoteFileSytemServer) + protected override readonly server: RpcProxy; +} diff --git a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts index 7a5a0c58bfe33..d9506c5dd8484 100644 --- a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts +++ b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts @@ -17,6 +17,7 @@ import { Emitter } from '@theia/core'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { RemotePortForwardingProvider } from '../../electron-common/remote-port-forwarding-provider'; +import { getCurrentPort } from '@theia/core/lib/electron-browser/messaging/electron-local-ws-connection-source'; export interface ForwardedPort { localPort?: number; @@ -50,7 +51,7 @@ export class PortForwardingService { } updatePort(port: ForwardedPort, newAdress: string): void { - const connectionPort = new URLSearchParams(location.search).get('port'); + const connectionPort = getCurrentPort(); if (!connectionPort) { // if there is no open remote connection we can't forward a port return; diff --git a/packages/remote/src/electron-browser/remote-frontend-contribution.ts b/packages/remote/src/electron-browser/remote-frontend-contribution.ts index c0a02f3cb2249..124e558a523df 100644 --- a/packages/remote/src/electron-browser/remote-frontend-contribution.ts +++ b/packages/remote/src/electron-browser/remote-frontend-contribution.ts @@ -21,6 +21,7 @@ import { RemoteStatus, RemoteStatusService } from '../electron-common/remote-sta import { RemoteRegistry, RemoteRegistryContribution } from './remote-registry-contribution'; import { RemoteService } from './remote-service'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { getLocalPort, getCurrentPort } from '@theia/core/lib/electron-browser/messaging/electron-local-ws-connection-source'; export namespace RemoteCommands { export const REMOTE_SELECT: Command = { @@ -59,7 +60,7 @@ export class RemoteFrontendContribution implements CommandContribution, Frontend protected remoteRegistry = new RemoteRegistry(); async configure(): Promise { - const port = new URLSearchParams(location.search).get('port'); + const port = getCurrentPort(); if (port) { const status = await this.remoteStatusService.getStatus(Number(port)); await this.setStatusBar(status); @@ -106,9 +107,9 @@ export class RemoteFrontendContribution implements CommandContribution, Frontend } protected async disconnectRemote(): Promise { - const searchParams = new URLSearchParams(location.search); - const localPort = searchParams.get('localPort'); + const localPort = getLocalPort(); if (localPort) { + const searchParams = new URLSearchParams(location.search); const currentPort = searchParams.get('port'); this.remoteStatusService.connectionClosed(parseInt(currentPort ?? '0')); this.windowService.reload({ search: { port: localPort } }); diff --git a/packages/remote/src/electron-browser/remote-frontend-module.ts b/packages/remote/src/electron-browser/remote-frontend-module.ts index 599c9ef3906d3..59d80fddd5e9e 100644 --- a/packages/remote/src/electron-browser/remote-frontend-module.ts +++ b/packages/remote/src/electron-browser/remote-frontend-module.ts @@ -16,7 +16,7 @@ import { bindContributionProvider, CommandContribution } from '@theia/core'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { bindViewContribution, FrontendApplicationContribution, isRemote, WidgetFactory } from '@theia/core/lib/browser'; import { RemoteSSHContribution } from './remote-ssh-contribution'; import { RemoteSSHConnectionProvider, RemoteSSHConnectionProviderPath } from '../electron-common/remote-ssh-connection-provider'; import { RemoteFrontendContribution } from './remote-frontend-contribution'; @@ -32,6 +32,11 @@ import { PortForwardingService } from './port-forwarding/port-forwarding-service import { RemotePortForwardingProvider, RemoteRemotePortForwardingProviderPath } from '../electron-common/remote-port-forwarding-provider'; import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; import '../../src/electron-browser/style/port-forwarding-widget.css'; +import { UserStorageContribution } from '@theia/userstorage/lib/browser/user-storage-contribution'; +import { RemoteUserStorageContribution } from './remote-user-storage-provider'; +import { remoteFileSystemPath, RemoteFileSystemProxyFactory, RemoteFileSystemServer } from '@theia/filesystem/lib/common/remote-file-system-provider'; +import { LocalEnvVariablesServer, LocalRemoteFileSystemProvider, LocalRemoteFileSytemServer } from './local-backend-services'; +import { envVariablesPath, EnvVariablesServer } from '@theia/core/lib/common/env-variables'; export default new ContainerModule((bind, _, __, rebind) => { bind(RemoteFrontendContribution).toSelf().inSingletonScope(); @@ -65,4 +70,16 @@ export default new ContainerModule((bind, _, __, rebind) => { bind(RemotePortForwardingProvider).toDynamicValue(ctx => ServiceConnectionProvider.createLocalProxy(ctx.container, RemoteRemotePortForwardingProviderPath)).inSingletonScope(); + bind(LocalRemoteFileSytemServer).toDynamicValue(ctx => + isRemote ? + ServiceConnectionProvider.createLocalProxy(ctx.container, remoteFileSystemPath, new RemoteFileSystemProxyFactory()) : + ctx.container.get(RemoteFileSystemServer)); + bind(LocalEnvVariablesServer).toDynamicValue(ctx => + isRemote ? + ServiceConnectionProvider.createLocalProxy(ctx.container, envVariablesPath) : + ctx.container.get(EnvVariablesServer)); + bind(LocalRemoteFileSystemProvider).toSelf().inSingletonScope(); + rebind(UserStorageContribution).to(RemoteUserStorageContribution); + }); + diff --git a/packages/remote/src/electron-browser/remote-user-storage-provider.ts b/packages/remote/src/electron-browser/remote-user-storage-provider.ts new file mode 100644 index 0000000000000..df44c233f501d --- /dev/null +++ b/packages/remote/src/electron-browser/remote-user-storage-provider.ts @@ -0,0 +1,64 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { FileService } from '@theia/filesystem/lib/browser/file-service'; +import { FileSystemProvider } from '@theia/filesystem/lib/common/files'; +import { UserStorageContribution } from '@theia/userstorage/lib/browser/user-storage-contribution'; +import { RemoteStatusService } from '../electron-common/remote-status-service'; +import { LocalEnvVariablesServer, LocalRemoteFileSystemProvider } from './local-backend-services'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import { URI } from '@theia/core'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import { getCurrentPort } from '@theia/core/lib/electron-browser/messaging/electron-local-ws-connection-source'; + +/** + * This overide is to have remote connections still use settings, keymaps, etc. from the local machine. + */ +@injectable() +export class RemoteUserStorageContribution extends UserStorageContribution { + @inject(RemoteStatusService) + protected readonly remoteStatusService: RemoteStatusService; + + @inject(LocalRemoteFileSystemProvider) + protected readonly localRemoteFileSystemProvider: LocalRemoteFileSystemProvider; + + @inject(LocalEnvVariablesServer) + protected readonly localEnvironments: EnvVariablesServer; + + isRemoteConnection: Deferred = new Deferred(); + + @postConstruct() + protected init(): void { + const port = getCurrentPort(); + if (port) { + this.remoteStatusService.getStatus(Number(port)).then(status => this.isRemoteConnection.resolve(status.alive)); + } + } + + protected override async getDelegate(service: FileService): Promise { + return await this.isRemoteConnection.promise ? + this.localRemoteFileSystemProvider + : service.activateProvider('file'); + } + + protected override async getCongigDirUri(): Promise { + return await this.isRemoteConnection.promise ? + new URI(await this.localEnvironments.getConfigDirUri()) + : super.getCongigDirUri(); + } + +} diff --git a/packages/remote/tsconfig.json b/packages/remote/tsconfig.json index 583ad6411ef85..6ab2143003036 100644 --- a/packages/remote/tsconfig.json +++ b/packages/remote/tsconfig.json @@ -14,6 +14,9 @@ }, { "path": "../filesystem" + }, + { + "path": "../userstorage" } ] } diff --git a/packages/userstorage/src/browser/user-storage-contribution.ts b/packages/userstorage/src/browser/user-storage-contribution.ts index b988885d5681d..cc7f6971324fb 100644 --- a/packages/userstorage/src/browser/user-storage-contribution.ts +++ b/packages/userstorage/src/browser/user-storage-contribution.ts @@ -22,6 +22,7 @@ import { FileSystemProvider } from '@theia/filesystem/lib/common/files'; import { FileService, FileServiceContribution } from '@theia/filesystem/lib/browser/file-service'; import { DelegatingFileSystemProvider } from '@theia/filesystem/lib/common/delegating-file-system-provider'; import { UserStorageUri } from './user-storage-uri'; +import { MaybePromise } from '@theia/core'; @injectable() export class UserStorageContribution implements FileServiceContribution { @@ -40,9 +41,17 @@ export class UserStorageContribution implements FileServiceContribution { }); } + protected getDelegate(service: FileService): MaybePromise { + return service.activateProvider('file'); + } + + protected async getCongigDirUri(): Promise { + return new URI(await this.environments.getConfigDirUri()); + } + protected async createProvider(service: FileService): Promise { - const delegate = await service.activateProvider('file'); - const configDirUri = new URI(await this.environments.getConfigDirUri()); + const delegate = await this.getDelegate(service); + const configDirUri = await this.getCongigDirUri(); return new DelegatingFileSystemProvider(delegate, { uriConverter: { to: resource => { From a5ec8cc38986bed83b4cd8e7a80d4dce9c15fa47 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 4 Dec 2024 08:52:11 +0100 Subject: [PATCH 44/75] basics to allow for hidden cells (#14573) * basics to allow for hidden cells --------- Signed-off-by: Jonah Iden --- .../src/browser/view-model/notebook-model.ts | 4 +++ .../browser/view/notebook-cell-list-view.tsx | 2 +- .../renderers/cell-output-webview.tsx | 33 +++++++++++-------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index a80ce44e4e4ea..b007e1a793490 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -289,6 +289,10 @@ export class NotebookModel implements Saveable, Disposable { } } + getVisibleCells(): NotebookCellModel[] { + return this.cells; + } + applyEdits(rawEdits: CellEditOperation[], computeUndoRedo: boolean): void { const editsWithDetails = rawEdits.map((edit, index) => { let cellIndex: number = -1; diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 7753a74e97f3b..39eeada382d0d 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -120,7 +120,7 @@ export class NotebookCellListView extends React.Component this.onDragStart(e)}> - {this.props.notebookModel.cells + {this.props.notebookModel.getVisibleCells() .map((cell, index) => cell.handle)); + const updateOutputMessage: OutputChangedMessage = { type: 'outputChanged', - changes: updates.map(update => ({ - cellHandle: update.cellHandle, - newOutputs: update.newOutputs.map(output => ({ - id: output.outputId, - items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })), - metadata: output.metadata - })), - start: update.start, - deleteCount: update.deleteCount - })) + changes: updates + .filter(update => visibleCells.has(update.cellHandle)) + .map(update => ({ + cellHandle: update.cellHandle, + newOutputs: update.newOutputs.map(output => ({ + id: output.outputId, + items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })), + metadata: output.metadata + })), + start: update.start, + deleteCount: update.deleteCount + })) }; - this.webviewWidget.sendMessage(updateOutputMessage); + if (updateOutputMessage.changes.length > 0) { + this.webviewWidget.sendMessage(updateOutputMessage); + } } cellsChanged(cellEvents: NotebookContentChangedEvent[]): void { @@ -382,11 +388,12 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { toIndex: event.newIdx + i, } as CellsMoved))); } else if (event.kind === NotebookCellsChangeType.ModelChange) { + const visibleCells = new Set(this.notebook.getVisibleCells()); changes.push(...event.changes.map(change => ({ type: 'cellsSpliced', start: change.start, deleteCount: change.deleteCount, - newCells: change.newItems.map(cell => cell.handle) + newCells: change.newItems.filter(cell => visibleCells.has(cell as NotebookCellModel)).map(cell => cell.handle) } as CellsSpliced))); } } @@ -429,7 +436,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { switch (message.type) { case 'initialized': - this.updateOutputs(this.notebook.cells.map(cell => ({ + this.updateOutputs(this.notebook.getVisibleCells().map(cell => ({ cellHandle: cell.handle, newOutputs: cell.outputs, start: 0, From 52945586d4d7c037cc0c35b15a9e1804fccc8e66 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 4 Dec 2024 09:17:33 +0100 Subject: [PATCH 45/75] DevContainers: Support THEIA_DEFAULT_PLUGINS (#14530) Signed-off-by: Jonah Iden --- .../src/main/node/plugin-remote-cli-contribution.ts | 7 ++++--- .../src/main/node/plugin-remote-copy-contribution.ts | 11 +++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts b/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts index 2df189ce8d57a..3cd945bf7b709 100644 --- a/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts @@ -27,10 +27,11 @@ export class PluginRemoteCliContribution implements RemoteCliContribution { enhanceArgs(context: RemoteCliContext): MaybePromise { const pluginsFolder = this.pluginCliContribution.localDir(); - if (!pluginsFolder) { - return []; - } else { + const defaultPlugins = process.env.THEIA_DEFAULT_PLUGINS; + if (pluginsFolder || defaultPlugins) { return ['--plugins=local-dir:./plugins']; } + return []; + } } diff --git a/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts b/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts index 2b7314f167118..2249b14323fbb 100644 --- a/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts @@ -27,10 +27,13 @@ export class PluginRemoteCopyContribution implements RemoteCopyContribution { async copy(registry: RemoteCopyRegistry): Promise { const localDir = this.pluginCliContribution.localDir(); - if (localDir) { - const fsPath = FileUri.fsPath(localDir); - await registry.directory(fsPath, 'plugins'); - } + const defaultPlugins = process.env.THEIA_DEFAULT_PLUGINS?.split(',') ?? []; + await Promise.all([localDir, ...defaultPlugins] + .filter(pluginDir => pluginDir && pluginDir.startsWith('local-dir:')) + .map(async (pluginDir: string) => { + const fsPath = FileUri.fsPath(pluginDir); + await registry.directory(fsPath, 'plugins'); + })); } } From f34a039cba659df172bd26591c82802f1b93906a Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 5 Dec 2024 09:07:52 +0100 Subject: [PATCH 46/75] Devcontainer workspaces openable through recent workspaces (#14567) * Devcontainer workspaces appearing in and openable through "open recent workspace" --------- Signed-off-by: Jonah Iden --- .../container-connection-contribution.ts | 62 ++++++++++++++++--- .../dev-container-frontend-module.ts | 2 + .../dev-container-workspaces.ts | 18 ++++++ .../remote-container-connection-provider.ts | 1 + .../dev-container-backend-module.ts | 5 ++ .../dev-container-workspace-handler.ts | 33 ++++++++++ .../electron-node/docker-container-service.ts | 13 ++-- .../remote-container-connection-provider.ts | 2 +- .../src/browser/quick-open-workspace.ts | 12 ++-- .../src/browser/workspace-frontend-module.ts | 6 +- .../src/browser/workspace-service.ts | 33 ++++++++-- .../src/node/default-workspace-server.spec.ts | 27 +++++--- .../src/node/default-workspace-server.ts | 34 ++++++++-- .../src/node/workspace-backend-module.ts | 9 ++- 14 files changed, 214 insertions(+), 43 deletions(-) create mode 100644 packages/dev-container/src/electron-common/dev-container-workspaces.ts create mode 100644 packages/dev-container/src/electron-node/dev-container-workspace-handler.ts diff --git a/packages/dev-container/src/electron-browser/container-connection-contribution.ts b/packages/dev-container/src/electron-browser/container-connection-contribution.ts index ad021109dce54..32c21f7c42690 100644 --- a/packages/dev-container/src/electron-browser/container-connection-contribution.ts +++ b/packages/dev-container/src/electron-browser/container-connection-contribution.ts @@ -16,12 +16,14 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { AbstractRemoteRegistryContribution, RemoteRegistry } from '@theia/remote/lib/electron-browser/remote-registry-contribution'; -import { LastContainerInfo, RemoteContainerConnectionProvider } from '../electron-common/remote-container-connection-provider'; +import { DevContainerFile, LastContainerInfo, RemoteContainerConnectionProvider } from '../electron-common/remote-container-connection-provider'; import { RemotePreferences } from '@theia/remote/lib/electron-browser/remote-preferences'; import { WorkspaceStorageService } from '@theia/workspace/lib/browser/workspace-storage-service'; -import { Command, QuickInputService } from '@theia/core'; -import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; +import { Command, MaybePromise, QuickInputService, URI } from '@theia/core'; +import { WorkspaceInput, WorkspaceOpenHandlerContribution, WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { ContainerOutputProvider } from './container-output-provider'; +import { WorkspaceServer } from '@theia/workspace/lib/common'; +import { DEV_CONTAINER_PATH_QUERY, DEV_CONTAINER_WORKSPACE_SCHEME } from '../electron-common/dev-container-workspaces'; export namespace RemoteContainerCommands { export const REOPEN_IN_CONTAINER = Command.toLocalizedCommand({ @@ -33,7 +35,7 @@ export namespace RemoteContainerCommands { const LAST_USED_CONTAINER = 'lastUsedContainer'; @injectable() -export class ContainerConnectionContribution extends AbstractRemoteRegistryContribution { +export class ContainerConnectionContribution extends AbstractRemoteRegistryContribution implements WorkspaceOpenHandlerContribution { @inject(RemoteContainerConnectionProvider) protected readonly connectionProvider: RemoteContainerConnectionProvider; @@ -47,6 +49,9 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; + @inject(WorkspaceServer) + protected readonly workspaceServer: WorkspaceServer; + @inject(QuickInputService) protected readonly quickInputService: QuickInputService; @@ -59,12 +64,47 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr }); } + canHandle(uri: URI): MaybePromise { + return uri.scheme === DEV_CONTAINER_WORKSPACE_SCHEME; + } + + async openWorkspace(uri: URI, options?: WorkspaceInput | undefined): Promise { + const filePath = new URLSearchParams(uri.query).get(DEV_CONTAINER_PATH_QUERY); + + if (!filePath) { + throw new Error('No devcontainer file specified for workspace'); + } + + const devcontainerFiles = await this.connectionProvider.getDevContainerFiles(uri.path.toString()); + const devcontainerFile = devcontainerFiles.find(file => file.path === filePath); + + if (!devcontainerFile) { + throw new Error(`Devcontainer file at ${filePath} not found in workspace`); + } + + return this.doOpenInContainer(devcontainerFile, uri.toString()); + } + + async getWorkspaceLabel(uri: URI): Promise { + const containerFilePath = new URLSearchParams(uri.query).get(DEV_CONTAINER_PATH_QUERY); + if (!containerFilePath) { + return; + }; + const files = await this.connectionProvider.getDevContainerFiles(uri.path.toString()); + const devcontainerFile = files.find(file => file.path === containerFilePath); + return `${uri.path.base} [Dev Container: ${devcontainerFile?.name}]`; + } + async openInContainer(): Promise { const devcontainerFile = await this.getOrSelectDevcontainerFile(); if (!devcontainerFile) { return; } - const lastContainerInfoKey = `${LAST_USED_CONTAINER}:${devcontainerFile}`; + this.doOpenInContainer(devcontainerFile); + } + + async doOpenInContainer(devcontainerFile: DevContainerFile, workspaceUri?: string): Promise { + const lastContainerInfoKey = `${LAST_USED_CONTAINER}:${devcontainerFile.path}`; const lastContainerInfo = await this.workspaceStorageService.getData(lastContainerInfoKey); this.containerOutputProvider.openChannel(); @@ -72,7 +112,8 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr const connectionResult = await this.connectionProvider.connectToContainer({ nodeDownloadTemplate: this.remotePreferences['remote.nodeDownloadTemplate'], lastContainerInfo, - devcontainerFile + devcontainerFile: devcontainerFile.path, + workspaceUri }); this.workspaceStorageService.setData(lastContainerInfoKey, { @@ -80,10 +121,13 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr lastUsed: Date.now() }); + this.workspaceServer.setMostRecentlyUsedWorkspace( + `${DEV_CONTAINER_WORKSPACE_SCHEME}:${this.workspaceService.workspace?.resource.path}?${DEV_CONTAINER_PATH_QUERY}=${devcontainerFile.path}`); + this.openRemote(connectionResult.port, false, connectionResult.workspacePath); } - async getOrSelectDevcontainerFile(): Promise { + async getOrSelectDevcontainerFile(): Promise { const workspace = this.workspaceService.workspace; if (!workspace) { return; @@ -91,14 +135,14 @@ export class ContainerConnectionContribution extends AbstractRemoteRegistryContr const devcontainerFiles = await this.connectionProvider.getDevContainerFiles(workspace.resource.path.toString()); if (devcontainerFiles.length === 1) { - return devcontainerFiles[0].path; + return devcontainerFiles[0]; } return (await this.quickInputService.pick(devcontainerFiles.map(file => ({ type: 'item', label: file.name, description: file.path, - file: file.path, + file: file, })), { title: 'Select a devcontainer.json file' }))?.file; diff --git a/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts b/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts index 807e5152cb2d9..4cf540f13cbcf 100644 --- a/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts +++ b/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts @@ -21,10 +21,12 @@ import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/ser import { ContainerOutputProvider } from './container-output-provider'; import { ContainerInfoContribution } from './container-info-contribution'; import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { WorkspaceOpenHandlerContribution } from '@theia/workspace/lib/browser/workspace-service'; export default new ContainerModule(bind => { bind(ContainerConnectionContribution).toSelf().inSingletonScope(); bind(RemoteRegistryContribution).toService(ContainerConnectionContribution); + bind(WorkspaceOpenHandlerContribution).toService(ContainerConnectionContribution); bind(ContainerOutputProvider).toSelf().inSingletonScope(); diff --git a/packages/dev-container/src/electron-common/dev-container-workspaces.ts b/packages/dev-container/src/electron-common/dev-container-workspaces.ts new file mode 100644 index 0000000000000..8248a796b818d --- /dev/null +++ b/packages/dev-container/src/electron-common/dev-container-workspaces.ts @@ -0,0 +1,18 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export const DEV_CONTAINER_WORKSPACE_SCHEME = 'devcontainer'; +export const DEV_CONTAINER_PATH_QUERY = 'containerfile'; diff --git a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts index b6f61ac59a14d..ef96e6045ddb9 100644 --- a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts @@ -26,6 +26,7 @@ export interface ContainerConnectionOptions { nodeDownloadTemplate?: string; lastContainerInfo?: LastContainerInfo devcontainerFile: string; + workspaceUri?: string; } export interface LastContainerInfo { diff --git a/packages/dev-container/src/electron-node/dev-container-backend-module.ts b/packages/dev-container/src/electron-node/dev-container-backend-module.ts index e03066275fba8..ae829cae4f165 100644 --- a/packages/dev-container/src/electron-node/dev-container-backend-module.ts +++ b/packages/dev-container/src/electron-node/dev-container-backend-module.ts @@ -26,6 +26,8 @@ import { ContainerOutputProvider } from '../electron-common/container-output-pro import { ExtensionsContribution, registerTheiaStartOptionsContributions, SettingsContribution } from './devcontainer-contributions/cli-enhancing-creation-contributions'; import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; import { ProfileFileModificationContribution } from './devcontainer-contributions/profile-file-modification-contribution'; +import { DevContainerWorkspaceHandler } from './dev-container-workspace-handler'; +import { WorkspaceHandlerContribution } from '@theia/workspace/lib/node/default-workspace-server'; export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { bindContributionProvider(bind, ContainerCreationContribution); @@ -55,4 +57,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(SettingsContribution).toSelf().inSingletonScope(); bind(RemoteCliContribution).toService(ExtensionsContribution); bind(RemoteCliContribution).toService(SettingsContribution); + + bind(DevContainerWorkspaceHandler).toSelf().inSingletonScope(); + bind(WorkspaceHandlerContribution).toService(DevContainerWorkspaceHandler); }); diff --git a/packages/dev-container/src/electron-node/dev-container-workspace-handler.ts b/packages/dev-container/src/electron-node/dev-container-workspace-handler.ts new file mode 100644 index 0000000000000..b4c1835f759af --- /dev/null +++ b/packages/dev-container/src/electron-node/dev-container-workspace-handler.ts @@ -0,0 +1,33 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { URI } from '@theia/core'; +import * as fs from '@theia/core/shared/fs-extra'; +import { injectable } from '@theia/core/shared/inversify'; +import { WorkspaceHandlerContribution } from '@theia/workspace/lib/node/default-workspace-server'; +import { DEV_CONTAINER_PATH_QUERY, DEV_CONTAINER_WORKSPACE_SCHEME } from '../electron-common/dev-container-workspaces'; + +@injectable() +export class DevContainerWorkspaceHandler implements WorkspaceHandlerContribution { + + canHandle(uri: URI): boolean { + return uri.scheme === DEV_CONTAINER_WORKSPACE_SCHEME; + } + + async workspaceStillExists(uri: URI): Promise { + const devcontainerFile = new URLSearchParams(uri.query).get(DEV_CONTAINER_PATH_QUERY); + return await fs.pathExists(uri.path.fsPath()) && !!devcontainerFile && fs.pathExists(devcontainerFile); + } +} diff --git a/packages/dev-container/src/electron-node/docker-container-service.ts b/packages/dev-container/src/electron-node/docker-container-service.ts index 70dfbc2fa72a0..8e52a3679987c 100644 --- a/packages/dev-container/src/electron-node/docker-container-service.ts +++ b/packages/dev-container/src/electron-node/docker-container-service.ts @@ -19,7 +19,7 @@ import { inject, injectable, named } from '@theia/core/shared/inversify'; import { WorkspaceServer } from '@theia/workspace/lib/common'; import * as fs from '@theia/core/shared/fs-extra'; import * as Docker from 'dockerode'; -import { LastContainerInfo } from '../electron-common/remote-container-connection-provider'; +import { ContainerConnectionOptions } from '../electron-common/remote-container-connection-provider'; import { DevContainerConfiguration } from './devcontainer-file'; import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; @@ -60,15 +60,14 @@ export class DockerContainerService { @inject(DevContainerFileService) protected readonly devContainerFileService: DevContainerFileService; - async getOrCreateContainer(docker: Docker, devcontainerFile: string, - lastContainerInfo?: LastContainerInfo, outputProvider?: ContainerOutputProvider): Promise { + async getOrCreateContainer(docker: Docker, options: ContainerConnectionOptions, outputProvider?: ContainerOutputProvider): Promise { let container; - const workspace = new URI(await this.workspaceServer.getMostRecentlyUsedWorkspace()); + const workspace = new URI(options.workspaceUri ?? await this.workspaceServer.getMostRecentlyUsedWorkspace()); - if (lastContainerInfo && fs.statSync(devcontainerFile).mtimeMs < lastContainerInfo.lastUsed) { + if (options.lastContainerInfo && fs.statSync(options.devcontainerFile).mtimeMs < options.lastContainerInfo.lastUsed) { try { - container = docker.getContainer(lastContainerInfo.id); + container = docker.getContainer(options.lastContainerInfo.id); if ((await container.inspect()).State.Running) { await container.restart(); } else { @@ -80,7 +79,7 @@ export class DockerContainerService { } } if (!container) { - container = await this.buildContainer(docker, devcontainerFile, workspace, outputProvider); + container = await this.buildContainer(docker, options.devcontainerFile, workspace, outputProvider); } return container; } diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 07d9afd4659ae..1a51e610f801c 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -79,7 +79,7 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection text: 'Creating container', }); try { - const container = await this.containerService.getOrCreateContainer(dockerConnection, options.devcontainerFile, options.lastContainerInfo, this.outputProvider); + const container = await this.containerService.getOrCreateContainer(dockerConnection, options, this.outputProvider); const devContainerConfig = await this.devContainerFileService.getConfiguration(options.devcontainerFile); // create actual connection diff --git a/packages/workspace/src/browser/quick-open-workspace.ts b/packages/workspace/src/browser/quick-open-workspace.ts index 91890ec2bf89f..0664cace3753a 100644 --- a/packages/workspace/src/browser/quick-open-workspace.ts +++ b/packages/workspace/src/browser/quick-open-workspace.ts @@ -14,12 +14,12 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject, optional } from '@theia/core/shared/inversify'; +import { injectable, inject, optional, named } from '@theia/core/shared/inversify'; import { QuickPickItem, LabelProvider, QuickInputService, QuickInputButton, QuickPickSeparator } from '@theia/core/lib/browser'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; -import { WorkspaceService } from './workspace-service'; +import { WorkspaceOpenHandlerContribution, WorkspaceService } from './workspace-service'; import URI from '@theia/core/lib/common/uri'; -import { nls, Path } from '@theia/core/lib/common'; +import { ContributionProvider, nls, Path } from '@theia/core/lib/common'; import { UntitledWorkspaceService } from '../common/untitled-workspace-service'; interface RecentlyOpenedPick extends QuickPickItem { @@ -36,6 +36,9 @@ export class QuickOpenWorkspace { @inject(EnvVariablesServer) protected readonly envServer: EnvVariablesServer; @inject(UntitledWorkspaceService) protected untitledWorkspaceService: UntitledWorkspaceService; + @inject(ContributionProvider) @named(WorkspaceOpenHandlerContribution) + protected readonly workspaceOpenHandlers: ContributionProvider; + protected readonly removeRecentWorkspaceButton: QuickInputButton = { iconClass: 'codicon-remove-close', tooltip: nls.localizeByDefault('Remove from Recently Opened') @@ -51,7 +54,8 @@ export class QuickOpenWorkspace { for (const workspace of workspaces) { const uri = new URI(workspace); - const label = uri.path.base; + const label = await this.workspaceOpenHandlers.getContributions() + .find(handler => handler.getWorkspaceLabel && handler.canHandle(uri))?.getWorkspaceLabel?.(uri) ?? uri.path.base; if (!label || this.untitledWorkspaceService.isUntitledWorkspace(uri)) { continue; // skip temporary workspace files & empty workspace names } diff --git a/packages/workspace/src/browser/workspace-frontend-module.ts b/packages/workspace/src/browser/workspace-frontend-module.ts index 9bec8751a2025..9f7e83fd52a94 100644 --- a/packages/workspace/src/browser/workspace-frontend-module.ts +++ b/packages/workspace/src/browser/workspace-frontend-module.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; -import { CommandContribution, MenuContribution } from '@theia/core/lib/common'; +import { CommandContribution, MenuContribution, bindContributionProvider } from '@theia/core/lib/common'; import { WebSocketConnectionProvider, FrontendApplicationContribution, KeybindingContribution } from '@theia/core/lib/browser'; import { OpenFileDialogFactory, @@ -32,7 +32,7 @@ import { LabelProviderContribution } from '@theia/core/lib/browser/label-provide import { VariableContribution } from '@theia/variable-resolver/lib/browser'; import { WorkspaceServer, workspacePath, UntitledWorkspaceService, WorkspaceFileService } from '../common'; import { WorkspaceFrontendContribution } from './workspace-frontend-contribution'; -import { WorkspaceService } from './workspace-service'; +import { WorkspaceOpenHandlerContribution, WorkspaceService } from './workspace-service'; import { WorkspaceCommandContribution, FileMenuContribution, EditMenuContribution } from './workspace-commands'; import { WorkspaceVariableContribution } from './workspace-variable-contribution'; import { WorkspaceStorageService } from './workspace-storage-service'; @@ -59,9 +59,11 @@ import { CanonicalUriService } from './canonical-uri-service'; export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => { bindWorkspacePreferences(bind); bindWorkspaceTrustPreferences(bind); + bindContributionProvider(bind, WorkspaceOpenHandlerContribution); bind(WorkspaceService).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(WorkspaceService); + bind(CanonicalUriService).toSelf().inSingletonScope(); bind(WorkspaceServer).toDynamicValue(ctx => { const provider = ctx.container.get(WebSocketConnectionProvider); diff --git a/packages/workspace/src/browser/workspace-service.ts b/packages/workspace/src/browser/workspace-service.ts index 44eeb15e58043..aff0d8689c0ef 100644 --- a/packages/workspace/src/browser/workspace-service.ts +++ b/packages/workspace/src/browser/workspace-service.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { WorkspaceServer, UntitledWorkspaceService, WorkspaceFileService } from '../common'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; @@ -24,7 +24,7 @@ import { } from '@theia/core/lib/browser'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; -import { ILogger, Disposable, DisposableCollection, Emitter, Event, MaybePromise, MessageService, nls } from '@theia/core'; +import { ILogger, Disposable, DisposableCollection, Emitter, Event, MaybePromise, MessageService, nls, ContributionProvider } from '@theia/core'; import { WorkspacePreferences } from './workspace-preferences'; import * as jsoncparser from 'jsonc-parser'; import * as Ajv from '@theia/core/shared/ajv'; @@ -36,11 +36,19 @@ import { workspaceSchema, WorkspaceSchemaUpdater } from './workspace-schema-upda import { IJSONSchema } from '@theia/core/lib/common/json-schema'; import { StopReason } from '@theia/core/lib/common/frontend-application-state'; +export const WorkspaceOpenHandlerContribution = Symbol('WorkspaceOpenHandlerContribution'); + +export interface WorkspaceOpenHandlerContribution { + canHandle(uri: URI): MaybePromise; + openWorkspace(uri: URI, options?: WorkspaceInput): MaybePromise; + getWorkspaceLabel?(uri: URI): MaybePromise; +} + /** * The workspace service. */ @injectable() -export class WorkspaceService implements FrontendApplicationContribution { +export class WorkspaceService implements FrontendApplicationContribution, WorkspaceOpenHandlerContribution { protected _workspace: FileStat | undefined; @@ -92,6 +100,9 @@ export class WorkspaceService implements FrontendApplicationContribution { @inject(WindowTitleService) protected readonly windowTitleService: WindowTitleService; + @inject(ContributionProvider) @named(WorkspaceOpenHandlerContribution) + protected readonly openHandlerContribution: ContributionProvider; + protected _ready = new Deferred(); get ready(): Promise { return this._ready.promise; @@ -350,7 +361,21 @@ export class WorkspaceService implements FrontendApplicationContribution { this.doOpen(uri, options); } - protected async doOpen(uri: URI, options?: WorkspaceInput): Promise { + protected async doOpen(uri: URI, options?: WorkspaceInput): Promise { + for (const handler of [...this.openHandlerContribution.getContributions(), this]) { + if (await handler.canHandle(uri)) { + handler.openWorkspace(uri, options); + return; + } + } + throw new Error(`Could not find a handler to open the workspace with uri ${uri.toString()}.`); + } + + async canHandle(uri: URI): Promise { + return uri.scheme === 'file'; + } + + async openWorkspace(uri: URI, options?: WorkspaceInput): Promise { const stat = await this.toFileStat(uri); if (stat) { if (!stat.isDirectory && !this.isWorkspaceFile(stat)) { diff --git a/packages/workspace/src/node/default-workspace-server.spec.ts b/packages/workspace/src/node/default-workspace-server.spec.ts index 18574775ff842..ba0cb33d5711b 100644 --- a/packages/workspace/src/node/default-workspace-server.spec.ts +++ b/packages/workspace/src/node/default-workspace-server.spec.ts @@ -14,16 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Container } from '@theia/core/shared/inversify'; +import { Container, ContainerModule } from '@theia/core/shared/inversify'; import { MockEnvVariablesServerImpl } from '@theia/core/lib/browser/test/mock-env-variables-server'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import URI from '@theia/core/lib/common/uri'; import { FileUri } from '@theia/core/lib/node'; import { WorkspaceFileService, UntitledWorkspaceService } from '../common'; -import { DefaultWorkspaceServer, WorkspaceCliContribution } from './default-workspace-server'; +import { DefaultWorkspaceServer, FileWorkspaceHandlerContribution, WorkspaceCliContribution, WorkspaceHandlerContribution } from './default-workspace-server'; import { expect } from 'chai'; import * as temp from 'temp'; import * as fs from 'fs'; +import { ILogger, bindContributionProvider } from '@theia/core'; +import { MockLogger } from '@theia/core/lib/common/test/mock-logger'; describe('DefaultWorkspaceServer', function (): void { @@ -40,12 +42,21 @@ describe('DefaultWorkspaceServer', function (): void { // create a container with the necessary bindings for the DefaultWorkspaceServer const container = new Container(); - container.bind(WorkspaceCliContribution).toSelf().inSingletonScope(); - container.bind(DefaultWorkspaceServer).toSelf().inSingletonScope(); - container.bind(WorkspaceFileService).toSelf().inSingletonScope(); - container.bind(UntitledWorkspaceService).toSelf().inSingletonScope(); - container.bind(EnvVariablesServer).toConstantValue(new MockEnvVariablesServerImpl(tmpConfigDir)); - + const containerModule = new ContainerModule(bind => { + /* Mock logger binding*/ + bind(ILogger).to(MockLogger); + + bindContributionProvider(bind, WorkspaceHandlerContribution); + bind(FileWorkspaceHandlerContribution).toSelf().inSingletonScope(); + bind(WorkspaceHandlerContribution).toService(FileWorkspaceHandlerContribution); + bind(WorkspaceCliContribution).toSelf().inSingletonScope(); + bind(DefaultWorkspaceServer).toSelf().inSingletonScope(); + bind(WorkspaceFileService).toSelf().inSingletonScope(); + bind(UntitledWorkspaceService).toSelf().inSingletonScope(); + bind(EnvVariablesServer).toConstantValue(new MockEnvVariablesServerImpl(tmpConfigDir)); + }); + + container.load(containerModule); workspaceServer = container.get(DefaultWorkspaceServer); }); diff --git a/packages/workspace/src/node/default-workspace-server.ts b/packages/workspace/src/node/default-workspace-server.ts index 251d8dc6f54de..aa9352a0f4d7e 100644 --- a/packages/workspace/src/node/default-workspace-server.ts +++ b/packages/workspace/src/node/default-workspace-server.ts @@ -18,15 +18,20 @@ import * as path from 'path'; import * as yargs from '@theia/core/shared/yargs'; import * as fs from '@theia/core/shared/fs-extra'; import * as jsoncparser from 'jsonc-parser'; -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify'; import { FileUri, BackendApplicationContribution } from '@theia/core/lib/node'; import { CliContribution } from '@theia/core/lib/node/cli'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { WorkspaceServer, UntitledWorkspaceService } from '../common'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import URI from '@theia/core/lib/common/uri'; -import { notEmpty } from '@theia/core'; +import { ContributionProvider, notEmpty } from '@theia/core'; +export const WorkspaceHandlerContribution = Symbol('workspaceHandlerContribution'); +export interface WorkspaceHandlerContribution { + canHandle(uri: URI): boolean; + workspaceStillExists(uri: URI): Promise; +} @injectable() export class WorkspaceCliContribution implements CliContribution { @@ -101,6 +106,9 @@ export class DefaultWorkspaceServer implements WorkspaceServer, BackendApplicati @inject(UntitledWorkspaceService) protected readonly untitledWorkspaceService: UntitledWorkspaceService; + @inject(ContributionProvider) @named(WorkspaceHandlerContribution) + protected readonly workspaceHandlers: ContributionProvider; + @postConstruct() protected init(): void { this.doInit(); @@ -162,11 +170,13 @@ export class DefaultWorkspaceServer implements WorkspaceServer, BackendApplicati protected async workspaceStillExist(workspaceRootUri: string): Promise { const uri = new URI(workspaceRootUri); - // Non file system workspaces cannot be checked for existence - if (uri.scheme !== 'file') { - return false; + + for (const handler of this.workspaceHandlers.getContributions()) { + if (handler.canHandle(uri)) { + return handler.workspaceStillExists(uri); + } } - return fs.pathExists(uri.path.fsPath()); + return false; } protected async getWorkspaceURIFromCli(): Promise { @@ -228,6 +238,18 @@ export class DefaultWorkspaceServer implements WorkspaceServer, BackendApplicati } } +@injectable() +export class FileWorkspaceHandlerContribution implements WorkspaceHandlerContribution { + + canHandle(uri: URI): boolean { + return uri.scheme === 'file'; + } + + async workspaceStillExists(uri: URI): Promise { + return fs.pathExists(uri.path.fsPath()); + } +} + export interface RecentWorkspacePathsData { recentRoots: string[]; } diff --git a/packages/workspace/src/node/workspace-backend-module.ts b/packages/workspace/src/node/workspace-backend-module.ts index e6e43370021dd..198c3dd091962 100644 --- a/packages/workspace/src/node/workspace-backend-module.ts +++ b/packages/workspace/src/node/workspace-backend-module.ts @@ -15,9 +15,9 @@ // ***************************************************************************** import { ContainerModule } from '@theia/core/shared/inversify'; -import { ConnectionHandler, RpcConnectionHandler } from '@theia/core/lib/common'; +import { ConnectionHandler, RpcConnectionHandler, bindContributionProvider } from '@theia/core/lib/common'; import { WorkspaceServer, workspacePath, UntitledWorkspaceService, WorkspaceFileService } from '../common'; -import { DefaultWorkspaceServer, WorkspaceCliContribution } from './default-workspace-server'; +import { DefaultWorkspaceServer, FileWorkspaceHandlerContribution, WorkspaceCliContribution, WorkspaceHandlerContribution } from './default-workspace-server'; import { CliContribution } from '@theia/core/lib/node/cli'; import { BackendApplicationContribution } from '@theia/core/lib/node'; @@ -30,6 +30,11 @@ export default new ContainerModule(bind => { bind(UntitledWorkspaceService).toSelf().inSingletonScope(); bind(WorkspaceFileService).toSelf().inSingletonScope(); + bindContributionProvider(bind, WorkspaceHandlerContribution); + + bind(FileWorkspaceHandlerContribution).toSelf().inSingletonScope(); + bind(WorkspaceHandlerContribution).toService(FileWorkspaceHandlerContribution); + bind(ConnectionHandler).toDynamicValue(ctx => new RpcConnectionHandler(workspacePath, () => ctx.container.get(WorkspaceServer) From 837885885dca7e31869a5980640f04100708794f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 5 Dec 2024 09:59:50 +0100 Subject: [PATCH 47/75] Pin perfect-scrollbar to 1.5.5 (#14587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14586 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/package.json | 2 +- packages/messages/package.json | 3 +++ packages/notebook/package.json | 4 ++++ packages/toolbar/package.json | 2 +- yarn.lock | 2 +- 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index bb20d499acf0c..08d64d1a658f0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -59,7 +59,7 @@ "markdown-it": "^12.3.2", "msgpackr": "^1.10.2", "p-debounce": "^2.1.0", - "perfect-scrollbar": "^1.3.0", + "perfect-scrollbar": "1.5.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-tooltip": "^4.2.21", diff --git a/packages/messages/package.json b/packages/messages/package.json index a90a274c485ad..0b727ced51eb5 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -8,6 +8,9 @@ "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, + "resolutions": { + "perfect-scrollbar": "1.5.5" + }, "publishConfig": { "access": "public" }, diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 58cb2d7ac7db9..ebe3b0afacdc8 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -13,6 +13,10 @@ "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" }, + + "resolutions": { + "perfect-scrollbar": "1.5.5" + }, "publishConfig": { "access": "public" }, diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index 82adb84ac1cdb..cb900a83e4479 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -38,7 +38,7 @@ "@theia/workspace": "1.56.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", - "perfect-scrollbar": "^1.3.0", + "perfect-scrollbar": "1.5.5", "tslib": "^2.6.2" }, "theiaExtensions": [ diff --git a/yarn.lock b/yarn.lock index 21d2692e9f2aa..6cea9ef9c6995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9893,7 +9893,7 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== -perfect-scrollbar@^1.3.0, perfect-scrollbar@^1.5.0: +perfect-scrollbar@1.5.5, perfect-scrollbar@^1.5.0: version "1.5.5" resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz#41a211a2fb52a7191eff301432134ea47052b27f" integrity sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g== From 83fb8c1cc828db3cbbd1c51af74f5d7224e61514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Fri, 6 Dec 2024 10:15:33 +0100 Subject: [PATCH 48/75] Update perfect-scrollbar and pin version in root package.json (#14592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14586 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- package.json | 3 ++- packages/core/package.json | 2 +- packages/messages/package.json | 3 --- packages/notebook/package.json | 3 --- packages/toolbar/package.json | 2 +- yarn.lock | 2 +- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 04f0c7ae15d84..1fa803a3e513e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ }, "resolutions": { "**/@types/node": "18", - "**/nan": "2.20.0" + "**/nan": "2.20.0", + "**/perfect-scrollbar": "1.5.5" }, "devDependencies": { "@types/chai": "4.3.0", diff --git a/packages/core/package.json b/packages/core/package.json index 08d64d1a658f0..856cd99cbed3e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -59,7 +59,7 @@ "markdown-it": "^12.3.2", "msgpackr": "^1.10.2", "p-debounce": "^2.1.0", - "perfect-scrollbar": "1.5.5", + "perfect-scrollbar": "^1.5.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-tooltip": "^4.2.21", diff --git a/packages/messages/package.json b/packages/messages/package.json index 0b727ced51eb5..a90a274c485ad 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -8,9 +8,6 @@ "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, - "resolutions": { - "perfect-scrollbar": "1.5.5" - }, "publishConfig": { "access": "public" }, diff --git a/packages/notebook/package.json b/packages/notebook/package.json index ebe3b0afacdc8..617e20ec1d49f 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -14,9 +14,6 @@ "tslib": "^2.6.2" }, - "resolutions": { - "perfect-scrollbar": "1.5.5" - }, "publishConfig": { "access": "public" }, diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index cb900a83e4479..0b9c2c8e46738 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -38,7 +38,7 @@ "@theia/workspace": "1.56.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", - "perfect-scrollbar": "1.5.5", + "perfect-scrollbar": "^1.5.5", "tslib": "^2.6.2" }, "theiaExtensions": [ diff --git a/yarn.lock b/yarn.lock index 6cea9ef9c6995..a5a480ffa075c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9893,7 +9893,7 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== -perfect-scrollbar@1.5.5, perfect-scrollbar@^1.5.0: +perfect-scrollbar@1.5.5, perfect-scrollbar@^1.5.0, perfect-scrollbar@^1.5.5: version "1.5.5" resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz#41a211a2fb52a7191eff301432134ea47052b27f" integrity sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g== From 5e81662521977d62891da3efc4db26d969c63916 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Mon, 9 Dec 2024 10:08:10 +0100 Subject: [PATCH 49/75] [vscode] refactor uri or light/dark uri or ThemeIcon as IconPath type (#14590) fixes #14579 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- packages/plugin/src/theia.d.ts | 71 ++++++++++++---------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 43006cac4e89f..ecc71be68318d 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -2163,7 +2163,7 @@ export module '@theia/plugin' { /** * The icon path or {@link ThemeIcon} for the QuickPickItem. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: IconPath; /** * A human-readable string which is rendered less prominent in the same line. Supports rendering of @@ -2740,6 +2740,21 @@ export module '@theia/plugin' { private constructor(id: string, color?: ThemeColor); } + /** + * Represents an icon in the UI. This is either an uri, separate uris for the light- and dark-themes, + * or a {@link ThemeIcon theme icon}. + */ + export type IconPath = Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; + /** * Represents the state of a window. */ @@ -3520,7 +3535,7 @@ export module '@theia/plugin' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: IconPath; /** * The icon {@link ThemeColor} for the terminal. @@ -3640,7 +3655,7 @@ export module '@theia/plugin' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: IconPath; /** * The icon {@link ThemeColor} for the terminal. @@ -6227,7 +6242,7 @@ export module '@theia/plugin' { /** * Icon for the button. */ - readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; + readonly iconPath: IconPath; /** * An optional tooltip. @@ -6841,7 +6856,7 @@ export module '@theia/plugin' { * When `falsy`, {@link ThemeIcon.Folder Folder Theme Icon} is assigned, if item is collapsible otherwise {@link ThemeIcon.File File Theme Icon}. * When a {@link ThemeIcon ThemeIcon} is specified, icon is derived from the current file icon theme for the specified theme icon using {@link TreeItem.resourceUri resourceUri} (if provided). */ - iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; + iconPath?: string | IconPath; /** * A human readable string which is rendered less prominent. @@ -10612,7 +10627,7 @@ export module '@theia/plugin' { /** * The icon path or {@link ThemeIcon ThemeIcon} for the edit. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: IconPath; } /** @@ -17473,16 +17488,7 @@ export module '@theia/plugin' { /** * An icon for the participant shown in UI. */ - iconPath?: Uri | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - } | ThemeIcon; + iconPath?: IconPath; /** * The handler for requests to this participant. @@ -17652,16 +17658,7 @@ export module '@theia/plugin' { * @param value A uri or location * @param iconPath Icon for the reference shown in UI */ - reference(value: Uri | Location, iconPath?: Uri | ThemeIcon | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - }): void; + reference(value: Uri | Location, iconPath?: IconPath): void; /** * Pushes a part to this stream. @@ -17781,32 +17778,14 @@ export module '@theia/plugin' { /** * The icon for the reference. */ - iconPath?: Uri | ThemeIcon | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - }; + iconPath?: IconPath; /** * Create a new ChatResponseReferencePart. * @param value A uri or location * @param iconPath Icon for the reference shown in UI */ - constructor(value: Uri | Location, iconPath?: Uri | ThemeIcon | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - }); + constructor(value: Uri | Location, iconPath?: IconPath); } /** From fb09901d9359973e765d9cc954b93b56a601238c Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 10 Dec 2024 10:26:55 +0100 Subject: [PATCH 50/75] Improve searchForDevontainerJsonFiles to not block server (#14563) * improve searchForDevontainerJsonFiles to not block server Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../src/electron-node/dev-container-file-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev-container/src/electron-node/dev-container-file-service.ts b/packages/dev-container/src/electron-node/dev-container-file-service.ts index 1eca1a0e48d28..6987c0e73641a 100644 --- a/packages/dev-container/src/electron-node/dev-container-file-service.ts +++ b/packages/dev-container/src/electron-node/dev-container-file-service.ts @@ -49,7 +49,7 @@ export class DevContainerFileService { } protected async searchForDevontainerJsonFiles(directory: string, depth: number): Promise { - if (depth < 0 || !fs.existsSync(directory)) { + if (depth < 0 || !await fs.pathExists(directory)) { return []; } const filesPaths = (await fs.readdir(directory)).map(file => new Path(directory).join(file).fsPath()); From 9ddca0249fc42f771c9a9e239b2dd48ee09d3195 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Tue, 10 Dec 2024 16:05:50 +0100 Subject: [PATCH 51/75] Updating publishing guide and set markdown formatter (#14553) - Set formatter for markdown files - Format all markdown files - Update publishing guide Signed-off-by: Simon Graband --- .theia/settings.json | 10 +- .vscode/extensions.json | 3 +- .vscode/settings.json | 10 +- CODE_OF_CONDUCT.md | 6 +- CONTRIBUTING.md | 4 +- NOTICE.md | 128 ++++--- README.md | 1 + dev-packages/application-manager/README.md | 3 +- dev-packages/application-package/README.md | 3 +- dev-packages/cli/README.md | 15 +- dev-packages/localization-manager/README.md | 4 +- dev-packages/native-webpack-plugin/README.md | 3 +- dev-packages/ovsx-client/README.md | 3 +- dev-packages/private-eslint-plugin/README.md | 18 +- dev-packages/private-ext-scripts/README.md | 4 +- dev-packages/request/README.md | 3 +- doc/Developing.md | 236 +++++++------ doc/Migration.md | 6 +- doc/Publishing.md | 327 ++++++++++++------ doc/Testing.md | 23 +- doc/api-management.md | 7 +- doc/api-testing.md | 3 + doc/changelogs/CHANGELOG-2019.md | 44 +-- doc/changelogs/CHANGELOG-2020.md | 34 +- doc/changelogs/CHANGELOG-2021.md | 2 +- doc/changelogs/CHANGELOG-2022.md | 12 +- doc/code-organization.md | 2 +- doc/coding-guidelines.md | 96 ++++- doc/pull-requests.md | 27 +- doc/runtime-policy.md | 6 +- doc/vscode-usage.md | 6 +- examples/api-provider-sample/README.md | 6 +- examples/api-samples/README.md | 4 +- examples/playwright/README.md | 2 +- package.json | 1 + packages/ai-chat-ui/README.md | 3 +- packages/ai-chat/README.md | 3 +- packages/ai-code-completion/README.md | 3 +- packages/ai-core/README.md | 3 +- packages/ai-history/README.md | 3 +- packages/ai-hugging-face/README.md | 2 +- packages/ai-llamafile/README.md | 14 +- packages/ai-openai/README.md | 3 +- packages/ai-terminal/README.md | 10 +- packages/ai-workspace-agent/README.md | 3 +- packages/bulk-edit/README.md | 3 +- packages/callhierarchy/README.md | 3 +- packages/collaboration/README.md | 3 +- packages/console/README.md | 3 +- packages/core/README.md | 82 ++--- packages/core/README_TEMPLATE.md | 10 +- packages/debug/README.md | 21 +- packages/dev-container/README.md | 7 +- packages/editor-preview/README.md | 16 +- packages/editor/README.md | 3 +- packages/electron/README.md | 11 +- packages/electron/README_TEMPLATE.md | 5 +- packages/external-terminal/README.md | 6 +- packages/file-search/README.md | 3 +- packages/filesystem/README.md | 3 +- .../filesystem/src/common/download/README.md | 12 +- packages/getting-started/README.md | 4 +- packages/git/README.md | 4 +- packages/keymaps/README.md | 9 +- packages/markers/README.md | 4 +- packages/memory-inspector/README.md | 4 +- packages/messages/README.md | 3 +- packages/metrics/README.md | 3 +- packages/mini-browser/README.md | 3 +- packages/monaco/README.md | 33 +- packages/navigator/README.md | 3 +- packages/notebook/README.md | 3 +- packages/outline-view/README.md | 3 +- packages/output/README.md | 3 +- packages/plugin-dev/README.md | 4 +- packages/plugin-ext-headless/README.md | 3 +- packages/plugin-ext-vscode/README.md | 4 +- packages/plugin-ext/README.md | 5 +- packages/plugin-metrics/README.md | 7 +- packages/plugin/README.md | 26 +- packages/preferences/README.md | 23 +- packages/preview/README.md | 3 +- packages/process/README.md | 3 +- packages/property-view/README.md | 5 +- packages/remote/README.md | 2 +- packages/scm-extra/README.md | 4 +- packages/scm/README.md | 3 +- packages/search-in-workspace/README.md | 4 +- packages/task/README.md | 13 +- packages/terminal/README.md | 3 +- packages/timeline/README.md | 3 +- packages/toolbar/README.md | 5 +- packages/typehierarchy/README.md | 3 +- packages/userstorage/README.md | 7 +- packages/variable-resolver/README.md | 9 +- packages/vsx-registry/README.md | 3 +- packages/workspace/README.md | 3 +- .../sample-namespace/plugin-gotd/README.md | 4 +- scripts/performance/README.md | 46 +-- 99 files changed, 961 insertions(+), 613 deletions(-) diff --git a/.theia/settings.json b/.theia/settings.json index 23199246981cf..d6872327e715c 100644 --- a/.theia/settings.json +++ b/.theia/settings.json @@ -11,5 +11,13 @@ "editor.tabSize": 2 }, "typescript.tsdk": "node_modules/typescript/lib", - "clang-format.language.typescript.enable": false + "clang-format.language.typescript.enable": false, + "[markdown]": { + "editor.defaultFormatter": "davidanson.vscode-markdownlint" + }, + "markdownlint.config": { + "MD032": false, // don't require blank line around lists + "MD033": false, // allow inline html + "MD041": false // don't require h1 in first line + }, } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 517d760915049..4f803f4512200 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,8 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "dbaeumer.vscode-eslint" + "dbaeumer.vscode-eslint", + "DavidAnson.vscode-markdownlint" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] diff --git a/.vscode/settings.json b/.vscode/settings.json index 8166ed2cb0656..a07b759be7e50 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -51,6 +51,9 @@ "editor.tabSize": 2, "editor.defaultFormatter": "vscode.json-language-features", }, + "[markdown]": { + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" + }, "typescript.tsdk": "node_modules/typescript/lib", "files.insertFinalNewline": true, "clang-format.language.typescript.enable": false, @@ -63,5 +66,10 @@ "editor.defaultFormatter": "vscode.typescript-language-features", "typescript.preferences.quoteStyle": "single", "editor.tabSize": 4, - } + }, + "markdownlint.config": { + "MD032": false, // don't require blank line around lists + "MD033": false, // allow inline html + "MD041": false // don't require h1 in first line + }, } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f5254ea3e68ce..88ca1cbd71529 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -43,15 +43,15 @@ This Code of Conduct applies within all project spaces, and it also applies when ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at codeofconduct@eclipse.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at -For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq +For answers to common questions about this code of conduct, see