Move wit extensions into their own module

Richard Feldman created

Change summary

crates/extension_api/src/extension_api.rs                |    8 
crates/extension_api/wit/since_v0.7.0/common.wit         |   12 
crates/extension_api/wit/since_v0.7.0/context-server.wit |   11 
crates/extension_api/wit/since_v0.7.0/dap.wit            |  123 
crates/extension_api/wit/since_v0.7.0/extension.wit      |  284 -
crates/extension_api/wit/since_v0.7.0/github.wit         |   35 
crates/extension_api/wit/since_v0.7.0/http-client.wit    |   67 
crates/extension_api/wit/since_v0.7.0/llm-provider.wit   |  302 -
crates/extension_api/wit/since_v0.7.0/lsp.wit            |   90 
crates/extension_api/wit/since_v0.7.0/nodejs.wit         |   13 
crates/extension_api/wit/since_v0.7.0/platform.wit       |   24 
crates/extension_api/wit/since_v0.7.0/process.wit        |   29 
crates/extension_api/wit/since_v0.7.0/settings.rs        |   40 
crates/extension_api/wit/since_v0.7.0/slash-command.wit  |   41 
crates/extension_api/wit/since_v0.8.0/extension.wit      |   56 
crates/extension_api/wit/since_v0.8.0/llm-provider.wit   |   51 
crates/extension_host/src/wasm_host/wit.rs               |  209 
crates/extension_host/src/wasm_host/wit/since_v0_7_0.rs  | 2022 ----------
crates/extension_host/src/wasm_host/wit/since_v0_8_0.rs  |   38 
19 files changed, 78 insertions(+), 3,377 deletions(-)

Detailed changes

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,

crates/extension_api/wit/since_v0.7.0/common.wit 🔗

@@ -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<tuple<string, string>>;
-}

crates/extension_api/wit/since_v0.7.0/context-server.wit 🔗

@@ -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,
-    }
-}

crates/extension_api/wit/since_v0.7.0/dap.wit 🔗

@@ -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<tcp-arguments, string>;
-
-    record launch-request {
-        program: string,
-        cwd: option<string>,
-        args: list<string>,
-        envs: env-vars,
-    }
-
-    record attach-request {
-        process-id: option<u32>,
-    }
-
-    variant debug-request {
-        launch(launch-request),
-        attach(attach-request)
-    }
-
-    record tcp-arguments {
-        port: u16,
-        host: u32,
-        timeout: option<u64>,
-    }
-
-    record tcp-arguments-template {
-        port: option<u16>,
-        host: option<u32>,
-        timeout: option<u64>,
-    }
-
-    /// 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<bool>,
-    }
-
-    record task-template {
-        /// Human readable name of the task to display in the UI.
-        label: string,
-        /// Executable command to spawn.
-        command: string,
-        args: list<string>,
-        env: env-vars,
-        cwd: option<string>,
-    }
-
-    /// 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<string>,
-        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<build-task-definition>,
-        /// JSON-encoded configuration for a given debug adapter.
-        config: string,
-        /// TCP connection parameters (if they were specified by user)
-        tcp-connection: option<tcp-arguments-template>,
-    }
-
-    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<tcp-arguments-template>,
-    }
-
-    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<string>,
-        arguments: list<string>,
-        envs: env-vars,
-        cwd: option<string>,
-        /// Zed will use TCP transport if `connection` is specified.
-        connection: option<tcp-arguments>,
-        request-args: start-debugging-request-arguments
-    }
-}

crates/extension_api/wit/since_v0.7.0/extension.wit 🔗

