From 73421006d5f0826ebe40f876e9a92a38929d1931 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 6 Aug 2025 08:40:17 -0400 Subject: [PATCH 01/37] v0.199.x preview --- crates/zed/RELEASE_CHANNEL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zed/RELEASE_CHANNEL b/crates/zed/RELEASE_CHANNEL index 38f8e886e1..4de2f126df 100644 --- a/crates/zed/RELEASE_CHANNEL +++ b/crates/zed/RELEASE_CHANNEL @@ -1 +1 @@ -dev +preview \ No newline at end of file From 20efc2333ecec888463320baca45acf627f97d5a Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 7 Aug 2025 20:58:33 +0300 Subject: [PATCH 02/37] Actually update remote collab capabilities (#35809) Follow-up of https://github.com/zed-industries/zed/pull/35682 Release Notes: - N/A --- crates/collab/src/db/queries/projects.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 31635575a8..82f74d910b 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -699,7 +699,10 @@ impl Database { language_server::Column::ProjectId, language_server::Column::Id, ]) - .update_column(language_server::Column::Name) + .update_columns([ + language_server::Column::Name, + language_server::Column::Capabilities, + ]) .to_owned(), ) .exec(&*tx) From 40642aa3ac8a8265171d848687adb03bb527e5ee Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 7 Aug 2025 14:18:15 -0400 Subject: [PATCH 03/37] Add GPT-5 support through OpenAI API --- .../language_models/src/provider/open_ai.rs | 5 +++- crates/open_ai/src/open_ai.rs | 26 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 5185e979b7..04d8fd9f2b 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -673,7 +673,10 @@ pub fn count_open_ai_tokens( | Model::O1 | Model::O3 | Model::O3Mini - | Model::O4Mini => tiktoken_rs::num_tokens_from_messages(model.id(), &messages), + | Model::O4Mini + | Model::Five + | Model::FiveMini + | Model::FiveNano => tiktoken_rs::num_tokens_from_messages(model.id(), &messages), } .map(|tokens| tokens as u64) }) diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index 12a5cf52d2..2661ab936e 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -74,6 +74,12 @@ pub enum Model { O3, #[serde(rename = "o4-mini")] O4Mini, + #[serde(rename = "gpt-5")] + Five, + #[serde(rename = "gpt-5-mini")] + FiveMini, + #[serde(rename = "gpt-5-nano")] + FiveNano, #[serde(rename = "custom")] Custom { @@ -105,6 +111,9 @@ impl Model { "o3-mini" => Ok(Self::O3Mini), "o3" => Ok(Self::O3), "o4-mini" => Ok(Self::O4Mini), + "gpt-5" => Ok(Self::Five), + "gpt-5-mini" => Ok(Self::FiveMini), + "gpt-5-nano" => Ok(Self::FiveNano), invalid_id => anyhow::bail!("invalid model id '{invalid_id}'"), } } @@ -123,6 +132,9 @@ impl Model { Self::O3Mini => "o3-mini", Self::O3 => "o3", Self::O4Mini => "o4-mini", + Self::Five => "gpt-5", + Self::FiveMini => "gpt-5-mini", + Self::FiveNano => "gpt-5-nano", Self::Custom { name, .. } => name, } } @@ -141,6 +153,9 @@ impl Model { Self::O3Mini => "o3-mini", Self::O3 => "o3", Self::O4Mini => "o4-mini", + Self::Five => "gpt-5", + Self::FiveMini => "gpt-5-mini", + Self::FiveNano => "gpt-5-nano", Self::Custom { name, display_name, .. } => display_name.as_ref().unwrap_or(name), @@ -161,6 +176,9 @@ impl Model { Self::O3Mini => 200_000, Self::O3 => 200_000, Self::O4Mini => 200_000, + Self::Five => 256_000, + Self::FiveMini => 256_000, + Self::FiveNano => 256_000, Self::Custom { max_tokens, .. } => *max_tokens, } } @@ -182,6 +200,9 @@ impl Model { Self::O3Mini => Some(100_000), Self::O3 => Some(100_000), Self::O4Mini => Some(100_000), + Self::Five => Some(65_536), + Self::FiveMini => Some(65_536), + Self::FiveNano => Some(65_536), } } @@ -197,7 +218,10 @@ impl Model { | Self::FourOmniMini | Self::FourPointOne | Self::FourPointOneMini - | Self::FourPointOneNano => true, + | Self::FourPointOneNano + | Self::Five + | Self::FiveMini + | Self::FiveNano => true, Self::O1 | Self::O3 | Self::O3Mini | Self::O4Mini | Model::Custom { .. } => false, } } From 52fad99449d3267680f60cf1683ab85e58e0cf73 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 7 Aug 2025 14:27:16 -0400 Subject: [PATCH 04/37] Update GPT-5 input/output token counts --- crates/open_ai/src/open_ai.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index 2661ab936e..4697d71ed3 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -176,9 +176,9 @@ impl Model { Self::O3Mini => 200_000, Self::O3 => 200_000, Self::O4Mini => 200_000, - Self::Five => 256_000, - Self::FiveMini => 256_000, - Self::FiveNano => 256_000, + Self::Five => 272_000, + Self::FiveMini => 272_000, + Self::FiveNano => 272_000, Self::Custom { max_tokens, .. } => *max_tokens, } } @@ -200,9 +200,9 @@ impl Model { Self::O3Mini => Some(100_000), Self::O3 => Some(100_000), Self::O4Mini => Some(100_000), - Self::Five => Some(65_536), - Self::FiveMini => Some(65_536), - Self::FiveNano => Some(65_536), + Self::Five => Some(128_000), + Self::FiveMini => Some(128_000), + Self::FiveNano => Some(128_000), } } From e2be6611dbf69e86fde684de16784b3df523aca3 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 7 Aug 2025 14:39:58 -0400 Subject: [PATCH 05/37] Use gpt-4o tokenizer for gpt-5 for now --- crates/language_models/src/provider/open_ai.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 04d8fd9f2b..ee74562687 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -673,10 +673,11 @@ pub fn count_open_ai_tokens( | Model::O1 | Model::O3 | Model::O3Mini - | Model::O4Mini - | Model::Five - | Model::FiveMini - | Model::FiveNano => tiktoken_rs::num_tokens_from_messages(model.id(), &messages), + | Model::O4Mini => tiktoken_rs::num_tokens_from_messages(model.id(), &messages), + // GPT-5 models don't have tiktoken support yet; fall back on gpt-4o tokenizer + Model::Five | Model::FiveMini | Model::FiveNano => { + tiktoken_rs::num_tokens_from_messages("gpt-4o", &messages) + } } .map(|tokens| tokens as u64) }) From 526bdf6a03db0f198bcd31858df3324d6c8ec637 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Thu, 7 Aug 2025 15:15:12 -0400 Subject: [PATCH 06/37] zed 0.199.1 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e034212748..3831d6c4d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20418,7 +20418,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.0" +version = "0.199.1" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 5bd6d981fa..e11af59f2a 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.0" +version = "0.199.1" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "] From da8da08bc3e08e0abe61b83013a4125d4e546383 Mon Sep 17 00:00:00 2001 From: Peter Tripp Date: Thu, 7 Aug 2025 16:59:11 -0400 Subject: [PATCH 07/37] ci: Switch from BuildJet to GitHub runners (#35826) In response to an ongoing BuildJet outage, consider migrating CI to GitHub hosted runners. Also includes revert of (causing flaky tests): - https://github.com/zed-industries/zed/pull/35741 Downsides: - Cost (2x) - Force migration to Ubuntu 22.04 from 20.04 will bump our glibc minimum from 2.31 to 2.35. Which would break RHEL 9.x (glibc 2.34), Ubuntu 20.04 (EOL) and derivatives. Release Notes: - N/A --- .github/actionlint.yml | 5 +++++ .github/actions/build_docs/action.yml | 2 +- .github/workflows/bump_patch_version.yml | 2 +- .github/workflows/ci.yml | 19 ++++++++++--------- .github/workflows/deploy_cloudflare.yml | 2 +- .github/workflows/deploy_collab.yml | 4 ++-- .github/workflows/eval.yml | 4 ++-- .github/workflows/nix.yml | 2 +- .github/workflows/randomized_tests.yml | 2 +- .github/workflows/release_nightly.yml | 5 +++-- .github/workflows/unit_evals.yml | 4 ++-- 11 files changed, 29 insertions(+), 22 deletions(-) diff --git a/.github/actionlint.yml b/.github/actionlint.yml index 6bfbc27705..06b48b9b54 100644 --- a/.github/actionlint.yml +++ b/.github/actionlint.yml @@ -5,6 +5,11 @@ self-hosted-runner: # GitHub-hosted Runners - github-8vcpu-ubuntu-2404 - github-16vcpu-ubuntu-2404 + - github-32vcpu-ubuntu-2404 + - github-8vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 + - github-32vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204-arm - windows-2025-16 - windows-2025-32 - windows-2025-64 diff --git a/.github/actions/build_docs/action.yml b/.github/actions/build_docs/action.yml index a7effad247..d2e62d5b22 100644 --- a/.github/actions/build_docs/action.yml +++ b/.github/actions/build_docs/action.yml @@ -13,7 +13,7 @@ runs: uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2 with: save-if: ${{ github.ref == 'refs/heads/main' }} - cache-provider: "buildjet" + # cache-provider: "buildjet" - name: Install Linux dependencies shell: bash -euxo pipefail {0} diff --git a/.github/workflows/bump_patch_version.yml b/.github/workflows/bump_patch_version.yml index 8a48ff96f1..bc44066ea6 100644 --- a/.github/workflows/bump_patch_version.yml +++ b/.github/workflows/bump_patch_version.yml @@ -16,7 +16,7 @@ jobs: bump_patch_version: if: github.repository_owner == 'zed-industries' runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43d305faae..8f4f4d2b11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,7 +137,7 @@ jobs: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_tests == 'true' runs-on: - - buildjet-8vcpu-ubuntu-2204 + - github-8vcpu-ubuntu-2204 steps: - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -168,7 +168,7 @@ jobs: needs: [job_spec] if: github.repository_owner == 'zed-industries' runs-on: - - buildjet-8vcpu-ubuntu-2204 + - github-8vcpu-ubuntu-2204 steps: - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -221,7 +221,7 @@ jobs: github.repository_owner == 'zed-industries' && (needs.job_spec.outputs.run_tests == 'true' || needs.job_spec.outputs.run_docs == 'true') runs-on: - - buildjet-8vcpu-ubuntu-2204 + - github-8vcpu-ubuntu-2204 steps: - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -328,7 +328,7 @@ jobs: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_tests == 'true' runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" @@ -342,7 +342,7 @@ jobs: uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2 with: save-if: ${{ github.ref == 'refs/heads/main' }} - cache-provider: "buildjet" + # cache-provider: "buildjet" - name: Install Linux dependencies run: ./script/linux @@ -380,7 +380,7 @@ jobs: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_tests == 'true' runs-on: - - buildjet-8vcpu-ubuntu-2204 + - github-8vcpu-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" @@ -394,7 +394,7 @@ jobs: uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2 with: save-if: ${{ github.ref == 'refs/heads/main' }} - cache-provider: "buildjet" + # cache-provider: "buildjet" - name: Install Clang & Mold run: ./script/remote-server && ./script/install-mold 2.34.0 @@ -597,7 +597,8 @@ jobs: timeout-minutes: 60 name: Linux x86_x64 release bundle runs-on: - - buildjet-16vcpu-ubuntu-2004 # ubuntu 20.04 for minimal glibc + - github-16vcpu-ubuntu-2204 + # - buildjet-16vcpu-ubuntu-2004 # ubuntu 20.04 for minimal glibc if: | startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') @@ -650,7 +651,7 @@ jobs: timeout-minutes: 60 name: Linux arm64 release bundle runs-on: - - buildjet-32vcpu-ubuntu-2204-arm + - github-16vcpu-ubuntu-2204-arm if: | startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') diff --git a/.github/workflows/deploy_cloudflare.yml b/.github/workflows/deploy_cloudflare.yml index fe443d493e..3a294fdc17 100644 --- a/.github/workflows/deploy_cloudflare.yml +++ b/.github/workflows/deploy_cloudflare.yml @@ -9,7 +9,7 @@ jobs: deploy-docs: name: Deploy Docs if: github.repository_owner == 'zed-industries' - runs-on: buildjet-16vcpu-ubuntu-2204 + runs-on: github-16vcpu-ubuntu-2204 steps: - name: Checkout repo diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml index f7348a1069..2e65b62d81 100644 --- a/.github/workflows/deploy_collab.yml +++ b/.github/workflows/deploy_collab.yml @@ -61,7 +61,7 @@ jobs: - style - tests runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Install doctl uses: digitalocean/action-doctl@v2 @@ -94,7 +94,7 @@ jobs: needs: - publish runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Checkout repo diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index 2ad302a602..196e00519b 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -32,7 +32,7 @@ jobs: github.repository_owner == 'zed-industries' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-eval')) runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" @@ -46,7 +46,7 @@ jobs: uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2 with: save-if: ${{ github.ref == 'refs/heads/main' }} - cache-provider: "buildjet" + # cache-provider: "buildjet" - name: Install Linux dependencies run: ./script/linux diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 6c3a97c163..913d6cfe9f 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -20,7 +20,7 @@ jobs: matrix: system: - os: x86 Linux - runner: buildjet-16vcpu-ubuntu-2204 + runner: github-16vcpu-ubuntu-2204 install_nix: true - os: arm Mac runner: [macOS, ARM64, test] diff --git a/.github/workflows/randomized_tests.yml b/.github/workflows/randomized_tests.yml index db4d44318e..3a7b476ba0 100644 --- a/.github/workflows/randomized_tests.yml +++ b/.github/workflows/randomized_tests.yml @@ -20,7 +20,7 @@ jobs: name: Run randomized tests if: github.repository_owner == 'zed-industries' runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Install Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml index c847149984..c5be72fca2 100644 --- a/.github/workflows/release_nightly.yml +++ b/.github/workflows/release_nightly.yml @@ -128,7 +128,8 @@ jobs: name: Create a Linux *.tar.gz bundle for x86 if: github.repository_owner == 'zed-industries' runs-on: - - buildjet-16vcpu-ubuntu-2004 + - github-16vcpu-ubuntu-2204 + # - buildjet-16vcpu-ubuntu-2004 needs: tests steps: - name: Checkout repo @@ -168,7 +169,7 @@ jobs: name: Create a Linux *.tar.gz bundle for ARM if: github.repository_owner == 'zed-industries' runs-on: - - buildjet-32vcpu-ubuntu-2204-arm + - github-16vcpu-ubuntu-2204-arm needs: tests steps: - name: Checkout repo diff --git a/.github/workflows/unit_evals.yml b/.github/workflows/unit_evals.yml index cb4e39d151..225fca558f 100644 --- a/.github/workflows/unit_evals.yml +++ b/.github/workflows/unit_evals.yml @@ -23,7 +23,7 @@ jobs: timeout-minutes: 60 name: Run unit evals runs-on: - - buildjet-16vcpu-ubuntu-2204 + - github-16vcpu-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" @@ -37,7 +37,7 @@ jobs: uses: swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2 with: save-if: ${{ github.ref == 'refs/heads/main' }} - cache-provider: "buildjet" + # cache-provider: "buildjet" - name: Install Linux dependencies run: ./script/linux From 6dbd498cea7623ef06366ecfaba8e5df7411bb5a Mon Sep 17 00:00:00 2001 From: Peter Tripp Date: Thu, 7 Aug 2025 19:16:25 -0400 Subject: [PATCH 08/37] ci: Switch to Namespace (#35835) Follow-up to: - https://github.com/zed-industries/zed/pull/35826 Release Notes: - N/A --- .github/actionlint.yml | 27 ++++++++++-------------- .github/workflows/bump_patch_version.yml | 2 +- .github/workflows/ci.yml | 15 ++++++------- .github/workflows/deploy_cloudflare.yml | 2 +- .github/workflows/deploy_collab.yml | 4 ++-- .github/workflows/eval.yml | 2 +- .github/workflows/nix.yml | 2 +- .github/workflows/randomized_tests.yml | 2 +- .github/workflows/release_nightly.yml | 5 ++--- .github/workflows/unit_evals.yml | 2 +- 10 files changed, 28 insertions(+), 35 deletions(-) diff --git a/.github/actionlint.yml b/.github/actionlint.yml index 06b48b9b54..ad09545902 100644 --- a/.github/actionlint.yml +++ b/.github/actionlint.yml @@ -13,22 +13,17 @@ self-hosted-runner: - windows-2025-16 - windows-2025-32 - windows-2025-64 - # Buildjet Ubuntu 20.04 - AMD x86_64 - - buildjet-2vcpu-ubuntu-2004 - - buildjet-4vcpu-ubuntu-2004 - - buildjet-8vcpu-ubuntu-2004 - - buildjet-16vcpu-ubuntu-2004 - - buildjet-32vcpu-ubuntu-2004 - # Buildjet Ubuntu 22.04 - AMD x86_64 - - buildjet-2vcpu-ubuntu-2204 - - buildjet-4vcpu-ubuntu-2204 - - buildjet-8vcpu-ubuntu-2204 - - buildjet-16vcpu-ubuntu-2204 - - buildjet-32vcpu-ubuntu-2204 - # Buildjet Ubuntu 22.04 - Graviton aarch64 - - buildjet-8vcpu-ubuntu-2204-arm - - buildjet-16vcpu-ubuntu-2204-arm - - buildjet-32vcpu-ubuntu-2204-arm + # Namespace Ubuntu 20.04 (Release builds) + - namespace-profile-16x32-ubuntu-2004 + - namespace-profile-32x64-ubuntu-2004 + - namespace-profile-16x32-ubuntu-2004-arm + - namespace-profile-32x64-ubuntu-2004-arm + # Namespace Ubuntu 22.04 (Everything else) + - namespace-profile-2x4-ubuntu-2204 + - namespace-profile-4x8-ubuntu-2204 + - namespace-profile-8x16-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 + - namespace-profile-32x64-ubuntu-2204 # Self Hosted Runners - self-mini-macos - self-32vcpu-windows-2022 diff --git a/.github/workflows/bump_patch_version.yml b/.github/workflows/bump_patch_version.yml index bc44066ea6..bfaf7a271b 100644 --- a/.github/workflows/bump_patch_version.yml +++ b/.github/workflows/bump_patch_version.yml @@ -16,7 +16,7 @@ jobs: bump_patch_version: if: github.repository_owner == 'zed-industries' runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f4f4d2b11..84907351fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,7 +137,7 @@ jobs: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_tests == 'true' runs-on: - - github-8vcpu-ubuntu-2204 + - namespace-profile-8x16-ubuntu-2204 steps: - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -168,7 +168,7 @@ jobs: needs: [job_spec] if: github.repository_owner == 'zed-industries' runs-on: - - github-8vcpu-ubuntu-2204 + - namespace-profile-4x8-ubuntu-2204 steps: - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -221,7 +221,7 @@ jobs: github.repository_owner == 'zed-industries' && (needs.job_spec.outputs.run_tests == 'true' || needs.job_spec.outputs.run_docs == 'true') runs-on: - - github-8vcpu-ubuntu-2204 + - namespace-profile-8x16-ubuntu-2204 steps: - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -328,7 +328,7 @@ jobs: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_tests == 'true' runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" @@ -380,7 +380,7 @@ jobs: github.repository_owner == 'zed-industries' && needs.job_spec.outputs.run_tests == 'true' runs-on: - - github-8vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" @@ -597,8 +597,7 @@ jobs: timeout-minutes: 60 name: Linux x86_x64 release bundle runs-on: - - github-16vcpu-ubuntu-2204 - # - buildjet-16vcpu-ubuntu-2004 # ubuntu 20.04 for minimal glibc + - namespace-profile-16x32-ubuntu-2004 # ubuntu 20.04 for minimal glibc if: | startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') @@ -651,7 +650,7 @@ jobs: timeout-minutes: 60 name: Linux arm64 release bundle runs-on: - - github-16vcpu-ubuntu-2204-arm + - namespace-profile-32x64-ubuntu-2004-arm # ubuntu 20.04 for minimal glibc if: | startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') diff --git a/.github/workflows/deploy_cloudflare.yml b/.github/workflows/deploy_cloudflare.yml index 3a294fdc17..df35d44ca9 100644 --- a/.github/workflows/deploy_cloudflare.yml +++ b/.github/workflows/deploy_cloudflare.yml @@ -9,7 +9,7 @@ jobs: deploy-docs: name: Deploy Docs if: github.repository_owner == 'zed-industries' - runs-on: github-16vcpu-ubuntu-2204 + runs-on: namespace-profile-16x32-ubuntu-2204 steps: - name: Checkout repo diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml index 2e65b62d81..8f56cac5d9 100644 --- a/.github/workflows/deploy_collab.yml +++ b/.github/workflows/deploy_collab.yml @@ -61,7 +61,7 @@ jobs: - style - tests runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Install doctl uses: digitalocean/action-doctl@v2 @@ -94,7 +94,7 @@ jobs: needs: - publish runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Checkout repo diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index 196e00519b..b5da9e7b7c 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -32,7 +32,7 @@ jobs: github.repository_owner == 'zed-industries' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'run-eval')) runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 913d6cfe9f..e682ce5890 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -20,7 +20,7 @@ jobs: matrix: system: - os: x86 Linux - runner: github-16vcpu-ubuntu-2204 + runner: namespace-profile-16x32-ubuntu-2204 install_nix: true - os: arm Mac runner: [macOS, ARM64, test] diff --git a/.github/workflows/randomized_tests.yml b/.github/workflows/randomized_tests.yml index 3a7b476ba0..de96c3df78 100644 --- a/.github/workflows/randomized_tests.yml +++ b/.github/workflows/randomized_tests.yml @@ -20,7 +20,7 @@ jobs: name: Run randomized tests if: github.repository_owner == 'zed-industries' runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Install Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml index c5be72fca2..b3500a085b 100644 --- a/.github/workflows/release_nightly.yml +++ b/.github/workflows/release_nightly.yml @@ -128,8 +128,7 @@ jobs: name: Create a Linux *.tar.gz bundle for x86 if: github.repository_owner == 'zed-industries' runs-on: - - github-16vcpu-ubuntu-2204 - # - buildjet-16vcpu-ubuntu-2004 + - namespace-profile-16x32-ubuntu-2004 # ubuntu 20.04 for minimal glibc needs: tests steps: - name: Checkout repo @@ -169,7 +168,7 @@ jobs: name: Create a Linux *.tar.gz bundle for ARM if: github.repository_owner == 'zed-industries' runs-on: - - github-16vcpu-ubuntu-2204-arm + - namespace-profile-32x64-ubuntu-2004-arm # ubuntu 20.04 for minimal glibc needs: tests steps: - name: Checkout repo diff --git a/.github/workflows/unit_evals.yml b/.github/workflows/unit_evals.yml index 225fca558f..2e03fb028f 100644 --- a/.github/workflows/unit_evals.yml +++ b/.github/workflows/unit_evals.yml @@ -23,7 +23,7 @@ jobs: timeout-minutes: 60 name: Run unit evals runs-on: - - github-16vcpu-ubuntu-2204 + - namespace-profile-16x32-ubuntu-2204 steps: - name: Add Rust to the PATH run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" From f31762f19b78681496846b138f3341f5e42d52bd Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:27:48 +0200 Subject: [PATCH 09/37] lsp: Correctly serialize errors for LSP requests + improve handling of unrecognized methods (#35738) We used to not respond at all to requests that we didn't have a handler for, which is yuck. It may have left the language server waiting for the response for no good reason. The other (worse) finding is that we did not have a full definition of an Error type for LSP, which made it so that a spec-compliant language server would fail to deserialize our response (with an error). This then could lead to all sorts of funkiness, including hangs and crashes on the language server's part. Co-authored-by: Lukas Co-authored-by: Remco Smits Co-authored-by: Anthony Eid Closes #ISSUE Release Notes: - Improved reporting of errors to language servers, which should improve the stability of LSPs ran by Zed. --------- Co-authored-by: Lukas Co-authored-by: Remco Smits Co-authored-by: Anthony Eid --- crates/lsp/src/input_handler.rs | 11 +++--- crates/lsp/src/lsp.rs | 70 ++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/crates/lsp/src/input_handler.rs b/crates/lsp/src/input_handler.rs index db3f1190fc..001ebf1fc9 100644 --- a/crates/lsp/src/input_handler.rs +++ b/crates/lsp/src/input_handler.rs @@ -13,14 +13,15 @@ use parking_lot::Mutex; use smol::io::BufReader; use crate::{ - AnyNotification, AnyResponse, CONTENT_LEN_HEADER, IoHandler, IoKind, RequestId, ResponseHandler, + AnyResponse, CONTENT_LEN_HEADER, IoHandler, IoKind, NotificationOrRequest, RequestId, + ResponseHandler, }; const HEADER_DELIMITER: &[u8; 4] = b"\r\n\r\n"; /// Handler for stdout of language server. pub struct LspStdoutHandler { pub(super) loop_handle: Task>, - pub(super) notifications_channel: UnboundedReceiver, + pub(super) incoming_messages: UnboundedReceiver, } async fn read_headers(reader: &mut BufReader, buffer: &mut Vec) -> Result<()> @@ -54,13 +55,13 @@ impl LspStdoutHandler { let loop_handle = cx.spawn(Self::handler(stdout, tx, response_handlers, io_handlers)); Self { loop_handle, - notifications_channel, + incoming_messages: notifications_channel, } } async fn handler( stdout: Input, - notifications_sender: UnboundedSender, + notifications_sender: UnboundedSender, response_handlers: Arc>>>, io_handlers: Arc>>, ) -> anyhow::Result<()> @@ -96,7 +97,7 @@ impl LspStdoutHandler { } } - if let Ok(msg) = serde_json::from_slice::(&buffer) { + if let Ok(msg) = serde_json::from_slice::(&buffer) { notifications_sender.unbounded_send(msg)?; } else if let Ok(AnyResponse { id, error, result, .. diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index b9701a83d2..3f45d2e6fc 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -242,7 +242,7 @@ struct Notification<'a, T> { /// Language server RPC notification message before it is deserialized into a concrete type. #[derive(Debug, Clone, Deserialize)] -struct AnyNotification { +struct NotificationOrRequest { #[serde(default)] id: Option, method: String, @@ -252,7 +252,10 @@ struct AnyNotification { #[derive(Debug, Serialize, Deserialize)] struct Error { + code: i64, message: String, + #[serde(default)] + data: Option, } pub trait LspRequestFuture: Future> { @@ -364,6 +367,7 @@ impl LanguageServer { notification.method, serde_json::to_string_pretty(¬ification.params).unwrap(), ); + false }, ); @@ -389,7 +393,7 @@ impl LanguageServer { Stdin: AsyncWrite + Unpin + Send + 'static, Stdout: AsyncRead + Unpin + Send + 'static, Stderr: AsyncRead + Unpin + Send + 'static, - F: FnMut(AnyNotification) + 'static + Send + Sync + Clone, + F: Fn(&NotificationOrRequest) -> bool + 'static + Send + Sync + Clone, { let (outbound_tx, outbound_rx) = channel::unbounded::(); let (output_done_tx, output_done_rx) = barrier::channel(); @@ -400,14 +404,34 @@ impl LanguageServer { let io_handlers = Arc::new(Mutex::new(HashMap::default())); let stdout_input_task = cx.spawn({ - let on_unhandled_notification = on_unhandled_notification.clone(); + let unhandled_notification_wrapper = { + let response_channel = outbound_tx.clone(); + async move |msg: NotificationOrRequest| { + let did_handle = on_unhandled_notification(&msg); + if !did_handle && let Some(message_id) = msg.id { + let response = AnyResponse { + jsonrpc: JSON_RPC_VERSION, + id: message_id, + error: Some(Error { + code: -32601, + message: format!("Unrecognized method `{}`", msg.method), + data: None, + }), + result: None, + }; + if let Ok(response) = serde_json::to_string(&response) { + response_channel.send(response).await.ok(); + } + } + } + }; let notification_handlers = notification_handlers.clone(); let response_handlers = response_handlers.clone(); let io_handlers = io_handlers.clone(); async move |cx| { - Self::handle_input( + Self::handle_incoming_messages( stdout, - on_unhandled_notification, + unhandled_notification_wrapper, notification_handlers, response_handlers, io_handlers, @@ -433,7 +457,7 @@ impl LanguageServer { stdout.or(stderr) }); let output_task = cx.background_spawn({ - Self::handle_output( + Self::handle_outgoing_messages( stdin, outbound_rx, output_done_tx, @@ -479,9 +503,9 @@ impl LanguageServer { self.code_action_kinds.clone() } - async fn handle_input( + async fn handle_incoming_messages( stdout: Stdout, - mut on_unhandled_notification: F, + on_unhandled_notification: impl AsyncFn(NotificationOrRequest) + 'static + Send, notification_handlers: Arc>>, response_handlers: Arc>>>, io_handlers: Arc>>, @@ -489,7 +513,6 @@ impl LanguageServer { ) -> anyhow::Result<()> where Stdout: AsyncRead + Unpin + Send + 'static, - F: FnMut(AnyNotification) + 'static + Send, { use smol::stream::StreamExt; let stdout = BufReader::new(stdout); @@ -506,15 +529,19 @@ impl LanguageServer { cx.background_executor().clone(), ); - while let Some(msg) = input_handler.notifications_channel.next().await { - { + while let Some(msg) = input_handler.incoming_messages.next().await { + let unhandled_message = { let mut notification_handlers = notification_handlers.lock(); if let Some(handler) = notification_handlers.get_mut(msg.method.as_str()) { handler(msg.id, msg.params.unwrap_or(Value::Null), cx); + None } else { - drop(notification_handlers); - on_unhandled_notification(msg); + Some(msg) } + }; + + if let Some(msg) = unhandled_message { + on_unhandled_notification(msg).await; } // Don't starve the main thread when receiving lots of notifications at once. @@ -558,7 +585,7 @@ impl LanguageServer { } } - async fn handle_output( + async fn handle_outgoing_messages( stdin: Stdin, outbound_rx: channel::Receiver, output_done_tx: barrier::Sender, @@ -1036,7 +1063,9 @@ impl LanguageServer { jsonrpc: JSON_RPC_VERSION, id, value: LspResult::Error(Some(Error { + code: lsp_types::error_codes::REQUEST_FAILED, message: error.to_string(), + data: None, })), }, }; @@ -1057,7 +1086,9 @@ impl LanguageServer { id, result: None, error: Some(Error { + code: -32700, // Parse error message: error.to_string(), + data: None, }), }; if let Some(response) = serde_json::to_string(&response).log_err() { @@ -1559,7 +1590,7 @@ impl FakeLanguageServer { root, Some(workspace_folders.clone()), cx, - |_| {}, + |_| false, ); server.process_name = process_name; let fake = FakeLanguageServer { @@ -1582,9 +1613,10 @@ impl FakeLanguageServer { notifications_tx .try_send(( msg.method.to_string(), - msg.params.unwrap_or(Value::Null).to_string(), + msg.params.as_ref().unwrap_or(&Value::Null).to_string(), )) .ok(); + true }, ); server.process_name = name.as_str().into(); @@ -1862,7 +1894,7 @@ mod tests { #[gpui::test] fn test_deserialize_string_digit_id() { let json = r#"{"jsonrpc":"2.0","id":"2","method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/mph/Devel/personal/hello-scala/","section":"metals"}]}}"#; - let notification = serde_json::from_str::(json) + let notification = serde_json::from_str::(json) .expect("message with string id should be parsed"); let expected_id = RequestId::Str("2".to_string()); assert_eq!(notification.id, Some(expected_id)); @@ -1871,7 +1903,7 @@ mod tests { #[gpui::test] fn test_deserialize_string_id() { let json = r#"{"jsonrpc":"2.0","id":"anythingAtAll","method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/mph/Devel/personal/hello-scala/","section":"metals"}]}}"#; - let notification = serde_json::from_str::(json) + let notification = serde_json::from_str::(json) .expect("message with string id should be parsed"); let expected_id = RequestId::Str("anythingAtAll".to_string()); assert_eq!(notification.id, Some(expected_id)); @@ -1880,7 +1912,7 @@ mod tests { #[gpui::test] fn test_deserialize_int_id() { let json = r#"{"jsonrpc":"2.0","id":2,"method":"workspace/configuration","params":{"items":[{"scopeUri":"file:///Users/mph/Devel/personal/hello-scala/","section":"metals"}]}}"#; - let notification = serde_json::from_str::(json) + let notification = serde_json::from_str::(json) .expect("message with string id should be parsed"); let expected_id = RequestId::Int(2); assert_eq!(notification.id, Some(expected_id)); From 4d465099eb2ba2307ebb8905001e7b839a62d33d Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 6 Aug 2025 19:20:04 -0400 Subject: [PATCH 10/37] cloud_api_types: Add types for WebSocket protocol (#35753) This PR adds types for the Cloud WebSocket protocol to the `cloud_api_types` crate. Release Notes: - N/A --- Cargo.lock | 2 ++ Cargo.toml | 1 + crates/cloud_api_types/Cargo.toml | 2 ++ crates/cloud_api_types/src/cloud_api_types.rs | 1 + .../cloud_api_types/src/websocket_protocol.rs | 28 +++++++++++++++++++ 5 files changed, 34 insertions(+) create mode 100644 crates/cloud_api_types/src/websocket_protocol.rs diff --git a/Cargo.lock b/Cargo.lock index 3831d6c4d6..ea40cff81c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3081,7 +3081,9 @@ dependencies = [ name = "cloud_api_types" version = "0.1.0" dependencies = [ + "anyhow", "chrono", + "ciborium", "cloud_llm_client", "pretty_assertions", "serde", diff --git a/Cargo.toml b/Cargo.toml index 7b82fd1910..a60a65fcd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -461,6 +461,7 @@ bytes = "1.0" cargo_metadata = "0.19" cargo_toml = "0.21" chrono = { version = "0.4", features = ["serde"] } +ciborium = "0.2" circular-buffer = "1.0" clap = { version = "4.4", features = ["derive"] } cocoa = "0.26" diff --git a/crates/cloud_api_types/Cargo.toml b/crates/cloud_api_types/Cargo.toml index 868797df3b..28e0a36a44 100644 --- a/crates/cloud_api_types/Cargo.toml +++ b/crates/cloud_api_types/Cargo.toml @@ -12,7 +12,9 @@ workspace = true path = "src/cloud_api_types.rs" [dependencies] +anyhow.workspace = true chrono.workspace = true +ciborium.workspace = true cloud_llm_client.workspace = true serde.workspace = true workspace-hack.workspace = true diff --git a/crates/cloud_api_types/src/cloud_api_types.rs b/crates/cloud_api_types/src/cloud_api_types.rs index b38b38cde1..fa189cd3b5 100644 --- a/crates/cloud_api_types/src/cloud_api_types.rs +++ b/crates/cloud_api_types/src/cloud_api_types.rs @@ -1,4 +1,5 @@ mod timestamp; +pub mod websocket_protocol; use serde::{Deserialize, Serialize}; diff --git a/crates/cloud_api_types/src/websocket_protocol.rs b/crates/cloud_api_types/src/websocket_protocol.rs new file mode 100644 index 0000000000..c90d09e370 --- /dev/null +++ b/crates/cloud_api_types/src/websocket_protocol.rs @@ -0,0 +1,28 @@ +use anyhow::{Context as _, Result}; +use serde::{Deserialize, Serialize}; + +/// The version of the Cloud WebSocket protocol. +pub const PROTOCOL_VERSION: u32 = 0; + +/// The name of the header used to indicate the protocol version in use. +pub const PROTOCOL_VERSION_HEADER_NAME: &str = "x-zed-protocol-version"; + +/// A message from Cloud to the Zed client. +#[derive(Serialize, Deserialize)] +pub enum MessageToClient { + /// The user was updated and should be refreshed. + UserUpdated, +} + +impl MessageToClient { + pub fn serialize(&self) -> Result> { + let mut buffer = Vec::new(); + ciborium::into_writer(self, &mut buffer).context("failed to serialize message")?; + + Ok(buffer) + } + + pub fn deserialize(data: &[u8]) -> Result { + ciborium::from_reader(data).context("failed to deserialize message") + } +} From 00352aa185623974e18a2c250ae6ec5f2d3eeb88 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 6 Aug 2025 21:28:41 -0400 Subject: [PATCH 11/37] Establish WebSocket connection to Cloud (#35734) This PR adds a new WebSocket connection to Cloud. This connection will be used to push down notifications from the server to the client. Release Notes: - N/A --------- Co-authored-by: Marshall Bowers --- Cargo.lock | 48 +++++++++++- Cargo.toml | 3 + crates/client/src/client.rs | 35 +++++++++ crates/cloud_api_client/Cargo.toml | 3 + .../cloud_api_client/src/cloud_api_client.rs | 43 +++++++++++ crates/cloud_api_client/src/websocket.rs | 73 +++++++++++++++++++ .../cloud_api_types/src/websocket_protocol.rs | 2 +- tooling/workspace-hack/Cargo.toml | 24 +++--- 8 files changed, 214 insertions(+), 17 deletions(-) create mode 100644 crates/cloud_api_client/src/websocket.rs diff --git a/Cargo.lock b/Cargo.lock index ea40cff81c..860dcfb2ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1411,7 +1411,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom", + "nom 7.1.3", "num-rational", "v_frame", ] @@ -2785,7 +2785,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -3071,10 +3071,13 @@ dependencies = [ "anyhow", "cloud_api_types", "futures 0.3.31", + "gpui", + "gpui_tokio", "http_client", "parking_lot", "serde_json", "workspace-hack", + "yawc", ] [[package]] @@ -10582,6 +10585,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "noop_proc_macro" version = "0.3.0" @@ -15403,7 +15415,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" dependencies = [ - "nom", + "nom 7.1.3", "unicode_categories", ] @@ -19979,7 +19991,7 @@ dependencies = [ "naga", "nix 0.28.0", "nix 0.29.0", - "nom", + "nom 7.1.3", "num-bigint", "num-bigint-dig", "num-integer", @@ -20314,6 +20326,34 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yawc" +version = "0.2.4" +source = "git+https://github.com/deviant-forks/yawc?rev=1899688f3e69ace4545aceb97b2a13881cf26142#1899688f3e69ace4545aceb97b2a13881cf26142" +dependencies = [ + "base64 0.22.1", + "bytes 1.10.1", + "flate2", + "futures 0.3.31", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "js-sys", + "nom 8.0.0", + "pin-project", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "tokio", + "tokio-rustls 0.26.2", + "tokio-util", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + [[package]] name = "yazi" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index a60a65fcd8..a04d8f6099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -661,6 +661,9 @@ which = "6.0.0" windows-core = "0.61" wit-component = "0.221" workspace-hack = "0.1.0" +# We can switch back to the published version once https://github.com/infinitefield/yawc/pull/16 is merged and a new +# version is released. +yawc = { git = "https://github.com/deviant-forks/yawc", rev = "1899688f3e69ace4545aceb97b2a13881cf26142" } zstd = "0.11" [workspace.dependencies.async-stripe] diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index b4894cddcf..0480ed1c3e 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -14,6 +14,7 @@ use async_tungstenite::tungstenite::{ }; use clock::SystemClock; use cloud_api_client::CloudApiClient; +use cloud_api_client::websocket_protocol::MessageToClient; use credentials_provider::CredentialsProvider; use futures::{ AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt, TryFutureExt as _, TryStreamExt, @@ -933,6 +934,32 @@ impl Client { } } + /// Establishes a WebSocket connection with Cloud for receiving updates from the server. + async fn connect_to_cloud(self: &Arc, cx: &AsyncApp) -> Result<()> { + let connect_task = cx.update({ + let cloud_client = self.cloud_client.clone(); + move |cx| cloud_client.connect(cx) + })??; + let connection = connect_task.await?; + + let (mut messages, task) = cx.update(|cx| connection.spawn(cx))?; + task.detach(); + + cx.spawn({ + let this = self.clone(); + async move |cx| { + while let Some(message) = messages.next().await { + if let Some(message) = message.log_err() { + this.handle_message_to_client(message, cx); + } + } + } + }) + .detach(); + + Ok(()) + } + /// Performs a sign-in and also connects to Collab. /// /// This is called in places where we *don't* need to connect in the future. We will replace these calls with calls @@ -944,6 +971,8 @@ impl Client { ) -> Result<()> { let credentials = self.sign_in(try_provider, cx).await?; + self.connect_to_cloud(cx).await.log_err(); + let connect_result = match self.connect_with_credentials(credentials, cx).await { ConnectionResult::Timeout => Err(anyhow!("connection timed out")), ConnectionResult::ConnectionReset => Err(anyhow!("connection reset")), @@ -1622,6 +1651,12 @@ impl Client { } } + fn handle_message_to_client(self: &Arc, message: MessageToClient, _cx: &AsyncApp) { + match message { + MessageToClient::UserUpdated => {} + } + } + pub fn telemetry(&self) -> &Arc { &self.telemetry } diff --git a/crates/cloud_api_client/Cargo.toml b/crates/cloud_api_client/Cargo.toml index d56aa94c6e..8e50ccb191 100644 --- a/crates/cloud_api_client/Cargo.toml +++ b/crates/cloud_api_client/Cargo.toml @@ -15,7 +15,10 @@ path = "src/cloud_api_client.rs" anyhow.workspace = true cloud_api_types.workspace = true futures.workspace = true +gpui.workspace = true +gpui_tokio.workspace = true http_client.workspace = true parking_lot.workspace = true serde_json.workspace = true workspace-hack.workspace = true +yawc.workspace = true diff --git a/crates/cloud_api_client/src/cloud_api_client.rs b/crates/cloud_api_client/src/cloud_api_client.rs index edac051a0e..ef9a1a9a55 100644 --- a/crates/cloud_api_client/src/cloud_api_client.rs +++ b/crates/cloud_api_client/src/cloud_api_client.rs @@ -1,11 +1,19 @@ +mod websocket; + use std::sync::Arc; use anyhow::{Context, Result, anyhow}; +use cloud_api_types::websocket_protocol::{PROTOCOL_VERSION, PROTOCOL_VERSION_HEADER_NAME}; pub use cloud_api_types::*; use futures::AsyncReadExt as _; +use gpui::{App, Task}; +use gpui_tokio::Tokio; use http_client::http::request; use http_client::{AsyncBody, HttpClientWithUrl, Method, Request, StatusCode}; use parking_lot::RwLock; +use yawc::WebSocket; + +use crate::websocket::Connection; struct Credentials { user_id: u32, @@ -78,6 +86,41 @@ impl CloudApiClient { Ok(serde_json::from_str(&body)?) } + pub fn connect(&self, cx: &App) -> Result>> { + let mut connect_url = self + .http_client + .build_zed_cloud_url("/client/users/connect", &[])?; + connect_url + .set_scheme(match connect_url.scheme() { + "https" => "wss", + "http" => "ws", + scheme => Err(anyhow!("invalid URL scheme: {scheme}"))?, + }) + .map_err(|_| anyhow!("failed to set URL scheme"))?; + + let credentials = self.credentials.read(); + let credentials = credentials.as_ref().context("no credentials provided")?; + let authorization_header = format!("{} {}", credentials.user_id, credentials.access_token); + + Ok(cx.spawn(async move |cx| { + let handle = cx + .update(|cx| Tokio::handle(cx)) + .ok() + .context("failed to get Tokio handle")?; + let _guard = handle.enter(); + + let ws = WebSocket::connect(connect_url) + .with_request( + request::Builder::new() + .header("Authorization", authorization_header) + .header(PROTOCOL_VERSION_HEADER_NAME, PROTOCOL_VERSION.to_string()), + ) + .await?; + + Ok(Connection::new(ws)) + })) + } + pub async fn accept_terms_of_service(&self) -> Result { let request = self.build_request( Request::builder().method(Method::POST).uri( diff --git a/crates/cloud_api_client/src/websocket.rs b/crates/cloud_api_client/src/websocket.rs new file mode 100644 index 0000000000..48a628db78 --- /dev/null +++ b/crates/cloud_api_client/src/websocket.rs @@ -0,0 +1,73 @@ +use std::pin::Pin; +use std::time::Duration; + +use anyhow::Result; +use cloud_api_types::websocket_protocol::MessageToClient; +use futures::channel::mpsc::unbounded; +use futures::stream::{SplitSink, SplitStream}; +use futures::{FutureExt as _, SinkExt as _, Stream, StreamExt as _, TryStreamExt as _, pin_mut}; +use gpui::{App, BackgroundExecutor, Task}; +use yawc::WebSocket; +use yawc::frame::{FrameView, OpCode}; + +const KEEPALIVE_INTERVAL: Duration = Duration::from_secs(1); + +pub type MessageStream = Pin>>>; + +pub struct Connection { + tx: SplitSink, + rx: SplitStream, +} + +impl Connection { + pub fn new(ws: WebSocket) -> Self { + let (tx, rx) = ws.split(); + + Self { tx, rx } + } + + pub fn spawn(self, cx: &App) -> (MessageStream, Task<()>) { + let (mut tx, rx) = (self.tx, self.rx); + + let (message_tx, message_rx) = unbounded(); + + let handle_io = |executor: BackgroundExecutor| async move { + // Send messages on this frequency so the connection isn't closed. + let keepalive_timer = executor.timer(KEEPALIVE_INTERVAL).fuse(); + futures::pin_mut!(keepalive_timer); + + let rx = rx.fuse(); + pin_mut!(rx); + + loop { + futures::select_biased! { + _ = keepalive_timer => { + let _ = tx.send(FrameView::ping(Vec::new())).await; + + keepalive_timer.set(executor.timer(KEEPALIVE_INTERVAL).fuse()); + } + frame = rx.next() => { + let Some(frame) = frame else { + break; + }; + + match frame.opcode { + OpCode::Binary => { + let message_result = MessageToClient::deserialize(&frame.payload); + message_tx.unbounded_send(message_result).ok(); + } + OpCode::Close => { + break; + } + _ => {} + } + } + } + } + }; + + let task = cx.spawn(async move |cx| handle_io(cx.background_executor().clone()).await); + + (message_rx.into_stream().boxed(), task) + } +} diff --git a/crates/cloud_api_types/src/websocket_protocol.rs b/crates/cloud_api_types/src/websocket_protocol.rs index c90d09e370..75f6a73b43 100644 --- a/crates/cloud_api_types/src/websocket_protocol.rs +++ b/crates/cloud_api_types/src/websocket_protocol.rs @@ -8,7 +8,7 @@ pub const PROTOCOL_VERSION: u32 = 0; pub const PROTOCOL_VERSION_HEADER_NAME: &str = "x-zed-protocol-version"; /// A message from Cloud to the Zed client. -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub enum MessageToClient { /// The user was updated and should be refreshed. UserUpdated, diff --git a/tooling/workspace-hack/Cargo.toml b/tooling/workspace-hack/Cargo.toml index 5678e46236..338985ed95 100644 --- a/tooling/workspace-hack/Cargo.toml +++ b/tooling/workspace-hack/Cargo.toml @@ -305,7 +305,7 @@ scopeguard = { version = "1" } security-framework = { version = "3", features = ["OSX_10_14"] } security-framework-sys = { version = "2", features = ["OSX_10_14"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } @@ -334,7 +334,7 @@ scopeguard = { version = "1" } security-framework = { version = "3", features = ["OSX_10_14"] } security-framework-sys = { version = "2", features = ["OSX_10_14"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } @@ -362,7 +362,7 @@ scopeguard = { version = "1" } security-framework = { version = "3", features = ["OSX_10_14"] } security-framework-sys = { version = "2", features = ["OSX_10_14"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } @@ -391,7 +391,7 @@ scopeguard = { version = "1" } security-framework = { version = "3", features = ["OSX_10_14"] } security-framework-sys = { version = "2", features = ["OSX_10_14"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } @@ -429,7 +429,7 @@ rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", scopeguard = { version = "1" } syn-f595c2ba2a3f28df = { package = "syn", version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } toml_datetime = { version = "0.6", default-features = false, features = ["serde"] } @@ -468,7 +468,7 @@ rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["ev rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", "net", "process", "termios", "time"] } scopeguard = { version = "1" } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } toml_datetime = { version = "0.6", default-features = false, features = ["serde"] } @@ -509,7 +509,7 @@ rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", scopeguard = { version = "1" } syn-f595c2ba2a3f28df = { package = "syn", version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } toml_datetime = { version = "0.6", default-features = false, features = ["serde"] } @@ -548,7 +548,7 @@ rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["ev rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", "net", "process", "termios", "time"] } scopeguard = { version = "1" } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } toml_datetime = { version = "0.6", default-features = false, features = ["serde"] } @@ -568,7 +568,7 @@ ring = { version = "0.17", features = ["std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] } scopeguard = { version = "1" } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } @@ -592,7 +592,7 @@ ring = { version = "0.17", features = ["std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] } scopeguard = { version = "1" } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } @@ -636,7 +636,7 @@ rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", scopeguard = { version = "1" } syn-f595c2ba2a3f28df = { package = "syn", version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } toml_datetime = { version = "0.6", default-features = false, features = ["serde"] } @@ -675,7 +675,7 @@ rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["ev rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", "net", "process", "termios", "time"] } scopeguard = { version = "1" } sync_wrapper = { version = "1", default-features = false, features = ["futures"] } -tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] } tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } toml_datetime = { version = "0.6", default-features = false, features = ["serde"] } From 8c460ba15c8eda39b064fca360bae7566db62d23 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 7 Aug 2025 17:44:53 -0400 Subject: [PATCH 12/37] client: Re-fetch the authenticated user when receiving a `UserUpdated` message from Cloud (#35807) This PR wires up handling for the new `UserUpdated` message coming from Cloud over the WebSocket connection. When we receive this message we will refresh the authenticated user. Release Notes: - N/A Co-authored-by: Richard --- crates/client/src/client.rs | 24 ++++++++++++++++++++---- crates/client/src/user.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 0480ed1c3e..9d58692c0d 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -192,6 +192,8 @@ pub fn init(client: &Arc, cx: &mut App) { }); } +pub type MessageToClientHandler = Box; + struct GlobalClient(Arc); impl Global for GlobalClient {} @@ -205,6 +207,7 @@ pub struct Client { credentials_provider: ClientCredentialsProvider, state: RwLock, handler_set: parking_lot::Mutex, + message_to_client_handlers: parking_lot::Mutex>, #[allow(clippy::type_complexity)] #[cfg(any(test, feature = "test-support"))] @@ -554,6 +557,7 @@ impl Client { credentials_provider: ClientCredentialsProvider::new(cx), state: Default::default(), handler_set: Default::default(), + message_to_client_handlers: parking_lot::Mutex::new(Vec::new()), #[cfg(any(test, feature = "test-support"))] authenticate: Default::default(), @@ -1651,10 +1655,22 @@ impl Client { } } - fn handle_message_to_client(self: &Arc, message: MessageToClient, _cx: &AsyncApp) { - match message { - MessageToClient::UserUpdated => {} - } + pub fn add_message_to_client_handler( + self: &Arc, + handler: impl Fn(&MessageToClient, &App) + Send + Sync + 'static, + ) { + self.message_to_client_handlers + .lock() + .push(Box::new(handler)); + } + + fn handle_message_to_client(self: &Arc, message: MessageToClient, cx: &AsyncApp) { + cx.update(|cx| { + for handler in self.message_to_client_handlers.lock().iter() { + handler(&message, cx); + } + }) + .ok(); } pub fn telemetry(&self) -> &Arc { diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 3c125a0882..9f76dd7ad0 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -1,6 +1,7 @@ use super::{Client, Status, TypedEnvelope, proto}; use anyhow::{Context as _, Result, anyhow}; use chrono::{DateTime, Utc}; +use cloud_api_client::websocket_protocol::MessageToClient; use cloud_api_client::{GetAuthenticatedUserResponse, PlanInfo}; use cloud_llm_client::{ EDIT_PREDICTIONS_USAGE_AMOUNT_HEADER_NAME, EDIT_PREDICTIONS_USAGE_LIMIT_HEADER_NAME, @@ -181,6 +182,12 @@ impl UserStore { client.add_message_handler(cx.weak_entity(), Self::handle_update_invite_info), client.add_message_handler(cx.weak_entity(), Self::handle_show_contacts), ]; + + client.add_message_to_client_handler({ + let this = cx.weak_entity(); + move |message, cx| Self::handle_message_to_client(this.clone(), message, cx) + }); + Self { users: Default::default(), by_github_login: Default::default(), @@ -813,6 +820,32 @@ impl UserStore { cx.emit(Event::PrivateUserInfoUpdated); } + fn handle_message_to_client(this: WeakEntity, message: &MessageToClient, cx: &App) { + cx.spawn(async move |cx| { + match message { + MessageToClient::UserUpdated => { + let cloud_client = cx + .update(|cx| { + this.read_with(cx, |this, _cx| { + this.client.upgrade().map(|client| client.cloud_client()) + }) + })?? + .ok_or(anyhow::anyhow!("Failed to get Cloud client"))?; + + let response = cloud_client.get_authenticated_user().await?; + cx.update(|cx| { + this.update(cx, |this, cx| { + this.update_authenticated_user(response, cx); + }) + })??; + } + } + + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + pub fn watch_current_user(&self) -> watch::Receiver>> { self.current_user.clone() } From d50af2f39c85d9bb56fe1b349c0c2c69a54f17d4 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 7 Aug 2025 18:14:25 -0400 Subject: [PATCH 13/37] client: Only connect to Collab automatically for Zed staff (#35827) This PR makes it so that only Zed staff connect to Collab automatically. Anyone else can connect to Collab manually when they want to collaborate (but this is not required for using Zed's LLM features). Release Notes: - N/A --------- Co-authored-by: Richard --- crates/client/src/client.rs | 45 ++++++++++++++++++----- crates/feature_flags/src/feature_flags.rs | 24 ++++++++++++ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 9d58692c0d..12ea4bcd3e 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -16,6 +16,7 @@ use clock::SystemClock; use cloud_api_client::CloudApiClient; use cloud_api_client::websocket_protocol::MessageToClient; use credentials_provider::CredentialsProvider; +use feature_flags::FeatureFlagAppExt as _; use futures::{ AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt, TryFutureExt as _, TryStreamExt, channel::oneshot, future::BoxFuture, @@ -964,25 +965,51 @@ impl Client { Ok(()) } - /// Performs a sign-in and also connects to Collab. + /// Performs a sign-in and also (optionally) connects to Collab. /// - /// This is called in places where we *don't* need to connect in the future. We will replace these calls with calls - /// to `sign_in` when we're ready to remove auto-connection to Collab. + /// Only Zed staff automatically connect to Collab. pub async fn sign_in_with_optional_connect( self: &Arc, try_provider: bool, cx: &AsyncApp, ) -> Result<()> { + let (is_staff_tx, is_staff_rx) = oneshot::channel::(); + let mut is_staff_tx = Some(is_staff_tx); + cx.update(|cx| { + cx.on_flags_ready(move |state, _cx| { + if let Some(is_staff_tx) = is_staff_tx.take() { + is_staff_tx.send(state.is_staff).log_err(); + } + }) + .detach(); + }) + .log_err(); + let credentials = self.sign_in(try_provider, cx).await?; self.connect_to_cloud(cx).await.log_err(); - let connect_result = match self.connect_with_credentials(credentials, cx).await { - ConnectionResult::Timeout => Err(anyhow!("connection timed out")), - ConnectionResult::ConnectionReset => Err(anyhow!("connection reset")), - ConnectionResult::Result(result) => result.context("client auth and connect"), - }; - connect_result.log_err(); + cx.update(move |cx| { + cx.spawn({ + let client = self.clone(); + async move |cx| { + let is_staff = is_staff_rx.await?; + if is_staff { + match client.connect_with_credentials(credentials, cx).await { + ConnectionResult::Timeout => Err(anyhow!("connection timed out")), + ConnectionResult::ConnectionReset => Err(anyhow!("connection reset")), + ConnectionResult::Result(result) => { + result.context("client auth and connect") + } + } + } else { + Ok(()) + } + } + }) + .detach_and_log_err(cx); + }) + .log_err(); Ok(()) } diff --git a/crates/feature_flags/src/feature_flags.rs b/crates/feature_flags/src/feature_flags.rs index 631bafc841..ef357adf35 100644 --- a/crates/feature_flags/src/feature_flags.rs +++ b/crates/feature_flags/src/feature_flags.rs @@ -158,6 +158,11 @@ where } } +#[derive(Debug)] +pub struct OnFlagsReady { + pub is_staff: bool, +} + pub trait FeatureFlagAppExt { fn wait_for_flag(&mut self) -> WaitForFlag; @@ -169,6 +174,10 @@ pub trait FeatureFlagAppExt { fn has_flag(&self) -> bool; fn is_staff(&self) -> bool; + fn on_flags_ready(&mut self, callback: F) -> Subscription + where + F: FnMut(OnFlagsReady, &mut App) + 'static; + fn observe_flag(&mut self, callback: F) -> Subscription where F: FnMut(bool, &mut App) + 'static; @@ -198,6 +207,21 @@ impl FeatureFlagAppExt for App { .unwrap_or(false) } + fn on_flags_ready(&mut self, mut callback: F) -> Subscription + where + F: FnMut(OnFlagsReady, &mut App) + 'static, + { + self.observe_global::(move |cx| { + let feature_flags = cx.global::(); + callback( + OnFlagsReady { + is_staff: feature_flags.staff, + }, + cx, + ); + }) + } + fn observe_flag(&mut self, mut callback: F) -> Subscription where F: FnMut(bool, &mut App) + 'static, From 33804db0ef948479e4094a151cd4b5efb271bea1 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 7 Aug 2025 18:29:59 -0400 Subject: [PATCH 14/37] collab_ui: Show signed-out state when not connected to Collab (#35832) This PR updates signed-out state of the Collab panel to show when not connected to Collab, as opposed to just when the user is signed-out. Release Notes: - N/A --- crates/collab_ui/src/collab_panel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index bb7c2ba1cd..51e4ff8965 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -3053,7 +3053,7 @@ impl Render for CollabPanel { .on_action(cx.listener(CollabPanel::move_channel_down)) .track_focus(&self.focus_handle) .size_full() - .child(if self.user_store.read(cx).current_user().is_none() { + .child(if !self.client.status().borrow().is_connected() { self.render_signed_out(cx) } else { self.render_signed_in(window, cx) From 12d9fdc4e395ca2e74a1c348e973fd5c2d8e6ea3 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 7 Aug 2025 19:00:45 -0400 Subject: [PATCH 15/37] language_model: Refresh the LLM token upon receiving a `UserUpdated` message from Cloud (#35839) This PR makes it so we refresh the LLM token upon receiving a `UserUpdated` message from Cloud over the WebSocket connection. Release Notes: - N/A --- Cargo.lock | 1 + crates/client/src/client.rs | 4 +-- crates/language_model/Cargo.toml | 1 + .../language_model/src/model/cloud_model.rs | 34 +++++++++---------- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 860dcfb2ec..2d76efa9f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9123,6 +9123,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "client", + "cloud_api_types", "cloud_llm_client", "collections", "futures 0.3.31", diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 12ea4bcd3e..f09c012a85 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -193,7 +193,7 @@ pub fn init(client: &Arc, cx: &mut App) { }); } -pub type MessageToClientHandler = Box; +pub type MessageToClientHandler = Box; struct GlobalClient(Arc); @@ -1684,7 +1684,7 @@ impl Client { pub fn add_message_to_client_handler( self: &Arc, - handler: impl Fn(&MessageToClient, &App) + Send + Sync + 'static, + handler: impl Fn(&MessageToClient, &mut App) + Send + Sync + 'static, ) { self.message_to_client_handlers .lock() diff --git a/crates/language_model/Cargo.toml b/crates/language_model/Cargo.toml index 841be60b0e..f9920623b5 100644 --- a/crates/language_model/Cargo.toml +++ b/crates/language_model/Cargo.toml @@ -20,6 +20,7 @@ anthropic = { workspace = true, features = ["schemars"] } anyhow.workspace = true base64.workspace = true client.workspace = true +cloud_api_types.workspace = true cloud_llm_client.workspace = true collections.workspace = true futures.workspace = true diff --git a/crates/language_model/src/model/cloud_model.rs b/crates/language_model/src/model/cloud_model.rs index 8ae5893410..3b4c1fa269 100644 --- a/crates/language_model/src/model/cloud_model.rs +++ b/crates/language_model/src/model/cloud_model.rs @@ -3,11 +3,9 @@ use std::sync::Arc; use anyhow::Result; use client::Client; +use cloud_api_types::websocket_protocol::MessageToClient; use cloud_llm_client::Plan; -use gpui::{ - App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, ReadGlobal as _, -}; -use proto::TypedEnvelope; +use gpui::{App, AppContext as _, Context, Entity, EventEmitter, Global, ReadGlobal as _}; use smol::lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; use thiserror::Error; @@ -82,9 +80,7 @@ impl Global for GlobalRefreshLlmTokenListener {} pub struct RefreshLlmTokenEvent; -pub struct RefreshLlmTokenListener { - _llm_token_subscription: client::Subscription, -} +pub struct RefreshLlmTokenListener; impl EventEmitter for RefreshLlmTokenListener {} @@ -99,17 +95,21 @@ impl RefreshLlmTokenListener { } fn new(client: Arc, cx: &mut Context) -> Self { - Self { - _llm_token_subscription: client - .add_message_handler(cx.weak_entity(), Self::handle_refresh_llm_token), - } + client.add_message_to_client_handler({ + let this = cx.entity(); + move |message, cx| { + Self::handle_refresh_llm_token(this.clone(), message, cx); + } + }); + + Self } - async fn handle_refresh_llm_token( - this: Entity, - _: TypedEnvelope, - mut cx: AsyncApp, - ) -> Result<()> { - this.update(&mut cx, |_this, cx| cx.emit(RefreshLlmTokenEvent)) + fn handle_refresh_llm_token(this: Entity, message: &MessageToClient, cx: &mut App) { + match message { + MessageToClient::UserUpdated => { + this.update(cx, |_this, cx| cx.emit(RefreshLlmTokenEvent)); + } + } } } From 9c1d47bc5983ae241c265c5887a95eec609236b6 Mon Sep 17 00:00:00 2001 From: Zed Bot Date: Fri, 8 Aug 2025 12:52:09 +0000 Subject: [PATCH 16/37] Bump to 0.199.2 for @maxdeviant --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d76efa9f7..2501a73e1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20461,7 +20461,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.1" +version = "0.199.2" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index e11af59f2a..4ca4ee817f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.1" +version = "0.199.2" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "] From ff196875d9467d81a7249c32d07883e6f353e4cc Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Fri, 8 Aug 2025 14:10:09 -0400 Subject: [PATCH 17/37] Ensure Edit Prediction provider is properly assigned on sign-in (#35885) This PR fixes an issue where Edit Predictions would not be available in buffers that were opened when the workspace loaded. The issue was that there was a race condition between fetching/setting the authenticated user state and when we assigned the Edit Prediction provider to buffers that were already opened. We now wait for the event that we emit when we have successfully loaded the user in order to assign the Edit Prediction provider, as we'll know the user has been loaded into the `UserStore` by that point. Closes https://github.com/zed-industries/zed/issues/35883 Release Notes: - Fixed an issue where Edit Predictions were not working in buffers that were open when the workspace initially loaded. Co-authored-by: Richard Feldman --- crates/client/src/user.rs | 39 +++++++++++++------ .../zed/src/zed/edit_prediction_registry.rs | 29 ++++++-------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 9f76dd7ad0..faf46945d8 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -226,17 +226,35 @@ impl UserStore { match status { Status::Authenticated | Status::Connected { .. } => { if let Some(user_id) = client.user_id() { - let response = client.cloud_client().get_authenticated_user().await; - let mut current_user = None; + let response = client + .cloud_client() + .get_authenticated_user() + .await + .log_err(); + + let current_user_and_response = if let Some(response) = response { + let user = Arc::new(User { + id: user_id, + github_login: response.user.github_login.clone().into(), + avatar_uri: response.user.avatar_url.clone().into(), + name: response.user.name.clone(), + }); + + Some((user, response)) + } else { + None + }; + current_user_tx + .send( + current_user_and_response + .as_ref() + .map(|(user, _)| user.clone()), + ) + .await + .ok(); + cx.update(|cx| { - if let Some(response) = response.log_err() { - let user = Arc::new(User { - id: user_id, - github_login: response.user.github_login.clone().into(), - avatar_uri: response.user.avatar_url.clone().into(), - name: response.user.name.clone(), - }); - current_user = Some(user.clone()); + if let Some((user, response)) = current_user_and_response { this.update(cx, |this, cx| { this.by_github_login .insert(user.github_login.clone(), user_id); @@ -247,7 +265,6 @@ impl UserStore { anyhow::Ok(()) } })??; - current_user_tx.send(current_user).await.ok(); this.update(cx, |_, cx| cx.notify())?; } diff --git a/crates/zed/src/zed/edit_prediction_registry.rs b/crates/zed/src/zed/edit_prediction_registry.rs index b9f561c0e7..da4b6e78c6 100644 --- a/crates/zed/src/zed/edit_prediction_registry.rs +++ b/crates/zed/src/zed/edit_prediction_registry.rs @@ -5,11 +5,9 @@ use editor::Editor; use gpui::{AnyWindowHandle, App, AppContext as _, Context, Entity, WeakEntity}; use language::language_settings::{EditPredictionProvider, all_language_settings}; use settings::SettingsStore; -use smol::stream::StreamExt; use std::{cell::RefCell, rc::Rc, sync::Arc}; use supermaven::{Supermaven, SupermavenCompletionProvider}; use ui::Window; -use util::ResultExt; use workspace::Workspace; use zeta::{ProviderDataCollection, ZetaEditPredictionProvider}; @@ -59,25 +57,20 @@ pub fn init(client: Arc, user_store: Entity, cx: &mut App) { cx.on_action(clear_zeta_edit_history); let mut provider = all_language_settings(None, cx).edit_predictions.provider; - cx.spawn({ - let user_store = user_store.clone(); + cx.subscribe(&user_store, { let editors = editors.clone(); let client = client.clone(); - - async move |cx| { - let mut status = client.status(); - while let Some(_status) = status.next().await { - cx.update(|cx| { - assign_edit_prediction_providers( - &editors, - provider, - &client, - user_store.clone(), - cx, - ); - }) - .log_err(); + move |user_store, event, cx| match event { + client::user::Event::PrivateUserInfoUpdated => { + assign_edit_prediction_providers( + &editors, + provider, + &client, + user_store.clone(), + cx, + ); } + _ => {} } }) .detach(); From d6223feb34dca3c6718260920ec5e13bc31c7581 Mon Sep 17 00:00:00 2001 From: Zed Bot Date: Fri, 8 Aug 2025 18:34:50 +0000 Subject: [PATCH 18/37] Bump to 0.199.3 for @maxdeviant --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2501a73e1e..8053bdd6d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20461,7 +20461,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.2" +version = "0.199.3" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 4ca4ee817f..910c471f3a 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.2" +version = "0.199.3" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "] From a1ad6af1b1e20c3a1bfe8ee7f9bd56154588db1c Mon Sep 17 00:00:00 2001 From: Peter Tripp Date: Fri, 8 Aug 2025 15:20:10 -0400 Subject: [PATCH 19/37] ci: Use faster Linux ARM runners (#35880) Switch our Linux aarch_64 release builds from Linux on Graviton (32 vCPU, 64GB) to Linux running on Apple M4 Pro (8vCPU, 32GB). Builds are faster (20mins vs 30mins) for the same cost (960 unit minutes; ~$0.96/ea). Screenshot 2025-08-08 at 13 14 41 Release Notes: - N/A --- .github/actionlint.yml | 3 +++ .github/workflows/ci.yml | 16 +++++++--------- .github/workflows/release_nightly.yml | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/actionlint.yml b/.github/actionlint.yml index ad09545902..0ee6af8a1d 100644 --- a/.github/actionlint.yml +++ b/.github/actionlint.yml @@ -24,6 +24,9 @@ self-hosted-runner: - namespace-profile-8x16-ubuntu-2204 - namespace-profile-16x32-ubuntu-2204 - namespace-profile-32x64-ubuntu-2204 + # Namespace Limited Preview + - namespace-profile-8x16-ubuntu-2004-arm-m4 + - namespace-profile-8x32-ubuntu-2004-arm-m4 # Self Hosted Runners - self-mini-macos - self-32vcpu-windows-2022 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84907351fe..02edc99824 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -511,8 +511,8 @@ jobs: runs-on: - self-mini-macos if: | - startsWith(github.ref, 'refs/tags/v') - || contains(github.event.pull_request.labels.*.name, 'run-bundling') + ( startsWith(github.ref, 'refs/tags/v') + || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) needs: [macos_tests] env: MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} @@ -599,8 +599,8 @@ jobs: runs-on: - namespace-profile-16x32-ubuntu-2004 # ubuntu 20.04 for minimal glibc if: | - startsWith(github.ref, 'refs/tags/v') - || contains(github.event.pull_request.labels.*.name, 'run-bundling') + ( startsWith(github.ref, 'refs/tags/v') + || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) needs: [linux_tests] steps: - name: Checkout repo @@ -650,7 +650,7 @@ jobs: timeout-minutes: 60 name: Linux arm64 release bundle runs-on: - - namespace-profile-32x64-ubuntu-2004-arm # ubuntu 20.04 for minimal glibc + - namespace-profile-8x32-ubuntu-2004-arm-m4 # ubuntu 20.04 for minimal glibc if: | startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') @@ -703,10 +703,8 @@ jobs: timeout-minutes: 60 runs-on: github-8vcpu-ubuntu-2404 if: | - false && ( - startsWith(github.ref, 'refs/tags/v') - || contains(github.event.pull_request.labels.*.name, 'run-bundling') - ) + ( startsWith(github.ref, 'refs/tags/v') + || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) needs: [linux_tests] name: Build Zed on FreeBSD steps: diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml index b3500a085b..ed9f4c8450 100644 --- a/.github/workflows/release_nightly.yml +++ b/.github/workflows/release_nightly.yml @@ -168,7 +168,7 @@ jobs: name: Create a Linux *.tar.gz bundle for ARM if: github.repository_owner == 'zed-industries' runs-on: - - namespace-profile-32x64-ubuntu-2004-arm # ubuntu 20.04 for minimal glibc + - namespace-profile-8x32-ubuntu-2004-arm-m4 # ubuntu 20.04 for minimal glibc needs: tests steps: - name: Checkout repo From e24aa382a4fa1ad2eb5086e1d383893dd15a0cee Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 8 Aug 2025 23:12:41 +0300 Subject: [PATCH 20/37] Fill capabilities on project (re)join (#35892) Follow-up of https://github.com/zed-industries/zed/pull/35682 Release Notes: - N/A Co-authored-by: Smit Barmase --- crates/project/src/lsp_store.rs | 11 +++++++++-- crates/project/src/project.rs | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 4489f9f043..a4c04cd06a 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -7722,12 +7722,19 @@ impl LspStore { pub(crate) fn set_language_server_statuses_from_proto( &mut self, language_servers: Vec, + server_capabilities: Vec, ) { self.language_server_statuses = language_servers .into_iter() - .map(|server| { + .zip(server_capabilities) + .map(|(server, server_capabilities)| { + let server_id = LanguageServerId(server.id as usize); + if let Ok(server_capabilities) = serde_json::from_str(&server_capabilities) { + self.lsp_server_capabilities + .insert(server_id, server_capabilities); + } ( - LanguageServerId(server.id as usize), + server_id, LanguageServerStatus { name: LanguageServerName::from_proto(server.name), pending_work: Default::default(), diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index cca026ec87..7838f5744a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1488,7 +1488,10 @@ impl Project { fs.clone(), cx, ); - lsp_store.set_language_server_statuses_from_proto(response.payload.language_servers); + lsp_store.set_language_server_statuses_from_proto( + response.payload.language_servers, + response.payload.language_server_capabilities, + ); lsp_store })?; @@ -2319,7 +2322,10 @@ impl Project { self.set_worktrees_from_proto(message.worktrees, cx)?; self.set_collaborators_from_proto(message.collaborators, cx)?; self.lsp_store.update(cx, |lsp_store, _| { - lsp_store.set_language_server_statuses_from_proto(message.language_servers) + lsp_store.set_language_server_statuses_from_proto( + message.language_servers, + message.language_server_capabilities, + ) }); self.enqueue_buffer_ordered_message(BufferOrderedMessage::Resync) .unwrap(); From 008e905b44277a17e59adbcdfb26b32b6763531f Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:50:24 +0200 Subject: [PATCH 21/37] python: Create DAP download directory sooner (#35986) Closes #35980 Release Notes: - Fixed Python Debug sessions not starting up when a session is started up for the first time. --- crates/dap_adapters/src/python.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/dap_adapters/src/python.rs b/crates/dap_adapters/src/python.rs index 461ce6fbb3..a2bd934311 100644 --- a/crates/dap_adapters/src/python.rs +++ b/crates/dap_adapters/src/python.rs @@ -152,6 +152,9 @@ impl PythonDebugAdapter { maybe!(async move { let response = latest_release.filter(|response| response.status().is_success())?; + let download_dir = debug_adapters_dir().join(Self::ADAPTER_NAME); + std::fs::create_dir_all(&download_dir).ok()?; + let mut output = String::new(); response .into_body() From 4659eaf3928b9e5e2d0c66177fee9462cbed7dce Mon Sep 17 00:00:00 2001 From: smit Date: Thu, 7 Aug 2025 11:39:27 +0530 Subject: [PATCH 22/37] language_models: Fix high memory consumption while using Agent Panel (#35764) Closes #31108 The `num_tokens_from_messages` method we use from `tiktoken-rs` creates new BPE every time that method is called. This creation of BPE is expensive as well as has some underlying issue that keeps memory from releasing once the method is finished, specifically noticeable on Linux. This leads to a gradual increase in memory every time that method is called in my case around +50MB on each call. We call this method with debounce every time user types in Agent Panel to calculate tokens. This can add up really fast. This PR lands quick fix, while I/maintainers figure out underlying issue. See upstream discussion: https://github.com/zurawiki/tiktoken-rs/issues/39. Here on fork https://github.com/zed-industries/tiktoken-rs/pull/1, instead of creating BPE instances every time that method is called, we use singleton BPE instances instead. So, whatever memory it is holding on to, at least that is only once per model. Before: Increase of 700MB+ on extensive use On init: prev-init First message: prev-first-call Extensive use: prev-extensive-use After: Increase of 50MB+ on extensive use On init: now-init First message: now-first-call Extensive use: now-extensive-use Release Notes: - Fixed issue where Agent Panel would cause high memory consumption over prolonged use. --- Cargo.lock | 5 ++--- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8053bdd6d5..7c6ca7bd29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16619,9 +16619,8 @@ dependencies = [ [[package]] name = "tiktoken-rs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25563eeba904d770acf527e8b370fe9a5547bacd20ff84a0b6c3bc41288e5625" +version = "0.8.0" +source = "git+https://github.com/zed-industries/tiktoken-rs?rev=30c32a4522751699adeda0d5840c71c3b75ae73d#30c32a4522751699adeda0d5840c71c3b75ae73d" dependencies = [ "anyhow", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index a04d8f6099..86f1b8b0a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -601,7 +601,7 @@ sysinfo = "0.31.0" take-until = "0.2.0" tempfile = "3.20.0" thiserror = "2.0.12" -tiktoken-rs = "0.7.0" +tiktoken-rs = { git = "https://github.com/zed-industries/tiktoken-rs", rev = "30c32a4522751699adeda0d5840c71c3b75ae73d" } time = { version = "0.3", features = [ "macros", "parsing", From b13d048737f73ee5660e0480fb11cdae5038e1f9 Mon Sep 17 00:00:00 2001 From: smit Date: Fri, 8 Aug 2025 06:17:37 +0530 Subject: [PATCH 23/37] editor: Fix Follow Agent unexpectedly stopping during edits (#35845) Closes #34881 For horizontal scroll, we weren't keeping track of the `local` bool, so whenever the agent tries to autoscroll horizontally, it would be seen as a user scroll event resulting in unfollow. Release Notes: - Fixed an issue where the Follow Agent could unexpectedly stop following during edits. --- crates/editor/src/element.rs | 19 ++++++++++++++++--- crates/editor/src/scroll.rs | 4 ++-- crates/editor/src/scroll/autoscroll.rs | 14 +++++++------- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index e1647215bc..6980037664 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -8028,12 +8028,20 @@ impl Element for EditorElement { autoscroll_containing_element, needs_horizontal_autoscroll, ) = self.editor.update(cx, |editor, cx| { - let autoscroll_request = editor.autoscroll_request(); + let autoscroll_request = editor.scroll_manager.take_autoscroll_request(); + let autoscroll_containing_element = autoscroll_request.is_some() || editor.has_pending_selection(); let (needs_horizontal_autoscroll, was_scrolled) = editor - .autoscroll_vertically(bounds, line_height, max_scroll_top, window, cx); + .autoscroll_vertically( + bounds, + line_height, + max_scroll_top, + autoscroll_request, + window, + cx, + ); if was_scrolled.0 { snapshot = editor.snapshot(window, cx); } @@ -8423,7 +8431,11 @@ impl Element for EditorElement { Ok(blocks) => blocks, Err(resized_blocks) => { self.editor.update(cx, |editor, cx| { - editor.resize_blocks(resized_blocks, autoscroll_request, cx) + editor.resize_blocks( + resized_blocks, + autoscroll_request.map(|(autoscroll, _)| autoscroll), + cx, + ) }); return self.prepaint(None, _inspector_id, bounds, &mut (), window, cx); } @@ -8468,6 +8480,7 @@ impl Element for EditorElement { scroll_width, em_advance, &line_layouts, + autoscroll_request, window, cx, ) diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index ecaf7c11e4..08ff23f8f7 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -348,8 +348,8 @@ impl ScrollManager { self.show_scrollbars } - pub fn autoscroll_request(&self) -> Option { - self.autoscroll_request.map(|(autoscroll, _)| autoscroll) + pub fn take_autoscroll_request(&mut self) -> Option<(Autoscroll, bool)> { + self.autoscroll_request.take() } pub fn active_scrollbar_state(&self) -> Option<&ActiveScrollbarState> { diff --git a/crates/editor/src/scroll/autoscroll.rs b/crates/editor/src/scroll/autoscroll.rs index e8a1f8da73..88d3b52d76 100644 --- a/crates/editor/src/scroll/autoscroll.rs +++ b/crates/editor/src/scroll/autoscroll.rs @@ -102,15 +102,12 @@ impl AutoscrollStrategy { pub(crate) struct NeedsHorizontalAutoscroll(pub(crate) bool); impl Editor { - pub fn autoscroll_request(&self) -> Option { - self.scroll_manager.autoscroll_request() - } - pub(crate) fn autoscroll_vertically( &mut self, bounds: Bounds, line_height: Pixels, max_scroll_top: f32, + autoscroll_request: Option<(Autoscroll, bool)>, window: &mut Window, cx: &mut Context, ) -> (NeedsHorizontalAutoscroll, WasScrolled) { @@ -137,7 +134,7 @@ impl Editor { WasScrolled(false) }; - let Some((autoscroll, local)) = self.scroll_manager.autoscroll_request.take() else { + let Some((autoscroll, local)) = autoscroll_request else { return (NeedsHorizontalAutoscroll(false), editor_was_scrolled); }; @@ -284,9 +281,12 @@ impl Editor { scroll_width: Pixels, em_advance: Pixels, layouts: &[LineWithInvisibles], + autoscroll_request: Option<(Autoscroll, bool)>, window: &mut Window, cx: &mut Context, ) -> Option> { + let (_, local) = autoscroll_request?; + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let selections = self.selections.all::(cx); let mut scroll_position = self.scroll_manager.scroll_position(&display_map); @@ -335,10 +335,10 @@ impl Editor { let was_scrolled = if target_left < scroll_left { scroll_position.x = target_left / em_advance; - self.set_scroll_position_internal(scroll_position, true, true, window, cx) + self.set_scroll_position_internal(scroll_position, local, true, window, cx) } else if target_right > scroll_right { scroll_position.x = (target_right - viewport_width) / em_advance; - self.set_scroll_position_internal(scroll_position, true, true, window, cx) + self.set_scroll_position_internal(scroll_position, local, true, window, cx) } else { WasScrolled(false) }; From 0220c5c92e2ca5f2a41e50e24615fa35d59f809c Mon Sep 17 00:00:00 2001 From: Zed Bot Date: Tue, 12 Aug 2025 05:59:18 +0000 Subject: [PATCH 24/37] Bump to 0.199.4 for @smitbarmase --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c6ca7bd29..76a47c6d14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20460,7 +20460,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.3" +version = "0.199.4" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 910c471f3a..6e1f9359a5 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.3" +version = "0.199.4" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "] From a72432d968b61365ecccae22b341b388a3550724 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Tue, 12 Aug 2025 00:47:54 -0600 Subject: [PATCH 25/37] zeta: Add latency telemetry for 1% of edit predictions (#36020) Release Notes: - N/A Co-authored-by: Oleksiy --- Cargo.lock | 1 + crates/zeta/Cargo.toml | 3 ++- crates/zeta/src/zeta.rs | 24 ++++++++++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76a47c6d14..cbbef41d47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20863,6 +20863,7 @@ dependencies = [ "menu", "postage", "project", + "rand 0.8.5", "regex", "release_channel", "reqwest_client", diff --git a/crates/zeta/Cargo.toml b/crates/zeta/Cargo.toml index 9f1d02b790..ee76308ff3 100644 --- a/crates/zeta/Cargo.toml +++ b/crates/zeta/Cargo.toml @@ -26,6 +26,7 @@ collections.workspace = true command_palette_hooks.workspace = true copilot.workspace = true db.workspace = true +edit_prediction.workspace = true editor.workspace = true feature_flags.workspace = true fs.workspace = true @@ -33,13 +34,13 @@ futures.workspace = true gpui.workspace = true http_client.workspace = true indoc.workspace = true -edit_prediction.workspace = true language.workspace = true language_model.workspace = true log.workspace = true menu.workspace = true postage.workspace = true project.workspace = true +rand.workspace = true regex.workspace = true release_channel.workspace = true serde.workspace = true diff --git a/crates/zeta/src/zeta.rs b/crates/zeta/src/zeta.rs index b1bd737dbf..c711b2734c 100644 --- a/crates/zeta/src/zeta.rs +++ b/crates/zeta/src/zeta.rs @@ -432,6 +432,7 @@ impl Zeta { body, editable_range, } = gather_task.await?; + let done_gathering_context_at = Instant::now(); log::debug!( "Events:\n{}\nExcerpt:\n{:?}", @@ -484,6 +485,7 @@ impl Zeta { } }; + let received_response_at = Instant::now(); log::debug!("completion response: {}", &response.output_excerpt); if let Some(usage) = usage { @@ -495,7 +497,7 @@ impl Zeta { .ok(); } - Self::process_completion_response( + let edit_prediction = Self::process_completion_response( response, buffer, &snapshot, @@ -508,7 +510,25 @@ impl Zeta { buffer_snapshotted_at, &cx, ) - .await + .await; + + let finished_at = Instant::now(); + + // record latency for ~1% of requests + if rand::random::() <= 2 { + telemetry::event!( + "Edit Prediction Request", + context_latency = done_gathering_context_at + .duration_since(buffer_snapshotted_at) + .as_millis(), + request_latency = received_response_at + .duration_since(done_gathering_context_at) + .as_millis(), + process_latency = finished_at.duration_since(received_response_at).as_millis() + ); + } + + edit_prediction }) } From de4cf6e42368e812ae5482037f687dab27c01a24 Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Tue, 12 Aug 2025 21:49:19 +0300 Subject: [PATCH 26/37] open_ai: Log inputs that caused parsing errors (#36063) Release Notes: - N/A Co-authored-by: Michael Sloan --- Cargo.lock | 1 + crates/open_ai/Cargo.toml | 1 + crates/open_ai/src/open_ai.rs | 10 +++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index cbbef41d47..c042681604 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11184,6 +11184,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", + "log", "schemars", "serde", "serde_json", diff --git a/crates/open_ai/Cargo.toml b/crates/open_ai/Cargo.toml index 2d40cd2735..bae00f0a8e 100644 --- a/crates/open_ai/Cargo.toml +++ b/crates/open_ai/Cargo.toml @@ -20,6 +20,7 @@ anyhow.workspace = true futures.workspace = true http_client.workspace = true schemars = { workspace = true, optional = true } +log.workspace = true serde.workspace = true serde_json.workspace = true strum.workspace = true diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index 4697d71ed3..a6fd03a296 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -445,7 +445,15 @@ pub async fn stream_completion( Ok(ResponseStreamResult::Err { error }) => { Some(Err(anyhow!(error))) } - Err(error) => Some(Err(anyhow!(error))), + Err(error) => { + log::error!( + "Failed to parse OpenAI response into ResponseStreamResult: `{}`\n\ + Response: `{}`", + error, + line, + ); + Some(Err(anyhow!(error))) + } } } } From 6c90e882cc43d9f83ff57028fad3097aaedb41b0 Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Tue, 12 Aug 2025 21:51:23 +0300 Subject: [PATCH 27/37] open_ai: Send `prompt_cache_key` to improve caching (#36065) Release Notes: - N/A Co-authored-by: Michael Sloan --- crates/language_models/src/provider/open_ai.rs | 1 + crates/open_ai/src/open_ai.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index ee74562687..67bc081df3 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -455,6 +455,7 @@ pub fn into_open_ai( } else { None }, + prompt_cache_key: request.thread_id, tools: request .tools .into_iter() diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index a6fd03a296..919b1d9ebf 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -244,6 +244,8 @@ pub struct Request { pub parallel_tool_calls: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub tools: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub prompt_cache_key: Option, } #[derive(Debug, Serialize, Deserialize)] From 2c312cef7f6c65422fedfecfd8ca286baf68baf2 Mon Sep 17 00:00:00 2001 From: Rishabh Bothra <37180068+07rjain@users.noreply.github.com> Date: Tue, 12 Aug 2025 21:34:51 +0530 Subject: [PATCH 28/37] language_models: Add vision support for OpenAI gpt-5, gpt-5-mini, and gpt-5-nano models (#36047) ## Summary Enable image processing capabilities for GPT-5 series models by updating the `supports_images()` method. ## Changes - Add vision support for `gpt-5`, `gpt-5-mini`, and `gpt-5-nano` models - Update `supports_images()` method in `crates/language_models/src/provider/open_ai.rs` ## Models with Vision Support (after this PR) - gpt-4o - gpt-4o-mini - gpt-4.1 - gpt-4.1-mini - gpt-4.1-nano - gpt-5 (new) - gpt-5-mini (new) - gpt-5-nano (new) - o1 - o3 - o4-mini This brings GPT-5 vision capabilities in line with other OpenAI models that support image processing. Release Notes: - Added vision support for OpenAI models --- .../language_models/src/provider/open_ai.rs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 67bc081df3..50363e5c70 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -301,7 +301,25 @@ impl LanguageModel for OpenAiLanguageModel { } fn supports_images(&self) -> bool { - false + use open_ai::Model; + match &self.model { + Model::FourOmni + | Model::FourOmniMini + | Model::FourPointOne + | Model::FourPointOneMini + | Model::FourPointOneNano + | Model::Five + | Model::FiveMini + | Model::FiveNano + | Model::O1 + | Model::O3 + | Model::O4Mini => true, + Model::ThreePointFiveTurbo + | Model::Four + | Model::FourTurbo + | Model::O3Mini + | Model::Custom { .. } => false, + } } fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool { From 5067174d9b943b001ffc5f00f8a2304465060bf6 Mon Sep 17 00:00:00 2001 From: Zed Bot Date: Tue, 12 Aug 2025 19:18:59 +0000 Subject: [PATCH 29/37] Bump to 0.199.5 for @osyvokon --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c042681604..5332f9b9ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20461,7 +20461,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.4" +version = "0.199.5" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6e1f9359a5..f7918d45cb 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.4" +version = "0.199.5" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "] From 0be6cdc5f6d0bf818f933845c106a3c10e8824a1 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 13 Aug 2025 12:47:57 -0400 Subject: [PATCH 30/37] v0.199.x stable --- crates/zed/RELEASE_CHANNEL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zed/RELEASE_CHANNEL b/crates/zed/RELEASE_CHANNEL index 4de2f126df..870bbe4e50 100644 --- a/crates/zed/RELEASE_CHANNEL +++ b/crates/zed/RELEASE_CHANNEL @@ -1 +1 @@ -preview \ No newline at end of file +stable \ No newline at end of file From 8a181b2745071e7a83d534e0c15061ab6b3f4825 Mon Sep 17 00:00:00 2001 From: smit Date: Thu, 14 Aug 2025 00:19:37 +0530 Subject: [PATCH 31/37] copilot: Fix Copilot fails to sign in (#36138) Closes #36093 Pin copilot version to 1.354 for now until further investigation. Release Notes: - Fixes issue where Copilot failed to sign in. Co-authored-by: MrSubidubi --- crates/copilot/src/copilot.rs | 12 ++++++------ crates/languages/src/css.rs | 8 +++++++- crates/languages/src/json.rs | 8 +++++++- crates/languages/src/python.rs | 1 + crates/languages/src/tailwind.rs | 8 +++++++- crates/languages/src/typescript.rs | 1 + crates/languages/src/vtsls.rs | 2 ++ crates/languages/src/yaml.rs | 8 +++++++- crates/node_runtime/src/node_runtime.rs | 15 ++++++++++++++- 9 files changed, 52 insertions(+), 11 deletions(-) diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 49ae2b9d9c..166a582c70 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -21,7 +21,7 @@ use language::{ point_from_lsp, point_to_lsp, }; use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId, LanguageServerName}; -use node_runtime::NodeRuntime; +use node_runtime::{NodeRuntime, VersionCheck}; use parking_lot::Mutex; use project::DisableAiSettings; use request::StatusNotification; @@ -1169,9 +1169,8 @@ async fn get_copilot_lsp(fs: Arc, node_runtime: NodeRuntime) -> anyhow:: const SERVER_PATH: &str = "node_modules/@github/copilot-language-server/dist/language-server.js"; - let latest_version = node_runtime - .npm_package_latest_version(PACKAGE_NAME) - .await?; + // pinning it: https://github.com/zed-industries/zed/issues/36093 + const PINNED_VERSION: &str = "1.354"; let server_path = paths::copilot_dir().join(SERVER_PATH); fs.create_dir(paths::copilot_dir()).await?; @@ -1181,12 +1180,13 @@ async fn get_copilot_lsp(fs: Arc, node_runtime: NodeRuntime) -> anyhow:: PACKAGE_NAME, &server_path, paths::copilot_dir(), - &latest_version, + &PINNED_VERSION, + VersionCheck::VersionMismatch, ) .await; if should_install { node_runtime - .npm_install_packages(paths::copilot_dir(), &[(PACKAGE_NAME, &latest_version)]) + .npm_install_packages(paths::copilot_dir(), &[(PACKAGE_NAME, &PINNED_VERSION)]) .await?; } diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index 7725e079be..19329fcc6e 100644 --- a/crates/languages/src/css.rs +++ b/crates/languages/src/css.rs @@ -103,7 +103,13 @@ impl LspAdapter for CssLspAdapter { let should_install_language_server = self .node - .should_install_npm_package(Self::PACKAGE_NAME, &server_path, &container_dir, &version) + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + &version, + Default::default(), + ) .await; if should_install_language_server { diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index ca82bb2431..019b45d396 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -340,7 +340,13 @@ impl LspAdapter for JsonLspAdapter { let should_install_language_server = self .node - .should_install_npm_package(Self::PACKAGE_NAME, &server_path, &container_dir, &version) + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + &version, + Default::default(), + ) .await; if should_install_language_server { diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 0524c02fd5..5513324487 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -206,6 +206,7 @@ impl LspAdapter for PythonLspAdapter { &server_path, &container_dir, &version, + Default::default(), ) .await; diff --git a/crates/languages/src/tailwind.rs b/crates/languages/src/tailwind.rs index a7edbb148c..6f03eeda8d 100644 --- a/crates/languages/src/tailwind.rs +++ b/crates/languages/src/tailwind.rs @@ -108,7 +108,13 @@ impl LspAdapter for TailwindLspAdapter { let should_install_language_server = self .node - .should_install_npm_package(Self::PACKAGE_NAME, &server_path, &container_dir, &version) + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + &version, + Default::default(), + ) .await; if should_install_language_server { diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index f976b62614..a8ba880889 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -589,6 +589,7 @@ impl LspAdapter for TypeScriptLspAdapter { &server_path, &container_dir, version.typescript_version.as_str(), + Default::default(), ) .await; diff --git a/crates/languages/src/vtsls.rs b/crates/languages/src/vtsls.rs index 33751f733e..73498fc579 100644 --- a/crates/languages/src/vtsls.rs +++ b/crates/languages/src/vtsls.rs @@ -116,6 +116,7 @@ impl LspAdapter for VtslsLspAdapter { &server_path, &container_dir, &latest_version.server_version, + Default::default(), ) .await { @@ -129,6 +130,7 @@ impl LspAdapter for VtslsLspAdapter { &container_dir.join(Self::TYPESCRIPT_TSDK_PATH), &container_dir, &latest_version.typescript_version, + Default::default(), ) .await { diff --git a/crates/languages/src/yaml.rs b/crates/languages/src/yaml.rs index 815605d524..28be2cc1a4 100644 --- a/crates/languages/src/yaml.rs +++ b/crates/languages/src/yaml.rs @@ -104,7 +104,13 @@ impl LspAdapter for YamlLspAdapter { let should_install_language_server = self .node - .should_install_npm_package(Self::PACKAGE_NAME, &server_path, &container_dir, &version) + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + &version, + Default::default(), + ) .await; if should_install_language_server { diff --git a/crates/node_runtime/src/node_runtime.rs b/crates/node_runtime/src/node_runtime.rs index 08698a1d6c..6fcc3a728a 100644 --- a/crates/node_runtime/src/node_runtime.rs +++ b/crates/node_runtime/src/node_runtime.rs @@ -29,6 +29,15 @@ pub struct NodeBinaryOptions { pub use_paths: Option<(PathBuf, PathBuf)>, } +#[derive(Default)] +pub enum VersionCheck { + /// Check whether the installed and requested version have a mismatch + VersionMismatch, + /// Only check whether the currently installed version is older than the newest one + #[default] + OlderVersion, +} + #[derive(Clone)] pub struct NodeRuntime(Arc>); @@ -287,6 +296,7 @@ impl NodeRuntime { local_executable_path: &Path, local_package_directory: &Path, latest_version: &str, + version_check: VersionCheck, ) -> bool { // In the case of the local system not having the package installed, // or in the instances where we fail to parse package.json data, @@ -311,7 +321,10 @@ impl NodeRuntime { return true; }; - installed_version < latest_version + match version_check { + VersionCheck::VersionMismatch => installed_version != latest_version, + VersionCheck::OlderVersion => installed_version < latest_version, + } } } From 1879ef5ddb98acb52e88066cd68fb87ac956da33 Mon Sep 17 00:00:00 2001 From: smit Date: Thu, 14 Aug 2025 00:59:12 +0530 Subject: [PATCH 32/37] ci: Disable FreeBSD builds (#36140) Revert accidental change introduced in [#35880](https://github.com/zed-industries/zed/pull/35880/files#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03fL706) Release Notes: - N/A --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02edc99824..e53b64552b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -703,7 +703,7 @@ jobs: timeout-minutes: 60 runs-on: github-8vcpu-ubuntu-2404 if: | - ( startsWith(github.ref, 'refs/tags/v') + false && ( startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) needs: [linux_tests] name: Build Zed on FreeBSD From 332a02ab0aea47186523ad876cbdd6713d16943c Mon Sep 17 00:00:00 2001 From: Zed Bot Date: Wed, 13 Aug 2025 20:05:23 +0000 Subject: [PATCH 33/37] Bump to 0.199.6 for @smitbarmase --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5332f9b9ce..9752898741 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20461,7 +20461,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.5" +version = "0.199.6" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index f7918d45cb..5d9996f732 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.5" +version = "0.199.6" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "] From 5f4734749cfdb6b612aabc9d018ce774844403fc Mon Sep 17 00:00:00 2001 From: Cretezy Date: Wed, 13 Aug 2025 02:09:16 -0400 Subject: [PATCH 34/37] language_models: Add reasoning_effort for custom models (#35929) Release Notes: - Added `reasoning_effort` support to custom models Tested using the following config: ```json5 "language_models": { "openai": { "available_models": [ { "name": "gpt-5-mini", "display_name": "GPT 5 Mini (custom reasoning)", "max_output_tokens": 128000, "max_tokens": 272000, "reasoning_effort": "high" // Can be minimal, low, medium (default), and high } ], "version": "1" } } ``` Docs: https://platform.openai.com/docs/api-reference/chat/create#chat_create-reasoning_effort This work could be used to split the GPT 5/5-mini/5-nano into each of it's reasoning effort variant. E.g. `gpt-5`, `gpt-5 low`, `gpt-5 minimal`, `gpt-5 high`, and same for mini/nano. Release Notes: * Added a setting to control `reasoning_effort` in OpenAI models --- crates/language_models/src/provider/cloud.rs | 1 + .../language_models/src/provider/open_ai.rs | 7 +++++- .../src/provider/open_ai_compatible.rs | 8 ++++++- crates/language_models/src/provider/vercel.rs | 1 + crates/language_models/src/provider/x_ai.rs | 1 + crates/open_ai/src/open_ai.rs | 22 +++++++++++++++++++ 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index 40dd120761..0f0757f30f 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -942,6 +942,7 @@ impl LanguageModel for CloudLanguageModel { model.id(), model.supports_parallel_tool_calls(), None, + None, ); let llm_api_token = self.llm_api_token.clone(); let future = self.request_limiter.stream(async move { diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 50363e5c70..7814fb0e1f 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -14,7 +14,7 @@ use language_model::{ RateLimiter, Role, StopReason, TokenUsage, }; use menu; -use open_ai::{ImageUrl, Model, ResponseStreamEvent, stream_completion}; +use open_ai::{ImageUrl, Model, ReasoningEffort, ResponseStreamEvent, stream_completion}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; @@ -45,6 +45,7 @@ pub struct AvailableModel { pub max_tokens: u64, pub max_output_tokens: Option, pub max_completion_tokens: Option, + pub reasoning_effort: Option, } pub struct OpenAiLanguageModelProvider { @@ -213,6 +214,7 @@ impl LanguageModelProvider for OpenAiLanguageModelProvider { max_tokens: model.max_tokens, max_output_tokens: model.max_output_tokens, max_completion_tokens: model.max_completion_tokens, + reasoning_effort: model.reasoning_effort.clone(), }, ); } @@ -369,6 +371,7 @@ impl LanguageModel for OpenAiLanguageModel { self.model.id(), self.model.supports_parallel_tool_calls(), self.max_output_tokens(), + self.model.reasoning_effort(), ); let completions = self.stream_completion(request, cx); async move { @@ -384,6 +387,7 @@ pub fn into_open_ai( model_id: &str, supports_parallel_tool_calls: bool, max_output_tokens: Option, + reasoning_effort: Option, ) -> open_ai::Request { let stream = !model_id.starts_with("o1-"); @@ -490,6 +494,7 @@ pub fn into_open_ai( LanguageModelToolChoice::Any => open_ai::ToolChoice::Required, LanguageModelToolChoice::None => open_ai::ToolChoice::None, }), + reasoning_effort, } } diff --git a/crates/language_models/src/provider/open_ai_compatible.rs b/crates/language_models/src/provider/open_ai_compatible.rs index 38bd7cee06..6e912765cd 100644 --- a/crates/language_models/src/provider/open_ai_compatible.rs +++ b/crates/language_models/src/provider/open_ai_compatible.rs @@ -355,7 +355,13 @@ impl LanguageModel for OpenAiCompatibleLanguageModel { LanguageModelCompletionError, >, > { - let request = into_open_ai(request, &self.model.name, true, self.max_output_tokens()); + let request = into_open_ai( + request, + &self.model.name, + true, + self.max_output_tokens(), + None, + ); let completions = self.stream_completion(request, cx); async move { let mapper = OpenAiEventMapper::new(); diff --git a/crates/language_models/src/provider/vercel.rs b/crates/language_models/src/provider/vercel.rs index 037ce467d0..57a89ba4aa 100644 --- a/crates/language_models/src/provider/vercel.rs +++ b/crates/language_models/src/provider/vercel.rs @@ -356,6 +356,7 @@ impl LanguageModel for VercelLanguageModel { self.model.id(), self.model.supports_parallel_tool_calls(), self.max_output_tokens(), + None, ); let completions = self.stream_completion(request, cx); async move { diff --git a/crates/language_models/src/provider/x_ai.rs b/crates/language_models/src/provider/x_ai.rs index 5f6034571b..5e7190ea96 100644 --- a/crates/language_models/src/provider/x_ai.rs +++ b/crates/language_models/src/provider/x_ai.rs @@ -360,6 +360,7 @@ impl LanguageModel for XAiLanguageModel { self.model.id(), self.model.supports_parallel_tool_calls(), self.max_output_tokens(), + None, ); let completions = self.stream_completion(request, cx); async move { diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index 919b1d9ebf..5801f29623 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -89,11 +89,13 @@ pub enum Model { max_tokens: u64, max_output_tokens: Option, max_completion_tokens: Option, + reasoning_effort: Option, }, } impl Model { pub fn default_fast() -> Self { + // TODO: Replace with FiveMini since all other models are deprecated Self::FourPointOneMini } @@ -206,6 +208,15 @@ impl Model { } } + pub fn reasoning_effort(&self) -> Option { + match self { + Self::Custom { + reasoning_effort, .. + } => reasoning_effort.to_owned(), + _ => None, + } + } + /// Returns whether the given model supports the `parallel_tool_calls` parameter. /// /// If the model does not support the parameter, do not pass it up, or the API will return an error. @@ -246,6 +257,7 @@ pub struct Request { pub tools: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option, + pub reasoning_effort: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -257,6 +269,16 @@ pub enum ToolChoice { Other(ToolDefinition), } +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +#[serde(rename_all = "lowercase")] +pub enum ReasoningEffort { + Minimal, + Low, + Medium, + High, +} + #[derive(Clone, Deserialize, Serialize, Debug)] #[serde(tag = "type", rename_all = "snake_case")] pub enum ToolDefinition { From f7fefa34060f2597957dbcac3cc9495a596a5357 Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Fri, 15 Aug 2025 12:12:18 +0300 Subject: [PATCH 35/37] openai: Don't send reasoning_effort if it's not set (#36228) Release Notes: - N/A --- crates/open_ai/src/open_ai.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index 5801f29623..8bbe858995 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -257,6 +257,7 @@ pub struct Request { pub tools: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub reasoning_effort: Option, } From 09e9ef4705f0f67d7b8cafab61effa99f030e391 Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Fri, 15 Aug 2025 13:54:24 +0300 Subject: [PATCH 36/37] openai: Don't send prompt_cache_key for OpenAI-compatible models (#36231) Some APIs fail when they get this parameter Closes #36215 Release Notes: - Fixed OpenAI-compatible providers that don't support prompt caching and/or reasoning --- crates/language_models/src/provider/cloud.rs | 1 + crates/language_models/src/provider/open_ai.rs | 8 +++++++- crates/language_models/src/provider/open_ai_compatible.rs | 5 ++++- crates/language_models/src/provider/vercel.rs | 1 + crates/language_models/src/provider/x_ai.rs | 1 + crates/open_ai/src/open_ai.rs | 7 +++++++ crates/vercel/src/vercel.rs | 4 ++++ crates/x_ai/src/x_ai.rs | 4 ++++ 8 files changed, 29 insertions(+), 2 deletions(-) diff --git a/crates/language_models/src/provider/cloud.rs b/crates/language_models/src/provider/cloud.rs index 0f0757f30f..8aa5c54844 100644 --- a/crates/language_models/src/provider/cloud.rs +++ b/crates/language_models/src/provider/cloud.rs @@ -941,6 +941,7 @@ impl LanguageModel for CloudLanguageModel { request, model.id(), model.supports_parallel_tool_calls(), + model.supports_prompt_cache_key(), None, None, ); diff --git a/crates/language_models/src/provider/open_ai.rs b/crates/language_models/src/provider/open_ai.rs index 7814fb0e1f..64114d5a3e 100644 --- a/crates/language_models/src/provider/open_ai.rs +++ b/crates/language_models/src/provider/open_ai.rs @@ -370,6 +370,7 @@ impl LanguageModel for OpenAiLanguageModel { request, self.model.id(), self.model.supports_parallel_tool_calls(), + self.model.supports_prompt_cache_key(), self.max_output_tokens(), self.model.reasoning_effort(), ); @@ -386,6 +387,7 @@ pub fn into_open_ai( request: LanguageModelRequest, model_id: &str, supports_parallel_tool_calls: bool, + supports_prompt_cache_key: bool, max_output_tokens: Option, reasoning_effort: Option, ) -> open_ai::Request { @@ -477,7 +479,11 @@ pub fn into_open_ai( } else { None }, - prompt_cache_key: request.thread_id, + prompt_cache_key: if supports_prompt_cache_key { + request.thread_id + } else { + None + }, tools: request .tools .into_iter() diff --git a/crates/language_models/src/provider/open_ai_compatible.rs b/crates/language_models/src/provider/open_ai_compatible.rs index 6e912765cd..5f546f5219 100644 --- a/crates/language_models/src/provider/open_ai_compatible.rs +++ b/crates/language_models/src/provider/open_ai_compatible.rs @@ -355,10 +355,13 @@ impl LanguageModel for OpenAiCompatibleLanguageModel { LanguageModelCompletionError, >, > { + let supports_parallel_tool_call = true; + let supports_prompt_cache_key = false; let request = into_open_ai( request, &self.model.name, - true, + supports_parallel_tool_call, + supports_prompt_cache_key, self.max_output_tokens(), None, ); diff --git a/crates/language_models/src/provider/vercel.rs b/crates/language_models/src/provider/vercel.rs index 57a89ba4aa..9f447cb68b 100644 --- a/crates/language_models/src/provider/vercel.rs +++ b/crates/language_models/src/provider/vercel.rs @@ -355,6 +355,7 @@ impl LanguageModel for VercelLanguageModel { request, self.model.id(), self.model.supports_parallel_tool_calls(), + self.model.supports_prompt_cache_key(), self.max_output_tokens(), None, ); diff --git a/crates/language_models/src/provider/x_ai.rs b/crates/language_models/src/provider/x_ai.rs index 5e7190ea96..fed6fe92bf 100644 --- a/crates/language_models/src/provider/x_ai.rs +++ b/crates/language_models/src/provider/x_ai.rs @@ -359,6 +359,7 @@ impl LanguageModel for XAiLanguageModel { request, self.model.id(), self.model.supports_parallel_tool_calls(), + self.model.supports_prompt_cache_key(), self.max_output_tokens(), None, ); diff --git a/crates/open_ai/src/open_ai.rs b/crates/open_ai/src/open_ai.rs index 8bbe858995..604e8fe622 100644 --- a/crates/open_ai/src/open_ai.rs +++ b/crates/open_ai/src/open_ai.rs @@ -236,6 +236,13 @@ impl Model { Self::O1 | Self::O3 | Self::O3Mini | Self::O4Mini | Model::Custom { .. } => false, } } + + /// Returns whether the given model supports the `prompt_cache_key` parameter. + /// + /// If the model does not support the parameter, do not pass it up. + pub fn supports_prompt_cache_key(&self) -> bool { + return true; + } } #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/vercel/src/vercel.rs b/crates/vercel/src/vercel.rs index 1ae22c5fef..8686fda53f 100644 --- a/crates/vercel/src/vercel.rs +++ b/crates/vercel/src/vercel.rs @@ -71,4 +71,8 @@ impl Model { Model::Custom { .. } => false, } } + + pub fn supports_prompt_cache_key(&self) -> bool { + false + } } diff --git a/crates/x_ai/src/x_ai.rs b/crates/x_ai/src/x_ai.rs index ac116b2f8f..23cd5b9320 100644 --- a/crates/x_ai/src/x_ai.rs +++ b/crates/x_ai/src/x_ai.rs @@ -105,6 +105,10 @@ impl Model { } } + pub fn supports_prompt_cache_key(&self) -> bool { + false + } + pub fn supports_tool(&self) -> bool { match self { Self::Grok2Vision From 76ca96c1bfff54dfccee014dc08d3655b3156ae1 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Fri, 15 Aug 2025 13:19:52 -0400 Subject: [PATCH 37/37] zed 0.199.7 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9752898741..8f72e783c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20461,7 +20461,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.199.6" +version = "0.199.7" dependencies = [ "activity_indicator", "agent", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 5d9996f732..04af522e18 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -2,7 +2,7 @@ description = "The fast, collaborative code editor." edition.workspace = true name = "zed" -version = "0.199.6" +version = "0.199.7" publish.workspace = true license = "GPL-3.0-or-later" authors = ["Zed Team "]