since_v0_7_0.rs

   1use crate::wasm_host::wit::since_v0_7_0::{
   2    dap::{
   3        AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, LaunchRequest,
   4        StartDebuggingRequestArguments, TcpArguments, TcpArgumentsTemplate,
   5    },
   6    lsp::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind},
   7    slash_command::SlashCommandOutputSection,
   8};
   9use crate::wasm_host::{WasmState, wit::ToWasmtimeResult};
  10use ::http_client::{AsyncBody, HttpRequestExt};
  11use ::settings::{Settings, WorktreeId};
  12use anyhow::{Context as _, Result, bail};
  13use async_compression::futures::bufread::GzipDecoder;
  14use async_tar::Archive;
  15use async_trait::async_trait;
  16use credentials_provider::CredentialsProvider;
  17use extension::{
  18    ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate,
  19};
  20use futures::{AsyncReadExt, lock::Mutex};
  21use futures::{FutureExt as _, io::BufReader};
  22use gpui::{BackgroundExecutor, SharedString};
  23use language::{BinaryStatus, LanguageName, language_settings::AllLanguageSettings};
  24use project::project_settings::ProjectSettings;
  25use semver::Version;
  26use std::{
  27    env,
  28    net::Ipv4Addr,
  29    path::{Path, PathBuf},
  30    str::FromStr,
  31    sync::{Arc, OnceLock},
  32};
  33use task::{SpawnInTerminal, ZedDebugConfig};
  34use url::Url;
  35use util::{
  36    archive::extract_zip, fs::make_file_executable, maybe, paths::PathStyle, rel_path::RelPath,
  37};
  38use wasmtime::component::{Linker, Resource};
  39
  40pub const MIN_VERSION: Version = Version::new(0, 7, 0);
  41#[allow(dead_code)]
  42pub const MAX_VERSION: Version = Version::new(0, 8, 0);
  43
  44wasmtime::component::bindgen!({
  45    async: true,
  46    trappable_imports: true,
  47    path: "../extension_api/wit/since_v0.7.0",
  48    with: {
  49         "worktree": ExtensionWorktree,
  50         "project": ExtensionProject,
  51         "key-value-store": ExtensionKeyValueStore,
  52         "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream,
  53    },
  54});
  55
  56// This is the latest version, so we pub use to make types available to parent module.
  57// Note: The parent wit.rs module re-exports specific types from here as the "latest" types.
  58pub use self::zed::extension::*;
  59
  60mod settings {
  61    #![allow(dead_code)]
  62    include!(concat!(env!("OUT_DIR"), "/since_v0.7.0/settings.rs"));
  63}
  64
  65pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
  66pub type ExtensionProject = Arc<dyn ProjectDelegate>;
  67pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
  68pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
  69
  70pub fn linker(executor: &BackgroundExecutor) -> &'static Linker<WasmState> {
  71    static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
  72    LINKER.get_or_init(|| super::new_linker(executor, Extension::add_to_linker))
  73}
  74
  75impl From<Range> for std::ops::Range<usize> {
  76    fn from(range: Range) -> Self {
  77        let start = range.start as usize;
  78        let end = range.end as usize;
  79        start..end
  80    }
  81}
  82
  83impl From<Command> for extension::Command {
  84    fn from(value: Command) -> Self {
  85        Self {
  86            command: value.command.into(),
  87            args: value.args,
  88            env: value.env,
  89        }
  90    }
  91}
  92
  93impl From<StartDebuggingRequestArgumentsRequest>
  94    for extension::StartDebuggingRequestArgumentsRequest
  95{
  96    fn from(value: StartDebuggingRequestArgumentsRequest) -> Self {
  97        match value {
  98            StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
  99            StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
 100        }
 101    }
 102}
 103impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingRequestArguments {
 104    type Error = anyhow::Error;
 105
 106    fn try_from(value: StartDebuggingRequestArguments) -> Result<Self, Self::Error> {
 107        Ok(Self {
 108            configuration: serde_json::from_str(&value.configuration)?,
 109            request: value.request.into(),
 110        })
 111    }
 112}
 113impl From<TcpArguments> for extension::TcpArguments {
 114    fn from(value: TcpArguments) -> Self {
 115        Self {
 116            host: value.host.into(),
 117            port: value.port,
 118            timeout: value.timeout,
 119        }
 120    }
 121}
 122
 123impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
 124    fn from(value: extension::TcpArgumentsTemplate) -> Self {
 125        Self {
 126            host: value.host.map(Ipv4Addr::to_bits),
 127            port: value.port,
 128            timeout: value.timeout,
 129        }
 130    }
 131}
 132
 133impl From<TcpArgumentsTemplate> for extension::TcpArgumentsTemplate {
 134    fn from(value: TcpArgumentsTemplate) -> Self {
 135        Self {
 136            host: value.host.map(Ipv4Addr::from_bits),
 137            port: value.port,
 138            timeout: value.timeout,
 139        }
 140    }
 141}
 142
 143impl TryFrom<extension::DebugTaskDefinition> for DebugTaskDefinition {
 144    type Error = anyhow::Error;
 145    fn try_from(value: extension::DebugTaskDefinition) -> Result<Self, Self::Error> {
 146        Ok(Self {
 147            label: value.label.to_string(),
 148            adapter: value.adapter.to_string(),
 149            config: value.config.to_string(),
 150            tcp_connection: value.tcp_connection.map(Into::into),
 151        })
 152    }
 153}
 154
 155impl From<task::DebugRequest> for DebugRequest {
 156    fn from(value: task::DebugRequest) -> Self {
 157        match value {
 158            task::DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
 159            task::DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
 160        }
 161    }
 162}
 163
 164impl From<DebugRequest> for task::DebugRequest {
 165    fn from(value: DebugRequest) -> Self {
 166        match value {
 167            DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
 168            DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
 169        }
 170    }
 171}
 172
 173impl From<task::LaunchRequest> for LaunchRequest {
 174    fn from(value: task::LaunchRequest) -> Self {
 175        Self {
 176            program: value.program,
 177            cwd: value.cwd.map(|p| p.to_string_lossy().into_owned()),
 178            args: value.args,
 179            envs: value.env.into_iter().collect(),
 180        }
 181    }
 182}
 183
 184impl From<task::AttachRequest> for AttachRequest {
 185    fn from(value: task::AttachRequest) -> Self {
 186        Self {
 187            process_id: value.process_id,
 188        }
 189    }
 190}
 191
 192impl From<LaunchRequest> for task::LaunchRequest {
 193    fn from(value: LaunchRequest) -> Self {
 194        Self {
 195            program: value.program,
 196            cwd: value.cwd.map(|p| p.into()),
 197            args: value.args,
 198            env: value.envs.into_iter().collect(),
 199        }
 200    }
 201}
 202impl From<AttachRequest> for task::AttachRequest {
 203    fn from(value: AttachRequest) -> Self {
 204        Self {
 205            process_id: value.process_id,
 206        }
 207    }
 208}
 209
 210impl From<ZedDebugConfig> for DebugConfig {
 211    fn from(value: ZedDebugConfig) -> Self {
 212        Self {
 213            label: value.label.into(),
 214            adapter: value.adapter.into(),
 215            request: value.request.into(),
 216            stop_on_entry: value.stop_on_entry,
 217        }
 218    }
 219}
 220impl TryFrom<DebugAdapterBinary> for extension::DebugAdapterBinary {
 221    type Error = anyhow::Error;
 222    fn try_from(value: DebugAdapterBinary) -> Result<Self, Self::Error> {
 223        Ok(Self {
 224            command: value.command,
 225            arguments: value.arguments,
 226            envs: value.envs.into_iter().collect(),
 227            cwd: value.cwd.map(|s| s.into()),
 228            connection: value.connection.map(Into::into),
 229            request_args: value.request_args.try_into()?,
 230        })
 231    }
 232}
 233
 234impl From<BuildTaskDefinition> for extension::BuildTaskDefinition {
 235    fn from(value: BuildTaskDefinition) -> Self {
 236        match value {
 237            BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
 238            BuildTaskDefinition::Template(build_task_template) => Self::Template {
 239                task_template: build_task_template.template.into(),
 240                locator_name: build_task_template.locator_name.map(SharedString::from),
 241            },
 242        }
 243    }
 244}
 245
 246impl From<extension::BuildTaskDefinition> for BuildTaskDefinition {
 247    fn from(value: extension::BuildTaskDefinition) -> Self {
 248        match value {
 249            extension::BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
 250            extension::BuildTaskDefinition::Template {
 251                task_template,
 252                locator_name,
 253            } => Self::Template(BuildTaskDefinitionTemplatePayload {
 254                template: task_template.into(),
 255                locator_name: locator_name.map(String::from),
 256            }),
 257        }
 258    }
 259}
 260impl From<BuildTaskTemplate> for extension::BuildTaskTemplate {
 261    fn from(value: BuildTaskTemplate) -> Self {
 262        Self {
 263            label: value.label,
 264            command: value.command,
 265            args: value.args,
 266            env: value.env.into_iter().collect(),
 267            cwd: value.cwd,
 268            ..Default::default()
 269        }
 270    }
 271}
 272impl From<extension::BuildTaskTemplate> for BuildTaskTemplate {
 273    fn from(value: extension::BuildTaskTemplate) -> Self {
 274        Self {
 275            label: value.label,
 276            command: value.command,
 277            args: value.args,
 278            env: value.env.into_iter().collect(),
 279            cwd: value.cwd,
 280        }
 281    }
 282}
 283
 284impl TryFrom<DebugScenario> for extension::DebugScenario {
 285    type Error = anyhow::Error;
 286
 287    fn try_from(value: DebugScenario) -> std::result::Result<Self, Self::Error> {
 288        Ok(Self {
 289            adapter: value.adapter.into(),
 290            label: value.label.into(),
 291            build: value.build.map(Into::into),
 292            config: serde_json::Value::from_str(&value.config)?,
 293            tcp_connection: value.tcp_connection.map(Into::into),
 294        })
 295    }
 296}
 297
 298impl From<extension::DebugScenario> for DebugScenario {
 299    fn from(value: extension::DebugScenario) -> Self {
 300        Self {
 301            adapter: value.adapter.into(),
 302            label: value.label.into(),
 303            build: value.build.map(Into::into),
 304            config: value.config.to_string(),
 305            tcp_connection: value.tcp_connection.map(Into::into),
 306        }
 307    }
 308}
 309
 310impl TryFrom<SpawnInTerminal> for ResolvedTask {
 311    type Error = anyhow::Error;
 312
 313    fn try_from(value: SpawnInTerminal) -> Result<Self, Self::Error> {
 314        Ok(Self {
 315            label: value.label,
 316            command: value.command.context("missing command")?,
 317            args: value.args,
 318            env: value.env.into_iter().collect(),
 319            cwd: value.cwd.map(|s| {
 320                let s = s.to_string_lossy();
 321                if cfg!(target_os = "windows") {
 322                    s.replace('\\', "/")
 323                } else {
 324                    s.into_owned()
 325                }
 326            }),
 327        })
 328    }
 329}
 330
 331impl From<CodeLabel> for extension::CodeLabel {
 332    fn from(value: CodeLabel) -> Self {
 333        Self {
 334            code: value.code,
 335            spans: value.spans.into_iter().map(Into::into).collect(),
 336            filter_range: value.filter_range.into(),
 337        }
 338    }
 339}
 340
 341impl From<CodeLabelSpan> for extension::CodeLabelSpan {
 342    fn from(value: CodeLabelSpan) -> Self {
 343        match value {
 344            CodeLabelSpan::CodeRange(range) => Self::CodeRange(range.into()),
 345            CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()),
 346        }
 347    }
 348}
 349
 350impl From<CodeLabelSpanLiteral> for extension::CodeLabelSpanLiteral {
 351    fn from(value: CodeLabelSpanLiteral) -> Self {
 352        Self {
 353            text: value.text,
 354            highlight_name: value.highlight_name,
 355        }
 356    }
 357}
 358
 359impl From<extension::Completion> for Completion {
 360    fn from(value: extension::Completion) -> Self {
 361        Self {
 362            label: value.label,
 363            label_details: value.label_details.map(Into::into),
 364            detail: value.detail,
 365            kind: value.kind.map(Into::into),
 366            insert_text_format: value.insert_text_format.map(Into::into),
 367        }
 368    }
 369}
 370
 371impl From<extension::CompletionLabelDetails> for CompletionLabelDetails {
 372    fn from(value: extension::CompletionLabelDetails) -> Self {
 373        Self {
 374            detail: value.detail,
 375            description: value.description,
 376        }
 377    }
 378}
 379
 380impl From<extension::CompletionKind> for CompletionKind {
 381    fn from(value: extension::CompletionKind) -> Self {
 382        match value {
 383            extension::CompletionKind::Text => Self::Text,
 384            extension::CompletionKind::Method => Self::Method,
 385            extension::CompletionKind::Function => Self::Function,
 386            extension::CompletionKind::Constructor => Self::Constructor,
 387            extension::CompletionKind::Field => Self::Field,
 388            extension::CompletionKind::Variable => Self::Variable,
 389            extension::CompletionKind::Class => Self::Class,
 390            extension::CompletionKind::Interface => Self::Interface,
 391            extension::CompletionKind::Module => Self::Module,
 392            extension::CompletionKind::Property => Self::Property,
 393            extension::CompletionKind::Unit => Self::Unit,
 394            extension::CompletionKind::Value => Self::Value,
 395            extension::CompletionKind::Enum => Self::Enum,
 396            extension::CompletionKind::Keyword => Self::Keyword,
 397            extension::CompletionKind::Snippet => Self::Snippet,
 398            extension::CompletionKind::Color => Self::Color,
 399            extension::CompletionKind::File => Self::File,
 400            extension::CompletionKind::Reference => Self::Reference,
 401            extension::CompletionKind::Folder => Self::Folder,
 402            extension::CompletionKind::EnumMember => Self::EnumMember,
 403            extension::CompletionKind::Constant => Self::Constant,
 404            extension::CompletionKind::Struct => Self::Struct,
 405            extension::CompletionKind::Event => Self::Event,
 406            extension::CompletionKind::Operator => Self::Operator,
 407            extension::CompletionKind::TypeParameter => Self::TypeParameter,
 408            extension::CompletionKind::Other(value) => Self::Other(value),
 409        }
 410    }
 411}
 412
 413impl From<extension::InsertTextFormat> for InsertTextFormat {
 414    fn from(value: extension::InsertTextFormat) -> Self {
 415        match value {
 416            extension::InsertTextFormat::PlainText => Self::PlainText,
 417            extension::InsertTextFormat::Snippet => Self::Snippet,
 418            extension::InsertTextFormat::Other(value) => Self::Other(value),
 419        }
 420    }
 421}
 422
 423impl From<extension::Symbol> for Symbol {
 424    fn from(value: extension::Symbol) -> Self {
 425        Self {
 426            kind: value.kind.into(),
 427            name: value.name,
 428        }
 429    }
 430}
 431
 432impl From<extension::SymbolKind> for SymbolKind {
 433    fn from(value: extension::SymbolKind) -> Self {
 434        match value {
 435            extension::SymbolKind::File => Self::File,
 436            extension::SymbolKind::Module => Self::Module,
 437            extension::SymbolKind::Namespace => Self::Namespace,
 438            extension::SymbolKind::Package => Self::Package,
 439            extension::SymbolKind::Class => Self::Class,
 440            extension::SymbolKind::Method => Self::Method,
 441            extension::SymbolKind::Property => Self::Property,
 442            extension::SymbolKind::Field => Self::Field,
 443            extension::SymbolKind::Constructor => Self::Constructor,
 444            extension::SymbolKind::Enum => Self::Enum,
 445            extension::SymbolKind::Interface => Self::Interface,
 446            extension::SymbolKind::Function => Self::Function,
 447            extension::SymbolKind::Variable => Self::Variable,
 448            extension::SymbolKind::Constant => Self::Constant,
 449            extension::SymbolKind::String => Self::String,
 450            extension::SymbolKind::Number => Self::Number,
 451            extension::SymbolKind::Boolean => Self::Boolean,
 452            extension::SymbolKind::Array => Self::Array,
 453            extension::SymbolKind::Object => Self::Object,
 454            extension::SymbolKind::Key => Self::Key,
 455            extension::SymbolKind::Null => Self::Null,
 456            extension::SymbolKind::EnumMember => Self::EnumMember,
 457            extension::SymbolKind::Struct => Self::Struct,
 458            extension::SymbolKind::Event => Self::Event,
 459            extension::SymbolKind::Operator => Self::Operator,
 460            extension::SymbolKind::TypeParameter => Self::TypeParameter,
 461            extension::SymbolKind::Other(value) => Self::Other(value),
 462        }
 463    }
 464}
 465
 466impl From<extension::SlashCommand> for SlashCommand {
 467    fn from(value: extension::SlashCommand) -> Self {
 468        Self {
 469            name: value.name,
 470            description: value.description,
 471            tooltip_text: value.tooltip_text,
 472            requires_argument: value.requires_argument,
 473        }
 474    }
 475}
 476
 477impl From<SlashCommandOutput> for extension::SlashCommandOutput {
 478    fn from(value: SlashCommandOutput) -> Self {
 479        Self {
 480            text: value.text,
 481            sections: value.sections.into_iter().map(Into::into).collect(),
 482        }
 483    }
 484}
 485
 486impl From<SlashCommandOutputSection> for extension::SlashCommandOutputSection {
 487    fn from(value: SlashCommandOutputSection) -> Self {
 488        Self {
 489            range: value.range.start as usize..value.range.end as usize,
 490            label: value.label,
 491        }
 492    }
 493}
 494
 495impl From<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCompletion {
 496    fn from(value: SlashCommandArgumentCompletion) -> Self {
 497        Self {
 498            label: value.label,
 499            new_text: value.new_text,
 500            run_command: value.run_command,
 501        }
 502    }
 503}
 504
 505impl TryFrom<ContextServerConfiguration> for extension::ContextServerConfiguration {
 506    type Error = anyhow::Error;
 507
 508    fn try_from(value: ContextServerConfiguration) -> Result<Self, Self::Error> {
 509        let settings_schema: serde_json::Value = serde_json::from_str(&value.settings_schema)
 510            .context("Failed to parse settings_schema")?;
 511
 512        Ok(Self {
 513            installation_instructions: value.installation_instructions,
 514            default_settings: value.default_settings,
 515            settings_schema,
 516        })
 517    }
 518}
 519
 520impl HostKeyValueStore for WasmState {
 521    async fn insert(
 522        &mut self,
 523        kv_store: Resource<ExtensionKeyValueStore>,
 524        key: String,
 525        value: String,
 526    ) -> wasmtime::Result<Result<(), String>> {
 527        let kv_store = self.table.get(&kv_store)?;
 528        kv_store.insert(key, value).await.to_wasmtime_result()
 529    }
 530
 531    async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
 532        // We only ever hand out borrows of key-value stores.
 533        Ok(())
 534    }
 535}
 536
 537impl HostProject for WasmState {
 538    async fn worktree_ids(
 539        &mut self,
 540        project: Resource<ExtensionProject>,
 541    ) -> wasmtime::Result<Vec<u64>> {
 542        let project = self.table.get(&project)?;
 543        Ok(project.worktree_ids())
 544    }
 545
 546    async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
 547        // We only ever hand out borrows of projects.
 548        Ok(())
 549    }
 550}
 551
 552impl HostWorktree for WasmState {
 553    async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
 554        let delegate = self.table.get(&delegate)?;
 555        Ok(delegate.id())
 556    }
 557
 558    async fn root_path(
 559        &mut self,
 560        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 561    ) -> wasmtime::Result<String> {
 562        let delegate = self.table.get(&delegate)?;
 563        Ok(delegate.root_path())
 564    }
 565
 566    async fn read_text_file(
 567        &mut self,
 568        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 569        path: String,
 570    ) -> wasmtime::Result<Result<String, String>> {
 571        let delegate = self.table.get(&delegate)?;
 572        Ok(delegate
 573            .read_text_file(&RelPath::new(Path::new(&path), PathStyle::Posix)?)
 574            .await
 575            .map_err(|error| error.to_string()))
 576    }
 577
 578    async fn shell_env(
 579        &mut self,
 580        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 581    ) -> wasmtime::Result<EnvVars> {
 582        let delegate = self.table.get(&delegate)?;
 583        Ok(delegate.shell_env().await.into_iter().collect())
 584    }
 585
 586    async fn which(
 587        &mut self,
 588        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 589        binary_name: String,
 590    ) -> wasmtime::Result<Option<String>> {
 591        let delegate = self.table.get(&delegate)?;
 592        Ok(delegate.which(binary_name).await)
 593    }
 594
 595    async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
 596        // We only ever hand out borrows of worktrees.
 597        Ok(())
 598    }
 599}
 600
 601impl common::Host for WasmState {}
 602
 603impl http_client::Host for WasmState {
 604    async fn fetch(
 605        &mut self,
 606        request: http_client::HttpRequest,
 607    ) -> wasmtime::Result<Result<http_client::HttpResponse, String>> {
 608        maybe!(async {
 609            let url = &request.url;
 610            let request = convert_request(&request)?;
 611            let mut response = self.host.http_client.send(request).await?;
 612
 613            if response.status().is_client_error() || response.status().is_server_error() {
 614                bail!("failed to fetch '{url}': status code {}", response.status())
 615            }
 616            convert_response(&mut response).await
 617        })
 618        .await
 619        .to_wasmtime_result()
 620    }
 621
 622    async fn fetch_stream(
 623        &mut self,
 624        request: http_client::HttpRequest,
 625    ) -> wasmtime::Result<Result<Resource<ExtensionHttpResponseStream>, String>> {
 626        let request = convert_request(&request)?;
 627        let response = self.host.http_client.send(request);
 628        maybe!(async {
 629            let response = response.await?;
 630            let stream = Arc::new(Mutex::new(response));
 631            let resource = self.table.push(stream)?;
 632            Ok(resource)
 633        })
 634        .await
 635        .to_wasmtime_result()
 636    }
 637}
 638
 639impl http_client::HostHttpResponseStream for WasmState {
 640    async fn next_chunk(
 641        &mut self,
 642        resource: Resource<ExtensionHttpResponseStream>,
 643    ) -> wasmtime::Result<Result<Option<Vec<u8>>, String>> {
 644        let stream = self.table.get(&resource)?.clone();
 645        maybe!(async move {
 646            let mut response = stream.lock().await;
 647            let mut buffer = vec![0; 8192]; // 8KB buffer
 648            let bytes_read = response.body_mut().read(&mut buffer).await?;
 649            if bytes_read == 0 {
 650                Ok(None)
 651            } else {
 652                buffer.truncate(bytes_read);
 653                Ok(Some(buffer))
 654            }
 655        })
 656        .await
 657        .to_wasmtime_result()
 658    }
 659
 660    async fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
 661        Ok(())
 662    }
 663}
 664
 665impl From<http_client::HttpMethod> for ::http_client::Method {
 666    fn from(value: http_client::HttpMethod) -> Self {
 667        match value {
 668            http_client::HttpMethod::Get => Self::GET,
 669            http_client::HttpMethod::Post => Self::POST,
 670            http_client::HttpMethod::Put => Self::PUT,
 671            http_client::HttpMethod::Delete => Self::DELETE,
 672            http_client::HttpMethod::Head => Self::HEAD,
 673            http_client::HttpMethod::Options => Self::OPTIONS,
 674            http_client::HttpMethod::Patch => Self::PATCH,
 675        }
 676    }
 677}
 678
 679fn convert_request(
 680    extension_request: &http_client::HttpRequest,
 681) -> anyhow::Result<::http_client::Request<AsyncBody>> {
 682    let mut request = ::http_client::Request::builder()
 683        .method(::http_client::Method::from(extension_request.method))
 684        .uri(&extension_request.url)
 685        .follow_redirects(match extension_request.redirect_policy {
 686            http_client::RedirectPolicy::NoFollow => ::http_client::RedirectPolicy::NoFollow,
 687            http_client::RedirectPolicy::FollowLimit(limit) => {
 688                ::http_client::RedirectPolicy::FollowLimit(limit)
 689            }
 690            http_client::RedirectPolicy::FollowAll => ::http_client::RedirectPolicy::FollowAll,
 691        });
 692    for (key, value) in &extension_request.headers {
 693        request = request.header(key, value);
 694    }
 695    let body = extension_request
 696        .body
 697        .clone()
 698        .map(AsyncBody::from)
 699        .unwrap_or_default();
 700    request.body(body).map_err(anyhow::Error::from)
 701}
 702
 703async fn convert_response(
 704    response: &mut ::http_client::Response<AsyncBody>,
 705) -> anyhow::Result<http_client::HttpResponse> {
 706    let mut extension_response = http_client::HttpResponse {
 707        body: Vec::new(),
 708        headers: Vec::new(),
 709    };
 710
 711    for (key, value) in response.headers() {
 712        extension_response
 713            .headers
 714            .push((key.to_string(), value.to_str().unwrap_or("").to_string()));
 715    }
 716
 717    response
 718        .body_mut()
 719        .read_to_end(&mut extension_response.body)
 720        .await?;
 721
 722    Ok(extension_response)
 723}
 724
 725impl nodejs::Host for WasmState {
 726    async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
 727        self.host
 728            .node_runtime
 729            .binary_path()
 730            .await
 731            .map(|path| path.to_string_lossy().into_owned())
 732            .to_wasmtime_result()
 733    }
 734
 735    async fn npm_package_latest_version(
 736        &mut self,
 737        package_name: String,
 738    ) -> wasmtime::Result<Result<String, String>> {
 739        self.host
 740            .node_runtime
 741            .npm_package_latest_version(&package_name)
 742            .await
 743            .to_wasmtime_result()
 744    }
 745
 746    async fn npm_package_installed_version(
 747        &mut self,
 748        package_name: String,
 749    ) -> wasmtime::Result<Result<Option<String>, String>> {
 750        self.host
 751            .node_runtime
 752            .npm_package_installed_version(&self.work_dir(), &package_name)
 753            .await
 754            .to_wasmtime_result()
 755    }
 756
 757    async fn npm_install_package(
 758        &mut self,
 759        package_name: String,
 760        version: String,
 761    ) -> wasmtime::Result<Result<(), String>> {
 762        self.capability_granter
 763            .grant_npm_install_package(&package_name)?;
 764
 765        self.host
 766            .node_runtime
 767            .npm_install_packages(&self.work_dir(), &[(&package_name, &version)])
 768            .await
 769            .to_wasmtime_result()
 770    }
 771}
 772
 773#[async_trait]
 774impl lsp::Host for WasmState {}
 775
 776impl From<::http_client::github::GithubRelease> for github::GithubRelease {
 777    fn from(value: ::http_client::github::GithubRelease) -> Self {
 778        Self {
 779            version: value.tag_name,
 780            assets: value.assets.into_iter().map(Into::into).collect(),
 781        }
 782    }
 783}
 784
 785impl From<::http_client::github::GithubReleaseAsset> for github::GithubReleaseAsset {
 786    fn from(value: ::http_client::github::GithubReleaseAsset) -> Self {
 787        Self {
 788            name: value.name,
 789            download_url: value.browser_download_url,
 790        }
 791    }
 792}
 793
 794impl github::Host for WasmState {
 795    async fn latest_github_release(
 796        &mut self,
 797        repo: String,
 798        options: github::GithubReleaseOptions,
 799    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
 800        maybe!(async {
 801            let release = ::http_client::github::latest_github_release(
 802                &repo,
 803                options.require_assets,
 804                options.pre_release,
 805                self.host.http_client.clone(),
 806            )
 807            .await?;
 808            Ok(release.into())
 809        })
 810        .await
 811        .to_wasmtime_result()
 812    }
 813
 814    async fn github_release_by_tag_name(
 815        &mut self,
 816        repo: String,
 817        tag: String,
 818    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
 819        maybe!(async {
 820            let release = ::http_client::github::get_release_by_tag_name(
 821                &repo,
 822                &tag,
 823                self.host.http_client.clone(),
 824            )
 825            .await?;
 826            Ok(release.into())
 827        })
 828        .await
 829        .to_wasmtime_result()
 830    }
 831}
 832
 833impl platform::Host for WasmState {
 834    async fn current_platform(&mut self) -> Result<(platform::Os, platform::Architecture)> {
 835        Ok((
 836            match env::consts::OS {
 837                "macos" => platform::Os::Mac,
 838                "linux" => platform::Os::Linux,
 839                "windows" => platform::Os::Windows,
 840                _ => panic!("unsupported os"),
 841            },
 842            match env::consts::ARCH {
 843                "aarch64" => platform::Architecture::Aarch64,
 844                "x86" => platform::Architecture::X86,
 845                "x86_64" => platform::Architecture::X8664,
 846                _ => panic!("unsupported architecture"),
 847            },
 848        ))
 849    }
 850}
 851
 852impl From<std::process::Output> for process::Output {
 853    fn from(output: std::process::Output) -> Self {
 854        Self {
 855            status: output.status.code(),
 856            stdout: output.stdout,
 857            stderr: output.stderr,
 858        }
 859    }
 860}
 861
 862impl process::Host for WasmState {
 863    async fn run_command(
 864        &mut self,
 865        command: process::Command,
 866    ) -> wasmtime::Result<Result<process::Output, String>> {
 867        maybe!(async {
 868            self.capability_granter
 869                .grant_exec(&command.command, &command.args)?;
 870
 871            let output = util::command::new_smol_command(command.command.as_str())
 872                .args(&command.args)
 873                .envs(command.env)
 874                .output()
 875                .await?;
 876
 877            Ok(output.into())
 878        })
 879        .await
 880        .to_wasmtime_result()
 881    }
 882}
 883
 884#[async_trait]
 885impl slash_command::Host for WasmState {}
 886
 887#[async_trait]
 888impl context_server::Host for WasmState {}
 889
 890impl dap::Host for WasmState {
 891    async fn resolve_tcp_template(
 892        &mut self,
 893        template: TcpArgumentsTemplate,
 894    ) -> wasmtime::Result<Result<TcpArguments, String>> {
 895        maybe!(async {
 896            let (host, port, timeout) =
 897                ::dap::configure_tcp_connection(task::TcpArgumentsTemplate {
 898                    port: template.port,
 899                    host: template.host.map(Ipv4Addr::from_bits),
 900                    timeout: template.timeout,
 901                })
 902                .await?;
 903            Ok(TcpArguments {
 904                port,
 905                host: host.to_bits(),
 906                timeout,
 907            })
 908        })
 909        .await
 910        .to_wasmtime_result()
 911    }
 912}
 913
 914impl ExtensionImports for WasmState {
 915    async fn get_settings(
 916        &mut self,
 917        location: Option<self::SettingsLocation>,
 918        category: String,
 919        key: Option<String>,
 920    ) -> wasmtime::Result<Result<String, String>> {
 921        self.on_main_thread(|cx| {
 922            async move {
 923                let path = location.as_ref().and_then(|location| {
 924                    RelPath::new(Path::new(&location.path), PathStyle::Posix).ok()
 925                });
 926                let location = path
 927                    .as_ref()
 928                    .zip(location.as_ref())
 929                    .map(|(path, location)| ::settings::SettingsLocation {
 930                        worktree_id: WorktreeId::from_proto(location.worktree_id),
 931                        path,
 932                    });
 933
 934                cx.update(|cx| match category.as_str() {
 935                    "language" => {
 936                        let key = key.map(|k| LanguageName::new(&k));
 937                        let settings = AllLanguageSettings::get(location, cx).language(
 938                            location,
 939                            key.as_ref(),
 940                            cx,
 941                        );
 942                        Ok(serde_json::to_string(&settings::LanguageSettings {
 943                            tab_size: settings.tab_size,
 944                        })?)
 945                    }
 946                    "lsp" => {
 947                        let settings = key
 948                            .and_then(|key| {
 949                                ProjectSettings::get(location, cx)
 950                                    .lsp
 951                                    .get(&::lsp::LanguageServerName::from_proto(key))
 952                            })
 953                            .cloned()
 954                            .unwrap_or_default();
 955                        Ok(serde_json::to_string(&settings::LspSettings {
 956                            binary: settings.binary.map(|binary| settings::CommandSettings {
 957                                path: binary.path,
 958                                arguments: binary.arguments,
 959                                env: binary.env.map(|env| env.into_iter().collect()),
 960                            }),
 961                            settings: settings.settings,
 962                            initialization_options: settings.initialization_options,
 963                        })?)
 964                    }
 965                    "context_servers" => {
 966                        let settings = key
 967                            .and_then(|key| {
 968                                ProjectSettings::get(location, cx)
 969                                    .context_servers
 970                                    .get(key.as_str())
 971                            })
 972                            .cloned()
 973                            .unwrap_or_else(|| {
 974                                project::project_settings::ContextServerSettings::default_extension(
 975                                )
 976                            });
 977
 978                        match settings {
 979                            project::project_settings::ContextServerSettings::Stdio {
 980                                enabled: _,
 981                                command,
 982                            } => Ok(serde_json::to_string(&settings::ContextServerSettings {
 983                                command: Some(settings::CommandSettings {
 984                                    path: command.path.to_str().map(|path| path.to_string()),
 985                                    arguments: Some(command.args),
 986                                    env: command.env.map(|env| env.into_iter().collect()),
 987                                }),
 988                                settings: None,
 989                            })?),
 990                            project::project_settings::ContextServerSettings::Extension {
 991                                enabled: _,
 992                                settings,
 993                            } => Ok(serde_json::to_string(&settings::ContextServerSettings {
 994                                command: None,
 995                                settings: Some(settings),
 996                            })?),
 997                            project::project_settings::ContextServerSettings::Http { .. } => {
 998                                bail!("remote context server settings not supported in 0.6.0")
 999                            }
1000                        }
1001                    }
1002                    _ => {
1003                        bail!("Unknown settings category: {}", category);
1004                    }
1005                })
1006            }
1007            .boxed_local()
1008        })
1009        .await?
1010        .to_wasmtime_result()
1011    }
1012
1013    async fn set_language_server_installation_status(
1014        &mut self,
1015        server_name: String,
1016        status: LanguageServerInstallationStatus,
1017    ) -> wasmtime::Result<()> {
1018        let status = match status {
1019            LanguageServerInstallationStatus::CheckingForUpdate => BinaryStatus::CheckingForUpdate,
1020            LanguageServerInstallationStatus::Downloading => BinaryStatus::Downloading,
1021            LanguageServerInstallationStatus::None => BinaryStatus::None,
1022            LanguageServerInstallationStatus::Failed(error) => BinaryStatus::Failed { error },
1023        };
1024
1025        self.host
1026            .proxy
1027            .update_language_server_status(::lsp::LanguageServerName(server_name.into()), status);
1028
1029        Ok(())
1030    }
1031
1032    async fn download_file(
1033        &mut self,
1034        url: String,
1035        path: String,
1036        file_type: DownloadedFileType,
1037    ) -> wasmtime::Result<Result<(), String>> {
1038        maybe!(async {
1039            let parsed_url = Url::parse(&url)?;
1040            self.capability_granter.grant_download_file(&parsed_url)?;
1041
1042            let path = PathBuf::from(path);
1043            let extension_work_dir = self.host.work_dir.join(self.manifest.id.as_ref());
1044
1045            self.host.fs.create_dir(&extension_work_dir).await?;
1046
1047            let destination_path = self
1048                .host
1049                .writeable_path_from_extension(&self.manifest.id, &path)?;
1050
1051            let mut response = self
1052                .host
1053                .http_client
1054                .get(&url, Default::default(), true)
1055                .await
1056                .context("downloading release")?;
1057
1058            anyhow::ensure!(
1059                response.status().is_success(),
1060                "download failed with status {}",
1061                response.status()
1062            );
1063            let body = BufReader::new(response.body_mut());
1064
1065            match file_type {
1066                DownloadedFileType::Uncompressed => {
1067                    futures::pin_mut!(body);
1068                    self.host
1069                        .fs
1070                        .create_file_with(&destination_path, body)
1071                        .await?;
1072                }
1073                DownloadedFileType::Gzip => {
1074                    let body = GzipDecoder::new(body);
1075                    futures::pin_mut!(body);
1076                    self.host
1077                        .fs
1078                        .create_file_with(&destination_path, body)
1079                        .await?;
1080                }
1081                DownloadedFileType::GzipTar => {
1082                    let body = GzipDecoder::new(body);
1083                    futures::pin_mut!(body);
1084                    self.host
1085                        .fs
1086                        .extract_tar_file(&destination_path, Archive::new(body))
1087                        .await?;
1088                }
1089                DownloadedFileType::Zip => {
1090                    futures::pin_mut!(body);
1091                    extract_zip(&destination_path, body)
1092                        .await
1093                        .with_context(|| format!("unzipping {path:?} archive"))?;
1094                }
1095            }
1096
1097            Ok(())
1098        })
1099        .await
1100        .to_wasmtime_result()
1101    }
1102
1103    async fn make_file_executable(&mut self, path: String) -> wasmtime::Result<Result<(), String>> {
1104        let path = self
1105            .host
1106            .writeable_path_from_extension(&self.manifest.id, Path::new(&path))?;
1107
1108        make_file_executable(&path)
1109            .await
1110            .with_context(|| format!("setting permissions for path {path:?}"))
1111            .to_wasmtime_result()
1112    }
1113
1114    // =========================================================================
1115    // LLM Provider Import Implementations
1116    // =========================================================================
1117
1118    async fn llm_request_credential(
1119        &mut self,
1120        _provider_id: String,
1121        _credential_type: llm_provider::CredentialType,
1122        _label: String,
1123        _placeholder: String,
1124    ) -> wasmtime::Result<Result<bool, String>> {
1125        // For now, credential requests return false (not provided)
1126        // Extensions should use llm_get_env_var to check for env vars first,
1127        // then llm_store_credential/llm_get_credential for manual storage
1128        // Full UI credential prompting will be added in a future phase
1129        Ok(Ok(false))
1130    }
1131
1132    async fn llm_get_credential(
1133        &mut self,
1134        provider_id: String,
1135    ) -> wasmtime::Result<Option<String>> {
1136        let extension_id = self.manifest.id.clone();
1137        let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
1138
1139        self.on_main_thread(move |cx| {
1140            async move {
1141                let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
1142                let result = credentials_provider
1143                    .read_credentials(&credential_key, cx)
1144                    .await
1145                    .ok()
1146                    .flatten();
1147                Ok(result.map(|(_, password)| String::from_utf8_lossy(&password).to_string()))
1148            }
1149            .boxed_local()
1150        })
1151        .await
1152    }
1153
1154    async fn llm_store_credential(
1155        &mut self,
1156        provider_id: String,
1157        value: String,
1158    ) -> wasmtime::Result<Result<(), String>> {
1159        let extension_id = self.manifest.id.clone();
1160        let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
1161
1162        self.on_main_thread(move |cx| {
1163            async move {
1164                let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
1165                credentials_provider
1166                    .write_credentials(&credential_key, "api_key", value.as_bytes(), cx)
1167                    .await
1168                    .map_err(|e| anyhow::anyhow!("{}", e))
1169            }
1170            .boxed_local()
1171        })
1172        .await
1173        .to_wasmtime_result()
1174    }
1175
1176    async fn llm_delete_credential(
1177        &mut self,
1178        provider_id: String,
1179    ) -> wasmtime::Result<Result<(), String>> {
1180        let extension_id = self.manifest.id.clone();
1181        let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
1182
1183        self.on_main_thread(move |cx| {
1184            async move {
1185                let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
1186                credentials_provider
1187                    .delete_credentials(&credential_key, cx)
1188                    .await
1189                    .map_err(|e| anyhow::anyhow!("{}", e))
1190            }
1191            .boxed_local()
1192        })
1193        .await
1194        .to_wasmtime_result()
1195    }
1196
1197    async fn llm_get_env_var(&mut self, name: String) -> wasmtime::Result<Option<String>> {
1198        Ok(env::var(&name).ok())
1199    }
1200}
1201
1202// =============================================================================
1203// LLM Provider Host Implementations
1204// =============================================================================
1205
1206impl llm_provider::Host for WasmState {}
1207
1208// =============================================================================
1209// LLM Provider Type Conversions (v0.7.0 -> latest/v0.8.0)
1210// =============================================================================
1211
1212use super::since_v0_8_0 as latest;
1213
1214impl From<llm_provider::ProviderInfo> for latest::llm_provider::ProviderInfo {
1215    fn from(value: llm_provider::ProviderInfo) -> Self {
1216        Self {
1217            id: value.id,
1218            name: value.name,
1219            icon: value.icon,
1220        }
1221    }
1222}
1223
1224impl From<llm_provider::ModelInfo> for latest::llm_provider::ModelInfo {
1225    fn from(value: llm_provider::ModelInfo) -> Self {
1226        Self {
1227            id: value.id,
1228            name: value.name,
1229            max_token_count: value.max_token_count,
1230            max_output_tokens: value.max_output_tokens,
1231            capabilities: value.capabilities.into(),
1232            is_default: value.is_default,
1233            is_default_fast: value.is_default_fast,
1234        }
1235    }
1236}
1237
1238impl From<llm_provider::ModelCapabilities> for latest::llm_provider::ModelCapabilities {
1239    fn from(value: llm_provider::ModelCapabilities) -> Self {
1240        Self {
1241            supports_images: value.supports_images,
1242            supports_tools: value.supports_tools,
1243            supports_tool_choice_auto: value.supports_tool_choice_auto,
1244            supports_tool_choice_any: value.supports_tool_choice_any,
1245            supports_tool_choice_none: value.supports_tool_choice_none,
1246            supports_thinking: value.supports_thinking,
1247            tool_input_format: value.tool_input_format.into(),
1248        }
1249    }
1250}
1251
1252impl From<llm_provider::ToolInputFormat> for latest::llm_provider::ToolInputFormat {
1253    fn from(value: llm_provider::ToolInputFormat) -> Self {
1254        match value {
1255            llm_provider::ToolInputFormat::JsonSchema => Self::JsonSchema,
1256            llm_provider::ToolInputFormat::Simplified => Self::Simplified,
1257        }
1258    }
1259}
1260
1261impl From<llm_provider::CompletionEvent> for latest::llm_provider::CompletionEvent {
1262    fn from(value: llm_provider::CompletionEvent) -> Self {
1263        match value {
1264            llm_provider::CompletionEvent::Started => Self::Started,
1265            llm_provider::CompletionEvent::Text(s) => Self::Text(s),
1266            llm_provider::CompletionEvent::Thinking(t) => Self::Thinking(t.into()),
1267            llm_provider::CompletionEvent::RedactedThinking(s) => Self::RedactedThinking(s),
1268            llm_provider::CompletionEvent::ToolUse(t) => Self::ToolUse(t.into()),
1269            llm_provider::CompletionEvent::ToolUseJsonParseError(e) => {
1270                Self::ToolUseJsonParseError(e.into())
1271            }
1272            llm_provider::CompletionEvent::Stop(r) => Self::Stop(r.into()),
1273            llm_provider::CompletionEvent::Usage(u) => Self::Usage(u.into()),
1274            llm_provider::CompletionEvent::ReasoningDetails(s) => Self::ReasoningDetails(s),
1275        }
1276    }
1277}
1278
1279impl From<llm_provider::ThinkingContent> for latest::llm_provider::ThinkingContent {
1280    fn from(value: llm_provider::ThinkingContent) -> Self {
1281        Self {
1282            text: value.text,
1283            signature: value.signature,
1284        }
1285    }
1286}
1287
1288impl From<llm_provider::ToolUse> for latest::llm_provider::ToolUse {
1289    fn from(value: llm_provider::ToolUse) -> Self {
1290        Self {
1291            id: value.id,
1292            name: value.name,
1293            input: value.input,
1294            thought_signature: value.thought_signature,
1295        }
1296    }
1297}
1298
1299impl From<llm_provider::ToolUseJsonParseError> for latest::llm_provider::ToolUseJsonParseError {
1300    fn from(value: llm_provider::ToolUseJsonParseError) -> Self {
1301        Self {
1302            id: value.id,
1303            tool_name: value.tool_name,
1304            raw_input: value.raw_input,
1305            error: value.error,
1306        }
1307    }
1308}
1309
1310impl From<llm_provider::StopReason> for latest::llm_provider::StopReason {
1311    fn from(value: llm_provider::StopReason) -> Self {
1312        match value {
1313            llm_provider::StopReason::EndTurn => Self::EndTurn,
1314            llm_provider::StopReason::MaxTokens => Self::MaxTokens,
1315            llm_provider::StopReason::ToolUse => Self::ToolUse,
1316            llm_provider::StopReason::Refusal => Self::Refusal,
1317        }
1318    }
1319}
1320
1321impl From<llm_provider::TokenUsage> for latest::llm_provider::TokenUsage {
1322    fn from(value: llm_provider::TokenUsage) -> Self {
1323        Self {
1324            input_tokens: value.input_tokens,
1325            output_tokens: value.output_tokens,
1326            cache_creation_input_tokens: value.cache_creation_input_tokens,
1327            cache_read_input_tokens: value.cache_read_input_tokens,
1328        }
1329    }
1330}
1331
1332impl From<llm_provider::CacheConfiguration> for latest::llm_provider::CacheConfiguration {
1333    fn from(value: llm_provider::CacheConfiguration) -> Self {
1334        Self {
1335            max_cache_anchors: value.max_cache_anchors,
1336            should_cache_tool_definitions: value.should_cache_tool_definitions,
1337            min_total_token_count: value.min_total_token_count,
1338        }
1339    }
1340}
1341
1342// Conversions from latest (v0.8.0) -> v0.7.0 for requests
1343
1344impl From<latest::llm_provider::CompletionRequest> for llm_provider::CompletionRequest {
1345    fn from(value: latest::llm_provider::CompletionRequest) -> Self {
1346        Self {
1347            messages: value.messages.into_iter().map(Into::into).collect(),
1348            tools: value.tools.into_iter().map(Into::into).collect(),
1349            tool_choice: value.tool_choice.map(Into::into),
1350            stop_sequences: value.stop_sequences,
1351            temperature: value.temperature,
1352            thinking_allowed: value.thinking_allowed,
1353            max_tokens: value.max_tokens,
1354        }
1355    }
1356}
1357
1358impl From<latest::llm_provider::RequestMessage> for llm_provider::RequestMessage {
1359    fn from(value: latest::llm_provider::RequestMessage) -> Self {
1360        Self {
1361            role: value.role.into(),
1362            content: value.content.into_iter().map(Into::into).collect(),
1363            cache: value.cache,
1364        }
1365    }
1366}
1367
1368impl From<latest::llm_provider::MessageRole> for llm_provider::MessageRole {
1369    fn from(value: latest::llm_provider::MessageRole) -> Self {
1370        match value {
1371            latest::llm_provider::MessageRole::User => Self::User,
1372            latest::llm_provider::MessageRole::Assistant => Self::Assistant,
1373            latest::llm_provider::MessageRole::System => Self::System,
1374        }
1375    }
1376}
1377
1378impl From<latest::llm_provider::MessageContent> for llm_provider::MessageContent {
1379    fn from(value: latest::llm_provider::MessageContent) -> Self {
1380        match value {
1381            latest::llm_provider::MessageContent::Text(s) => Self::Text(s),
1382            latest::llm_provider::MessageContent::Image(i) => Self::Image(i.into()),
1383            latest::llm_provider::MessageContent::ToolUse(t) => Self::ToolUse(t.into()),
1384            latest::llm_provider::MessageContent::ToolResult(t) => Self::ToolResult(t.into()),
1385            latest::llm_provider::MessageContent::Thinking(t) => Self::Thinking(t.into()),
1386            latest::llm_provider::MessageContent::RedactedThinking(s) => Self::RedactedThinking(s),
1387        }
1388    }
1389}
1390
1391impl From<latest::llm_provider::ImageData> for llm_provider::ImageData {
1392    fn from(value: latest::llm_provider::ImageData) -> Self {
1393        Self {
1394            source: value.source,
1395            width: value.width,
1396            height: value.height,
1397        }
1398    }
1399}
1400
1401impl From<latest::llm_provider::ToolUse> for llm_provider::ToolUse {
1402    fn from(value: latest::llm_provider::ToolUse) -> Self {
1403        Self {
1404            id: value.id,
1405            name: value.name,
1406            input: value.input,
1407            thought_signature: value.thought_signature,
1408        }
1409    }
1410}
1411
1412impl From<latest::llm_provider::ToolResult> for llm_provider::ToolResult {
1413    fn from(value: latest::llm_provider::ToolResult) -> Self {
1414        Self {
1415            tool_use_id: value.tool_use_id,
1416            tool_name: value.tool_name,
1417            is_error: value.is_error,
1418            content: value.content.into(),
1419        }
1420    }
1421}
1422
1423impl From<latest::llm_provider::ToolResultContent> for llm_provider::ToolResultContent {
1424    fn from(value: latest::llm_provider::ToolResultContent) -> Self {
1425        match value {
1426            latest::llm_provider::ToolResultContent::Text(s) => Self::Text(s),
1427            latest::llm_provider::ToolResultContent::Image(i) => Self::Image(i.into()),
1428        }
1429    }
1430}
1431
1432impl From<latest::llm_provider::ThinkingContent> for llm_provider::ThinkingContent {
1433    fn from(value: latest::llm_provider::ThinkingContent) -> Self {
1434        Self {
1435            text: value.text,
1436            signature: value.signature,
1437        }
1438    }
1439}
1440
1441impl From<latest::llm_provider::ToolDefinition> for llm_provider::ToolDefinition {
1442    fn from(value: latest::llm_provider::ToolDefinition) -> Self {
1443        Self {
1444            name: value.name,
1445            description: value.description,
1446            input_schema: value.input_schema,
1447        }
1448    }
1449}
1450
1451impl From<latest::llm_provider::ToolChoice> for llm_provider::ToolChoice {
1452    fn from(value: latest::llm_provider::ToolChoice) -> Self {
1453        match value {
1454            latest::llm_provider::ToolChoice::Auto => Self::Auto,
1455            latest::llm_provider::ToolChoice::Any => Self::Any,
1456            latest::llm_provider::ToolChoice::None => Self::None,
1457        }
1458    }
1459}
1460
1461// =============================================================================
1462// Command Type Conversions (v0.7.0 -> latest/v0.8.0)
1463// =============================================================================
1464
1465impl From<Command> for latest::Command {
1466    fn from(value: Command) -> Self {
1467        Self {
1468            command: value.command,
1469            args: value.args,
1470            env: value.env,
1471        }
1472    }
1473}
1474
1475// =============================================================================
1476// LSP Type Conversions (latest/v0.8.0 -> v0.7.0)
1477// =============================================================================
1478
1479impl From<latest::lsp::Completion> for lsp::Completion {
1480    fn from(value: latest::lsp::Completion) -> Self {
1481        Self {
1482            label: value.label,
1483            label_details: value.label_details.map(Into::into),
1484            detail: value.detail,
1485            kind: value.kind.map(Into::into),
1486            insert_text_format: value.insert_text_format.map(Into::into),
1487        }
1488    }
1489}
1490
1491impl From<latest::lsp::CompletionLabelDetails> for lsp::CompletionLabelDetails {
1492    fn from(value: latest::lsp::CompletionLabelDetails) -> Self {
1493        Self {
1494            detail: value.detail,
1495            description: value.description,
1496        }
1497    }
1498}
1499
1500impl From<latest::lsp::CompletionKind> for lsp::CompletionKind {
1501    fn from(value: latest::lsp::CompletionKind) -> Self {
1502        match value {
1503            latest::lsp::CompletionKind::Text => Self::Text,
1504            latest::lsp::CompletionKind::Method => Self::Method,
1505            latest::lsp::CompletionKind::Function => Self::Function,
1506            latest::lsp::CompletionKind::Constructor => Self::Constructor,
1507            latest::lsp::CompletionKind::Field => Self::Field,
1508            latest::lsp::CompletionKind::Variable => Self::Variable,
1509            latest::lsp::CompletionKind::Class => Self::Class,
1510            latest::lsp::CompletionKind::Interface => Self::Interface,
1511            latest::lsp::CompletionKind::Module => Self::Module,
1512            latest::lsp::CompletionKind::Property => Self::Property,
1513            latest::lsp::CompletionKind::Unit => Self::Unit,
1514            latest::lsp::CompletionKind::Value => Self::Value,
1515            latest::lsp::CompletionKind::Enum => Self::Enum,
1516            latest::lsp::CompletionKind::Keyword => Self::Keyword,
1517            latest::lsp::CompletionKind::Snippet => Self::Snippet,
1518            latest::lsp::CompletionKind::Color => Self::Color,
1519            latest::lsp::CompletionKind::File => Self::File,
1520            latest::lsp::CompletionKind::Reference => Self::Reference,
1521            latest::lsp::CompletionKind::Folder => Self::Folder,
1522            latest::lsp::CompletionKind::EnumMember => Self::EnumMember,
1523            latest::lsp::CompletionKind::Constant => Self::Constant,
1524            latest::lsp::CompletionKind::Struct => Self::Struct,
1525            latest::lsp::CompletionKind::Event => Self::Event,
1526            latest::lsp::CompletionKind::Operator => Self::Operator,
1527            latest::lsp::CompletionKind::TypeParameter => Self::TypeParameter,
1528            latest::lsp::CompletionKind::Other(n) => Self::Other(n),
1529        }
1530    }
1531}
1532
1533impl From<latest::lsp::InsertTextFormat> for lsp::InsertTextFormat {
1534    fn from(value: latest::lsp::InsertTextFormat) -> Self {
1535        match value {
1536            latest::lsp::InsertTextFormat::PlainText => Self::PlainText,
1537            latest::lsp::InsertTextFormat::Snippet => Self::Snippet,
1538            latest::lsp::InsertTextFormat::Other(n) => Self::Other(n),
1539        }
1540    }
1541}
1542
1543impl From<latest::lsp::Symbol> for lsp::Symbol {
1544    fn from(value: latest::lsp::Symbol) -> Self {
1545        Self {
1546            kind: value.kind.into(),
1547            name: value.name,
1548        }
1549    }
1550}
1551
1552impl From<latest::lsp::SymbolKind> for lsp::SymbolKind {
1553    fn from(value: latest::lsp::SymbolKind) -> Self {
1554        match value {
1555            latest::lsp::SymbolKind::File => Self::File,
1556            latest::lsp::SymbolKind::Module => Self::Module,
1557            latest::lsp::SymbolKind::Namespace => Self::Namespace,
1558            latest::lsp::SymbolKind::Package => Self::Package,
1559            latest::lsp::SymbolKind::Class => Self::Class,
1560            latest::lsp::SymbolKind::Method => Self::Method,
1561            latest::lsp::SymbolKind::Property => Self::Property,
1562            latest::lsp::SymbolKind::Field => Self::Field,
1563            latest::lsp::SymbolKind::Constructor => Self::Constructor,
1564            latest::lsp::SymbolKind::Enum => Self::Enum,
1565            latest::lsp::SymbolKind::Interface => Self::Interface,
1566            latest::lsp::SymbolKind::Function => Self::Function,
1567            latest::lsp::SymbolKind::Variable => Self::Variable,
1568            latest::lsp::SymbolKind::Constant => Self::Constant,
1569            latest::lsp::SymbolKind::String => Self::String,
1570            latest::lsp::SymbolKind::Number => Self::Number,
1571            latest::lsp::SymbolKind::Boolean => Self::Boolean,
1572            latest::lsp::SymbolKind::Array => Self::Array,
1573            latest::lsp::SymbolKind::Object => Self::Object,
1574            latest::lsp::SymbolKind::Key => Self::Key,
1575            latest::lsp::SymbolKind::Null => Self::Null,
1576            latest::lsp::SymbolKind::EnumMember => Self::EnumMember,
1577            latest::lsp::SymbolKind::Struct => Self::Struct,
1578            latest::lsp::SymbolKind::Event => Self::Event,
1579            latest::lsp::SymbolKind::Operator => Self::Operator,
1580            latest::lsp::SymbolKind::TypeParameter => Self::TypeParameter,
1581            latest::lsp::SymbolKind::Other(n) => Self::Other(n),
1582        }
1583    }
1584}
1585
1586// =============================================================================
1587// CodeLabel Type Conversions (v0.7.0 -> latest/v0.8.0)
1588// =============================================================================
1589
1590impl From<CodeLabel> for latest::CodeLabel {
1591    fn from(value: CodeLabel) -> Self {
1592        Self {
1593            code: value.code,
1594            spans: value.spans.into_iter().map(Into::into).collect(),
1595            filter_range: value.filter_range.into(),
1596        }
1597    }
1598}
1599
1600impl From<CodeLabelSpan> for latest::CodeLabelSpan {
1601    fn from(value: CodeLabelSpan) -> Self {
1602        match value {
1603            CodeLabelSpan::CodeRange(r) => Self::CodeRange(r.into()),
1604            CodeLabelSpan::Literal(l) => Self::Literal(l.into()),
1605        }
1606    }
1607}
1608
1609impl From<CodeLabelSpanLiteral> for latest::CodeLabelSpanLiteral {
1610    fn from(value: CodeLabelSpanLiteral) -> Self {
1611        Self {
1612            text: value.text,
1613            highlight_name: value.highlight_name,
1614        }
1615    }
1616}
1617
1618impl From<Range> for latest::Range {
1619    fn from(value: Range) -> Self {
1620        Self {
1621            start: value.start,
1622            end: value.end,
1623        }
1624    }
1625}
1626
1627// =============================================================================
1628// SlashCommand Type Conversions (latest/v0.8.0 -> v0.7.0)
1629// =============================================================================
1630
1631impl From<&latest::SlashCommand> for slash_command::SlashCommand {
1632    fn from(value: &latest::SlashCommand) -> Self {
1633        Self {
1634            name: value.name.clone(),
1635            description: value.description.clone(),
1636            tooltip_text: value.tooltip_text.clone(),
1637            requires_argument: value.requires_argument,
1638        }
1639    }
1640}
1641
1642// =============================================================================
1643// SlashCommand Type Conversions (v0.7.0 -> latest/v0.8.0)
1644// =============================================================================
1645
1646impl From<slash_command::SlashCommandArgumentCompletion>
1647    for latest::SlashCommandArgumentCompletion
1648{
1649    fn from(value: slash_command::SlashCommandArgumentCompletion) -> Self {
1650        Self {
1651            label: value.label,
1652            new_text: value.new_text,
1653            run_command: value.run_command,
1654        }
1655    }
1656}
1657
1658impl From<slash_command::SlashCommandOutput> for latest::SlashCommandOutput {
1659    fn from(value: slash_command::SlashCommandOutput) -> Self {
1660        Self {
1661            sections: value.sections.into_iter().map(Into::into).collect(),
1662            text: value.text,
1663        }
1664    }
1665}
1666
1667impl From<SlashCommandOutputSection> for latest::slash_command::SlashCommandOutputSection {
1668    fn from(value: SlashCommandOutputSection) -> Self {
1669        Self {
1670            range: value.range.into(),
1671            label: value.label,
1672        }
1673    }
1674}
1675
1676// =============================================================================
1677// ContextServer Type Conversions (v0.7.0 -> latest/v0.8.0)
1678// =============================================================================
1679
1680impl From<context_server::ContextServerConfiguration>
1681    for latest::context_server::ContextServerConfiguration
1682{
1683    fn from(value: context_server::ContextServerConfiguration) -> Self {
1684        Self {
1685            installation_instructions: value.installation_instructions,
1686            settings_schema: value.settings_schema,
1687            default_settings: value.default_settings,
1688        }
1689    }
1690}
1691
1692// =============================================================================
1693// DAP Type Conversions (v0.7.0 -> latest/v0.8.0)
1694// =============================================================================
1695
1696impl From<dap::DebugAdapterBinary> for latest::dap::DebugAdapterBinary {
1697    fn from(value: dap::DebugAdapterBinary) -> Self {
1698        Self {
1699            command: value.command,
1700            arguments: value.arguments,
1701            envs: value.envs,
1702            cwd: value.cwd,
1703            connection: value.connection.map(|c| latest::dap::TcpArguments {
1704                host: c.host,
1705                port: c.port,
1706                timeout: c.timeout,
1707            }),
1708            request_args: latest::dap::StartDebuggingRequestArguments {
1709                configuration: value.request_args.configuration,
1710                request: match value.request_args.request {
1711                    dap::StartDebuggingRequestArgumentsRequest::Launch => {
1712                        latest::dap::StartDebuggingRequestArgumentsRequest::Launch
1713                    }
1714                    dap::StartDebuggingRequestArgumentsRequest::Attach => {
1715                        latest::dap::StartDebuggingRequestArgumentsRequest::Attach
1716                    }
1717                },
1718            },
1719        }
1720    }
1721}
1722
1723impl From<dap::StartDebuggingRequestArgumentsRequest>
1724    for latest::dap::StartDebuggingRequestArgumentsRequest
1725{
1726    fn from(value: dap::StartDebuggingRequestArgumentsRequest) -> Self {
1727        match value {
1728            dap::StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
1729            dap::StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
1730        }
1731    }
1732}
1733
1734impl From<dap::DebugScenario> for latest::dap::DebugScenario {
1735    fn from(value: dap::DebugScenario) -> Self {
1736        Self {
1737            adapter: value.adapter,
1738            label: value.label,
1739            build: value.build.map(|b| match b {
1740                dap::BuildTaskDefinition::ByName(name) => {
1741                    latest::dap::BuildTaskDefinition::ByName(name)
1742                }
1743                dap::BuildTaskDefinition::Template(t) => {
1744                    latest::dap::BuildTaskDefinition::Template(
1745                        latest::dap::BuildTaskDefinitionTemplatePayload {
1746                            locator_name: t.locator_name,
1747                            template: latest::dap::BuildTaskTemplate {
1748                                label: t.template.label,
1749                                command: t.template.command,
1750                                args: t.template.args,
1751                                env: t.template.env,
1752                                cwd: t.template.cwd,
1753                            },
1754                        },
1755                    )
1756                }
1757            }),
1758            config: value.config,
1759            tcp_connection: value
1760                .tcp_connection
1761                .map(|t| latest::dap::TcpArgumentsTemplate {
1762                    host: t.host,
1763                    port: t.port,
1764                    timeout: t.timeout,
1765                }),
1766        }
1767    }
1768}
1769
1770impl From<dap::DebugRequest> for latest::dap::DebugRequest {
1771    fn from(value: dap::DebugRequest) -> Self {
1772        match value {
1773            dap::DebugRequest::Attach(a) => Self::Attach(latest::dap::AttachRequest {
1774                process_id: a.process_id,
1775            }),
1776            dap::DebugRequest::Launch(l) => Self::Launch(latest::dap::LaunchRequest {
1777                program: l.program,
1778                cwd: l.cwd,
1779                args: l.args,
1780                envs: l.envs,
1781            }),
1782        }
1783    }
1784}