@@ -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<settings-location>, category: string, key: option<string>) -> result<string, string>;
-
-    /// 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<string, string>;
-        /// Returns the path to the given binary name, if one is present on the `$PATH`.
-        which: func(binary-name: string) -> option<string>;
-        /// 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<u64>;
-    }
-
-    /// 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<worktree>) -> result<command, string>;
-
-    /// 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<worktree>) -> result<option<string>, string>;
-
-    /// Returns the workspace configuration options to pass to the language server.
-    export language-server-workspace-configuration: func(language-server-id: string, worktree: borrow<worktree>) -> result<option<string>, 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<worktree>) -> result<option<string>, 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<worktree>) -> result<option<string>, 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<code-label-span>,
-        /// 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<string>,
-    }
-
-    export labels-for-completions: func(language-server-id: string, completions: list<completion>) -> result<list<option<code-label>>, string>;
-    export labels-for-symbols: func(language-server-id: string, symbols: list<symbol>) -> result<list<option<code-label>>, 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<string>) -> result<list<slash-command-argument-completion>, string>;
-
-    /// Returns the output from running the provided slash command.
-    export run-slash-command: func(command: slash-command, args: list<string>, worktree: option<borrow<worktree>>) -> result<slash-command-output, string>;
-
-    /// Returns the command used to start up a context server.
-    export context-server-command: func(context-server-id: string, project: borrow<project>) -> result<command, string>;
-
-    /// Returns the configuration for a context server.
-    export context-server-configuration: func(context-server-id: string, project: borrow<project>) -> result<option<context-server-configuration>, 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<list<string>, string>;
-
-    /// Indexes the docs for the specified package.
-    export index-docs: func(provider-name: string, package-name: string, database: borrow<key-value-store>) -> 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<string>, worktree: borrow<worktree>) -> result<debug-adapter-binary, string>;
-    /// Returns the kind of a debug scenario (launch or attach).
-    export dap-request-kind: func(adapter-name: string, config: string) -> result<start-debugging-request-arguments-request, string>;
-    export dap-config-to-scenario: func(config: debug-config) -> result<debug-scenario, string>;
-    export dap-locator-create-scenario: func(locator-name: string, build-config-template: build-task-template, resolved-label: string, debug-adapter-name: string) -> option<debug-scenario>;
-    export run-dap-locator: func(locator-name: string, config: resolved-task) -> result<debug-request, string>;
-
-    // =========================================================================
-    // Language Model Provider Extension API
-    // =========================================================================
-
-    /// Returns information about language model providers offered by this extension.
-    export llm-providers: func() -> list<provider-info>;
-
-    /// Returns the models available for a provider.
-    export llm-provider-models: func(provider-id: string) -> result<list<model-info>, 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<string>;
-
-    /// 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<u64, string>;
-
-    /// 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<string, string>;
-
-    /// 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<option<completion-event>, 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<cache-configuration>;
-
-    // =========================================================================
-    // 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<bool, string>;
-
-    /// Get a stored credential for this provider.
-    import llm-get-credential: func(provider-id: string) -> option<string>;
-
-    /// 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<string>;
-
-    // =========================================================================
-    // 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<oauth-web-auth-result, string>;
-
-    /// 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<oauth-http-response, string>;
-
-    /// 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>;
-}

crates/extension_api/wit/since_v0.7.0/github.wit 🔗

@@ -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<github-release-asset>,
-    }
-
-    /// 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 "<owner-name>/<repo-name>", for example: "zed-industries/zed".
-    latest-github-release: func(repo: string, options: github-release-options) -> result<github-release, string>;
-
-    /// 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<github-release, string>;
-}

crates/extension_api/wit/since_v0.7.0/http-client.wit 🔗

@@ -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<tuple<string, string>>,
-        /// The request body.
-        body: option<list<u8>>,
-        /// 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<tuple<string, string>>,
-        /// The response body.
-        body: list<u8>,
-    }
-
-    /// Performs an HTTP request and returns the response.
-    fetch: func(req: http-request) -> result<http-response, string>;
-
-    /// 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<option<list<u8>>, string>;
-    }
-
-    /// Performs an HTTP request and returns a response stream.
-    fetch-stream: func(req: http-request) -> result<http-response-stream, string>;
-}

