diff --git a/crates/extension_api/src/extension_api.rs b/crates/extension_api/src/extension_api.rs index 1bb2a84059fb309cad8736a83133b7754dbed4bc..7764b13f19672f4211715ec4804b15037de9925e 100644 --- a/crates/extension_api/src/extension_api.rs +++ b/crates/extension_api/src/extension_api.rs @@ -17,8 +17,6 @@ pub use serde_json; pub use wit::{ CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars, KeyValueStore, LanguageServerInstallationStatus, Project, Range, Worktree, download_file, - llm_delete_credential, llm_get_credential, llm_get_env_var, llm_oauth_http_request, - llm_oauth_open_browser, llm_oauth_start_web_auth, llm_request_credential, llm_store_credential, make_file_executable, zed::extension::context_server::ContextServerConfiguration, zed::extension::dap::{ @@ -45,6 +43,12 @@ pub use wit::{ ToolInputFormat as LlmToolInputFormat, ToolResult as LlmToolResult, ToolResultContent as LlmToolResultContent, ToolUse as LlmToolUse, ToolUseJsonParseError as LlmToolUseJsonParseError, + delete_credential as llm_delete_credential, get_credential as llm_get_credential, + get_env_var as llm_get_env_var, oauth_open_browser as llm_oauth_open_browser, + oauth_start_web_auth as llm_oauth_start_web_auth, + request_credential as llm_request_credential, + send_oauth_http_request as llm_oauth_http_request, + store_credential as llm_store_credential, }, zed::extension::nodejs::{ node_binary_path, npm_install_package, npm_package_installed_version, diff --git a/crates/extension_api/wit/since_v0.7.0/common.wit b/crates/extension_api/wit/since_v0.7.0/common.wit deleted file mode 100644 index 139e7ba0ca4d1cc5ac78ccd23673ca749d6e46b2..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/common.wit +++ /dev/null @@ -1,12 +0,0 @@ -interface common { - /// A (half-open) range (`[start, end)`). - record range { - /// The start of the range (inclusive). - start: u32, - /// The end of the range (exclusive). - end: u32, - } - - /// A list of environment variables. - type env-vars = list>; -} diff --git a/crates/extension_api/wit/since_v0.7.0/context-server.wit b/crates/extension_api/wit/since_v0.7.0/context-server.wit deleted file mode 100644 index 7234e0e6d0f6d444e92a056a92f6c90c7dc053b4..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/context-server.wit +++ /dev/null @@ -1,11 +0,0 @@ -interface context-server { - /// Configuration for context server setup and installation. - record context-server-configuration { - /// Installation instructions in Markdown format. - installation-instructions: string, - /// JSON schema for settings validation. - settings-schema: string, - /// Default settings template. - default-settings: string, - } -} diff --git a/crates/extension_api/wit/since_v0.7.0/dap.wit b/crates/extension_api/wit/since_v0.7.0/dap.wit deleted file mode 100644 index 693befe02f9c313455facd4839572528c3408fd1..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/dap.wit +++ /dev/null @@ -1,123 +0,0 @@ -interface dap { - use common.{env-vars}; - - /// Resolves a specified TcpArgumentsTemplate into TcpArguments - resolve-tcp-template: func(template: tcp-arguments-template) -> result; - - record launch-request { - program: string, - cwd: option, - args: list, - envs: env-vars, - } - - record attach-request { - process-id: option, - } - - variant debug-request { - launch(launch-request), - attach(attach-request) - } - - record tcp-arguments { - port: u16, - host: u32, - timeout: option, - } - - record tcp-arguments-template { - port: option, - host: option, - timeout: option, - } - - /// Debug Config is the "highest-level" configuration for a debug session. - /// It comes from a new process modal UI; thus, it is essentially debug-adapter-agnostic. - /// It is expected of the extension to translate this generic configuration into something that can be debugged by the adapter (debug scenario). - record debug-config { - /// Name of the debug task - label: string, - /// The debug adapter to use - adapter: string, - request: debug-request, - stop-on-entry: option, - } - - record task-template { - /// Human readable name of the task to display in the UI. - label: string, - /// Executable command to spawn. - command: string, - args: list, - env: env-vars, - cwd: option, - } - - /// A task template with substituted task variables. - type resolved-task = task-template; - - /// A task template for building a debug target. - type build-task-template = task-template; - - variant build-task-definition { - by-name(string), - template(build-task-definition-template-payload ) - } - record build-task-definition-template-payload { - locator-name: option, - template: build-task-template - } - - /// Debug Scenario is the user-facing configuration type (used in debug.json). It is still concerned with what to debug and not necessarily how to do it (except for any - /// debug-adapter-specific configuration options). - record debug-scenario { - /// Unsubstituted label for the task.DebugAdapterBinary - label: string, - /// Name of the Debug Adapter this configuration is intended for. - adapter: string, - /// An optional build step to be ran prior to starting a debug session. Build steps are used by Zed's locators to locate the executable to debug. - build: option, - /// JSON-encoded configuration for a given debug adapter. - config: string, - /// TCP connection parameters (if they were specified by user) - tcp-connection: option, - } - - enum start-debugging-request-arguments-request { - launch, - attach, - } - - record debug-task-definition { - /// Unsubstituted label for the task.DebugAdapterBinary - label: string, - /// Name of the Debug Adapter this configuration is intended for. - adapter: string, - /// JSON-encoded configuration for a given debug adapter. - config: string, - /// TCP connection parameters (if they were specified by user) - tcp-connection: option, - } - - record start-debugging-request-arguments { - /// JSON-encoded configuration for a given debug adapter. It is specific to each debug adapter. - /// `configuration` will have it's Zed variable references substituted prior to being passed to the debug adapter. - configuration: string, - request: start-debugging-request-arguments-request, - } - - /// The lowest-level representation of a debug session, which specifies: - /// - How to start a debug adapter process - /// - How to start a debug session with it (using DAP protocol) - /// for a given debug scenario. - record debug-adapter-binary { - command: option, - arguments: list, - envs: env-vars, - cwd: option, - /// Zed will use TCP transport if `connection` is specified. - connection: option, - request-args: start-debugging-request-arguments - } -} diff --git a/crates/extension_api/wit/since_v0.7.0/extension.wit b/crates/extension_api/wit/since_v0.7.0/extension.wit deleted file mode 100644 index f95dfa04dac25f792d14f896ee8c00ffa8dcf804..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/extension.wit +++ /dev/null @@ -1,284 +0,0 @@ -package zed:extension; - -world extension { - import context-server; - import dap; - import github; - import http-client; - import platform; - import process; - import nodejs; - import llm-provider; - - use common.{env-vars, range}; - use context-server.{context-server-configuration}; - use dap.{attach-request, build-task-template, debug-config, debug-adapter-binary, debug-task-definition, debug-request, debug-scenario, launch-request, resolved-task, start-debugging-request-arguments-request}; - use lsp.{completion, symbol}; - use process.{command}; - use slash-command.{slash-command, slash-command-argument-completion, slash-command-output}; - use llm-provider.{ - provider-info, model-info, completion-request, - credential-type, cache-configuration, completion-event, token-usage - }; - - /// Initializes the extension. - export init-extension: func(); - - /// The type of a downloaded file. - enum downloaded-file-type { - /// A gzipped file (`.gz`). - gzip, - /// A gzipped tar archive (`.tar.gz`). - gzip-tar, - /// A ZIP file (`.zip`). - zip, - /// An uncompressed file. - uncompressed, - } - - /// The installation status for a language server. - variant language-server-installation-status { - /// The language server has no installation status. - none, - /// The language server is being downloaded. - downloading, - /// The language server is checking for updates. - checking-for-update, - /// The language server installation failed for specified reason. - failed(string), - } - - record settings-location { - worktree-id: u64, - path: string, - } - - import get-settings: func(path: option, category: string, key: option) -> result; - - /// Downloads a file from the given URL and saves it to the given path within the extension's - /// working directory. - /// - /// The file will be extracted according to the given file type. - import download-file: func(url: string, file-path: string, file-type: downloaded-file-type) -> result<_, string>; - - /// Makes the file at the given path executable. - import make-file-executable: func(filepath: string) -> result<_, string>; - - /// Updates the installation status for the given language server. - import set-language-server-installation-status: func(language-server-name: string, status: language-server-installation-status); - - /// A Zed worktree. - resource worktree { - /// Returns the ID of the worktree. - id: func() -> u64; - /// Returns the root path of the worktree. - root-path: func() -> string; - /// Returns the textual contents of the specified file in the worktree. - read-text-file: func(path: string) -> result; - /// Returns the path to the given binary name, if one is present on the `$PATH`. - which: func(binary-name: string) -> option; - /// Returns the current shell environment. - shell-env: func() -> env-vars; - } - - /// A Zed project. - resource project { - /// Returns the IDs of all of the worktrees in this project. - worktree-ids: func() -> list; - } - - /// A key-value store. - resource key-value-store { - /// Inserts an entry under the specified key. - insert: func(key: string, value: string) -> result<_, string>; - } - - /// Returns the command used to start up the language server. - export language-server-command: func(language-server-id: string, worktree: borrow) -> result; - - /// Returns the initialization options to pass to the language server on startup. - /// - /// The initialization options are represented as a JSON string. - export language-server-initialization-options: func(language-server-id: string, worktree: borrow) -> result, string>; - - /// Returns the workspace configuration options to pass to the language server. - export language-server-workspace-configuration: func(language-server-id: string, worktree: borrow) -> result, string>; - - /// Returns the initialization options to pass to the other language server. - export language-server-additional-initialization-options: func(language-server-id: string, target-language-server-id: string, worktree: borrow) -> result, string>; - - /// Returns the workspace configuration options to pass to the other language server. - export language-server-additional-workspace-configuration: func(language-server-id: string, target-language-server-id: string, worktree: borrow) -> result, string>; - - /// A label containing some code. - record code-label { - /// The source code to parse with Tree-sitter. - code: string, - /// The spans to display in the label. - spans: list, - /// The range of the displayed label to include when filtering. - filter-range: range, - } - - /// A span within a code label. - variant code-label-span { - /// A range into the parsed code. - code-range(range), - /// A span containing a code literal. - literal(code-label-span-literal), - } - - /// A span containing a code literal. - record code-label-span-literal { - /// The literal text. - text: string, - /// The name of the highlight to use for this literal. - highlight-name: option, - } - - export labels-for-completions: func(language-server-id: string, completions: list) -> result>, string>; - export labels-for-symbols: func(language-server-id: string, symbols: list) -> result>, string>; - - - /// Returns the completions that should be shown when completing the provided slash command with the given query. - export complete-slash-command-argument: func(command: slash-command, args: list) -> result, string>; - - /// Returns the output from running the provided slash command. - export run-slash-command: func(command: slash-command, args: list, worktree: option>) -> result; - - /// Returns the command used to start up a context server. - export context-server-command: func(context-server-id: string, project: borrow) -> result; - - /// Returns the configuration for a context server. - export context-server-configuration: func(context-server-id: string, project: borrow) -> result, string>; - - /// Returns a list of packages as suggestions to be included in the `/docs` - /// search results. - /// - /// This can be used to provide completions for known packages (e.g., from the - /// local project or a registry) before a package has been indexed. - export suggest-docs-packages: func(provider-name: string) -> result, string>; - - /// Indexes the docs for the specified package. - export index-docs: func(provider-name: string, package-name: string, database: borrow) -> result<_, string>; - - /// Returns a configured debug adapter binary for a given debug task. - export get-dap-binary: func(adapter-name: string, config: debug-task-definition, user-installed-path: option, worktree: borrow) -> result; - /// Returns the kind of a debug scenario (launch or attach). - export dap-request-kind: func(adapter-name: string, config: string) -> result; - export dap-config-to-scenario: func(config: debug-config) -> result; - export dap-locator-create-scenario: func(locator-name: string, build-config-template: build-task-template, resolved-label: string, debug-adapter-name: string) -> option; - export run-dap-locator: func(locator-name: string, config: resolved-task) -> result; - - // ========================================================================= - // Language Model Provider Extension API - // ========================================================================= - - /// Returns information about language model providers offered by this extension. - export llm-providers: func() -> list; - - /// Returns the models available for a provider. - export llm-provider-models: func(provider-id: string) -> result, string>; - - /// Returns markdown content to display in the provider's settings UI. - /// This can include setup instructions, links to documentation, etc. - export llm-provider-settings-markdown: func(provider-id: string) -> option; - - /// Check if the provider is authenticated. - export llm-provider-is-authenticated: func(provider-id: string) -> bool; - - /// Attempt to authenticate the provider. - export llm-provider-authenticate: func(provider-id: string) -> result<_, string>; - - /// Reset credentials for the provider. - export llm-provider-reset-credentials: func(provider-id: string) -> result<_, string>; - - /// Count tokens for a request. - export llm-count-tokens: func( - provider-id: string, - model-id: string, - request: completion-request - ) -> result; - - /// Start streaming a completion from the model. - /// Returns a stream ID that can be used with llm-stream-next and llm-stream-close. - export llm-stream-completion-start: func( - provider-id: string, - model-id: string, - request: completion-request - ) -> result; - - /// Get the next event from a completion stream. - /// Returns None when the stream is complete. - export llm-stream-completion-next: func( - stream-id: string - ) -> result, string>; - - /// Close a completion stream and release its resources. - export llm-stream-completion-close: func( - stream-id: string - ); - - /// Get cache configuration for a model (if prompt caching is supported). - export llm-cache-configuration: func( - provider-id: string, - model-id: string - ) -> option; - - // ========================================================================= - // Language Model Provider Imports (callable by extensions) - // ========================================================================= - - /// Request a credential from the user. - /// Returns true if the credential was provided, false if the user cancelled. - import llm-request-credential: func( - provider-id: string, - credential-type: credential-type, - label: string, - placeholder: string - ) -> result; - - /// Get a stored credential for this provider. - import llm-get-credential: func(provider-id: string) -> option; - - /// Store a credential for this provider. - import llm-store-credential: func(provider-id: string, value: string) -> result<_, string>; - - /// Delete a stored credential for this provider. - import llm-delete-credential: func(provider-id: string) -> result<_, string>; - - /// Read an environment variable. - import llm-get-env-var: func(name: string) -> option; - - // ========================================================================= - // OAuth Web Auth Flow Imports - // ========================================================================= - - use llm-provider.{oauth-web-auth-config, oauth-web-auth-result, oauth-http-request, oauth-http-response}; - - /// Start an OAuth web authentication flow. - /// - /// This will: - /// 1. Start a localhost server to receive the OAuth callback - /// 2. Open the auth URL in the user's default browser - /// 3. Wait for the callback (up to the timeout) - /// 4. Return the callback URL with query parameters - /// - /// The extension is responsible for: - /// - Constructing the auth URL with client_id, redirect_uri, scope, state, etc. - /// - Parsing the callback URL to extract the authorization code - /// - Exchanging the code for tokens using llm-oauth-http-request - import llm-oauth-start-web-auth: func(config: oauth-web-auth-config) -> result; - - /// Make an HTTP request for OAuth token exchange. - /// - /// This is a simple HTTP client for OAuth flows, allowing the extension - /// to handle token exchange with full control over serialization. - import llm-oauth-http-request: func(request: oauth-http-request) -> result; - - /// Open a URL in the user's default browser. - /// - /// Useful for OAuth flows that need to open a browser but handle the - /// callback differently (e.g., polling-based flows). - import llm-oauth-open-browser: func(url: string) -> result<_, string>; -} diff --git a/crates/extension_api/wit/since_v0.7.0/github.wit b/crates/extension_api/wit/since_v0.7.0/github.wit deleted file mode 100644 index 21cd5d48056af08441d3bb5aa8547edd97a874d7..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/github.wit +++ /dev/null @@ -1,35 +0,0 @@ -interface github { - /// A GitHub release. - record github-release { - /// The version of the release. - version: string, - /// The list of assets attached to the release. - assets: list, - } - - /// An asset from a GitHub release. - record github-release-asset { - /// The name of the asset. - name: string, - /// The download URL for the asset. - download-url: string, - } - - /// The options used to filter down GitHub releases. - record github-release-options { - /// Whether releases without assets should be included. - require-assets: bool, - /// Whether pre-releases should be included. - pre-release: bool, - } - - /// Returns the latest release for the given GitHub repository. - /// - /// Takes repo as a string in the form "/", for example: "zed-industries/zed". - latest-github-release: func(repo: string, options: github-release-options) -> result; - - /// Returns the GitHub release with the specified tag name for the given GitHub repository. - /// - /// Returns an error if a release with the given tag name does not exist. - github-release-by-tag-name: func(repo: string, tag: string) -> result; -} diff --git a/crates/extension_api/wit/since_v0.7.0/http-client.wit b/crates/extension_api/wit/since_v0.7.0/http-client.wit deleted file mode 100644 index bb0206c17a52d4d20b99f445dca4ac606e0485f7..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/http-client.wit +++ /dev/null @@ -1,67 +0,0 @@ -interface http-client { - /// An HTTP request. - record http-request { - /// The HTTP method for the request. - method: http-method, - /// The URL to which the request should be made. - url: string, - /// The headers for the request. - headers: list>, - /// The request body. - body: option>, - /// The policy to use for redirects. - redirect-policy: redirect-policy, - } - - /// HTTP methods. - enum http-method { - /// `GET` - get, - /// `HEAD` - head, - /// `POST` - post, - /// `PUT` - put, - /// `DELETE` - delete, - /// `OPTIONS` - options, - /// `PATCH` - patch, - } - - /// The policy for dealing with redirects received from the server. - variant redirect-policy { - /// Redirects from the server will not be followed. - /// - /// This is the default behavior. - no-follow, - /// Redirects from the server will be followed up to the specified limit. - follow-limit(u32), - /// All redirects from the server will be followed. - follow-all, - } - - /// An HTTP response. - record http-response { - /// The response headers. - headers: list>, - /// The response body. - body: list, - } - - /// Performs an HTTP request and returns the response. - fetch: func(req: http-request) -> result; - - /// An HTTP response stream. - resource http-response-stream { - /// Retrieves the next chunk of data from the response stream. - /// - /// Returns `Ok(None)` if the stream has ended. - next-chunk: func() -> result>, string>; - } - - /// Performs an HTTP request and returns a response stream. - fetch-stream: func(req: http-request) -> result; -} diff --git a/crates/extension_api/wit/since_v0.7.0/llm-provider.wit b/crates/extension_api/wit/since_v0.7.0/llm-provider.wit deleted file mode 100644 index aec6569c2efda70faa38524e458951de732dc328..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/llm-provider.wit +++ /dev/null @@ -1,302 +0,0 @@ -interface llm-provider { - /// Information about a language model provider. - record provider-info { - /// Unique identifier for the provider (e.g., "my-extension.my-provider"). - id: string, - /// Display name for the provider. - name: string, - /// Path to an SVG icon file relative to the extension root (e.g., "icons/provider.svg"). - icon: option, - } - - /// Capabilities of a language model. - record model-capabilities { - /// Whether the model supports image inputs. - supports-images: bool, - /// Whether the model supports tool/function calling. - supports-tools: bool, - /// Whether the model supports the "auto" tool choice. - supports-tool-choice-auto: bool, - /// Whether the model supports the "any" tool choice. - supports-tool-choice-any: bool, - /// Whether the model supports the "none" tool choice. - supports-tool-choice-none: bool, - /// Whether the model supports extended thinking/reasoning. - supports-thinking: bool, - /// The format for tool input schemas. - tool-input-format: tool-input-format, - } - - /// Format for tool input schemas. - enum tool-input-format { - /// Standard JSON Schema format. - json-schema, - /// Simplified schema format for certain providers. - simplified, - } - - /// Information about a specific model. - record model-info { - /// Unique identifier for the model. - id: string, - /// Display name for the model. - name: string, - /// Maximum input token count. - max-token-count: u64, - /// Maximum output tokens (optional). - max-output-tokens: option, - /// Model capabilities. - capabilities: model-capabilities, - /// Whether this is the default model for the provider. - is-default: bool, - /// Whether this is the default fast model. - is-default-fast: bool, - } - - /// The role of a message participant. - enum message-role { - /// User message. - user, - /// Assistant message. - assistant, - /// System message. - system, - } - - /// A message in a completion request. - record request-message { - /// The role of the message sender. - role: message-role, - /// The content of the message. - content: list, - /// Whether to cache this message for prompt caching. - cache: bool, - } - - /// Content within a message. - variant message-content { - /// Plain text content. - text(string), - /// Image content. - image(image-data), - /// A tool use request from the assistant. - tool-use(tool-use), - /// A tool result from the user. - tool-result(tool-result), - /// Thinking/reasoning content. - thinking(thinking-content), - /// Redacted/encrypted thinking content. - redacted-thinking(string), - } - - /// Image data for vision models. - record image-data { - /// Base64-encoded image data. - source: string, - /// Image width in pixels (optional). - width: option, - /// Image height in pixels (optional). - height: option, - } - - /// A tool use request from the model. - record tool-use { - /// Unique identifier for this tool use. - id: string, - /// The name of the tool being used. - name: string, - /// JSON string of the tool input arguments. - input: string, - /// Thought signature for providers that support it (e.g., Anthropic). - thought-signature: option, - } - - /// A tool result to send back to the model. - record tool-result { - /// The ID of the tool use this is a result for. - tool-use-id: string, - /// The name of the tool. - tool-name: string, - /// Whether this result represents an error. - is-error: bool, - /// The content of the result. - content: tool-result-content, - } - - /// Content of a tool result. - variant tool-result-content { - /// Text result. - text(string), - /// Image result. - image(image-data), - } - - /// Thinking/reasoning content from models that support extended thinking. - record thinking-content { - /// The thinking text. - text: string, - /// Signature for the thinking block (provider-specific). - signature: option, - } - - /// A tool definition for function calling. - record tool-definition { - /// The name of the tool. - name: string, - /// Description of what the tool does. - description: string, - /// JSON Schema for input parameters. - input-schema: string, - } - - /// Tool choice preference for the model. - enum tool-choice { - /// Let the model decide whether to use tools. - auto, - /// Force the model to use at least one tool. - any, - /// Prevent the model from using tools. - none, - } - - /// A completion request to send to the model. - record completion-request { - /// The messages in the conversation. - messages: list, - /// Available tools for the model to use. - tools: list, - /// Tool choice preference. - tool-choice: option, - /// Stop sequences to end generation. - stop-sequences: list, - /// Temperature for sampling (0.0-1.0). - temperature: option, - /// Whether thinking/reasoning is allowed. - thinking-allowed: bool, - /// Maximum tokens to generate. - max-tokens: option, - } - - /// Events emitted during completion streaming. - variant completion-event { - /// Completion has started. - started, - /// Text content chunk. - text(string), - /// Thinking/reasoning content chunk. - thinking(thinking-content), - /// Redacted thinking (encrypted) chunk. - redacted-thinking(string), - /// Tool use request from the model. - tool-use(tool-use), - /// JSON parse error when parsing tool input. - tool-use-json-parse-error(tool-use-json-parse-error), - /// Completion stopped. - stop(stop-reason), - /// Token usage update. - usage(token-usage), - /// Reasoning details (provider-specific JSON). - reasoning-details(string), - } - - /// Error information when tool use JSON parsing fails. - record tool-use-json-parse-error { - /// The tool use ID. - id: string, - /// The tool name. - tool-name: string, - /// The raw input that failed to parse. - raw-input: string, - /// The parse error message. - error: string, - } - - /// Reason the completion stopped. - enum stop-reason { - /// The model finished generating. - end-turn, - /// Maximum tokens reached. - max-tokens, - /// The model wants to use a tool. - tool-use, - /// The model refused to respond. - refusal, - } - - /// Token usage statistics. - record token-usage { - /// Number of input tokens used. - input-tokens: u64, - /// Number of output tokens generated. - output-tokens: u64, - /// Tokens used for cache creation (if supported). - cache-creation-input-tokens: option, - /// Tokens read from cache (if supported). - cache-read-input-tokens: option, - } - - /// Credential types that can be requested. - enum credential-type { - /// An API key. - api-key, - /// An OAuth token. - oauth-token, - } - - /// Cache configuration for prompt caching. - record cache-configuration { - /// Maximum number of cache anchors. - max-cache-anchors: u32, - /// Whether caching should be applied to tool definitions. - should-cache-tool-definitions: bool, - /// Minimum token count for a message to be cached. - min-total-token-count: u64, - } - - // ========================================================================= - // OAuth Web Auth Flow Types - // ========================================================================= - - /// Configuration for starting an OAuth web authentication flow. - record oauth-web-auth-config { - /// The URL to open in the user's browser to start authentication. - /// This should include client_id, redirect_uri, scope, state, etc. - auth-url: string, - /// The path to listen on for the OAuth callback (e.g., "/callback"). - /// A localhost server will be started to receive the redirect. - callback-path: string, - /// Timeout in seconds to wait for the callback (default: 300 = 5 minutes). - timeout-secs: option, - } - - /// Result of an OAuth web authentication flow. - record oauth-web-auth-result { - /// The full callback URL that was received, including query parameters. - /// The extension is responsible for parsing the code, state, etc. - callback-url: string, - /// The port that was used for the localhost callback server. - port: u32, - } - - /// A generic HTTP request for OAuth token exchange. - record oauth-http-request { - /// The URL to request. - url: string, - /// HTTP method (e.g., "POST", "GET"). - method: string, - /// Request headers as key-value pairs. - headers: list>, - /// Request body as a string (for form-encoded or JSON bodies). - body: string, - } - - /// Response from an OAuth HTTP request. - record oauth-http-response { - /// HTTP status code. - status: u16, - /// Response headers as key-value pairs. - headers: list>, - /// Response body as a string. - body: string, - } -} diff --git a/crates/extension_api/wit/since_v0.7.0/lsp.wit b/crates/extension_api/wit/since_v0.7.0/lsp.wit deleted file mode 100644 index 91a36c93a66467ea7dc7d78932d3821dae79d864..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/lsp.wit +++ /dev/null @@ -1,90 +0,0 @@ -interface lsp { - /// An LSP completion. - record completion { - label: string, - label-details: option, - detail: option, - kind: option, - insert-text-format: option, - } - - /// The kind of an LSP completion. - variant completion-kind { - text, - method, - function, - %constructor, - field, - variable, - class, - %interface, - module, - property, - unit, - value, - %enum, - keyword, - snippet, - color, - file, - reference, - folder, - enum-member, - constant, - struct, - event, - operator, - type-parameter, - other(s32), - } - - /// Label details for an LSP completion. - record completion-label-details { - detail: option, - description: option, - } - - /// Defines how to interpret the insert text in a completion item. - variant insert-text-format { - plain-text, - snippet, - other(s32), - } - - /// An LSP symbol. - record symbol { - kind: symbol-kind, - name: string, - } - - /// The kind of an LSP symbol. - variant symbol-kind { - file, - module, - namespace, - %package, - class, - method, - property, - field, - %constructor, - %enum, - %interface, - function, - variable, - constant, - %string, - number, - boolean, - array, - object, - key, - null, - enum-member, - struct, - event, - operator, - type-parameter, - other(s32), - } -} diff --git a/crates/extension_api/wit/since_v0.7.0/nodejs.wit b/crates/extension_api/wit/since_v0.7.0/nodejs.wit deleted file mode 100644 index c814548314162c862e81a98b3fba6950dc2a7f41..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/nodejs.wit +++ /dev/null @@ -1,13 +0,0 @@ -interface nodejs { - /// Returns the path to the Node binary used by Zed. - node-binary-path: func() -> result; - - /// Returns the latest version of the given NPM package. - npm-package-latest-version: func(package-name: string) -> result; - - /// Returns the installed version of the given NPM package, if it exists. - npm-package-installed-version: func(package-name: string) -> result, string>; - - /// Installs the specified NPM package. - npm-install-package: func(package-name: string, version: string) -> result<_, string>; -} diff --git a/crates/extension_api/wit/since_v0.7.0/platform.wit b/crates/extension_api/wit/since_v0.7.0/platform.wit deleted file mode 100644 index 48472a99bc175fdc24231a690db021433d5a2505..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/platform.wit +++ /dev/null @@ -1,24 +0,0 @@ -interface platform { - /// An operating system. - enum os { - /// macOS. - mac, - /// Linux. - linux, - /// Windows. - windows, - } - - /// A platform architecture. - enum architecture { - /// AArch64 (e.g., Apple Silicon). - aarch64, - /// x86. - x86, - /// x86-64. - x8664, - } - - /// Gets the current operating system and architecture. - current-platform: func() -> tuple; -} diff --git a/crates/extension_api/wit/since_v0.7.0/process.wit b/crates/extension_api/wit/since_v0.7.0/process.wit deleted file mode 100644 index d9a5728a3d8f5bdaa578d9dd9fc087610688cf27..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/process.wit +++ /dev/null @@ -1,29 +0,0 @@ -interface process { - use common.{env-vars}; - - /// A command. - record command { - /// The command to execute. - command: string, - /// The arguments to pass to the command. - args: list, - /// The environment variables to set for the command. - env: env-vars, - } - - /// The output of a finished process. - record output { - /// The status (exit code) of the process. - /// - /// On Unix, this will be `None` if the process was terminated by a signal. - status: option, - /// The data that the process wrote to stdout. - stdout: list, - /// The data that the process wrote to stderr. - stderr: list, - } - - /// Executes the given command as a child process, waiting for it to finish - /// and collecting all of its output. - run-command: func(command: command) -> result; -} diff --git a/crates/extension_api/wit/since_v0.7.0/settings.rs b/crates/extension_api/wit/since_v0.7.0/settings.rs deleted file mode 100644 index 19e28c1ba955a998fe7b97f3eacb57c4b1104154..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/settings.rs +++ /dev/null @@ -1,40 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, num::NonZeroU32}; - -/// The settings for a particular language. -#[derive(Debug, Serialize, Deserialize)] -pub struct LanguageSettings { - /// How many columns a tab should occupy. - pub tab_size: NonZeroU32, -} - -/// The settings for a particular language server. -#[derive(Default, Debug, Serialize, Deserialize)] -pub struct LspSettings { - /// The settings for the language server binary. - pub binary: Option, - /// The initialization options to pass to the language server. - pub initialization_options: Option, - /// The settings to pass to language server. - pub settings: Option, -} - -/// The settings for a particular context server. -#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct ContextServerSettings { - /// The settings for the context server binary. - pub command: Option, - /// The settings to pass to the context server. - pub settings: Option, -} - -/// The settings for a command. -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct CommandSettings { - /// The path to the command. - pub path: Option, - /// The arguments to pass to the command. - pub arguments: Option>, - /// The environment variables. - pub env: Option>, -} diff --git a/crates/extension_api/wit/since_v0.7.0/slash-command.wit b/crates/extension_api/wit/since_v0.7.0/slash-command.wit deleted file mode 100644 index f52561c2ef412be071820f3a71621c3c4f3f9da3..0000000000000000000000000000000000000000 --- a/crates/extension_api/wit/since_v0.7.0/slash-command.wit +++ /dev/null @@ -1,41 +0,0 @@ -interface slash-command { - use common.{range}; - - /// A slash command for use in the Assistant. - record slash-command { - /// The name of the slash command. - name: string, - /// The description of the slash command. - description: string, - /// The tooltip text to display for the run button. - tooltip-text: string, - /// Whether this slash command requires an argument. - requires-argument: bool, - } - - /// The output of a slash command. - record slash-command-output { - /// The text produced by the slash command. - text: string, - /// The list of sections to show in the slash command placeholder. - sections: list, - } - - /// A section in the slash command output. - record slash-command-output-section { - /// The range this section occupies. - range: range, - /// The label to display in the placeholder for this section. - label: string, - } - - /// A completion for a slash command argument. - record slash-command-argument-completion { - /// The label to display for this completion. - label: string, - /// The new text that should be inserted into the command when this completion is accepted. - new-text: string, - /// Whether the command should be run when accepting this completion. - run-command: bool, - } -} diff --git a/crates/extension_api/wit/since_v0.8.0/extension.wit b/crates/extension_api/wit/since_v0.8.0/extension.wit index f95dfa04dac25f792d14f896ee8c00ffa8dcf804..8724369ec0472f13d16a1f0f8a509cfe381be4e1 100644 --- a/crates/extension_api/wit/since_v0.8.0/extension.wit +++ b/crates/extension_api/wit/since_v0.8.0/extension.wit @@ -225,60 +225,4 @@ world extension { model-id: string ) -> option; - // ========================================================================= - // Language Model Provider Imports (callable by extensions) - // ========================================================================= - - /// Request a credential from the user. - /// Returns true if the credential was provided, false if the user cancelled. - import llm-request-credential: func( - provider-id: string, - credential-type: credential-type, - label: string, - placeholder: string - ) -> result; - - /// Get a stored credential for this provider. - import llm-get-credential: func(provider-id: string) -> option; - - /// Store a credential for this provider. - import llm-store-credential: func(provider-id: string, value: string) -> result<_, string>; - - /// Delete a stored credential for this provider. - import llm-delete-credential: func(provider-id: string) -> result<_, string>; - - /// Read an environment variable. - import llm-get-env-var: func(name: string) -> option; - - // ========================================================================= - // OAuth Web Auth Flow Imports - // ========================================================================= - - use llm-provider.{oauth-web-auth-config, oauth-web-auth-result, oauth-http-request, oauth-http-response}; - - /// Start an OAuth web authentication flow. - /// - /// This will: - /// 1. Start a localhost server to receive the OAuth callback - /// 2. Open the auth URL in the user's default browser - /// 3. Wait for the callback (up to the timeout) - /// 4. Return the callback URL with query parameters - /// - /// The extension is responsible for: - /// - Constructing the auth URL with client_id, redirect_uri, scope, state, etc. - /// - Parsing the callback URL to extract the authorization code - /// - Exchanging the code for tokens using llm-oauth-http-request - import llm-oauth-start-web-auth: func(config: oauth-web-auth-config) -> result; - - /// Make an HTTP request for OAuth token exchange. - /// - /// This is a simple HTTP client for OAuth flows, allowing the extension - /// to handle token exchange with full control over serialization. - import llm-oauth-http-request: func(request: oauth-http-request) -> result; - - /// Open a URL in the user's default browser. - /// - /// Useful for OAuth flows that need to open a browser but handle the - /// callback differently (e.g., polling-based flows). - import llm-oauth-open-browser: func(url: string) -> result<_, string>; } diff --git a/crates/extension_api/wit/since_v0.8.0/llm-provider.wit b/crates/extension_api/wit/since_v0.8.0/llm-provider.wit index aec6569c2efda70faa38524e458951de732dc328..abf113c3910279b313ed5e97a7f56e01adac0780 100644 --- a/crates/extension_api/wit/since_v0.8.0/llm-provider.wit +++ b/crates/extension_api/wit/since_v0.8.0/llm-provider.wit @@ -299,4 +299,55 @@ interface llm-provider { /// Response body as a string. body: string, } + + // ========================================================================= + // Import Functions (callable by extensions) + // ========================================================================= + + /// Request a credential from the user. + /// Returns true if the credential was provided, false if the user cancelled. + request-credential: func( + provider-id: string, + credential-type: credential-type, + label: string, + placeholder: string + ) -> result; + + /// Get a stored credential for this provider. + get-credential: func(provider-id: string) -> option; + + /// Store a credential for this provider. + store-credential: func(provider-id: string, value: string) -> result<_, string>; + + /// Delete a stored credential for this provider. + delete-credential: func(provider-id: string) -> result<_, string>; + + /// Read an environment variable. + get-env-var: func(name: string) -> option; + + /// Start an OAuth web authentication flow. + /// + /// This will: + /// 1. Start a localhost server to receive the OAuth callback + /// 2. Open the auth URL in the user's default browser + /// 3. Wait for the callback (up to the timeout) + /// 4. Return the callback URL with query parameters + /// + /// The extension is responsible for: + /// - Constructing the auth URL with client_id, redirect_uri, scope, state, etc. + /// - Parsing the callback URL to extract the authorization code + /// - Exchanging the code for tokens using oauth-http-request + oauth-start-web-auth: func(config: oauth-web-auth-config) -> result; + + /// Make an HTTP request for OAuth token exchange. + /// + /// This is a simple HTTP client for OAuth flows, allowing the extension + /// to handle token exchange with full control over serialization. + send-oauth-http-request: func(request: oauth-http-request) -> result; + + /// Open a URL in the user's default browser. + /// + /// Useful for OAuth flows that need to open a browser but handle the + /// callback differently (e.g., polling-based flows). + oauth-open-browser: func(url: string) -> result<_, string>; } diff --git a/crates/extension_host/src/wasm_host/wit.rs b/crates/extension_host/src/wasm_host/wit.rs index 5fa62b032d6bd7ee4902ab58c975b340861db1e0..aeefa72237175ff124375f1b207bc18cdb0e2ba2 100644 --- a/crates/extension_host/src/wasm_host/wit.rs +++ b/crates/extension_host/src/wasm_host/wit.rs @@ -7,7 +7,6 @@ mod since_v0_3_0; mod since_v0_4_0; mod since_v0_5_0; mod since_v0_6_0; -mod since_v0_7_0; mod since_v0_8_0; use dap::DebugRequest; use extension::{DebugTaskDefinition, KeyValueStoreDelegate, WorktreeDelegate}; @@ -111,7 +110,6 @@ pub fn authorize_access_to_unreleased_wasm_api_version( pub enum Extension { V0_8_0(since_v0_8_0::Extension), - V0_7_0(since_v0_7_0::Extension), V0_6_0(since_v0_6_0::Extension), V0_5_0(since_v0_5_0::Extension), V0_4_0(since_v0_4_0::Extension), @@ -142,15 +140,6 @@ impl Extension { .await .context("failed to instantiate wasm extension")?; Ok(Self::V0_8_0(extension)) - } else if version >= since_v0_7_0::MIN_VERSION { - let extension = since_v0_7_0::Extension::instantiate_async( - store, - component, - since_v0_7_0::linker(executor), - ) - .await - .context("failed to instantiate wasm extension")?; - Ok(Self::V0_7_0(extension)) } else if version >= since_v0_6_0::MIN_VERSION { let extension = since_v0_6_0::Extension::instantiate_async( store, @@ -238,7 +227,6 @@ impl Extension { pub async fn call_init_extension(&self, store: &mut Store) -> Result<()> { match self { Extension::V0_8_0(ext) => ext.call_init_extension(store).await, - Extension::V0_7_0(ext) => ext.call_init_extension(store).await, Extension::V0_6_0(ext) => ext.call_init_extension(store).await, Extension::V0_5_0(ext) => ext.call_init_extension(store).await, Extension::V0_4_0(ext) => ext.call_init_extension(store).await, @@ -263,10 +251,6 @@ impl Extension { ext.call_language_server_command(store, &language_server_id.0, resource) .await } - Extension::V0_7_0(ext) => Ok(ext - .call_language_server_command(store, &language_server_id.0, resource) - .await? - .map(Into::into)), Extension::V0_6_0(ext) => { ext.call_language_server_command(store, &language_server_id.0, resource) .await @@ -337,14 +321,6 @@ impl Extension { ) .await } - Extension::V0_7_0(ext) => { - ext.call_language_server_initialization_options( - store, - &language_server_id.0, - resource, - ) - .await - } Extension::V0_6_0(ext) => { ext.call_language_server_initialization_options( store, @@ -442,14 +418,6 @@ impl Extension { ) .await } - Extension::V0_7_0(ext) => { - ext.call_language_server_workspace_configuration( - store, - &language_server_id.0, - resource, - ) - .await - } Extension::V0_6_0(ext) => { ext.call_language_server_workspace_configuration( store, @@ -527,15 +495,6 @@ impl Extension { ) .await } - Extension::V0_7_0(ext) => { - ext.call_language_server_additional_initialization_options( - store, - &language_server_id.0, - &target_language_server_id.0, - resource, - ) - .await - } Extension::V0_6_0(ext) => { ext.call_language_server_additional_initialization_options( store, @@ -589,15 +548,6 @@ impl Extension { ) .await } - Extension::V0_7_0(ext) => { - ext.call_language_server_additional_workspace_configuration( - store, - &language_server_id.0, - &target_language_server_id.0, - resource, - ) - .await - } Extension::V0_6_0(ext) => { ext.call_language_server_additional_workspace_configuration( store, @@ -645,20 +595,6 @@ impl Extension { ext.call_labels_for_completions(store, &language_server_id.0, &completions) .await } - Extension::V0_7_0(ext) => ext - .call_labels_for_completions( - store, - &language_server_id.0, - &completions - .iter() - .cloned() - .map(Into::into) - .collect::>(), - ) - .await - .map(|res| { - res.map(|labels| labels.into_iter().map(|l| l.map(Into::into)).collect()) - }), Extension::V0_6_0(ext) => Ok(ext .call_labels_for_completions( store, @@ -765,16 +701,6 @@ impl Extension { ext.call_labels_for_symbols(store, &language_server_id.0, &symbols) .await } - Extension::V0_7_0(ext) => ext - .call_labels_for_symbols( - store, - &language_server_id.0, - &symbols.iter().cloned().map(Into::into).collect::>(), - ) - .await - .map(|res| { - res.map(|labels| labels.into_iter().map(|l| l.map(Into::into)).collect()) - }), Extension::V0_6_0(ext) => Ok(ext .call_labels_for_symbols( store, @@ -881,13 +807,6 @@ impl Extension { ext.call_complete_slash_command_argument(store, command, arguments) .await } - Extension::V0_7_0(ext) => { - let command: since_v0_7_0::slash_command::SlashCommand = command.into(); - Ok(ext - .call_complete_slash_command_argument(store, &command, arguments) - .await? - .map(|completions| completions.into_iter().map(Into::into).collect())) - } Extension::V0_6_0(ext) => { ext.call_complete_slash_command_argument(store, command, arguments) .await @@ -930,13 +849,6 @@ impl Extension { ext.call_run_slash_command(store, command, arguments, resource) .await } - Extension::V0_7_0(ext) => { - let command: since_v0_7_0::slash_command::SlashCommand = command.into(); - Ok(ext - .call_run_slash_command(store, &command, arguments, resource) - .await? - .map(Into::into)) - } Extension::V0_6_0(ext) => { ext.call_run_slash_command(store, command, arguments, resource) .await @@ -978,10 +890,6 @@ impl Extension { ext.call_context_server_command(store, &context_server_id, project) .await } - Extension::V0_7_0(ext) => Ok(ext - .call_context_server_command(store, &context_server_id, project) - .await? - .map(Into::into)), Extension::V0_6_0(ext) => { ext.call_context_server_command(store, &context_server_id, project) .await @@ -1022,10 +930,6 @@ impl Extension { ext.call_context_server_configuration(store, &context_server_id, project) .await } - Extension::V0_7_0(ext) => Ok(ext - .call_context_server_configuration(store, &context_server_id, project) - .await? - .map(|opt| opt.map(Into::into))), Extension::V0_6_0(ext) => { ext.call_context_server_configuration(store, &context_server_id, project) .await @@ -1053,7 +957,6 @@ impl Extension { ) -> Result, String>> { match self { Extension::V0_8_0(ext) => ext.call_suggest_docs_packages(store, provider).await, - Extension::V0_7_0(ext) => ext.call_suggest_docs_packages(store, provider).await, Extension::V0_6_0(ext) => ext.call_suggest_docs_packages(store, provider).await, Extension::V0_5_0(ext) => ext.call_suggest_docs_packages(store, provider).await, Extension::V0_4_0(ext) => ext.call_suggest_docs_packages(store, provider).await, @@ -1078,10 +981,6 @@ impl Extension { ext.call_index_docs(store, provider, package_name, kv_store) .await } - Extension::V0_7_0(ext) => { - ext.call_index_docs(store, provider, package_name, kv_store) - .await - } Extension::V0_6_0(ext) => { ext.call_index_docs(store, provider, package_name, kv_store) .await @@ -1134,20 +1033,6 @@ impl Extension { Ok(Ok(dap_binary)) } - Extension::V0_7_0(ext) => { - let dap_binary = ext - .call_get_dap_binary( - store, - &adapter_name, - &task.try_into()?, - user_installed_path.as_ref().and_then(|p| p.to_str()), - resource, - ) - .await? - .map_err(|e| anyhow!("{e:?}"))?; - - Ok(Ok(dap_binary.into())) - } Extension::V0_6_0(ext) => { let dap_binary = ext .call_get_dap_binary( @@ -1182,16 +1067,6 @@ impl Extension { Ok(Ok(result)) } - Extension::V0_7_0(ext) => { - let config = - serde_json::to_string(&config).context("Adapter config is not a valid JSON")?; - let result = ext - .call_dap_request_kind(store, &adapter_name, &config) - .await? - .map_err(|e| anyhow!("{e:?}"))?; - - Ok(Ok(result.into())) - } Extension::V0_6_0(ext) => { let config = serde_json::to_string(&config).context("Adapter config is not a valid JSON")?; @@ -1220,15 +1095,6 @@ impl Extension { Ok(Ok(result.try_into()?)) } - Extension::V0_7_0(ext) => { - let config: since_v0_7_0::dap::DebugConfig = config.into(); - let result = ext - .call_dap_config_to_scenario(store, &config) - .await? - .map_err(|e| anyhow!("{e:?}"))?; - - Ok(Ok(result.try_into()?)) - } Extension::V0_6_0(ext) => { let config = config.into(); let dap_binary = ext @@ -1264,21 +1130,6 @@ impl Extension { Ok(result.map(TryInto::try_into).transpose()?) } - Extension::V0_7_0(ext) => { - let build_config_template: since_v0_7_0::dap::BuildTaskTemplate = - build_config_template.into(); - let result = ext - .call_dap_locator_create_scenario( - store, - &locator_name, - &build_config_template, - &resolved_label, - &debug_adapter_name, - ) - .await?; - - Ok(result.map(TryInto::try_into).transpose()?) - } Extension::V0_6_0(ext) => { let build_config_template = build_config_template.into(); let dap_binary = ext @@ -1303,7 +1154,7 @@ impl Extension { resolved_build_task: SpawnInTerminal, ) -> Result> { match self { - Extension::V0_7_0(ext) => { + Extension::V0_8_0(ext) => { let build_config_template = resolved_build_task.try_into()?; let dap_request = ext .call_run_dap_locator(store, &locator_name, &build_config_template) @@ -1326,7 +1177,7 @@ impl Extension { } // ========================================================================= - // LLM Provider Methods (v0.7.0+) + // LLM Provider Methods (v0.8.0+) // ========================================================================= pub async fn call_llm_providers( @@ -1335,12 +1186,6 @@ impl Extension { ) -> Result> { match self { Extension::V0_8_0(ext) => ext.call_llm_providers(store).await, - Extension::V0_7_0(ext) => Ok(ext - .call_llm_providers(store) - .await? - .into_iter() - .map(Into::into) - .collect()), _ => Ok(Vec::new()), } } @@ -1352,11 +1197,7 @@ impl Extension { ) -> Result, String>> { match self { Extension::V0_8_0(ext) => ext.call_llm_provider_models(store, provider_id).await, - Extension::V0_7_0(ext) => Ok(ext - .call_llm_provider_models(store, provider_id) - .await? - .map(|models| models.into_iter().map(Into::into).collect())), - _ => anyhow::bail!("`llm_provider_models` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_provider_models` not available prior to v0.8.0"), } } @@ -1370,10 +1211,6 @@ impl Extension { ext.call_llm_provider_settings_markdown(store, provider_id) .await } - Extension::V0_7_0(ext) => { - ext.call_llm_provider_settings_markdown(store, provider_id) - .await - } _ => Ok(None), } } @@ -1388,10 +1225,6 @@ impl Extension { ext.call_llm_provider_is_authenticated(store, provider_id) .await } - Extension::V0_7_0(ext) => { - ext.call_llm_provider_is_authenticated(store, provider_id) - .await - } _ => Ok(false), } } @@ -1403,8 +1236,7 @@ impl Extension { ) -> Result> { match self { Extension::V0_8_0(ext) => ext.call_llm_provider_authenticate(store, provider_id).await, - Extension::V0_7_0(ext) => ext.call_llm_provider_authenticate(store, provider_id).await, - _ => anyhow::bail!("`llm_provider_authenticate` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_provider_authenticate` not available prior to v0.8.0"), } } @@ -1418,11 +1250,7 @@ impl Extension { ext.call_llm_provider_reset_credentials(store, provider_id) .await } - Extension::V0_7_0(ext) => { - ext.call_llm_provider_reset_credentials(store, provider_id) - .await - } - _ => anyhow::bail!("`llm_provider_reset_credentials` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_provider_reset_credentials` not available prior to v0.8.0"), } } @@ -1438,12 +1266,7 @@ impl Extension { ext.call_llm_count_tokens(store, provider_id, model_id, request) .await } - Extension::V0_7_0(ext) => { - let request: since_v0_7_0::llm_provider::CompletionRequest = request.clone().into(); - ext.call_llm_count_tokens(store, provider_id, model_id, &request) - .await - } - _ => anyhow::bail!("`llm_count_tokens` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_count_tokens` not available prior to v0.8.0"), } } @@ -1459,12 +1282,7 @@ impl Extension { ext.call_llm_stream_completion_start(store, provider_id, model_id, request) .await } - Extension::V0_7_0(ext) => { - let request: since_v0_7_0::llm_provider::CompletionRequest = request.clone().into(); - ext.call_llm_stream_completion_start(store, provider_id, model_id, &request) - .await - } - _ => anyhow::bail!("`llm_stream_completion_start` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_stream_completion_start` not available prior to v0.8.0"), } } @@ -1475,11 +1293,7 @@ impl Extension { ) -> Result, String>> { match self { Extension::V0_8_0(ext) => ext.call_llm_stream_completion_next(store, stream_id).await, - Extension::V0_7_0(ext) => Ok(ext - .call_llm_stream_completion_next(store, stream_id) - .await? - .map(|opt| opt.map(Into::into))), - _ => anyhow::bail!("`llm_stream_completion_next` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_stream_completion_next` not available prior to v0.8.0"), } } @@ -1490,8 +1304,7 @@ impl Extension { ) -> Result<()> { match self { Extension::V0_8_0(ext) => ext.call_llm_stream_completion_close(store, stream_id).await, - Extension::V0_7_0(ext) => ext.call_llm_stream_completion_close(store, stream_id).await, - _ => anyhow::bail!("`llm_stream_completion_close` not available prior to v0.7.0"), + _ => anyhow::bail!("`llm_stream_completion_close` not available prior to v0.8.0"), } } @@ -1506,10 +1319,6 @@ impl Extension { ext.call_llm_cache_configuration(store, provider_id, model_id) .await } - Extension::V0_7_0(ext) => Ok(ext - .call_llm_cache_configuration(store, provider_id, model_id) - .await? - .map(Into::into)), _ => Ok(None), } } diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_7_0.rs b/crates/extension_host/src/wasm_host/wit/since_v0_7_0.rs deleted file mode 100644 index 6d1457bebd4fb1865dfe0e3ae52139ba5d435f2b..0000000000000000000000000000000000000000 --- a/crates/extension_host/src/wasm_host/wit/since_v0_7_0.rs +++ /dev/null @@ -1,2022 +0,0 @@ -use crate::ExtensionSettings; -use crate::wasm_host::wit::since_v0_7_0::{ - dap::{ - AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, LaunchRequest, - StartDebuggingRequestArguments, TcpArguments, TcpArgumentsTemplate, - }, - lsp::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind}, - slash_command::SlashCommandOutputSection, -}; -use crate::wasm_host::{WasmState, wit::ToWasmtimeResult}; -use ::http_client::{AsyncBody, HttpRequestExt}; -use ::settings::{Settings, WorktreeId}; -use anyhow::{Context as _, Result, bail}; -use async_compression::futures::bufread::GzipDecoder; -use async_tar::Archive; -use async_trait::async_trait; -use credentials_provider::CredentialsProvider; -use extension::{ - ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate, -}; -use futures::{AsyncReadExt, lock::Mutex}; -use futures::{FutureExt as _, io::BufReader}; -use gpui::{BackgroundExecutor, SharedString}; -use language::{BinaryStatus, LanguageName, language_settings::AllLanguageSettings}; -use project::project_settings::ProjectSettings; -use semver::Version; -use smol::net::TcpListener; -use std::{ - env, - net::Ipv4Addr, - path::{Path, PathBuf}, - str::FromStr, - sync::{Arc, OnceLock}, - time::Duration, -}; -use task::{SpawnInTerminal, ZedDebugConfig}; -use url::Url; -use util::{ - archive::extract_zip, fs::make_file_executable, maybe, paths::PathStyle, rel_path::RelPath, -}; -use wasmtime::component::{Linker, Resource}; - -pub const MIN_VERSION: Version = Version::new(0, 7, 0); -#[allow(dead_code)] -pub const MAX_VERSION: Version = Version::new(0, 8, 0); - -wasmtime::component::bindgen!({ - async: true, - trappable_imports: true, - path: "../extension_api/wit/since_v0.7.0", - with: { - "worktree": ExtensionWorktree, - "project": ExtensionProject, - "key-value-store": ExtensionKeyValueStore, - "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream, - }, -}); - -// This is the latest version, so we pub use to make types available to parent module. -// Note: The parent wit.rs module re-exports specific types from here as the "latest" types. -pub use self::zed::extension::*; - -mod settings { - #![allow(dead_code)] - include!(concat!(env!("OUT_DIR"), "/since_v0.7.0/settings.rs")); -} - -pub type ExtensionWorktree = Arc; -pub type ExtensionProject = Arc; -pub type ExtensionKeyValueStore = Arc; -pub type ExtensionHttpResponseStream = Arc>>; - -pub fn linker(executor: &BackgroundExecutor) -> &'static Linker { - static LINKER: OnceLock> = OnceLock::new(); - LINKER.get_or_init(|| super::new_linker(executor, Extension::add_to_linker)) -} - -impl From for std::ops::Range { - fn from(range: Range) -> Self { - let start = range.start as usize; - let end = range.end as usize; - start..end - } -} - -impl From for extension::Command { - fn from(value: Command) -> Self { - Self { - command: value.command.into(), - args: value.args, - env: value.env, - } - } -} - -impl From - for extension::StartDebuggingRequestArgumentsRequest -{ - fn from(value: StartDebuggingRequestArgumentsRequest) -> Self { - match value { - StartDebuggingRequestArgumentsRequest::Launch => Self::Launch, - StartDebuggingRequestArgumentsRequest::Attach => Self::Attach, - } - } -} -impl TryFrom for extension::StartDebuggingRequestArguments { - type Error = anyhow::Error; - - fn try_from(value: StartDebuggingRequestArguments) -> Result { - Ok(Self { - configuration: serde_json::from_str(&value.configuration)?, - request: value.request.into(), - }) - } -} -impl From for extension::TcpArguments { - fn from(value: TcpArguments) -> Self { - Self { - host: value.host.into(), - port: value.port, - timeout: value.timeout, - } - } -} - -impl From for TcpArgumentsTemplate { - fn from(value: extension::TcpArgumentsTemplate) -> Self { - Self { - host: value.host.map(Ipv4Addr::to_bits), - port: value.port, - timeout: value.timeout, - } - } -} - -impl From for extension::TcpArgumentsTemplate { - fn from(value: TcpArgumentsTemplate) -> Self { - Self { - host: value.host.map(Ipv4Addr::from_bits), - port: value.port, - timeout: value.timeout, - } - } -} - -impl TryFrom for DebugTaskDefinition { - type Error = anyhow::Error; - fn try_from(value: extension::DebugTaskDefinition) -> Result { - Ok(Self { - label: value.label.to_string(), - adapter: value.adapter.to_string(), - config: value.config.to_string(), - tcp_connection: value.tcp_connection.map(Into::into), - }) - } -} - -impl From for DebugRequest { - fn from(value: task::DebugRequest) -> Self { - match value { - task::DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()), - task::DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()), - } - } -} - -impl From for task::DebugRequest { - fn from(value: DebugRequest) -> Self { - match value { - DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()), - DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()), - } - } -} - -impl From for LaunchRequest { - fn from(value: task::LaunchRequest) -> Self { - Self { - program: value.program, - cwd: value.cwd.map(|p| p.to_string_lossy().into_owned()), - args: value.args, - envs: value.env.into_iter().collect(), - } - } -} - -impl From for AttachRequest { - fn from(value: task::AttachRequest) -> Self { - Self { - process_id: value.process_id, - } - } -} - -impl From for task::LaunchRequest { - fn from(value: LaunchRequest) -> Self { - Self { - program: value.program, - cwd: value.cwd.map(|p| p.into()), - args: value.args, - env: value.envs.into_iter().collect(), - } - } -} -impl From for task::AttachRequest { - fn from(value: AttachRequest) -> Self { - Self { - process_id: value.process_id, - } - } -} - -impl From for DebugConfig { - fn from(value: ZedDebugConfig) -> Self { - Self { - label: value.label.into(), - adapter: value.adapter.into(), - request: value.request.into(), - stop_on_entry: value.stop_on_entry, - } - } -} -impl TryFrom for extension::DebugAdapterBinary { - type Error = anyhow::Error; - fn try_from(value: DebugAdapterBinary) -> Result { - Ok(Self { - command: value.command, - arguments: value.arguments, - envs: value.envs.into_iter().collect(), - cwd: value.cwd.map(|s| s.into()), - connection: value.connection.map(Into::into), - request_args: value.request_args.try_into()?, - }) - } -} - -impl From for extension::BuildTaskDefinition { - fn from(value: BuildTaskDefinition) -> Self { - match value { - BuildTaskDefinition::ByName(name) => Self::ByName(name.into()), - BuildTaskDefinition::Template(build_task_template) => Self::Template { - task_template: build_task_template.template.into(), - locator_name: build_task_template.locator_name.map(SharedString::from), - }, - } - } -} - -impl From for BuildTaskDefinition { - fn from(value: extension::BuildTaskDefinition) -> Self { - match value { - extension::BuildTaskDefinition::ByName(name) => Self::ByName(name.into()), - extension::BuildTaskDefinition::Template { - task_template, - locator_name, - } => Self::Template(BuildTaskDefinitionTemplatePayload { - template: task_template.into(), - locator_name: locator_name.map(String::from), - }), - } - } -} -impl From for extension::BuildTaskTemplate { - fn from(value: BuildTaskTemplate) -> Self { - Self { - label: value.label, - command: value.command, - args: value.args, - env: value.env.into_iter().collect(), - cwd: value.cwd, - ..Default::default() - } - } -} -impl From for BuildTaskTemplate { - fn from(value: extension::BuildTaskTemplate) -> Self { - Self { - label: value.label, - command: value.command, - args: value.args, - env: value.env.into_iter().collect(), - cwd: value.cwd, - } - } -} - -impl TryFrom for extension::DebugScenario { - type Error = anyhow::Error; - - fn try_from(value: DebugScenario) -> std::result::Result { - Ok(Self { - adapter: value.adapter.into(), - label: value.label.into(), - build: value.build.map(Into::into), - config: serde_json::Value::from_str(&value.config)?, - tcp_connection: value.tcp_connection.map(Into::into), - }) - } -} - -impl From for DebugScenario { - fn from(value: extension::DebugScenario) -> Self { - Self { - adapter: value.adapter.into(), - label: value.label.into(), - build: value.build.map(Into::into), - config: value.config.to_string(), - tcp_connection: value.tcp_connection.map(Into::into), - } - } -} - -impl TryFrom for ResolvedTask { - type Error = anyhow::Error; - - fn try_from(value: SpawnInTerminal) -> Result { - Ok(Self { - label: value.label, - command: value.command.context("missing command")?, - args: value.args, - env: value.env.into_iter().collect(), - cwd: value.cwd.map(|s| { - let s = s.to_string_lossy(); - if cfg!(target_os = "windows") { - s.replace('\\', "/") - } else { - s.into_owned() - } - }), - }) - } -} - -impl From for extension::CodeLabel { - fn from(value: CodeLabel) -> Self { - Self { - code: value.code, - spans: value.spans.into_iter().map(Into::into).collect(), - filter_range: value.filter_range.into(), - } - } -} - -impl From for extension::CodeLabelSpan { - fn from(value: CodeLabelSpan) -> Self { - match value { - CodeLabelSpan::CodeRange(range) => Self::CodeRange(range.into()), - CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()), - } - } -} - -impl From for extension::CodeLabelSpanLiteral { - fn from(value: CodeLabelSpanLiteral) -> Self { - Self { - text: value.text, - highlight_name: value.highlight_name, - } - } -} - -impl From for Completion { - fn from(value: extension::Completion) -> Self { - Self { - label: value.label, - label_details: value.label_details.map(Into::into), - detail: value.detail, - kind: value.kind.map(Into::into), - insert_text_format: value.insert_text_format.map(Into::into), - } - } -} - -impl From for CompletionLabelDetails { - fn from(value: extension::CompletionLabelDetails) -> Self { - Self { - detail: value.detail, - description: value.description, - } - } -} - -impl From for CompletionKind { - fn from(value: extension::CompletionKind) -> Self { - match value { - extension::CompletionKind::Text => Self::Text, - extension::CompletionKind::Method => Self::Method, - extension::CompletionKind::Function => Self::Function, - extension::CompletionKind::Constructor => Self::Constructor, - extension::CompletionKind::Field => Self::Field, - extension::CompletionKind::Variable => Self::Variable, - extension::CompletionKind::Class => Self::Class, - extension::CompletionKind::Interface => Self::Interface, - extension::CompletionKind::Module => Self::Module, - extension::CompletionKind::Property => Self::Property, - extension::CompletionKind::Unit => Self::Unit, - extension::CompletionKind::Value => Self::Value, - extension::CompletionKind::Enum => Self::Enum, - extension::CompletionKind::Keyword => Self::Keyword, - extension::CompletionKind::Snippet => Self::Snippet, - extension::CompletionKind::Color => Self::Color, - extension::CompletionKind::File => Self::File, - extension::CompletionKind::Reference => Self::Reference, - extension::CompletionKind::Folder => Self::Folder, - extension::CompletionKind::EnumMember => Self::EnumMember, - extension::CompletionKind::Constant => Self::Constant, - extension::CompletionKind::Struct => Self::Struct, - extension::CompletionKind::Event => Self::Event, - extension::CompletionKind::Operator => Self::Operator, - extension::CompletionKind::TypeParameter => Self::TypeParameter, - extension::CompletionKind::Other(value) => Self::Other(value), - } - } -} - -impl From for InsertTextFormat { - fn from(value: extension::InsertTextFormat) -> Self { - match value { - extension::InsertTextFormat::PlainText => Self::PlainText, - extension::InsertTextFormat::Snippet => Self::Snippet, - extension::InsertTextFormat::Other(value) => Self::Other(value), - } - } -} - -impl From for Symbol { - fn from(value: extension::Symbol) -> Self { - Self { - kind: value.kind.into(), - name: value.name, - } - } -} - -impl From for SymbolKind { - fn from(value: extension::SymbolKind) -> Self { - match value { - extension::SymbolKind::File => Self::File, - extension::SymbolKind::Module => Self::Module, - extension::SymbolKind::Namespace => Self::Namespace, - extension::SymbolKind::Package => Self::Package, - extension::SymbolKind::Class => Self::Class, - extension::SymbolKind::Method => Self::Method, - extension::SymbolKind::Property => Self::Property, - extension::SymbolKind::Field => Self::Field, - extension::SymbolKind::Constructor => Self::Constructor, - extension::SymbolKind::Enum => Self::Enum, - extension::SymbolKind::Interface => Self::Interface, - extension::SymbolKind::Function => Self::Function, - extension::SymbolKind::Variable => Self::Variable, - extension::SymbolKind::Constant => Self::Constant, - extension::SymbolKind::String => Self::String, - extension::SymbolKind::Number => Self::Number, - extension::SymbolKind::Boolean => Self::Boolean, - extension::SymbolKind::Array => Self::Array, - extension::SymbolKind::Object => Self::Object, - extension::SymbolKind::Key => Self::Key, - extension::SymbolKind::Null => Self::Null, - extension::SymbolKind::EnumMember => Self::EnumMember, - extension::SymbolKind::Struct => Self::Struct, - extension::SymbolKind::Event => Self::Event, - extension::SymbolKind::Operator => Self::Operator, - extension::SymbolKind::TypeParameter => Self::TypeParameter, - extension::SymbolKind::Other(value) => Self::Other(value), - } - } -} - -impl From for SlashCommand { - fn from(value: extension::SlashCommand) -> Self { - Self { - name: value.name, - description: value.description, - tooltip_text: value.tooltip_text, - requires_argument: value.requires_argument, - } - } -} - -impl From for extension::SlashCommandOutput { - fn from(value: SlashCommandOutput) -> Self { - Self { - text: value.text, - sections: value.sections.into_iter().map(Into::into).collect(), - } - } -} - -impl From for extension::SlashCommandOutputSection { - fn from(value: SlashCommandOutputSection) -> Self { - Self { - range: value.range.start as usize..value.range.end as usize, - label: value.label, - } - } -} - -impl From for extension::SlashCommandArgumentCompletion { - fn from(value: SlashCommandArgumentCompletion) -> Self { - Self { - label: value.label, - new_text: value.new_text, - run_command: value.run_command, - } - } -} - -impl TryFrom for extension::ContextServerConfiguration { - type Error = anyhow::Error; - - fn try_from(value: ContextServerConfiguration) -> Result { - let settings_schema: serde_json::Value = serde_json::from_str(&value.settings_schema) - .context("Failed to parse settings_schema")?; - - Ok(Self { - installation_instructions: value.installation_instructions, - default_settings: value.default_settings, - settings_schema, - }) - } -} - -impl HostKeyValueStore for WasmState { - async fn insert( - &mut self, - kv_store: Resource, - key: String, - value: String, - ) -> wasmtime::Result> { - let kv_store = self.table.get(&kv_store)?; - kv_store.insert(key, value).await.to_wasmtime_result() - } - - async fn drop(&mut self, _worktree: Resource) -> Result<()> { - // We only ever hand out borrows of key-value stores. - Ok(()) - } -} - -impl HostProject for WasmState { - async fn worktree_ids( - &mut self, - project: Resource, - ) -> wasmtime::Result> { - let project = self.table.get(&project)?; - Ok(project.worktree_ids()) - } - - async fn drop(&mut self, _project: Resource) -> Result<()> { - // We only ever hand out borrows of projects. - Ok(()) - } -} - -impl HostWorktree for WasmState { - async fn id(&mut self, delegate: Resource>) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.id()) - } - - async fn root_path( - &mut self, - delegate: Resource>, - ) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.root_path()) - } - - async fn read_text_file( - &mut self, - delegate: Resource>, - path: String, - ) -> wasmtime::Result> { - let delegate = self.table.get(&delegate)?; - Ok(delegate - .read_text_file(&RelPath::new(Path::new(&path), PathStyle::Posix)?) - .await - .map_err(|error| error.to_string())) - } - - async fn shell_env( - &mut self, - delegate: Resource>, - ) -> wasmtime::Result { - let delegate = self.table.get(&delegate)?; - Ok(delegate.shell_env().await.into_iter().collect()) - } - - async fn which( - &mut self, - delegate: Resource>, - binary_name: String, - ) -> wasmtime::Result> { - let delegate = self.table.get(&delegate)?; - Ok(delegate.which(binary_name).await) - } - - async fn drop(&mut self, _worktree: Resource) -> Result<()> { - // We only ever hand out borrows of worktrees. - Ok(()) - } -} - -impl common::Host for WasmState {} - -impl http_client::Host for WasmState { - async fn fetch( - &mut self, - request: http_client::HttpRequest, - ) -> wasmtime::Result> { - maybe!(async { - let url = &request.url; - let request = convert_request(&request)?; - let mut response = self.host.http_client.send(request).await?; - - if response.status().is_client_error() || response.status().is_server_error() { - bail!("failed to fetch '{url}': status code {}", response.status()) - } - convert_response(&mut response).await - }) - .await - .to_wasmtime_result() - } - - async fn fetch_stream( - &mut self, - request: http_client::HttpRequest, - ) -> wasmtime::Result, String>> { - let request = convert_request(&request)?; - let response = self.host.http_client.send(request); - maybe!(async { - let response = response.await?; - let stream = Arc::new(Mutex::new(response)); - let resource = self.table.push(stream)?; - Ok(resource) - }) - .await - .to_wasmtime_result() - } -} - -impl http_client::HostHttpResponseStream for WasmState { - async fn next_chunk( - &mut self, - resource: Resource, - ) -> wasmtime::Result>, String>> { - let stream = self.table.get(&resource)?.clone(); - maybe!(async move { - let mut response = stream.lock().await; - let mut buffer = vec![0; 8192]; // 8KB buffer - let bytes_read = response.body_mut().read(&mut buffer).await?; - if bytes_read == 0 { - Ok(None) - } else { - buffer.truncate(bytes_read); - Ok(Some(buffer)) - } - }) - .await - .to_wasmtime_result() - } - - async fn drop(&mut self, _resource: Resource) -> Result<()> { - Ok(()) - } -} - -impl From for ::http_client::Method { - fn from(value: http_client::HttpMethod) -> Self { - match value { - http_client::HttpMethod::Get => Self::GET, - http_client::HttpMethod::Post => Self::POST, - http_client::HttpMethod::Put => Self::PUT, - http_client::HttpMethod::Delete => Self::DELETE, - http_client::HttpMethod::Head => Self::HEAD, - http_client::HttpMethod::Options => Self::OPTIONS, - http_client::HttpMethod::Patch => Self::PATCH, - } - } -} - -fn convert_request( - extension_request: &http_client::HttpRequest, -) -> anyhow::Result<::http_client::Request> { - let mut request = ::http_client::Request::builder() - .method(::http_client::Method::from(extension_request.method)) - .uri(&extension_request.url) - .follow_redirects(match extension_request.redirect_policy { - http_client::RedirectPolicy::NoFollow => ::http_client::RedirectPolicy::NoFollow, - http_client::RedirectPolicy::FollowLimit(limit) => { - ::http_client::RedirectPolicy::FollowLimit(limit) - } - http_client::RedirectPolicy::FollowAll => ::http_client::RedirectPolicy::FollowAll, - }); - for (key, value) in &extension_request.headers { - request = request.header(key, value); - } - let body = extension_request - .body - .clone() - .map(AsyncBody::from) - .unwrap_or_default(); - request.body(body).map_err(anyhow::Error::from) -} - -async fn convert_response( - response: &mut ::http_client::Response, -) -> anyhow::Result { - let mut extension_response = http_client::HttpResponse { - body: Vec::new(), - headers: Vec::new(), - }; - - for (key, value) in response.headers() { - extension_response - .headers - .push((key.to_string(), value.to_str().unwrap_or("").to_string())); - } - - response - .body_mut() - .read_to_end(&mut extension_response.body) - .await?; - - Ok(extension_response) -} - -impl nodejs::Host for WasmState { - async fn node_binary_path(&mut self) -> wasmtime::Result> { - self.host - .node_runtime - .binary_path() - .await - .map(|path| path.to_string_lossy().into_owned()) - .to_wasmtime_result() - } - - async fn npm_package_latest_version( - &mut self, - package_name: String, - ) -> wasmtime::Result> { - self.host - .node_runtime - .npm_package_latest_version(&package_name) - .await - .to_wasmtime_result() - } - - async fn npm_package_installed_version( - &mut self, - package_name: String, - ) -> wasmtime::Result, String>> { - self.host - .node_runtime - .npm_package_installed_version(&self.work_dir(), &package_name) - .await - .to_wasmtime_result() - } - - async fn npm_install_package( - &mut self, - package_name: String, - version: String, - ) -> wasmtime::Result> { - self.capability_granter - .grant_npm_install_package(&package_name)?; - - self.host - .node_runtime - .npm_install_packages(&self.work_dir(), &[(&package_name, &version)]) - .await - .to_wasmtime_result() - } -} - -#[async_trait] -impl lsp::Host for WasmState {} - -impl From<::http_client::github::GithubRelease> for github::GithubRelease { - fn from(value: ::http_client::github::GithubRelease) -> Self { - Self { - version: value.tag_name, - assets: value.assets.into_iter().map(Into::into).collect(), - } - } -} - -impl From<::http_client::github::GithubReleaseAsset> for github::GithubReleaseAsset { - fn from(value: ::http_client::github::GithubReleaseAsset) -> Self { - Self { - name: value.name, - download_url: value.browser_download_url, - } - } -} - -impl github::Host for WasmState { - async fn latest_github_release( - &mut self, - repo: String, - options: github::GithubReleaseOptions, - ) -> wasmtime::Result> { - maybe!(async { - let release = ::http_client::github::latest_github_release( - &repo, - options.require_assets, - options.pre_release, - self.host.http_client.clone(), - ) - .await?; - Ok(release.into()) - }) - .await - .to_wasmtime_result() - } - - async fn github_release_by_tag_name( - &mut self, - repo: String, - tag: String, - ) -> wasmtime::Result> { - maybe!(async { - let release = ::http_client::github::get_release_by_tag_name( - &repo, - &tag, - self.host.http_client.clone(), - ) - .await?; - Ok(release.into()) - }) - .await - .to_wasmtime_result() - } -} - -impl platform::Host for WasmState { - async fn current_platform(&mut self) -> Result<(platform::Os, platform::Architecture)> { - Ok(( - match env::consts::OS { - "macos" => platform::Os::Mac, - "linux" => platform::Os::Linux, - "windows" => platform::Os::Windows, - _ => panic!("unsupported os"), - }, - match env::consts::ARCH { - "aarch64" => platform::Architecture::Aarch64, - "x86" => platform::Architecture::X86, - "x86_64" => platform::Architecture::X8664, - _ => panic!("unsupported architecture"), - }, - )) - } -} - -impl From for process::Output { - fn from(output: std::process::Output) -> Self { - Self { - status: output.status.code(), - stdout: output.stdout, - stderr: output.stderr, - } - } -} - -impl process::Host for WasmState { - async fn run_command( - &mut self, - command: process::Command, - ) -> wasmtime::Result> { - maybe!(async { - self.capability_granter - .grant_exec(&command.command, &command.args)?; - - let output = util::command::new_smol_command(command.command.as_str()) - .args(&command.args) - .envs(command.env) - .output() - .await?; - - Ok(output.into()) - }) - .await - .to_wasmtime_result() - } -} - -#[async_trait] -impl slash_command::Host for WasmState {} - -#[async_trait] -impl context_server::Host for WasmState {} - -impl dap::Host for WasmState { - async fn resolve_tcp_template( - &mut self, - template: TcpArgumentsTemplate, - ) -> wasmtime::Result> { - maybe!(async { - let (host, port, timeout) = - ::dap::configure_tcp_connection(task::TcpArgumentsTemplate { - port: template.port, - host: template.host.map(Ipv4Addr::from_bits), - timeout: template.timeout, - }) - .await?; - Ok(TcpArguments { - port, - host: host.to_bits(), - timeout, - }) - }) - .await - .to_wasmtime_result() - } -} - -impl ExtensionImports for WasmState { - async fn get_settings( - &mut self, - location: Option, - category: String, - key: Option, - ) -> wasmtime::Result> { - self.on_main_thread(|cx| { - async move { - let path = location.as_ref().and_then(|location| { - RelPath::new(Path::new(&location.path), PathStyle::Posix).ok() - }); - let location = path - .as_ref() - .zip(location.as_ref()) - .map(|(path, location)| ::settings::SettingsLocation { - worktree_id: WorktreeId::from_proto(location.worktree_id), - path, - }); - - cx.update(|cx| match category.as_str() { - "language" => { - let key = key.map(|k| LanguageName::new(&k)); - let settings = AllLanguageSettings::get(location, cx).language( - location, - key.as_ref(), - cx, - ); - Ok(serde_json::to_string(&settings::LanguageSettings { - tab_size: settings.tab_size, - })?) - } - "lsp" => { - let settings = key - .and_then(|key| { - ProjectSettings::get(location, cx) - .lsp - .get(&::lsp::LanguageServerName::from_proto(key)) - }) - .cloned() - .unwrap_or_default(); - Ok(serde_json::to_string(&settings::LspSettings { - binary: settings.binary.map(|binary| settings::CommandSettings { - path: binary.path, - arguments: binary.arguments, - env: binary.env.map(|env| env.into_iter().collect()), - }), - settings: settings.settings, - initialization_options: settings.initialization_options, - })?) - } - "context_servers" => { - let settings = key - .and_then(|key| { - ProjectSettings::get(location, cx) - .context_servers - .get(key.as_str()) - }) - .cloned() - .unwrap_or_else(|| { - project::project_settings::ContextServerSettings::default_extension( - ) - }); - - match settings { - project::project_settings::ContextServerSettings::Stdio { - enabled: _, - command, - } => Ok(serde_json::to_string(&settings::ContextServerSettings { - command: Some(settings::CommandSettings { - path: command.path.to_str().map(|path| path.to_string()), - arguments: Some(command.args), - env: command.env.map(|env| env.into_iter().collect()), - }), - settings: None, - })?), - project::project_settings::ContextServerSettings::Extension { - enabled: _, - settings, - } => Ok(serde_json::to_string(&settings::ContextServerSettings { - command: None, - settings: Some(settings), - })?), - project::project_settings::ContextServerSettings::Http { .. } => { - bail!("remote context server settings not supported in 0.6.0") - } - } - } - _ => { - bail!("Unknown settings category: {}", category); - } - }) - } - .boxed_local() - }) - .await? - .to_wasmtime_result() - } - - async fn set_language_server_installation_status( - &mut self, - server_name: String, - status: LanguageServerInstallationStatus, - ) -> wasmtime::Result<()> { - let status = match status { - LanguageServerInstallationStatus::CheckingForUpdate => BinaryStatus::CheckingForUpdate, - LanguageServerInstallationStatus::Downloading => BinaryStatus::Downloading, - LanguageServerInstallationStatus::None => BinaryStatus::None, - LanguageServerInstallationStatus::Failed(error) => BinaryStatus::Failed { error }, - }; - - self.host - .proxy - .update_language_server_status(::lsp::LanguageServerName(server_name.into()), status); - - Ok(()) - } - - async fn download_file( - &mut self, - url: String, - path: String, - file_type: DownloadedFileType, - ) -> wasmtime::Result> { - maybe!(async { - let parsed_url = Url::parse(&url)?; - self.capability_granter.grant_download_file(&parsed_url)?; - - let path = PathBuf::from(path); - let extension_work_dir = self.host.work_dir.join(self.manifest.id.as_ref()); - - self.host.fs.create_dir(&extension_work_dir).await?; - - let destination_path = self - .host - .writeable_path_from_extension(&self.manifest.id, &path)?; - - let mut response = self - .host - .http_client - .get(&url, Default::default(), true) - .await - .context("downloading release")?; - - anyhow::ensure!( - response.status().is_success(), - "download failed with status {}", - response.status() - ); - let body = BufReader::new(response.body_mut()); - - match file_type { - DownloadedFileType::Uncompressed => { - futures::pin_mut!(body); - self.host - .fs - .create_file_with(&destination_path, body) - .await?; - } - DownloadedFileType::Gzip => { - let body = GzipDecoder::new(body); - futures::pin_mut!(body); - self.host - .fs - .create_file_with(&destination_path, body) - .await?; - } - DownloadedFileType::GzipTar => { - let body = GzipDecoder::new(body); - futures::pin_mut!(body); - self.host - .fs - .extract_tar_file(&destination_path, Archive::new(body)) - .await?; - } - DownloadedFileType::Zip => { - futures::pin_mut!(body); - extract_zip(&destination_path, body) - .await - .with_context(|| format!("unzipping {path:?} archive"))?; - } - } - - Ok(()) - }) - .await - .to_wasmtime_result() - } - - async fn make_file_executable(&mut self, path: String) -> wasmtime::Result> { - let path = self - .host - .writeable_path_from_extension(&self.manifest.id, Path::new(&path))?; - - make_file_executable(&path) - .await - .with_context(|| format!("setting permissions for path {path:?}")) - .to_wasmtime_result() - } - - // ========================================================================= - // LLM Provider Import Implementations - // ========================================================================= - - async fn llm_request_credential( - &mut self, - _provider_id: String, - _credential_type: llm_provider::CredentialType, - _label: String, - _placeholder: String, - ) -> wasmtime::Result> { - // For now, credential requests return false (not provided) - // Extensions should use llm_get_env_var to check for env vars first, - // then llm_store_credential/llm_get_credential for manual storage - // Full UI credential prompting will be added in a future phase - Ok(Ok(false)) - } - - async fn llm_get_credential( - &mut self, - provider_id: String, - ) -> wasmtime::Result> { - let extension_id = self.manifest.id.clone(); - let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id); - - self.on_main_thread(move |cx| { - async move { - let credentials_provider = cx.update(|cx| ::global(cx))?; - let result = credentials_provider - .read_credentials(&credential_key, cx) - .await - .ok() - .flatten(); - Ok(result.map(|(_, password)| String::from_utf8_lossy(&password).to_string())) - } - .boxed_local() - }) - .await - } - - async fn llm_store_credential( - &mut self, - provider_id: String, - value: String, - ) -> wasmtime::Result> { - let extension_id = self.manifest.id.clone(); - let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id); - - self.on_main_thread(move |cx| { - async move { - let credentials_provider = cx.update(|cx| ::global(cx))?; - credentials_provider - .write_credentials(&credential_key, "api_key", value.as_bytes(), cx) - .await - .map_err(|e| anyhow::anyhow!("{}", e)) - } - .boxed_local() - }) - .await - .to_wasmtime_result() - } - - async fn llm_delete_credential( - &mut self, - provider_id: String, - ) -> wasmtime::Result> { - let extension_id = self.manifest.id.clone(); - let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id); - - self.on_main_thread(move |cx| { - async move { - let credentials_provider = cx.update(|cx| ::global(cx))?; - credentials_provider - .delete_credentials(&credential_key, cx) - .await - .map_err(|e| anyhow::anyhow!("{}", e)) - } - .boxed_local() - }) - .await - .to_wasmtime_result() - } - - async fn llm_get_env_var(&mut self, name: String) -> wasmtime::Result> { - let extension_id = self.manifest.id.clone(); - - // Find which provider (if any) declares this env var in its auth config - let mut allowed_provider_id: Option> = None; - for (provider_id, provider_entry) in &self.manifest.language_model_providers { - if let Some(auth_config) = &provider_entry.auth { - if auth_config.env_var.as_deref() == Some(&name) { - allowed_provider_id = Some(provider_id.clone()); - break; - } - } - } - - // If no provider declares this env var, deny access - let Some(provider_id) = allowed_provider_id else { - log::warn!( - "Extension {} attempted to read env var {} which is not declared in any provider auth config", - extension_id, - name - ); - return Ok(None); - }; - - // Check if the user has allowed this provider to read env vars - let full_provider_id = format!("{}:{}", extension_id, provider_id); - let is_allowed = self - .on_main_thread(move |cx| { - async move { - cx.update(|cx| { - ExtensionSettings::get_global(cx) - .allowed_env_var_providers - .contains(full_provider_id.as_str()) - }) - .unwrap_or(false) - } - .boxed_local() - }) - .await; - - if !is_allowed { - log::debug!( - "Extension {} provider {} is not allowed to read env var {}", - extension_id, - provider_id, - name - ); - return Ok(None); - } - - Ok(env::var(&name).ok()) - } - - async fn llm_oauth_start_web_auth( - &mut self, - config: llm_provider::OauthWebAuthConfig, - ) -> wasmtime::Result> { - let auth_url = config.auth_url; - let callback_path = config.callback_path; - let timeout_secs = config.timeout_secs.unwrap_or(300); - - self.on_main_thread(move |cx| { - async move { - let listener = TcpListener::bind("127.0.0.1:0") - .await - .map_err(|e| anyhow::anyhow!("Failed to bind localhost server: {}", e))?; - let port = listener - .local_addr() - .map_err(|e| anyhow::anyhow!("Failed to get local address: {}", e))? - .port(); - - cx.update(|cx| { - cx.open_url(&auth_url); - })?; - - let accept_future = async { - let (mut stream, _) = listener - .accept() - .await - .map_err(|e| anyhow::anyhow!("Failed to accept connection: {}", e))?; - - let mut request_line = String::new(); - { - let mut reader = smol::io::BufReader::new(&mut stream); - smol::io::AsyncBufReadExt::read_line(&mut reader, &mut request_line) - .await - .map_err(|e| anyhow::anyhow!("Failed to read request: {}", e))?; - } - - let callback_url = if let Some(path_start) = request_line.find(' ') { - if let Some(path_end) = request_line[path_start + 1..].find(' ') { - let path = &request_line[path_start + 1..path_start + 1 + path_end]; - if path.starts_with(&callback_path) || path.starts_with(&format!("/{}", callback_path.trim_start_matches('/'))) { - format!("http://localhost:{}{}", port, path) - } else { - return Err(anyhow::anyhow!( - "Unexpected callback path: {}", - path - )); - } - } else { - return Err(anyhow::anyhow!("Malformed HTTP request")); - } - } else { - return Err(anyhow::anyhow!("Malformed HTTP request")); - }; - - let response = "HTTP/1.1 200 OK\r\n\ - Content-Type: text/html\r\n\ - Connection: close\r\n\ - \r\n\ - \ - Authentication Complete\ - \ -
\ -

Authentication Complete

\ -

You can close this window and return to Zed.

\ -
"; - - smol::io::AsyncWriteExt::write_all(&mut stream, response.as_bytes()) - .await - .ok(); - smol::io::AsyncWriteExt::flush(&mut stream).await.ok(); - - Ok(callback_url) - }; - - let timeout_duration = Duration::from_secs(timeout_secs as u64); - let callback_url = smol::future::or( - accept_future, - async { - smol::Timer::after(timeout_duration).await; - Err(anyhow::anyhow!( - "OAuth callback timed out after {} seconds", - timeout_secs - )) - }, - ) - .await?; - - Ok(llm_provider::OauthWebAuthResult { - callback_url, - port: port as u32, - }) - } - .boxed_local() - }) - .await - .to_wasmtime_result() - } - - async fn llm_oauth_http_request( - &mut self, - request: llm_provider::OauthHttpRequest, - ) -> wasmtime::Result> { - let http_client = self.host.http_client.clone(); - - self.on_main_thread(move |_cx| { - async move { - let method = match request.method.to_uppercase().as_str() { - "GET" => ::http_client::Method::GET, - "POST" => ::http_client::Method::POST, - "PUT" => ::http_client::Method::PUT, - "DELETE" => ::http_client::Method::DELETE, - "PATCH" => ::http_client::Method::PATCH, - _ => { - return Err(anyhow::anyhow!( - "Unsupported HTTP method: {}", - request.method - )); - } - }; - - let mut builder = ::http_client::Request::builder() - .method(method) - .uri(&request.url); - - for (key, value) in &request.headers { - builder = builder.header(key.as_str(), value.as_str()); - } - - let body = if request.body.is_empty() { - AsyncBody::empty() - } else { - AsyncBody::from(request.body.into_bytes()) - }; - - let http_request = builder - .body(body) - .map_err(|e| anyhow::anyhow!("Failed to build request: {}", e))?; - - let mut response = http_client - .send(http_request) - .await - .map_err(|e| anyhow::anyhow!("HTTP request failed: {}", e))?; - - let status = response.status().as_u16(); - let headers: Vec<(String, String)> = response - .headers() - .iter() - .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) - .collect(); - - let mut body_bytes = Vec::new(); - futures::AsyncReadExt::read_to_end(response.body_mut(), &mut body_bytes) - .await - .map_err(|e| anyhow::anyhow!("Failed to read response body: {}", e))?; - - let body = String::from_utf8_lossy(&body_bytes).to_string(); - - Ok(llm_provider::OauthHttpResponse { - status, - headers, - body, - }) - } - .boxed_local() - }) - .await - .to_wasmtime_result() - } - - async fn llm_oauth_open_browser( - &mut self, - url: String, - ) -> wasmtime::Result> { - self.on_main_thread(move |cx| { - async move { - cx.update(|cx| { - cx.open_url(&url); - })?; - Ok(()) - } - .boxed_local() - }) - .await - .to_wasmtime_result() - } -} - -// ============================================================================= -// LLM Provider Host Implementations -// ============================================================================= - -impl llm_provider::Host for WasmState {} - -// ============================================================================= -// LLM Provider Type Conversions (v0.7.0 -> latest/v0.8.0) -// ============================================================================= - -use super::since_v0_8_0 as latest; - -impl From for latest::llm_provider::ProviderInfo { - fn from(value: llm_provider::ProviderInfo) -> Self { - Self { - id: value.id, - name: value.name, - icon: value.icon, - } - } -} - -impl From for latest::llm_provider::ModelInfo { - fn from(value: llm_provider::ModelInfo) -> Self { - Self { - id: value.id, - name: value.name, - max_token_count: value.max_token_count, - max_output_tokens: value.max_output_tokens, - capabilities: value.capabilities.into(), - is_default: value.is_default, - is_default_fast: value.is_default_fast, - } - } -} - -impl From for latest::llm_provider::ModelCapabilities { - fn from(value: llm_provider::ModelCapabilities) -> Self { - Self { - supports_images: value.supports_images, - supports_tools: value.supports_tools, - supports_tool_choice_auto: value.supports_tool_choice_auto, - supports_tool_choice_any: value.supports_tool_choice_any, - supports_tool_choice_none: value.supports_tool_choice_none, - supports_thinking: value.supports_thinking, - tool_input_format: value.tool_input_format.into(), - } - } -} - -impl From for latest::llm_provider::ToolInputFormat { - fn from(value: llm_provider::ToolInputFormat) -> Self { - match value { - llm_provider::ToolInputFormat::JsonSchema => Self::JsonSchema, - llm_provider::ToolInputFormat::Simplified => Self::Simplified, - } - } -} - -impl From for latest::llm_provider::CompletionEvent { - fn from(value: llm_provider::CompletionEvent) -> Self { - match value { - llm_provider::CompletionEvent::Started => Self::Started, - llm_provider::CompletionEvent::Text(s) => Self::Text(s), - llm_provider::CompletionEvent::Thinking(t) => Self::Thinking(t.into()), - llm_provider::CompletionEvent::RedactedThinking(s) => Self::RedactedThinking(s), - llm_provider::CompletionEvent::ToolUse(t) => Self::ToolUse(t.into()), - llm_provider::CompletionEvent::ToolUseJsonParseError(e) => { - Self::ToolUseJsonParseError(e.into()) - } - llm_provider::CompletionEvent::Stop(r) => Self::Stop(r.into()), - llm_provider::CompletionEvent::Usage(u) => Self::Usage(u.into()), - llm_provider::CompletionEvent::ReasoningDetails(s) => Self::ReasoningDetails(s), - } - } -} - -impl From for latest::llm_provider::ThinkingContent { - fn from(value: llm_provider::ThinkingContent) -> Self { - Self { - text: value.text, - signature: value.signature, - } - } -} - -impl From for latest::llm_provider::ToolUse { - fn from(value: llm_provider::ToolUse) -> Self { - Self { - id: value.id, - name: value.name, - input: value.input, - thought_signature: value.thought_signature, - } - } -} - -impl From for latest::llm_provider::ToolUseJsonParseError { - fn from(value: llm_provider::ToolUseJsonParseError) -> Self { - Self { - id: value.id, - tool_name: value.tool_name, - raw_input: value.raw_input, - error: value.error, - } - } -} - -impl From for latest::llm_provider::StopReason { - fn from(value: llm_provider::StopReason) -> Self { - match value { - llm_provider::StopReason::EndTurn => Self::EndTurn, - llm_provider::StopReason::MaxTokens => Self::MaxTokens, - llm_provider::StopReason::ToolUse => Self::ToolUse, - llm_provider::StopReason::Refusal => Self::Refusal, - } - } -} - -impl From for latest::llm_provider::TokenUsage { - fn from(value: llm_provider::TokenUsage) -> Self { - Self { - input_tokens: value.input_tokens, - output_tokens: value.output_tokens, - cache_creation_input_tokens: value.cache_creation_input_tokens, - cache_read_input_tokens: value.cache_read_input_tokens, - } - } -} - -impl From for latest::llm_provider::CacheConfiguration { - fn from(value: llm_provider::CacheConfiguration) -> Self { - Self { - max_cache_anchors: value.max_cache_anchors, - should_cache_tool_definitions: value.should_cache_tool_definitions, - min_total_token_count: value.min_total_token_count, - } - } -} - -// Conversions from latest (v0.8.0) -> v0.7.0 for requests - -impl From for llm_provider::CompletionRequest { - fn from(value: latest::llm_provider::CompletionRequest) -> Self { - Self { - messages: value.messages.into_iter().map(Into::into).collect(), - tools: value.tools.into_iter().map(Into::into).collect(), - tool_choice: value.tool_choice.map(Into::into), - stop_sequences: value.stop_sequences, - temperature: value.temperature, - thinking_allowed: value.thinking_allowed, - max_tokens: value.max_tokens, - } - } -} - -impl From for llm_provider::RequestMessage { - fn from(value: latest::llm_provider::RequestMessage) -> Self { - Self { - role: value.role.into(), - content: value.content.into_iter().map(Into::into).collect(), - cache: value.cache, - } - } -} - -impl From for llm_provider::MessageRole { - fn from(value: latest::llm_provider::MessageRole) -> Self { - match value { - latest::llm_provider::MessageRole::User => Self::User, - latest::llm_provider::MessageRole::Assistant => Self::Assistant, - latest::llm_provider::MessageRole::System => Self::System, - } - } -} - -impl From for llm_provider::MessageContent { - fn from(value: latest::llm_provider::MessageContent) -> Self { - match value { - latest::llm_provider::MessageContent::Text(s) => Self::Text(s), - latest::llm_provider::MessageContent::Image(i) => Self::Image(i.into()), - latest::llm_provider::MessageContent::ToolUse(t) => Self::ToolUse(t.into()), - latest::llm_provider::MessageContent::ToolResult(t) => Self::ToolResult(t.into()), - latest::llm_provider::MessageContent::Thinking(t) => Self::Thinking(t.into()), - latest::llm_provider::MessageContent::RedactedThinking(s) => Self::RedactedThinking(s), - } - } -} - -impl From for llm_provider::ImageData { - fn from(value: latest::llm_provider::ImageData) -> Self { - Self { - source: value.source, - width: value.width, - height: value.height, - } - } -} - -impl From for llm_provider::ToolUse { - fn from(value: latest::llm_provider::ToolUse) -> Self { - Self { - id: value.id, - name: value.name, - input: value.input, - thought_signature: value.thought_signature, - } - } -} - -impl From for llm_provider::ToolResult { - fn from(value: latest::llm_provider::ToolResult) -> Self { - Self { - tool_use_id: value.tool_use_id, - tool_name: value.tool_name, - is_error: value.is_error, - content: value.content.into(), - } - } -} - -impl From for llm_provider::ToolResultContent { - fn from(value: latest::llm_provider::ToolResultContent) -> Self { - match value { - latest::llm_provider::ToolResultContent::Text(s) => Self::Text(s), - latest::llm_provider::ToolResultContent::Image(i) => Self::Image(i.into()), - } - } -} - -impl From for llm_provider::ThinkingContent { - fn from(value: latest::llm_provider::ThinkingContent) -> Self { - Self { - text: value.text, - signature: value.signature, - } - } -} - -impl From for llm_provider::ToolDefinition { - fn from(value: latest::llm_provider::ToolDefinition) -> Self { - Self { - name: value.name, - description: value.description, - input_schema: value.input_schema, - } - } -} - -impl From for llm_provider::ToolChoice { - fn from(value: latest::llm_provider::ToolChoice) -> Self { - match value { - latest::llm_provider::ToolChoice::Auto => Self::Auto, - latest::llm_provider::ToolChoice::Any => Self::Any, - latest::llm_provider::ToolChoice::None => Self::None, - } - } -} - -// ============================================================================= -// Command Type Conversions (v0.7.0 -> latest/v0.8.0) -// ============================================================================= - -impl From for latest::Command { - fn from(value: Command) -> Self { - Self { - command: value.command, - args: value.args, - env: value.env, - } - } -} - -// ============================================================================= -// LSP Type Conversions (latest/v0.8.0 -> v0.7.0) -// ============================================================================= - -impl From for lsp::Completion { - fn from(value: latest::lsp::Completion) -> Self { - Self { - label: value.label, - label_details: value.label_details.map(Into::into), - detail: value.detail, - kind: value.kind.map(Into::into), - insert_text_format: value.insert_text_format.map(Into::into), - } - } -} - -impl From for lsp::CompletionLabelDetails { - fn from(value: latest::lsp::CompletionLabelDetails) -> Self { - Self { - detail: value.detail, - description: value.description, - } - } -} - -impl From for lsp::CompletionKind { - fn from(value: latest::lsp::CompletionKind) -> Self { - match value { - latest::lsp::CompletionKind::Text => Self::Text, - latest::lsp::CompletionKind::Method => Self::Method, - latest::lsp::CompletionKind::Function => Self::Function, - latest::lsp::CompletionKind::Constructor => Self::Constructor, - latest::lsp::CompletionKind::Field => Self::Field, - latest::lsp::CompletionKind::Variable => Self::Variable, - latest::lsp::CompletionKind::Class => Self::Class, - latest::lsp::CompletionKind::Interface => Self::Interface, - latest::lsp::CompletionKind::Module => Self::Module, - latest::lsp::CompletionKind::Property => Self::Property, - latest::lsp::CompletionKind::Unit => Self::Unit, - latest::lsp::CompletionKind::Value => Self::Value, - latest::lsp::CompletionKind::Enum => Self::Enum, - latest::lsp::CompletionKind::Keyword => Self::Keyword, - latest::lsp::CompletionKind::Snippet => Self::Snippet, - latest::lsp::CompletionKind::Color => Self::Color, - latest::lsp::CompletionKind::File => Self::File, - latest::lsp::CompletionKind::Reference => Self::Reference, - latest::lsp::CompletionKind::Folder => Self::Folder, - latest::lsp::CompletionKind::EnumMember => Self::EnumMember, - latest::lsp::CompletionKind::Constant => Self::Constant, - latest::lsp::CompletionKind::Struct => Self::Struct, - latest::lsp::CompletionKind::Event => Self::Event, - latest::lsp::CompletionKind::Operator => Self::Operator, - latest::lsp::CompletionKind::TypeParameter => Self::TypeParameter, - latest::lsp::CompletionKind::Other(n) => Self::Other(n), - } - } -} - -impl From for lsp::InsertTextFormat { - fn from(value: latest::lsp::InsertTextFormat) -> Self { - match value { - latest::lsp::InsertTextFormat::PlainText => Self::PlainText, - latest::lsp::InsertTextFormat::Snippet => Self::Snippet, - latest::lsp::InsertTextFormat::Other(n) => Self::Other(n), - } - } -} - -impl From for lsp::Symbol { - fn from(value: latest::lsp::Symbol) -> Self { - Self { - kind: value.kind.into(), - name: value.name, - } - } -} - -impl From for lsp::SymbolKind { - fn from(value: latest::lsp::SymbolKind) -> Self { - match value { - latest::lsp::SymbolKind::File => Self::File, - latest::lsp::SymbolKind::Module => Self::Module, - latest::lsp::SymbolKind::Namespace => Self::Namespace, - latest::lsp::SymbolKind::Package => Self::Package, - latest::lsp::SymbolKind::Class => Self::Class, - latest::lsp::SymbolKind::Method => Self::Method, - latest::lsp::SymbolKind::Property => Self::Property, - latest::lsp::SymbolKind::Field => Self::Field, - latest::lsp::SymbolKind::Constructor => Self::Constructor, - latest::lsp::SymbolKind::Enum => Self::Enum, - latest::lsp::SymbolKind::Interface => Self::Interface, - latest::lsp::SymbolKind::Function => Self::Function, - latest::lsp::SymbolKind::Variable => Self::Variable, - latest::lsp::SymbolKind::Constant => Self::Constant, - latest::lsp::SymbolKind::String => Self::String, - latest::lsp::SymbolKind::Number => Self::Number, - latest::lsp::SymbolKind::Boolean => Self::Boolean, - latest::lsp::SymbolKind::Array => Self::Array, - latest::lsp::SymbolKind::Object => Self::Object, - latest::lsp::SymbolKind::Key => Self::Key, - latest::lsp::SymbolKind::Null => Self::Null, - latest::lsp::SymbolKind::EnumMember => Self::EnumMember, - latest::lsp::SymbolKind::Struct => Self::Struct, - latest::lsp::SymbolKind::Event => Self::Event, - latest::lsp::SymbolKind::Operator => Self::Operator, - latest::lsp::SymbolKind::TypeParameter => Self::TypeParameter, - latest::lsp::SymbolKind::Other(n) => Self::Other(n), - } - } -} - -// ============================================================================= -// CodeLabel Type Conversions (v0.7.0 -> latest/v0.8.0) -// ============================================================================= - -impl From for latest::CodeLabel { - fn from(value: CodeLabel) -> Self { - Self { - code: value.code, - spans: value.spans.into_iter().map(Into::into).collect(), - filter_range: value.filter_range.into(), - } - } -} - -impl From for latest::CodeLabelSpan { - fn from(value: CodeLabelSpan) -> Self { - match value { - CodeLabelSpan::CodeRange(r) => Self::CodeRange(r.into()), - CodeLabelSpan::Literal(l) => Self::Literal(l.into()), - } - } -} - -impl From for latest::CodeLabelSpanLiteral { - fn from(value: CodeLabelSpanLiteral) -> Self { - Self { - text: value.text, - highlight_name: value.highlight_name, - } - } -} - -impl From for latest::Range { - fn from(value: Range) -> Self { - Self { - start: value.start, - end: value.end, - } - } -} - -// ============================================================================= -// SlashCommand Type Conversions (latest/v0.8.0 -> v0.7.0) -// ============================================================================= - -impl From<&latest::SlashCommand> for slash_command::SlashCommand { - fn from(value: &latest::SlashCommand) -> Self { - Self { - name: value.name.clone(), - description: value.description.clone(), - tooltip_text: value.tooltip_text.clone(), - requires_argument: value.requires_argument, - } - } -} - -// ============================================================================= -// SlashCommand Type Conversions (v0.7.0 -> latest/v0.8.0) -// ============================================================================= - -impl From - for latest::SlashCommandArgumentCompletion -{ - fn from(value: slash_command::SlashCommandArgumentCompletion) -> Self { - Self { - label: value.label, - new_text: value.new_text, - run_command: value.run_command, - } - } -} - -impl From for latest::SlashCommandOutput { - fn from(value: slash_command::SlashCommandOutput) -> Self { - Self { - sections: value.sections.into_iter().map(Into::into).collect(), - text: value.text, - } - } -} - -impl From for latest::slash_command::SlashCommandOutputSection { - fn from(value: SlashCommandOutputSection) -> Self { - Self { - range: value.range.into(), - label: value.label, - } - } -} - -// ============================================================================= -// ContextServer Type Conversions (v0.7.0 -> latest/v0.8.0) -// ============================================================================= - -impl From - for latest::context_server::ContextServerConfiguration -{ - fn from(value: context_server::ContextServerConfiguration) -> Self { - Self { - installation_instructions: value.installation_instructions, - settings_schema: value.settings_schema, - default_settings: value.default_settings, - } - } -} - -// ============================================================================= -// DAP Type Conversions (v0.7.0 -> latest/v0.8.0) -// ============================================================================= - -impl From for latest::dap::DebugAdapterBinary { - fn from(value: dap::DebugAdapterBinary) -> Self { - Self { - command: value.command, - arguments: value.arguments, - envs: value.envs, - cwd: value.cwd, - connection: value.connection.map(|c| latest::dap::TcpArguments { - host: c.host, - port: c.port, - timeout: c.timeout, - }), - request_args: latest::dap::StartDebuggingRequestArguments { - configuration: value.request_args.configuration, - request: match value.request_args.request { - dap::StartDebuggingRequestArgumentsRequest::Launch => { - latest::dap::StartDebuggingRequestArgumentsRequest::Launch - } - dap::StartDebuggingRequestArgumentsRequest::Attach => { - latest::dap::StartDebuggingRequestArgumentsRequest::Attach - } - }, - }, - } - } -} - -impl From - for latest::dap::StartDebuggingRequestArgumentsRequest -{ - fn from(value: dap::StartDebuggingRequestArgumentsRequest) -> Self { - match value { - dap::StartDebuggingRequestArgumentsRequest::Launch => Self::Launch, - dap::StartDebuggingRequestArgumentsRequest::Attach => Self::Attach, - } - } -} - -impl From for latest::dap::DebugScenario { - fn from(value: dap::DebugScenario) -> Self { - Self { - adapter: value.adapter, - label: value.label, - build: value.build.map(|b| match b { - dap::BuildTaskDefinition::ByName(name) => { - latest::dap::BuildTaskDefinition::ByName(name) - } - dap::BuildTaskDefinition::Template(t) => { - latest::dap::BuildTaskDefinition::Template( - latest::dap::BuildTaskDefinitionTemplatePayload { - locator_name: t.locator_name, - template: latest::dap::BuildTaskTemplate { - label: t.template.label, - command: t.template.command, - args: t.template.args, - env: t.template.env, - cwd: t.template.cwd, - }, - }, - ) - } - }), - config: value.config, - tcp_connection: value - .tcp_connection - .map(|t| latest::dap::TcpArgumentsTemplate { - host: t.host, - port: t.port, - timeout: t.timeout, - }), - } - } -} - -impl From for latest::dap::DebugRequest { - fn from(value: dap::DebugRequest) -> Self { - match value { - dap::DebugRequest::Attach(a) => Self::Attach(latest::dap::AttachRequest { - process_id: a.process_id, - }), - dap::DebugRequest::Launch(l) => Self::Launch(latest::dap::LaunchRequest { - program: l.program, - cwd: l.cwd, - args: l.args, - envs: l.envs, - }), - } - } -} diff --git a/crates/extension_host/src/wasm_host/wit/since_v0_8_0.rs b/crates/extension_host/src/wasm_host/wit/since_v0_8_0.rs index 714caa05ff130159cf842c397ea2e2ed6503ff4f..b469349fb8c8a838bbc2ccd8a18e8c7c7a23b1a4 100644 --- a/crates/extension_host/src/wasm_host/wit/since_v0_8_0.rs +++ b/crates/extension_host/src/wasm_host/wit/since_v0_8_0.rs @@ -1110,12 +1110,10 @@ impl ExtensionImports for WasmState { .with_context(|| format!("setting permissions for path {path:?}")) .to_wasmtime_result() } +} - // ========================================================================= - // LLM Provider Import Implementations - // ========================================================================= - - async fn llm_request_credential( +impl llm_provider::Host for WasmState { + async fn request_credential( &mut self, _provider_id: String, _credential_type: llm_provider::CredentialType, @@ -1123,16 +1121,13 @@ impl ExtensionImports for WasmState { _placeholder: String, ) -> wasmtime::Result> { // For now, credential requests return false (not provided) - // Extensions should use llm_get_env_var to check for env vars first, - // then llm_store_credential/llm_get_credential for manual storage + // Extensions should use get_env_var to check for env vars first, + // then store_credential/get_credential for manual storage // Full UI credential prompting will be added in a future phase Ok(Ok(false)) } - async fn llm_get_credential( - &mut self, - provider_id: String, - ) -> wasmtime::Result> { + async fn get_credential(&mut self, provider_id: String) -> wasmtime::Result> { let extension_id = self.manifest.id.clone(); let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id); @@ -1151,7 +1146,7 @@ impl ExtensionImports for WasmState { .await } - async fn llm_store_credential( + async fn store_credential( &mut self, provider_id: String, value: String, @@ -1173,7 +1168,7 @@ impl ExtensionImports for WasmState { .to_wasmtime_result() } - async fn llm_delete_credential( + async fn delete_credential( &mut self, provider_id: String, ) -> wasmtime::Result> { @@ -1194,7 +1189,7 @@ impl ExtensionImports for WasmState { .to_wasmtime_result() } - async fn llm_get_env_var(&mut self, name: String) -> wasmtime::Result> { + async fn get_env_var(&mut self, name: String) -> wasmtime::Result> { let extension_id = self.manifest.id.clone(); // Find which provider (if any) declares this env var in its auth config @@ -1247,7 +1242,7 @@ impl ExtensionImports for WasmState { Ok(env::var(&name).ok()) } - async fn llm_oauth_start_web_auth( + async fn oauth_start_web_auth( &mut self, config: llm_provider::OauthWebAuthConfig, ) -> wasmtime::Result> { @@ -1345,7 +1340,7 @@ impl ExtensionImports for WasmState { .to_wasmtime_result() } - async fn llm_oauth_http_request( + async fn send_oauth_http_request( &mut self, request: llm_provider::OauthHttpRequest, ) -> wasmtime::Result> { @@ -1416,10 +1411,7 @@ impl ExtensionImports for WasmState { .to_wasmtime_result() } - async fn llm_oauth_open_browser( - &mut self, - url: String, - ) -> wasmtime::Result> { + async fn oauth_open_browser(&mut self, url: String) -> wasmtime::Result> { self.on_main_thread(move |cx| { async move { cx.update(|cx| { @@ -1433,9 +1425,3 @@ impl ExtensionImports for WasmState { .to_wasmtime_result() } } - -// ============================================================================= -// LLM Provider Host Implementations -// ============================================================================= - -impl llm_provider::Host for WasmState {}