crates/extension_api/wit/since_v0.7.0/llm-provider.wit 🔗

@@ -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<string>,
-    }
-
-    /// 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<u64>,
-        /// 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<message-content>,
-        /// 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<u32>,
-        /// Image height in pixels (optional).
-        height: option<u32>,
-    }
-
-    /// 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<string>,
-    }
-
-    /// 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<string>,
-    }
-
-    /// 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<request-message>,
-        /// Available tools for the model to use.
-        tools: list<tool-definition>,
-        /// Tool choice preference.
-        tool-choice: option<tool-choice>,
-        /// Stop sequences to end generation.
-        stop-sequences: list<string>,
-        /// Temperature for sampling (0.0-1.0).
-        temperature: option<f32>,
-        /// Whether thinking/reasoning is allowed.
-        thinking-allowed: bool,
-        /// Maximum tokens to generate.
-        max-tokens: option<u64>,
-    }
-
-    /// 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<u64>,
-        /// Tokens read from cache (if supported).
-        cache-read-input-tokens: option<u64>,
-    }
-
-    /// 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<u32>,
-    }
-
-    /// 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<tuple<string, string>>,
-        /// 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<tuple<string, string>>,
-        /// Response body as a string.
-        body: string,
-    }
-}

crates/extension_api/wit/since_v0.7.0/lsp.wit 🔗

@@ -1,90 +0,0 @@
-interface lsp {
-    /// An LSP completion.
-    record completion {
-        label: string,
-        label-details: option<completion-label-details>,
-        detail: option<string>,
-        kind: option<completion-kind>,
-        insert-text-format: option<insert-text-format>,
-    }
-
-    /// 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<string>,
-        description: option<string>,
-    }
-
-    /// 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),
-    }
-}

crates/extension_api/wit/since_v0.7.0/nodejs.wit 🔗

@@ -1,13 +0,0 @@
-interface nodejs {
-    /// Returns the path to the Node binary used by Zed.
-    node-binary-path: func() -> result<string, string>;
-
-    /// Returns the latest version of the given NPM package.
-    npm-package-latest-version: func(package-name: string) -> result<string, string>;
-
-    /// Returns the installed version of the given NPM package, if it exists.
-    npm-package-installed-version: func(package-name: string) -> result<option<string>, string>;
-
-    /// Installs the specified NPM package.
-    npm-install-package: func(package-name: string, version: string) -> result<_, string>;
-}

crates/extension_api/wit/since_v0.7.0/platform.wit 🔗

@@ -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<os, architecture>;
-}

crates/extension_api/wit/since_v0.7.0/process.wit 🔗

@@ -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<string>,
-        /// 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<s32>,
-        /// The data that the process wrote to stdout.
-        stdout: list<u8>,
-        /// The data that the process wrote to stderr.
-        stderr: list<u8>,
-    }
-
-    /// 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<output, string>;
-}

crates/extension_api/wit/since_v0.7.0/settings.rs 🔗

@@ -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<CommandSettings>,
-    /// The initialization options to pass to the language server.
-    pub initialization_options: Option<serde_json::Value>,
-    /// The settings to pass to language server.
-    pub settings: Option<serde_json::Value>,
-}
-
-/// 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<CommandSettings>,
-    /// The settings to pass to the context server.
-    pub settings: Option<serde_json::Value>,
-}
-
-/// The settings for a command.
-#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
-pub struct CommandSettings {
-    /// The path to the command.
-    pub path: Option<String>,
-    /// The arguments to pass to the command.
-    pub arguments: Option<Vec<String>>,
-    /// The environment variables.
-    pub env: Option<HashMap<String, String>>,
-}

crates/extension_api/wit/since_v0.7.0/slash-command.wit 🔗

@@ -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<slash-command-output-section>,
-    }
-
-    /// 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,
-    }
-}

crates/extension_api/wit/since_v0.8.0/extension.wit 🔗

@@ -225,60 +225,4 @@ world extension {
         model-id: string
     ) -> option<cache-configuration>;
 
-    // =========================================================================
-    // 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<bool, string>;
-
-    /// Get a stored credential for this provider.
-    import llm-get-credential: func(provider-id: string) -> option<string>;
-
-    /// 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<string>;
-
-    // =========================================================================
-    // 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<oauth-web-auth-result, string>;
-
-    /// 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<oauth-http-response, string>;
-
-    /// 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>;
 }

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<bool, string>;
+
+    /// Get a stored credential for this provider.
+    get-credential: func(provider-id: string) -> option<string>;
+
+    /// 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<string>;
+
+    /// 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<oauth-web-auth-result, string>;
+
+    /// 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<oauth-http-response, string>;
+
+    /// 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>;
 }

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<WasmState>) -> 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::<Vec<_>>(),
-                )
-                .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::<Vec<_>>(),
-                )
-                .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<Result<Vec<String>, 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<Result<DebugRequest, String>> {
         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<Vec<latest::llm_provider::ProviderInfo>> {
         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<Result<Vec<latest::llm_provider::ModelInfo>, 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<Result<(), String>> {
         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<Result<Option<latest::llm_provider::CompletionEvent>, 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),
         }
     }

crates/extension_host/src/wasm_host/wit/since_v0_7_0.rs 🔗

@@ -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<dyn WorktreeDelegate>;
-pub type ExtensionProject = Arc<dyn ProjectDelegate>;
-pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
-pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
-
-pub fn linker(executor: &BackgroundExecutor) -> &'static Linker<WasmState> {
-    static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
-    LINKER.get_or_init(|| super::new_linker(executor, Extension::add_to_linker))
-}
-
-impl From<Range> for std::ops::Range<usize> {
-    fn from(range: Range) -> Self {
-        let start = range.start as usize;
-        let end = range.end as usize;
-        start..end
-    }
-}
-
-impl From<Command> for extension::Command {
-    fn from(value: Command) -> Self {
-        Self {
-            command: value.command.into(),
-            args: value.args,
-            env: value.env,
-        }
-    }
-}
-
-impl From<StartDebuggingRequestArgumentsRequest>
-    for extension::StartDebuggingRequestArgumentsRequest
-{
-    fn from(value: StartDebuggingRequestArgumentsRequest) -> Self {
-        match value {
-            StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
-            StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
-        }
-    }
-}
-impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingRequestArguments {
-    type Error = anyhow::Error;
-
-    fn try_from(value: StartDebuggingRequestArguments) -> Result<Self, Self::Error> {
-        Ok(Self {
-            configuration: serde_json::from_str(&value.configuration)?,
-            request: value.request.into(),
-        })
-    }
-}
-impl From<TcpArguments> for extension::TcpArguments {
-    fn from(value: TcpArguments) -> Self {
-        Self {
-            host: value.host.into(),
-            port: value.port,
-            timeout: value.timeout,
-        }
-    }
-}
-
-impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
-    fn from(value: extension::TcpArgumentsTemplate) -> Self {
-        Self {
-            host: value.host.map(Ipv4Addr::to_bits),
-            port: value.port,
-            timeout: value.timeout,
-        }
-    }
-}
-
-impl From<TcpArgumentsTemplate> for extension::TcpArgumentsTemplate {
-    fn from(value: TcpArgumentsTemplate) -> Self {
-        Self {
-            host: value.host.map(Ipv4Addr::from_bits),
-            port: value.port,
-            timeout: value.timeout,
-        }
-    }
-}
-
-impl TryFrom<extension::DebugTaskDefinition> for DebugTaskDefinition {
-    type Error = anyhow::Error;
-    fn try_from(value: extension::DebugTaskDefinition) -> Result<Self, Self::Error> {
-        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<task::DebugRequest> 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<DebugRequest> 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<task::LaunchRequest> 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<task::AttachRequest> for AttachRequest {
-    fn from(value: task::AttachRequest) -> Self {
-        Self {
-            process_id: value.process_id,
-        }
-    }
-}
-
-impl From<LaunchRequest> 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<AttachRequest> for task::AttachRequest {
-    fn from(value: AttachRequest) -> Self {
-        Self {
-            process_id: value.process_id,
-        }
-    }
-}
-
-impl From<ZedDebugConfig> 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<DebugAdapterBinary> for extension::DebugAdapterBinary {
-    type Error = anyhow::Error;
-    fn try_from(value: DebugAdapterBinary) -> Result<Self, Self::Error> {
-        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<BuildTaskDefinition> 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<extension::BuildTaskDefinition> 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<BuildTaskTemplate> 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<extension::BuildTaskTemplate> 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<DebugScenario> for extension::DebugScenario {
-    type Error = anyhow::Error;
-
-    fn try_from(value: DebugScenario) -> std::result::Result<Self, Self::Error> {
-        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<extension::DebugScenario> 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<SpawnInTerminal> for ResolvedTask {
-    type Error = anyhow::Error;
-
-    fn try_from(value: SpawnInTerminal) -> Result<Self, Self::Error> {
-        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<CodeLabel> 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<CodeLabelSpan> 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<CodeLabelSpanLiteral> for extension::CodeLabelSpanLiteral {
-    fn from(value: CodeLabelSpanLiteral) -> Self {
-        Self {
-            text: value.text,
-            highlight_name: value.highlight_name,
-        }
-    }
-}
-
-impl From<extension::Completion> 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<extension::CompletionLabelDetails> for CompletionLabelDetails {
-    fn from(value: extension::CompletionLabelDetails) -> Self {
-        Self {
-            detail: value.detail,
-            description: value.description,
-        }
-    }
-}
-
-impl From<extension::CompletionKind> 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<extension::InsertTextFormat> 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<extension::Symbol> for Symbol {
-    fn from(value: extension::Symbol) -> Self {
-        Self {
-            kind: value.kind.into(),
-            name: value.name,
-        }
-    }
-}
-
-impl From<extension::SymbolKind> 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<extension::SlashCommand> 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<SlashCommandOutput> for extension::SlashCommandOutput {
-    fn from(value: SlashCommandOutput) -> Self {
-        Self {
-            text: value.text,
-            sections: value.sections.into_iter().map(Into::into).collect(),
-        }
-    }
-}
-
-impl From<SlashCommandOutputSection> 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<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCompletion {
-    fn from(value: SlashCommandArgumentCompletion) -> Self {
-        Self {
-            label: value.label,
-            new_text: value.new_text,
-            run_command: value.run_command,
-        }
-    }
-}
-
-impl TryFrom<ContextServerConfiguration> for extension::ContextServerConfiguration {
-    type Error = anyhow::Error;
-
-    fn try_from(value: ContextServerConfiguration) -> Result<Self, Self::Error> {
-        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<ExtensionKeyValueStore>,
-        key: String,
-        value: String,
-    ) -> wasmtime::Result<Result<(), String>> {
-        let kv_store = self.table.get(&kv_store)?;
-        kv_store.insert(key, value).await.to_wasmtime_result()
-    }
-
-    async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
-        // We only ever hand out borrows of key-value stores.
-        Ok(())
-    }
-}
-
-impl HostProject for WasmState {
-    async fn worktree_ids(
-        &mut self,
-        project: Resource<ExtensionProject>,
-    ) -> wasmtime::Result<Vec<u64>> {
-        let project = self.table.get(&project)?;
-        Ok(project.worktree_ids())
-    }
-
-    async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
-        // We only ever hand out borrows of projects.
-        Ok(())
-    }
-}
-
-impl HostWorktree for WasmState {
-    async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
-        let delegate = self.table.get(&delegate)?;
-        Ok(delegate.id())
-    }
-
-    async fn root_path(
-        &mut self,
-        delegate: Resource<Arc<dyn WorktreeDelegate>>,
-    ) -> wasmtime::Result<String> {
-        let delegate = self.table.get(&delegate)?;
-        Ok(delegate.root_path())
-    }
-
-    async fn read_text_file(
-        &mut self,
-        delegate: Resource<Arc<dyn WorktreeDelegate>>,
-        path: String,
-    ) -> wasmtime::Result<Result<String, String>> {
-        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<Arc<dyn WorktreeDelegate>>,
-    ) -> wasmtime::Result<EnvVars> {
-        let delegate = self.table.get(&delegate)?;
-        Ok(delegate.shell_env().await.into_iter().collect())
-    }
-
-    async fn which(
-        &mut self,
-        delegate: Resource<Arc<dyn WorktreeDelegate>>,
-        binary_name: String,
-    ) -> wasmtime::Result<Option<String>> {
-        let delegate = self.table.get(&delegate)?;
-        Ok(delegate.which(binary_name).await)
-    }
-
-    async fn drop(&mut self, _worktree: Resource<Worktree>) -> 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<Result<http_client::HttpResponse, String>> {
-        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<Result<Resource<ExtensionHttpResponseStream>, 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<ExtensionHttpResponseStream>,
-    ) -> wasmtime::Result<Result<Option<Vec<u8>>, 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<ExtensionHttpResponseStream>) -> Result<()> {
-        Ok(())
-    }
-}
-
-impl From<http_client::HttpMethod> 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<AsyncBody>> {
-    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<AsyncBody>,
-) -> anyhow::Result<http_client::HttpResponse> {
-    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<Result<String, String>> {
-        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<Result<String, String>> {
-        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<Result<Option<String>, 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<Result<(), String>> {
-        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<Result<github::GithubRelease, String>> {
-        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<Result<github::GithubRelease, String>> {
-        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<std::process::Output> 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<Result<process::Output, String>> {
-        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<Result<TcpArguments, String>> {
-        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<self::SettingsLocation>,
-        category: String,
-        key: Option<String>,
-    ) -> wasmtime::Result<Result<String, String>> {
-        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<Result<(), String>> {
-        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<Result<(), String>> {
-        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<Result<bool, String>> {
-        // 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<Option<String>> {
-        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| <dyn CredentialsProvider>::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<Result<(), String>> {
-        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| <dyn CredentialsProvider>::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<Result<(), String>> {
-        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| <dyn CredentialsProvider>::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<Option<String>> {
-        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<Arc<str>> = 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<Result<llm_provider::OauthWebAuthResult, String>> {
-        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\
-                        <!DOCTYPE html>\
-                        <html><head><title>Authentication Complete</title></head>\
-                        <body style=\"font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;\">\
-                        <div style=\"text-align: center;\">\
-                        <h1>Authentication Complete</h1>\
-                        <p>You can close this window and return to Zed.</p>\
-                        </div></body></html>";
-
-                    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<Result<llm_provider::OauthHttpResponse, String>> {
-        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<Result<(), String>> {
-        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<llm_provider::ProviderInfo> for latest::llm_provider::ProviderInfo {
-    fn from(value: llm_provider::ProviderInfo) -> Self {
-        Self {
-            id: value.id,
-            name: value.name,
-            icon: value.icon,
-        }
-    }
-}
-
-impl From<llm_provider::ModelInfo> 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<llm_provider::ModelCapabilities> 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<llm_provider::ToolInputFormat> 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<llm_provider::CompletionEvent> 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<llm_provider::ThinkingContent> for latest::llm_provider::ThinkingContent {
-    fn from(value: llm_provider::ThinkingContent) -> Self {
-        Self {
-            text: value.text,
-            signature: value.signature,
-        }
-    }
-}
-
-impl From<llm_provider::ToolUse> 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<llm_provider::ToolUseJsonParseError> 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<llm_provider::StopReason> 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<llm_provider::TokenUsage> 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<llm_provider::CacheConfiguration> 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<latest::llm_provider::CompletionRequest> 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<latest::llm_provider::RequestMessage> 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<latest::llm_provider::MessageRole> 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<latest::llm_provider::MessageContent> 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<latest::llm_provider::ImageData> for llm_provider::ImageData {
-    fn from(value: latest::llm_provider::ImageData) -> Self {
-        Self {
-            source: value.source,
-            width: value.width,
-            height: value.height,
-        }
-    }
-}
-
-impl From<latest::llm_provider::ToolUse> 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<latest::llm_provider::ToolResult> 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<latest::llm_provider::ToolResultContent> 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<latest::llm_provider::ThinkingContent> for llm_provider::ThinkingContent {
-    fn from(value: latest::llm_provider::ThinkingContent) -> Self {
-        Self {
-            text: value.text,
-            signature: value.signature,
-        }
-    }
-}
-
-impl From<latest::llm_provider::ToolDefinition> 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<latest::llm_provider::ToolChoice> 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<Command> 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<latest::lsp::Completion> 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<latest::lsp::CompletionLabelDetails> for lsp::CompletionLabelDetails {
-    fn from(value: latest::lsp::CompletionLabelDetails) -> Self {
-        Self {
-            detail: value.detail,
-            description: value.description,
-        }
-    }
-}
-
-impl From<latest::lsp::CompletionKind> 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<latest::lsp::InsertTextFormat> 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<latest::lsp::Symbol> for lsp::Symbol {
-    fn from(value: latest::lsp::Symbol) -> Self {
-        Self {
-            kind: value.kind.into(),
-            name: value.name,
-        }
-    }
-}
-
-impl From<latest::lsp::SymbolKind> 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<CodeLabel> 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<CodeLabelSpan> 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<CodeLabelSpanLiteral> for latest::CodeLabelSpanLiteral {
-    fn from(value: CodeLabelSpanLiteral) -> Self {
-        Self {
-            text: value.text,
-            highlight_name: value.highlight_name,
-        }
-    }
-}
-
-impl From<Range> 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<slash_command::SlashCommandArgumentCompletion>
-    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<slash_command::SlashCommandOutput> 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<SlashCommandOutputSection> 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<context_server::ContextServerConfiguration>
-    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<dap::DebugAdapterBinary> 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<dap::StartDebuggingRequestArgumentsRequest>
-    for latest::dap::StartDebuggingRequestArgumentsRequest
-{
-    fn from(value: dap::StartDebuggingRequestArgumentsRequest) -> Self {
-        match value {
-            dap::StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
-            dap::StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
-        }
-    }
-}
-
-impl From<dap::DebugScenario> 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<dap::DebugRequest> 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,
-            }),
-        }
-    }
-}

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<Result<bool, String>> {
         // 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<Option<String>> {
+    async fn get_credential(&mut self, provider_id: String) -> wasmtime::Result<Option<String>> {
         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<Result<(), String>> {
@@ -1194,7 +1189,7 @@ impl ExtensionImports for WasmState {
         .to_wasmtime_result()
     }
 
-    async fn llm_get_env_var(&mut self, name: String) -> wasmtime::Result<Option<String>> {
+    async fn get_env_var(&mut self, name: String) -> wasmtime::Result<Option<String>> {
         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<Result<llm_provider::OauthWebAuthResult, String>> {
@@ -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<Result<llm_provider::OauthHttpResponse, String>> {
@@ -1416,10 +1411,7 @@ impl ExtensionImports for WasmState {
         .to_wasmtime_result()
     }
 
-    async fn llm_oauth_open_browser(
-        &mut self,
-        url: String,
-    ) -> wasmtime::Result<Result<(), String>> {
+    async fn oauth_open_browser(&mut self, url: String) -> wasmtime::Result<Result<(), String>> {
         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 {}