diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index a56793ad6222e5..a948bb5acad707 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,12 +1,45 @@
-Self-Review Checklist:
+# Objective
+
+- Describe the objective or issue this PR addresses.
+- If you're fixing a specific issue, use "Fixes #X" for each issue as [described in the GitHub docs](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword).
+
+## Solution
+
+- Describe the solution used to achieve the objective above.
+
+## Testing
+
+- Did you test these changes? If so, how?
+- Are there any parts that need more testing?
+- How can other people (reviewers) test your changes? Is there anything specific they need to know?
+- If relevant, what platforms did you test these changes on, and are there any important ones you can't test?
+
+## Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
-- [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
+- [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
-Closes #ISSUE
+## Showcase
+
+> This section is optional. If this PR does not include a visual change or does not add a new user-facing feature, you can delete this section.
+
+- Help others understand the result of this PR by showcasing your awesome work!
+- If this PR includes a visual change, consider adding a screenshot, GIF, or video
+ - A before/after comparison is very useful for changes to existing features!
+
+While a showcase should aim to be brief and digestible, you can use a toggleable section to save space on longer showcases:
+
+
+ Click to view showcase
+
+My super cool demos here
+
+
+
+---
Release Notes:
diff --git a/.github/workflows/after_release.yml b/.github/workflows/after_release.yml
index b7ac11b263212e..7100909788765e 100644
--- a/.github/workflows/after_release.yml
+++ b/.github/workflows/after_release.yml
@@ -41,7 +41,7 @@ jobs:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
permissions:
contents: read
- uses: zed-industries/zed/.github/workflows/deploy_docs.yml@main
+ uses: zed-industries/zed/.github/workflows/deploy_docs.yml@3f16f7b9082f8828e4d6ae207d2349b1ef932517
secrets:
DOCS_AMPLITUDE_API_KEY: ${{ secrets.DOCS_AMPLITUDE_API_KEY }}
DOCS_CONSENT_IO_INSTANCE: ${{ secrets.DOCS_CONSENT_IO_INSTANCE }}
diff --git a/.github/workflows/assign-reviewers.yml b/.github/workflows/assign-reviewers.yml
deleted file mode 100644
index 2a12a69defdd4f..00000000000000
--- a/.github/workflows/assign-reviewers.yml
+++ /dev/null
@@ -1,104 +0,0 @@
-# Assign Reviewers — Smart team assignment based on diff weight
-#
-# Triggers on PR open and ready_for_review events. Checks out the coordinator
-# repo (zed-industries/codeowner-coordinator) to access the assignment script and rules,
-# then assigns the 1-2 most relevant teams as reviewers.
-#
-# NOTE: This file is stored in the codeowner-coordinator repo but must be deployed to
-# the zed repo at .github/workflows/assign-reviewers.yml. See INSTALL.md.
-#
-# AUTH NOTE: Uses a GitHub App (COORDINATOR_APP_ID + COORDINATOR_APP_PRIVATE_KEY)
-# for all API operations: cloning the private coordinator repo, requesting team
-# reviewers, and setting PR assignees. GITHUB_TOKEN is not used.
-#
-# SECURITY INVARIANTS (pull_request_target):
-# This workflow runs with access to secrets for ALL PRs including forks.
-# It is safe ONLY because:
-# 1. The checkout is the coordinator repo at ref: main — NEVER the PR head/branch
-# 2. No ${{ }} interpolation of event fields in run: blocks — all routed via env:
-# 3. The script never executes, sources, or reads files from the PR branch
-# Violating any of these enables remote code execution with secret access.
-
-name: Assign Reviewers
-
-on:
- # zizmor: ignore[dangerous-triggers] reviewed — no PR code checkout, only coordinator repo at ref: main
- pull_request_target:
- types: [opened, ready_for_review]
-
-# GITHUB_TOKEN is not used — all operations use the GitHub App token.
-# Declare minimal permissions so the default token has no write access.
-permissions: {}
-
-# Prevent duplicate runs for the same PR (e.g., rapid push + ready_for_review).
-concurrency:
- group: assign-reviewers-${{ github.event.pull_request.number }}
- cancel-in-progress: true
-
-# NOTE: For ready_for_review events, the webhook payload may still carry
-# draft: true due to a GitHub race condition (payload serialized before DB
-# update). We trust the event type instead — the script rechecks draft status
-# via a live API call as defense-in-depth.
-#
-# No author_association filter — external and fork PRs also get reviewer
-# assignments. Assigned reviewers are inherently scoped to org team members
-# by the GitHub Teams API.
-jobs:
- assign-reviewers:
- if: >-
- github.event.action == 'ready_for_review' || github.event.pull_request.draft == false
- runs-on: ubuntu-latest
- steps:
- - name: Generate app token
- id: app-token
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
- with:
- app-id: ${{ vars.COORDINATOR_APP_ID }}
- private-key: ${{ secrets.COORDINATOR_APP_PRIVATE_KEY }}
- repositories: codeowner-coordinator,zed
-
- # SECURITY: checks out the coordinator repo at ref: main, NOT the PR branch.
- # persist-credentials: false prevents the token from leaking into .git/config.
- - name: Checkout coordinator repo
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- with:
- repository: zed-industries/codeowner-coordinator
- ref: main
- path: codeowner-coordinator
- token: ${{ steps.app-token.outputs.token }}
- persist-credentials: false
-
- - name: Setup Python
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- with:
- python-version: "3.11"
-
- - name: Install dependencies
- run: |
- pip install --no-deps -q --only-binary ':all:' \
- -r /dev/stdin <<< "pyyaml==6.0.3 --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"
-
- - name: Assign reviewers
- env:
- GH_TOKEN: ${{ steps.app-token.outputs.token }}
- PR_URL: ${{ github.event.pull_request.html_url }}
- TARGET_REPO: ${{ github.repository }}
- ASSIGN_INTERNAL: ${{ vars.ASSIGN_INTERNAL || 'false' }}
- ASSIGN_EXTERNAL: ${{ vars.ASSIGN_EXTERNAL || 'true' }}
- run: |
- cd codeowner-coordinator
- python .github/scripts/assign-reviewers.py \
- --pr "$PR_URL" \
- --apply \
- --rules-file team-membership-rules.yml \
- --repo "$TARGET_REPO" \
- --org zed-industries \
- 2>&1 | tee /tmp/assign-reviewers-output.txt
-
- - name: Upload output
- if: always()
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
- with:
- name: assign-reviewers-output
- path: /tmp/assign-reviewers-output.txt
- retention-days: 30
diff --git a/.github/workflows/assign_contributor_issue.yml b/.github/workflows/assign_contributor_issue.yml
deleted file mode 100644
index 5e968611299e26..00000000000000
--- a/.github/workflows/assign_contributor_issue.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-# Assign Contributor Issue — auto-assign labeled contributor issues
-#
-# When an issue has both a `.contrib/good *` label and an `area:` label,
-# finds the least-busy contributor interested in that area (via Tally form
-# responses), assigns the issue, updates the project board, and notifies
-# the contributor on Slack.
-#
-# Errors and "no candidates" conditions are reported to the Slack activity
-# channel.
-
-name: Assign Contributor Issue
-
-on:
- issues:
- types: [labeled]
- workflow_dispatch:
- inputs:
- issue_number:
- description: "Issue number to test against"
- required: true
- type: number
-
-permissions:
- contents: read
-
-concurrency:
- group: assign-contributor-${{ github.event.issue.number || inputs.issue_number }}
- cancel-in-progress: true
-
-jobs:
- assign-contributor:
- if: >-
- github.event_name == 'workflow_dispatch' ||
- (github.repository == 'zed-industries/zed' &&
- github.event.issue.state == 'open' &&
- (startsWith(github.event.label.name, '.contrib/good ') || startsWith(github.event.label.name, 'area:')))
- runs-on: namespace-profile-2x4-ubuntu-2404
- timeout-minutes: 5
-
- steps:
- - name: Generate app token
- id: app-token
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
- with:
- app-id: ${{ secrets.ZED_COMMUNITY_BOT_APP_ID }}
- private-key: ${{ secrets.ZED_COMMUNITY_BOT_PRIVATE_KEY }}
- owner: zed-industries
-
- - name: Checkout repository
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- with:
- sparse-checkout: script/github-assign-contributor-issue.py
- sparse-checkout-cone-mode: false
-
- - name: Set up Python
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
- with:
- python-version: "3.12"
-
- - name: Install dependencies
- run: pip install requests
-
- - name: Assign contributor
- env:
- GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
- TALLY_API_KEY: ${{ secrets.TALLY_API_KEY }}
- TALLY_FORM_ID: ${{ vars.TALLY_CONTRIBUTOR_FORM_ID }}
- SLACK_BOT_TOKEN: ${{ secrets.SLACK_CONTRIBUTOR_BOT_TOKEN }}
- ISSUE_NUMBER: ${{ github.event.issue.number || inputs.issue_number }}
- run: python script/github-assign-contributor-issue.py "$ISSUE_NUMBER"
diff --git a/.github/workflows/background_agent_mvp.yml b/.github/workflows/background_agent_mvp.yml
deleted file mode 100644
index 2f048d572df6fb..00000000000000
--- a/.github/workflows/background_agent_mvp.yml
+++ /dev/null
@@ -1,331 +0,0 @@
-name: background_agent_mvp
-
-# NOTE: Scheduled runs disabled as of 2026-02-24. The workflow can still be
-# triggered manually via workflow_dispatch. See Notion doc "Background Agent
-# for Zed" for current status and contact info to resume this work.
-on:
- # schedule:
- # - cron: "0 16 * * 1-5"
- workflow_dispatch:
- inputs:
- crash_ids:
- description: "Optional comma-separated Sentry issue IDs (e.g. ZED-4VS,ZED-123)"
- required: false
- type: string
- reviewers:
- description: "Optional comma-separated GitHub reviewer handles"
- required: false
- type: string
- top:
- description: "Top N candidates when crash_ids is empty"
- required: false
- type: string
- default: "3"
-
-permissions:
- contents: write
- pull-requests: write
-
-env:
- FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
- DROID_MODEL: claude-opus-4-5-20251101
- SENTRY_ORG: zed-dev
-
-jobs:
- run-mvp:
- runs-on: ubuntu-latest
- timeout-minutes: 180
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- with:
- fetch-depth: 0
-
- - name: Install Droid CLI
- run: |
- curl -fsSL https://app.factory.ai/cli | sh
- echo "${HOME}/.local/bin" >> "$GITHUB_PATH"
- echo "DROID_BIN=${HOME}/.local/bin/droid" >> "$GITHUB_ENV"
- "${HOME}/.local/bin/droid" --version
-
- - name: Setup Python
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
- with:
- python-version: "3.12"
-
- - name: Resolve reviewers
- id: reviewers
- env:
- INPUT_REVIEWERS: ${{ inputs.reviewers }}
- DEFAULT_REVIEWERS: ${{ vars.BACKGROUND_AGENT_REVIEWERS }}
- run: |
- set -euo pipefail
- if [ -z "$DEFAULT_REVIEWERS" ]; then
- DEFAULT_REVIEWERS="eholk,morgankrey,osiewicz,bennetbo"
- fi
- REVIEWERS="${INPUT_REVIEWERS:-$DEFAULT_REVIEWERS}"
- REVIEWERS="$(echo "$REVIEWERS" | tr -d '[:space:]')"
- echo "reviewers=$REVIEWERS" >> "$GITHUB_OUTPUT"
-
- - name: Select crash candidates
- id: candidates
- env:
- INPUT_CRASH_IDS: ${{ inputs.crash_ids }}
- INPUT_TOP: ${{ inputs.top }}
- SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_BACKGROUND_AGENT_MVP_TOKEN }}
- run: |
- set -euo pipefail
-
- PREFETCH_DIR="/tmp/crash-data"
- ARGS=(--select-only --prefetch-dir "$PREFETCH_DIR" --org "$SENTRY_ORG")
- if [ -n "$INPUT_CRASH_IDS" ]; then
- ARGS+=(--crash-ids "$INPUT_CRASH_IDS")
- else
- TARGET_DRAFT_PRS="${INPUT_TOP:-3}"
- if ! [[ "$TARGET_DRAFT_PRS" =~ ^[0-9]+$ ]] || [ "$TARGET_DRAFT_PRS" -lt 1 ]; then
- TARGET_DRAFT_PRS="3"
- fi
- CANDIDATE_TOP=$((TARGET_DRAFT_PRS * 5))
- if [ "$CANDIDATE_TOP" -gt 100 ]; then
- CANDIDATE_TOP=100
- fi
- ARGS+=(--top "$CANDIDATE_TOP" --sample-size 100)
- fi
-
- IDS="$(python3 script/run-background-agent-mvp-local "${ARGS[@]}")"
-
- if [ -z "$IDS" ]; then
- echo "No candidates selected"
- exit 1
- fi
-
- echo "Using crash IDs: $IDS"
- echo "ids=$IDS" >> "$GITHUB_OUTPUT"
-
- - name: Run background agent pipeline per crash
- id: pipeline
- env:
- GH_TOKEN: ${{ github.token }}
- REVIEWERS: ${{ steps.reviewers.outputs.reviewers }}
- CRASH_IDS: ${{ steps.candidates.outputs.ids }}
- TARGET_DRAFT_PRS_INPUT: ${{ inputs.top }}
- run: |
- set -euo pipefail
-
- git config user.name "factory-droid[bot]"
- git config user.email "138933559+factory-droid[bot]@users.noreply.github.com"
-
- # Crash ID format validation regex
- CRASH_ID_PATTERN='^[A-Za-z0-9]+-[A-Za-z0-9]+$'
- TARGET_DRAFT_PRS="${TARGET_DRAFT_PRS_INPUT:-3}"
- if ! [[ "$TARGET_DRAFT_PRS" =~ ^[0-9]+$ ]] || [ "$TARGET_DRAFT_PRS" -lt 1 ]; then
- TARGET_DRAFT_PRS="3"
- fi
- CREATED_DRAFT_PRS=0
-
- IFS=',' read -r -a CRASH_ID_ARRAY <<< "$CRASH_IDS"
-
- for CRASH_ID in "${CRASH_ID_ARRAY[@]}"; do
- if [ "$CREATED_DRAFT_PRS" -ge "$TARGET_DRAFT_PRS" ]; then
- echo "Reached target draft PR count ($TARGET_DRAFT_PRS), stopping candidate processing"
- break
- fi
-
- CRASH_ID="$(echo "$CRASH_ID" | xargs)"
- [ -z "$CRASH_ID" ] && continue
-
- # Validate crash ID format to prevent injection via branch names or prompts
- if ! [[ "$CRASH_ID" =~ $CRASH_ID_PATTERN ]]; then
- echo "ERROR: Invalid crash ID format: '$CRASH_ID' — skipping"
- continue
- fi
-
- BRANCH="background-agent/mvp-${CRASH_ID,,}-$(date +%Y%m%d)"
- echo "Running crash pipeline for $CRASH_ID on $BRANCH"
-
- # Deduplication: skip if a draft PR already exists for this crash
- EXISTING_BRANCH_PR="$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number' || echo "")"
- if [ -n "$EXISTING_BRANCH_PR" ]; then
- echo "Draft PR #$EXISTING_BRANCH_PR already exists for $CRASH_ID — skipping"
- continue
- fi
-
- if ! git fetch origin main; then
- echo "WARNING: Failed to fetch origin/main for $CRASH_ID — skipping"
- continue
- fi
-
- if ! git checkout -B "$BRANCH" origin/main; then
- echo "WARNING: Failed to create checkout branch $BRANCH for $CRASH_ID — skipping"
- continue
- fi
-
- CRASH_DATA_FILE="/tmp/crash-data/crash-${CRASH_ID}.md"
- if [ ! -f "$CRASH_DATA_FILE" ]; then
- echo "WARNING: No pre-fetched crash data for $CRASH_ID at $CRASH_DATA_FILE — skipping"
- continue
- fi
-
- python3 -c "
- import sys
- crash_id, data_file = sys.argv[1], sys.argv[2]
- prompt = f'''You are running the weekly background crash-fix MVP pipeline for crash {crash_id}.
-
- The crash report has been pre-fetched and is available at: {data_file}
- Read this file to get the crash data. Do not call script/sentry-fetch.
-
- Required workflow:
- 1. Read the crash report from {data_file}
- 2. Read and follow .rules.
- 3. Follow .factory/prompts/crash/investigate.md and write ANALYSIS.md
- 4. Follow .factory/prompts/crash/link-issues.md and write LINKED_ISSUES.md
- 5. Follow .factory/prompts/crash/fix.md to implement a minimal fix with tests
- 6. Run validators required by the fix prompt for the affected code paths
- 7. Write PR_BODY.md with sections:
- - Crash Summary
- - Root Cause
- - Fix
- - Validation
- - Potentially Related Issues (High/Medium/Low from LINKED_ISSUES.md)
- - Reviewer Checklist
- - Release Notes (final section; format as Release Notes:, then a blank line, then one bullet like - N/A)
-
- Constraints:
- - Do not merge or auto-approve.
- - Keep changes narrowly scoped to this crash.
- - Do not modify files in .github/, .factory/, or script/ directories.
- - When investigating git history, limit your search to the last 2 weeks of commits. Do not traverse older history.
- - If the crash is not solvable with available context, write a clear blocker summary to PR_BODY.md.
- '''
- import textwrap
- with open('/tmp/background-agent-prompt.md', 'w') as f:
- f.write(textwrap.dedent(prompt))
- " "$CRASH_ID" "$CRASH_DATA_FILE"
-
- if ! "$DROID_BIN" exec --auto medium -m "$DROID_MODEL" -f /tmp/background-agent-prompt.md; then
- echo "Droid execution failed for $CRASH_ID, continuing to next candidate"
- continue
- fi
-
- for REPORT_FILE in ANALYSIS.md LINKED_ISSUES.md PR_BODY.md; do
- if [ -f "$REPORT_FILE" ]; then
- echo "::group::${CRASH_ID} ${REPORT_FILE}"
- cat "$REPORT_FILE"
- echo "::endgroup::"
- fi
- done
-
- if git diff --quiet; then
- echo "No code changes produced for $CRASH_ID"
- continue
- fi
-
- # Stage only expected file types — not git add -A
- git add -- '*.rs' '*.toml' 'Cargo.lock' 'ANALYSIS.md' 'LINKED_ISSUES.md' 'PR_BODY.md'
-
- # Reject changes to protected paths
- PROTECTED_CHANGES="$(git diff --cached --name-only | grep -E '^(\.github/|\.factory/|script/)' || true)"
- if [ -n "$PROTECTED_CHANGES" ]; then
- echo "ERROR: Agent modified protected paths — aborting commit for $CRASH_ID:"
- echo "$PROTECTED_CHANGES"
- git reset HEAD -- .
- continue
- fi
-
- if ! git diff --cached --quiet; then
- git commit -m "Fix crash ${CRASH_ID}"
- fi
-
- git push -u origin "$BRANCH"
-
- CRATE_PREFIX=""
- CHANGED_CRATES="$(git diff --cached --name-only | awk -F/ '/^crates\/[^/]+\// {print $2}' | sort -u)"
- if [ -n "$CHANGED_CRATES" ] && [ "$(printf "%s\n" "$CHANGED_CRATES" | wc -l | tr -d ' ')" -eq 1 ]; then
- CRATE_PREFIX="${CHANGED_CRATES}: "
- fi
-
- TITLE="${CRATE_PREFIX}Fix crash ${CRASH_ID}"
- BODY_FILE="PR_BODY.md"
- if [ ! -f "$BODY_FILE" ]; then
- BODY_FILE="/tmp/pr-body-${CRASH_ID}.md"
- printf "Automated draft crash-fix pipeline output for %s.\n\nNo PR_BODY.md was generated by the agent; please review commit and linked artifacts manually.\n" "$CRASH_ID" > "$BODY_FILE"
- fi
-
- python3 -c '
- import re
- import sys
-
- path = sys.argv[1]
- body = open(path, encoding="utf-8").read()
- pattern = re.compile(r"(^|\n)Release Notes:\r?\n(?:\r?\n)*(?P(?:\s*-\s+.*(?:\r?\n|$))+)", re.MULTILINE)
- match = pattern.search(body)
-
- if match:
- bullets = [
- re.sub(r"^\s*", "", bullet)
- for bullet in re.findall(r"^\s*-\s+.*$", match.group("bullets"), re.MULTILINE)
- ]
- if not bullets:
- bullets = ["- N/A"]
- section = "Release Notes:\n\n" + "\n".join(bullets)
- body_without_release_notes = (body[: match.start()] + body[match.end() :]).rstrip()
- if body_without_release_notes:
- normalized_body = f"{body_without_release_notes}\n\n{section}\n"
- else:
- normalized_body = f"{section}\n"
- else:
- normalized_body = body.rstrip() + "\n\nRelease Notes:\n\n- N/A\n"
-
- with open(path, "w", encoding="utf-8") as file:
- file.write(normalized_body)
- ' "$BODY_FILE"
-
- EXISTING_PR="$(gh pr list --head "$BRANCH" --json number --jq '.[0].number')"
- if [ -n "$EXISTING_PR" ]; then
- gh pr edit "$EXISTING_PR" --title "$TITLE" --body-file "$BODY_FILE"
- PR_NUMBER="$EXISTING_PR"
- else
- PR_URL="$(gh pr create --draft --base main --head "$BRANCH" --title "$TITLE" --body-file "$BODY_FILE")"
- PR_NUMBER="$(basename "$PR_URL")"
- fi
-
- if [ -n "$REVIEWERS" ]; then
- IFS=',' read -r -a REVIEWER_ARRAY <<< "$REVIEWERS"
- for REVIEWER in "${REVIEWER_ARRAY[@]}"; do
- [ -z "$REVIEWER" ] && continue
- gh pr edit "$PR_NUMBER" --add-reviewer "$REVIEWER" || true
- done
- fi
-
- CREATED_DRAFT_PRS=$((CREATED_DRAFT_PRS + 1))
- echo "Created/updated draft PRs this run: $CREATED_DRAFT_PRS/$TARGET_DRAFT_PRS"
- done
-
- echo "created_draft_prs=$CREATED_DRAFT_PRS" >> "$GITHUB_OUTPUT"
- echo "target_draft_prs=$TARGET_DRAFT_PRS" >> "$GITHUB_OUTPUT"
-
- - name: Cleanup pre-fetched crash data
- if: always()
- run: rm -rf /tmp/crash-data
-
- - name: Workflow summary
- if: always()
- env:
- SUMMARY_CRASH_IDS: ${{ steps.candidates.outputs.ids }}
- SUMMARY_REVIEWERS: ${{ steps.reviewers.outputs.reviewers }}
- SUMMARY_CREATED_DRAFT_PRS: ${{ steps.pipeline.outputs.created_draft_prs }}
- SUMMARY_TARGET_DRAFT_PRS: ${{ steps.pipeline.outputs.target_draft_prs }}
- run: |
- {
- echo "## Background Agent MVP"
- echo ""
- echo "- Crash IDs: ${SUMMARY_CRASH_IDS:-none}"
- echo "- Reviewer routing: ${SUMMARY_REVIEWERS:-NOT CONFIGURED}"
- echo "- Draft PRs created: ${SUMMARY_CREATED_DRAFT_PRS:-0}/${SUMMARY_TARGET_DRAFT_PRS:-3}"
- echo "- Pipeline: investigate -> link-issues -> fix -> draft PR"
- } >> "$GITHUB_STEP_SUMMARY"
-
-concurrency:
- group: background-agent-mvp
- cancel-in-progress: false
diff --git a/.github/workflows/community_close_stale_issues.yml b/.github/workflows/community_close_stale_issues.yml
index cae4084c1dc643..be1d8e66d046ae 100644
--- a/.github/workflows/community_close_stale_issues.yml
+++ b/.github/workflows/community_close_stale_issues.yml
@@ -26,7 +26,8 @@ jobs:
If you can reproduce this bug on the latest stable Zed, please let us know by leaving a comment with the Zed version,
it helps us focus on the right issues.
If the bug doesn't appear for you anymore, feel free to close the issue yourself; otherwise, the bot will close it in a couple of weeks.
- But even after it's closed by the bot, you can leave a comment with the version where the bug is reproducible and we'll reopen the issue.
+ But even after it's closed by the bot, you can leave a comment **with the version where the bug is reproducible** and we'll reopen the issue.
+ (This bot will only ask about this issue once)
Thanks!
close-issue-message: "This issue was closed due to inactivity. If you're still experiencing this problem, please leave a comment with your Zed version so that we can reopen the issue."
days-before-stale: 90
@@ -38,3 +39,4 @@ jobs:
debug-only: ${{ inputs.debug-only }}
stale-issue-label: "stale"
exempt-issue-labels: "never stale"
+ labels-to-add-when-unstale: "never stale"
diff --git a/.github/workflows/community_pr_board.yml b/.github/workflows/community_pr_board.yml
index 03bb9381739eb1..d155cf8275dbb5 100644
--- a/.github/workflows/community_pr_board.yml
+++ b/.github/workflows/community_pr_board.yml
@@ -13,7 +13,7 @@ name: Community PR Board
on:
pull_request_target:
- types: [labeled, unlabeled, assigned, review_requested]
+ types: [labeled, unlabeled, assigned, review_requested, edited]
issue_comment:
types: [created]
workflow_dispatch:
diff --git a/.github/workflows/community_pr_board_refresh.yml b/.github/workflows/community_pr_board_refresh.yml
new file mode 100644
index 00000000000000..1d77638c82d381
--- /dev/null
+++ b/.github/workflows/community_pr_board_refresh.yml
@@ -0,0 +1,56 @@
+# Community PR Board — daily meta information refresh
+#
+# Walks every open PR on the community board and recomputes its signal
+# fields. Backstop for changes that don't reach the event-driven workflow,
+# either because the relevant webhook isn't subscribed or doesn't fire at all.
+
+name: PR Board Meta Fields Refresh
+
+on:
+ schedule:
+ - cron: "0 9 * * *"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: community-pr-board-refresh
+ cancel-in-progress: true
+
+jobs:
+ refresh:
+ if: github.repository == 'zed-industries/zed'
+ runs-on: namespace-profile-2x4-ubuntu-2404
+ timeout-minutes: 15
+
+ steps:
+ - name: Generate app token
+ id: app-token
+ uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
+ with:
+ app-id: ${{ secrets.ZED_COMMUNITY_BOT_APP_ID }}
+ private-key: ${{ secrets.ZED_COMMUNITY_BOT_PRIVATE_KEY }}
+ owner: zed-industries
+
+ - name: Checkout repository
+ uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
+ with:
+ sparse-checkout: |
+ script/github-community-pr-board.py
+ sparse-checkout-cone-mode: false
+
+ - name: Set up Python
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ with:
+ python-version: "3.12"
+
+ - name: Install dependencies
+ run: pip install requests
+
+ - name: Refresh all board items
+ env:
+ GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
+ PROJECT_NUMBER: "85"
+ REFRESH_ALL: "1"
+ run: python script/github-community-pr-board.py
diff --git a/.github/workflows/compare_perf.yml b/.github/workflows/compare_perf.yml
deleted file mode 100644
index 154276a7104733..00000000000000
--- a/.github/workflows/compare_perf.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-# Generated from xtask::workflows::compare_perf
-# Rebuild with `cargo xtask workflows`.
-name: compare_perf
-on:
- workflow_dispatch:
- inputs:
- head:
- description: head
- required: true
- type: string
- base:
- description: base
- required: true
- type: string
- crate_name:
- description: crate_name
- type: string
- default: ''
-jobs:
- run_perf:
- runs-on: namespace-profile-16x32-ubuntu-2204
- steps:
- - name: steps::checkout_repo
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
- with:
- clean: false
- - name: steps::setup_cargo_config
- run: |
- mkdir -p ./../.cargo
- cp ./.cargo/ci-config.toml ./../.cargo/config.toml
- - name: steps::setup_linux
- run: ./script/linux
- - name: steps::download_wasi_sdk
- run: ./script/download-wasi-sdk
- - name: compare_perf::run_perf::install_hyperfine
- uses: taiki-e/install-action@b4f2d5cb8597b15997c8ede873eb6185efc5f0ad
- - name: steps::git_checkout
- run: git fetch origin "$REF_NAME" && git checkout "$REF_NAME"
- env:
- REF_NAME: ${{ inputs.base }}
- - name: compare_perf::run_perf::cargo_perf_test
- run: |2-
-
- if [ -n "$CRATE_NAME" ]; then
- cargo perf-test -p "$CRATE_NAME" -- --json="$REF_NAME";
- else
- cargo perf-test -p vim -- --json="$REF_NAME";
- fi
- env:
- REF_NAME: ${{ inputs.base }}
- CRATE_NAME: ${{ inputs.crate_name }}
- - name: steps::git_checkout
- run: git fetch origin "$REF_NAME" && git checkout "$REF_NAME"
- env:
- REF_NAME: ${{ inputs.head }}
- - name: compare_perf::run_perf::cargo_perf_test
- run: |2-
-
- if [ -n "$CRATE_NAME" ]; then
- cargo perf-test -p "$CRATE_NAME" -- --json="$REF_NAME";
- else
- cargo perf-test -p vim -- --json="$REF_NAME";
- fi
- env:
- REF_NAME: ${{ inputs.head }}
- CRATE_NAME: ${{ inputs.crate_name }}
- - name: compare_perf::run_perf::compare_runs
- run: cargo perf-compare --save=results.md "$BASE" "$HEAD"
- env:
- BASE: ${{ inputs.base }}
- HEAD: ${{ inputs.head }}
- - name: run_bundling::upload_artifact
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
- with:
- name: results.md
- path: results.md
- if-no-files-found: error
- - name: steps::cleanup_cargo_config
- if: always()
- run: |
- rm -rf ./../.cargo
-defaults:
- run:
- shell: bash -euxo pipefail {0}
diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index 62739b21675fec..4cb0df5735a5c8 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -2,6 +2,7 @@
# Rebuild with `cargo xtask workflows`.
name: danger
on:
+ merge_group: {}
pull_request:
types:
- opened
diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml
index 708fad67528d4c..ef6cb399a761ce 100644
--- a/.github/workflows/deploy_collab.yml
+++ b/.github/workflows/deploy_collab.yml
@@ -71,7 +71,7 @@ jobs:
run: cargo nextest run --package collab --no-fail-fast
services:
postgres:
- image: postgres:15
+ image: postgres:15@sha256:1b92e7a80c021647bf70f5d3eb66066a998e4f5cf43c07bb9dc9f729782cf88e
env:
POSTGRES_HOST_AUTH_METHOD: trust
ports:
@@ -85,7 +85,7 @@ jobs:
runs-on: namespace-profile-16x32-ubuntu-2204
steps:
- name: deploy_collab::publish::install_doctl
- uses: digitalocean/action-doctl@v2
+ uses: digitalocean/action-doctl@3cb3953159719656269e044e0e24ca16dd2a690f
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: deploy_collab::publish::sign_into_registry
@@ -115,7 +115,7 @@ jobs:
with:
clean: false
- name: deploy_collab::deploy::install_doctl
- uses: digitalocean/action-doctl@v2
+ uses: digitalocean/action-doctl@3cb3953159719656269e044e0e24ca16dd2a690f
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: deploy_collab::deploy::sign_into_kubernetes
diff --git a/.github/workflows/deploy_nightly_docs.yml b/.github/workflows/deploy_nightly_docs.yml
index 91dcc6a2773b27..acd904841bb33c 100644
--- a/.github/workflows/deploy_nightly_docs.yml
+++ b/.github/workflows/deploy_nightly_docs.yml
@@ -10,7 +10,7 @@ jobs:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
permissions:
contents: read
- uses: zed-industries/zed/.github/workflows/deploy_docs.yml@main
+ uses: zed-industries/zed/.github/workflows/deploy_docs.yml@3f16f7b9082f8828e4d6ae207d2349b1ef932517
secrets:
DOCS_AMPLITUDE_API_KEY: ${{ secrets.DOCS_AMPLITUDE_API_KEY }}
DOCS_CONSENT_IO_INSTANCE: ${{ secrets.DOCS_CONSENT_IO_INSTANCE }}
diff --git a/.github/workflows/extension_bump.yml b/.github/workflows/extension_bump.yml
index 11a3a70902218a..6e68db7af0f274 100644
--- a/.github/workflows/extension_bump.yml
+++ b/.github/workflows/extension_bump.yml
@@ -5,7 +5,7 @@ env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: '1'
CARGO_INCREMENTAL: '0'
- ZED_EXTENSION_CLI_SHA: 2a00db06ce6d01089bfafd207b6348078e980df9
+ ZED_EXTENSION_CLI_SHA: 9ee3c503a4bbbc6b4a0f8a789acca4871d773223
on:
workflow_call:
inputs:
diff --git a/.github/workflows/extension_tests.yml b/.github/workflows/extension_tests.yml
index c3503590e6063f..23efa368d17653 100644
--- a/.github/workflows/extension_tests.yml
+++ b/.github/workflows/extension_tests.yml
@@ -5,7 +5,7 @@ env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: '1'
CARGO_INCREMENTAL: '0'
- ZED_EXTENSION_CLI_SHA: 2a00db06ce6d01089bfafd207b6348078e980df9
+ ZED_EXTENSION_CLI_SHA: 9ee3c503a4bbbc6b4a0f8a789acca4871d773223
RUSTUP_TOOLCHAIN: stable
CARGO_BUILD_TARGET: wasm32-wasip2
on:
diff --git a/.github/workflows/publish_extension_cli.yml b/.github/workflows/publish_extension_cli.yml
index 4326feb812c094..5bef991887cdba 100644
--- a/.github/workflows/publish_extension_cli.yml
+++ b/.github/workflows/publish_extension_cli.yml
@@ -5,12 +5,15 @@ env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: '0'
on:
- push:
- tags:
- - extension-cli
+ workflow_dispatch:
+ inputs:
+ message:
+ description: Describe why the extension CLI is being bumped and/or what changes are included.
+ required: true
+ type: string
jobs:
publish_job:
- if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
+ if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main'
runs-on: namespace-profile-16x32-ubuntu-2204
steps:
- name: steps::checkout_repo
@@ -31,10 +34,29 @@ jobs:
env:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
+ - id: generate-token
+ name: steps::authenticate_as_zippy
+ uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859
+ with:
+ app-id: ${{ secrets.ZED_ZIPPY_APP_ID }}
+ private-key: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }}
+ permission-contents: write
+ - name: steps::update_tag
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
+ with:
+ script: |
+ github.rest.git.updateRef({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ ref: 'tags/extension-cli',
+ sha: context.sha,
+ force: true
+ })
+ github-token: ${{ steps.generate-token.outputs.token }}
update_sha_in_zed:
needs:
- publish_job
- if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
+ if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main'
runs-on: namespace-profile-8x16-ubuntu-2204
steps:
- id: generate-token
@@ -69,6 +91,8 @@ jobs:
body: |
This PR bumps the extension CLI version used in the extension workflows to `${{ github.sha }}`.
+ ${{ inputs.message }}
+
Release Notes:
- N/A
@@ -84,7 +108,7 @@ jobs:
update_sha_in_extensions:
needs:
- publish_job
- if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
+ if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main'
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- id: generate-token
@@ -114,6 +138,8 @@ jobs:
title: Bump extension CLI version to `${{ steps.short-sha.outputs.sha_short }}`
body: |
This PR bumps the extension CLI version to https://github.com/zed-industries/zed/commit/${{ github.sha }}.
+
+ ${{ inputs.message }}
commit-message: Bump extension CLI version to `${{ steps.short-sha.outputs.sha_short }}`
branch: update-extension-cli-sha
committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
diff --git a/.github/workflows/randomized_tests.yml b/.github/workflows/randomized_tests.yml
deleted file mode 100644
index 9655a81235d79e..00000000000000
--- a/.github/workflows/randomized_tests.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: Randomized Tests
-
-concurrency: randomized-tests
-
-on:
- push:
- branches:
- - randomized-tests-runner
- # schedule:
- # - cron: '0 * * * *'
-
-env:
- CARGO_TERM_COLOR: always
- CARGO_INCREMENTAL: 0
- RUST_BACKTRACE: 1
- ZED_SERVER_URL: https://zed.dev
-
-jobs:
- tests:
- name: Run randomized tests
- if: github.repository_owner == 'zed-industries'
- runs-on:
- - namespace-profile-16x32-ubuntu-2204
- steps:
- - name: Install Node
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
- with:
- node-version: "18"
-
- - name: Checkout repo
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- with:
- clean: false
-
- - name: Run randomized tests
- run: script/randomized-test-ci
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index d5934a4838dc79..f7fbff7363725e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -100,7 +100,7 @@ jobs:
timeout-minutes: 60
services:
postgres:
- image: postgres:15
+ image: postgres:15@sha256:1b92e7a80c021647bf70f5d3eb66066a998e4f5cf43c07bb9dc9f729782cf88e
env:
POSTGRES_HOST_AUTH_METHOD: trust
ports:
diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml
index 1035d1ab0a4bed..cdf1ef96e15b2c 100644
--- a/.github/workflows/release_nightly.yml
+++ b/.github/workflows/release_nightly.yml
@@ -83,7 +83,7 @@ jobs:
timeout-minutes: 60
services:
postgres:
- image: postgres:15
+ image: postgres:15@sha256:1b92e7a80c021647bf70f5d3eb66066a998e4f5cf43c07bb9dc9f729782cf88e
env:
POSTGRES_HOST_AUTH_METHOD: trust
ports:
diff --git a/.github/workflows/run_cron_unit_evals.yml b/.github/workflows/run_cron_unit_evals.yml
deleted file mode 100644
index c647597b44a7d6..00000000000000
--- a/.github/workflows/run_cron_unit_evals.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-# Generated from xtask::workflows::run_cron_unit_evals
-# Rebuild with `cargo xtask workflows`.
-name: run_cron_unit_evals
-env:
- CARGO_TERM_COLOR: always
- CARGO_INCREMENTAL: '0'
- RUST_BACKTRACE: '1'
- ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
-on:
- workflow_dispatch: {}
-jobs:
- cron_unit_evals:
- runs-on: namespace-profile-16x32-ubuntu-2204
- strategy:
- matrix:
- model:
- - anthropic/claude-sonnet-4-5-latest
- - anthropic/claude-opus-4-5-latest
- - google/gemini-3-pro
- - openai/gpt-5
- fail-fast: false
- steps:
- - name: steps::checkout_repo
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
- with:
- clean: false
- - name: steps::setup_cargo_config
- run: |
- mkdir -p ./../.cargo
- cp ./.cargo/ci-config.toml ./../.cargo/config.toml
- - name: steps::cache_rust_dependencies_namespace
- uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9
- with:
- cache: rust
- path: ~/.rustup
- - name: steps::setup_linux
- run: ./script/linux
- - name: steps::install_mold
- run: ./script/install-mold
- - name: steps::download_wasi_sdk
- run: ./script/download-wasi-sdk
- - name: steps::cargo_install_nextest
- uses: taiki-e/install-action@921e2c9f7148d7ba14cd819f417db338f63e733c
- - name: steps::clear_target_dir_if_large
- run: ./script/clear-target-dir-if-larger-than 350 200
- - name: steps::setup_sccache
- run: ./script/setup-sccache
- env:
- R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
- R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
- R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
- SCCACHE_BUCKET: sccache-zed
- - name: ./script/run-unit-evals
- run: ./script/run-unit-evals
- env:
- ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
- GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
- ZED_AGENT_MODEL: ${{ matrix.model }}
- - name: steps::show_sccache_stats
- run: sccache --show-stats || true
- - name: steps::cleanup_cargo_config
- if: always()
- run: |
- rm -rf ./../.cargo
- - name: run_agent_evals::cron_unit_evals::send_failure_to_slack
- if: ${{ failure() }}
- uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52
- with:
- method: chat.postMessage
- token: ${{ secrets.SLACK_APP_ZED_UNIT_EVALS_BOT_TOKEN }}
- payload: |
- channel: C04UDRNNJFQ
- text: "Unit Evals Failed: https://github.com/zed-industries/zed/actions/runs/${{ github.run_id }}"
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
- cancel-in-progress: true
-defaults:
- run:
- shell: bash -euxo pipefail {0}
diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml
index 1210b9b36b89b2..34fb9c123c91c5 100644
--- a/.github/workflows/run_tests.yml
+++ b/.github/workflows/run_tests.yml
@@ -399,7 +399,7 @@ jobs:
timeout-minutes: 60
services:
postgres:
- image: postgres:15
+ image: postgres:15@sha256:1b92e7a80c021647bf70f5d3eb66066a998e4f5cf43c07bb9dc9f729782cf88e
env:
POSTGRES_HOST_AUTH_METHOD: trust
ports:
@@ -794,12 +794,12 @@ jobs:
echo "BUF_BASE_BRANCH=$GITHUB_BASE_REF" >> "$GITHUB_ENV"
fi
- name: run_tests::check_postgres_and_protobuf_migrations::bufbuild_setup_action
- uses: bufbuild/buf-setup-action@v1
+ uses: bufbuild/buf-setup-action@a47c93e0b1648d5651a065437926377d060baa99
with:
version: v1.29.0
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: run_tests::check_postgres_and_protobuf_migrations::bufbuild_breaking_action
- uses: bufbuild/buf-breaking-action@v1
+ uses: bufbuild/buf-breaking-action@c57b3d842a5c3f3b454756ef65305a50a587c5ba
with:
input: crates/proto/proto/
against: https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/
diff --git a/.github/workflows/run_unit_evals.yml b/.github/workflows/run_unit_evals.yml
deleted file mode 100644
index 4f655bbb8e6ed8..00000000000000
--- a/.github/workflows/run_unit_evals.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-# Generated from xtask::workflows::run_unit_evals
-# Rebuild with `cargo xtask workflows`.
-name: run_unit_evals
-env:
- CARGO_TERM_COLOR: always
- CARGO_INCREMENTAL: '0'
- RUST_BACKTRACE: '1'
- ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
- ZED_EVAL_TELEMETRY: '1'
- MODEL_NAME: ${{ inputs.model_name }}
-on:
- workflow_dispatch:
- inputs:
- model_name:
- description: model_name
- required: true
- type: string
- commit_sha:
- description: commit_sha
- required: true
- type: string
-jobs:
- run_unit_evals:
- runs-on: namespace-profile-16x32-ubuntu-2204
- steps:
- - name: steps::checkout_repo
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
- with:
- clean: false
- - name: steps::setup_cargo_config
- run: |
- mkdir -p ./../.cargo
- cp ./.cargo/ci-config.toml ./../.cargo/config.toml
- - name: steps::cache_rust_dependencies_namespace
- uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9
- with:
- cache: rust
- path: ~/.rustup
- - name: steps::setup_linux
- run: ./script/linux
- - name: steps::install_mold
- run: ./script/install-mold
- - name: steps::download_wasi_sdk
- run: ./script/download-wasi-sdk
- - name: steps::cargo_install_nextest
- uses: taiki-e/install-action@921e2c9f7148d7ba14cd819f417db338f63e733c
- - name: steps::clear_target_dir_if_large
- run: ./script/clear-target-dir-if-larger-than 350 200
- - name: steps::setup_sccache
- run: ./script/setup-sccache
- env:
- R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
- R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
- R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
- SCCACHE_BUCKET: sccache-zed
- - name: ./script/run-unit-evals
- run: ./script/run-unit-evals
- env:
- ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- GOOGLE_AI_API_KEY: ${{ secrets.GOOGLE_AI_API_KEY }}
- GOOGLE_CLOUD_PROJECT: ${{ secrets.GOOGLE_CLOUD_PROJECT }}
- UNIT_EVAL_COMMIT: ${{ inputs.commit_sha }}
- - name: steps::show_sccache_stats
- run: sccache --show-stats || true
- - name: steps::cleanup_cargo_config
- if: always()
- run: |
- rm -rf ./../.cargo
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.run_id }}
- cancel-in-progress: true
-defaults:
- run:
- shell: bash -euxo pipefail {0}
diff --git a/.github/workflows/slack_notify_first_responders.yml b/.github/workflows/slack_notify_first_responders.yml
index a6f2d557a57477..3dd9ffeabae1b7 100644
--- a/.github/workflows/slack_notify_first_responders.yml
+++ b/.github/workflows/slack_notify_first_responders.yml
@@ -5,25 +5,54 @@ on:
types: [labeled]
env:
- FIRST_RESPONDER_LABELS: '["priority:P0", "priority:P1"]'
+ PRIORITY_LABELS: '["priority:P0", "priority:P1"]'
+ REPRODUCIBLE_LABEL: 'state:reproducible'
+ FREQUENCY_LABELS: '["frequency:always", "frequency:common"]'
jobs:
notify-slack:
if: github.repository_owner == 'zed-industries' && github.event.issue.state == 'open'
runs-on: namespace-profile-2x4-ubuntu-2404
+ # Serialize per-issue so concurrent `labeled` events can't both observe
+ # the trifecta and double-notify.
+ concurrency:
+ group: slack-notify-first-responders-${{ github.event.issue.number }}
+ cancel-in-progress: false
steps:
- - name: Check if label requires first responder notification
+ - name: Check if label combination requires first responder notification
id: check-label
env:
LABEL_NAME: ${{ github.event.label.name }}
+ ISSUE_LABELS_JSON: ${{ toJson(github.event.issue.labels.*.name) }}
run: |
- if echo '${{ env.FIRST_RESPONDER_LABELS }}' | jq -e --arg label "$LABEL_NAME" 'index($label) != null' > /dev/null; then
+ set -euo pipefail
+
+ # Gate on the just-added label so unrelated labeling on an
+ # already-qualifying issue doesn't re-fire the notification.
+ TRIGGER_LABELS=$(jq -cn \
+ --argjson priority "$PRIORITY_LABELS" \
+ --arg repro "$REPRODUCIBLE_LABEL" \
+ --argjson freq "$FREQUENCY_LABELS" \
+ '$priority + [$repro] + $freq')
+
+ if ! echo "$TRIGGER_LABELS" | jq -e --arg l "$LABEL_NAME" 'index($l) != null' > /dev/null; then
+ echo "Added label '$LABEL_NAME' is not in the trigger set, skipping"
+ echo "should_notify=false" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
+
+ MATCHED_PRIORITY=$(echo "$ISSUE_LABELS_JSON" | jq -r --argjson priority "$PRIORITY_LABELS" 'map(select(. as $x | $priority | index($x) != null)) | first // ""')
+ HAS_REPRO=$(echo "$ISSUE_LABELS_JSON" | jq --arg l "$REPRODUCIBLE_LABEL" 'index($l) != null')
+ HAS_FREQ=$(echo "$ISSUE_LABELS_JSON" | jq --argjson freq "$FREQUENCY_LABELS" 'any(.[]; . as $x | $freq | index($x) != null)')
+
+ if [ -n "$MATCHED_PRIORITY" ] && [ "$HAS_REPRO" = "true" ] && [ "$HAS_FREQ" = "true" ]; then
+ echo "Confirmed high-frequency $MATCHED_PRIORITY, notifying"
+ echo "notify_reason=confirmed $MATCHED_PRIORITY" >> "$GITHUB_OUTPUT"
echo "should_notify=true" >> "$GITHUB_OUTPUT"
- echo "Label '$LABEL_NAME' requires first responder notification"
else
+ echo "Combination not yet satisfied (priority=$MATCHED_PRIORITY, reproducible=$HAS_REPRO, frequency=$HAS_FREQ), skipping"
echo "should_notify=false" >> "$GITHUB_OUTPUT"
- echo "Label '$LABEL_NAME' does not require first responder notification, skipping"
fi
- name: Build Slack message payload
@@ -32,13 +61,13 @@ jobs:
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_URL: ${{ github.event.issue.html_url }}
LABELED_BY: ${{ github.event.sender.login }}
- LABEL_NAME: ${{ github.event.label.name }}
+ NOTIFY_REASON: ${{ steps.check-label.outputs.notify_reason }}
LABELS_JSON: ${{ toJson(github.event.issue.labels.*.name) }}
run: |
LABELS=$(echo "$LABELS_JSON" | jq -r 'join(", ")')
jq -n \
- --arg label_name "$LABEL_NAME" \
+ --arg notify_reason "$NOTIFY_REASON" \
--arg issue_title "$ISSUE_TITLE" \
--arg issue_url "$ISSUE_URL" \
--arg labeled_by "$LABELED_BY" \
@@ -49,7 +78,7 @@ jobs:
"type": "section",
"text": {
"type": "mrkdwn",
- "text": " Issue labeled *\($label_name)*"
+ "text": " New *\($notify_reason)* issue"
}
},
{
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e7e7629825b5f4..e0a3287387b47d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -44,6 +44,7 @@ submitted. If you'd like your PR to have the best chance of being merged:
but features should be confirmed with us first if you aim to avoid wasted
effort. If there isn't already a GitHub issue for your feature with staff
confirmation that we want it, start with a GitHub discussion rather than a PR.
+ - This especially applies to any changes proposed to the Zed Extension API.
- Include a clear description of **what you're solving**, and why it's important.
- Include **tests**. For UI changes, consider updating visual regression tests (see [Building Zed for macOS](./docs/src/development/macos.md#visual-regression-tests)).
- If it changes the UI, attach **screenshots** or screen recordings.
@@ -52,7 +53,17 @@ submitted. If you'd like your PR to have the best chance of being merged:
- Keep AI assistance under your judgement and responsibility: it's unlikely
we'll merge a vibe-coded PR that the author doesn't understand.
-The internal advice for reviewers is as follows:
+### AI Policy
+
+We welcome the use of LLMs for coding, but we hold a high bar for all contributions, and **we expect a human in the loop who genuinely understands the work an LLM produces** on their behalf. For that reason, we **don't accept contributions from autonomous agents**. Pull requests that appear to violate this may be closed, sometimes without notice.
+
+**Don't rely on LLMs to write the whole thing for you when communicating with the maintainers** (meaning replies to comments, PR descriptions, and alike). The readers are humans, and we'd like to hear from you, not from a model (we have models at home). If you're a non-native English speaker using an LLM to thoroughly edit or translate your messages to the maintainers, we'd encourage you to **put the machine translation in a quote block and include the original text in your native language after it**.
+
+If you think it's helpful/necessary to **share context from a chat with an LLM**, please put the **relevant part of it** in a quote block (e.g., using `>`), **disclose it as AI-generated**, and add your own commentary explaining **why it's relevant and what you take from it**.
+
+This policy was adapted from [ripgrep's AI policy](https://github.com/BurntSushi/ripgrep/blob/f0cec341ab95c25c691ad3d5754d4bd9eedde21f/AI_POLICY.md).
+
+### Internal advice for reviewers
- If the fix/feature is obviously great, and the code is great. Hit merge.
- If the fix/feature is obviously great, and the code is nearly great. Send PR comments, or offer to pair to get things perfect.
@@ -70,41 +81,49 @@ the change with code in hand.
When your changes affect UI, consult this checklist:
**Accessibility / Ergonomics**
+
- Do all keyboard shortcuts work as intended?
- Are shortcuts discoverable (tooltips, menus, docs)?
-- Do all mouse actions work (drag, context menus, resizing, scrolling)?
-- Does the feature look great in light mode and dark mode?
-- Are hover states, focus rings, and active states clear and consistent?
- Is it usable without a mouse (keyboard-only navigation)?
+- Do all mouse actions work (drag, context menus, resizing, scrolling)?
+- Does the feature look great in light and dark mode themes?
+- Are hover states and focus indicators clear and consistent?
**Responsiveness**
+
- Does the UI scale gracefully on:
- - Narrow panes (e.g., side-by-side split views)?
- - Short panes (e.g., laptops with 13" displays)?
- - High-DPI / Retina displays?
+ - Narrow panes (e.g., side-by-side split views)?
+ - Short panes (e.g., laptops with 13" displays)?
+ - High-DPI / Retina displays?
- Does resizing panes or windows keep the UI usable and attractive?
- Do dialogs or modals stay centered and within viewport bounds?
**Platform Consistency**
-- Is the feature fully usable on Windows, Linux, and Mac?
+
+- Is the feature fully usable on Windows, Linux, and macOS?
- Does it respect system-level settings (fonts, scaling, input methods)?
**Performance**
+
- All user interactions must have instant feedback.
- - If the user requests something slow (e.g. an LLM generation) there should be some indication of the work in progress.
+ - If the user requests something slow (e.g. an LLM generation) there should be some indication of the work in progress.
- Does it handle large files, big projects, or heavy workloads without degrading?
- Frames must take no more than 8ms (120fps)
**Consistency**
+
- Does it match Zed’s design language (spacing, typography, icons)?
+ - Make sure to visit [the icon design guidelines](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
- Are terminology, labels, and tone consistent with the rest of Zed?
- Are interactions consistent (e.g., how tabs close, how modals dismiss, how errors show)?
**Internationalization & Text**
+
- Are strings concise, clear, and unambiguous?
- Do we avoid internal Zed jargon that only insiders would know?
**User Paths & Edge Cases**
+
- What does the happy path look like?
- What does the unhappy path look like? (errors, rejections, invalid states)
- How does it work in offline vs. online states?
@@ -113,17 +132,18 @@ When your changes affect UI, consult this checklist:
- Are error messages actionable and consistent with Zed’s voice?
**Discoverability & Learning**
+
- Can a first-time user figure it out without docs?
- Is there an intuitive way to undo/redo actions?
- Are power features discoverable but not intrusive?
- Is there a path from beginner → expert usage (progressive disclosure)?
-
## Things we will (probably) not merge
Although there are few hard and fast rules, typically we don't merge:
- Anything that can be provided by an extension. For example a new language, or theme. For adding themes or support for a new language to Zed, check out our [docs on developing extensions](https://zed.dev/docs/extensions/developing-extensions).
+- Changes to the Zed Extension API submitted without prior discussion involving Zed staff.
- New file icons. Zed's default icon theme consists of icons that are hand-designed to fit together in a cohesive manner, please don't submit PRs with off-the-shelf SVGs.
- Features where (in our subjective opinion) the extra complexity isn't worth it for the number of people who will benefit.
- Giant refactorings.
diff --git a/Cargo.lock b/Cargo.lock
index 77790e285517dc..2762d7aa7991ef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -243,7 +243,6 @@ dependencies = [
"cloud_llm_client",
"collections",
"context_server",
- "criterion",
"ctor",
"db",
"editor",
@@ -271,6 +270,7 @@ dependencies = [
"pretty_assertions",
"project",
"prompt_store",
+ "proptest",
"quick-xml 0.38.3",
"rand 0.9.4",
"regex",
@@ -306,9 +306,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
-version = "0.13.1"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d197653697b91b3a2cfb579d061a3388cda9fdc79cb6f9393da65cbad46baf8"
+checksum = "5efba6592048ef8a9ac97de8d79b2d9933d8ac4d94f7a2de102348fed0c61103"
dependencies = [
"agent-client-protocol-derive",
"agent-client-protocol-schema",
@@ -328,9 +328,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol-derive"
-version = "0.13.1"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e4fbf6733a900814fb921b2aac06612e15b42020b76a356fbc1192e725cebc"
+checksum = "4d176a10d4cb06e0262a738c3c5bf21ff0968db13a666e31cbca94a3d3d72e7c"
dependencies = [
"quote",
"syn 2.0.117",
@@ -338,9 +338,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol-schema"
-version = "0.13.5"
+version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d419a87e28240978e4bfdf2a5b91bccb95ae8d5b06e10721bb07c449b9f43dd"
+checksum = "c290bfa00c6b52339db66f8e9cf711d5f08530800529f7d619ff24d6cba253d0"
dependencies = [
"anyhow",
"derive_more",
@@ -519,7 +519,6 @@ dependencies = [
"serde_json",
"serde_json_lenient",
"settings",
- "skill_creator",
"streaming_diff",
"task",
"telemetry",
@@ -2184,6 +2183,37 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
+[[package]]
+name = "benchmarks"
+version = "0.1.0"
+dependencies = [
+ "action_log",
+ "agent",
+ "agent_settings",
+ "assets",
+ "criterion",
+ "editor",
+ "futures 0.3.32",
+ "gpui",
+ "gpui_platform",
+ "itertools 0.14.0",
+ "language",
+ "language_model",
+ "lsp",
+ "multi_buffer",
+ "project",
+ "prompt_store",
+ "rand 0.9.4",
+ "serde_json",
+ "settings",
+ "text",
+ "theme",
+ "theme_settings",
+ "ui",
+ "util",
+ "zed_actions",
+]
+
[[package]]
name = "bigdecimal"
version = "0.4.8"
@@ -2501,7 +2531,6 @@ version = "0.1.0"
dependencies = [
"clock",
"ctor",
- "futures 0.3.32",
"gpui",
"imara-diff",
"language",
@@ -3255,6 +3284,7 @@ dependencies = [
"gpui_tokio",
"http_client",
"parking_lot",
+ "serde",
"serde_json",
"thiserror 2.0.17",
"yawc",
@@ -3434,7 +3464,6 @@ dependencies = [
"fs",
"futures 0.3.32",
"git",
- "git_graph",
"git_hosting_providers",
"git_ui",
"gpui",
@@ -3539,6 +3568,7 @@ dependencies = [
name = "collections"
version = "0.1.0"
dependencies = [
+ "gpui_util",
"indexmap 2.11.4",
"rustc-hash 2.1.1",
]
@@ -5297,7 +5327,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
- "windows-sys 0.61.2",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -5441,8 +5471,8 @@ dependencies = [
[[package]]
name = "dugong"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+version = "0.6.2"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"dugong-graphlib",
"rustc-hash 2.1.1",
@@ -5452,8 +5482,8 @@ dependencies = [
[[package]]
name = "dugong-graphlib"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+version = "0.6.2"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"hashbrown 0.16.1",
"rustc-hash 2.1.1",
@@ -5585,6 +5615,7 @@ dependencies = [
"debug_adapter_extension",
"dirs",
"edit_prediction",
+ "edit_prediction_context",
"edit_prediction_metrics",
"extension",
"flate2",
@@ -5654,6 +5685,7 @@ dependencies = [
"serde_json",
"settings",
"smallvec",
+ "telemetry",
"text",
"tree-sitter",
"util",
@@ -5741,7 +5773,6 @@ dependencies = [
"clock",
"collections",
"convert_case 0.11.0",
- "criterion",
"ctor",
"dap",
"db",
@@ -6093,7 +6124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -6327,6 +6358,7 @@ dependencies = [
"env_logger 0.11.8",
"extension",
"fs",
+ "futures 0.3.32",
"gpui_platform",
"language",
"log",
@@ -6924,7 +6956,7 @@ dependencies = [
"is_executable",
"libc",
"log",
- "notify 8.2.0",
+ "notify 9.0.0-rc.4",
"parking_lot",
"paths",
"proto",
@@ -7352,7 +7384,7 @@ dependencies = [
"gobject-sys",
"libc",
"system-deps",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -7392,40 +7424,6 @@ dependencies = [
"ztracing",
]
-[[package]]
-name = "git_graph"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-channel 2.5.0",
- "collections",
- "db",
- "editor",
- "fs",
- "git",
- "git_ui",
- "gpui",
- "language",
- "language_model",
- "menu",
- "picker",
- "project",
- "project_panel",
- "rand 0.9.4",
- "release_channel",
- "remote_connection",
- "search",
- "serde_json",
- "settings",
- "smallvec",
- "task",
- "theme",
- "theme_settings",
- "time",
- "ui",
- "workspace",
-]
-
[[package]]
name = "git_hosting_providers"
version = "0.1.0"
@@ -7455,6 +7453,7 @@ dependencies = [
"agent_settings",
"anyhow",
"askpass",
+ "async-channel 2.5.0",
"buffer_diff",
"call",
"collections",
@@ -7487,9 +7486,11 @@ dependencies = [
"prompt_store",
"proto",
"rand 0.9.4",
+ "release_channel",
"remote",
"remote_connection",
"schemars 1.0.4",
+ "search",
"serde",
"serde_json",
"settings",
@@ -7847,6 +7848,7 @@ dependencies = [
"core-graphics 0.24.0",
"core-text",
"core-video",
+ "criterion",
"ctor",
"derive_more",
"embed-resource",
@@ -9736,7 +9738,7 @@ dependencies = [
"fs",
"futures 0.3.32",
"futures-lite 1.13.0",
- "fuzzy",
+ "fuzzy_nucleo",
"globset",
"gpui",
"http_client",
@@ -9886,6 +9888,7 @@ dependencies = [
"clock",
"cloud_api_client",
"cloud_api_types",
+ "cloud_llm_client",
"collections",
"component",
"convert_case 0.11.0",
@@ -10703,8 +10706,8 @@ dependencies = [
[[package]]
name = "manatee"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+version = "0.6.2"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"indexmap 2.11.4",
"nalgebra",
@@ -10998,8 +11001,8 @@ dependencies = [
[[package]]
name = "merman"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+version = "0.6.2"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"merman-core",
"merman-render",
@@ -11008,8 +11011,8 @@ dependencies = [
[[package]]
name = "merman-core"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+version = "0.6.2"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"chrono",
"euclid",
@@ -11034,8 +11037,8 @@ dependencies = [
[[package]]
name = "merman-render"
-version = "0.4.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+version = "0.6.2"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -11238,7 +11241,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -11617,19 +11620,21 @@ dependencies = [
[[package]]
name = "notify"
-version = "8.2.0"
-source = "git+https://github.com/zed-industries/notify.git?rev=ce58c24cad542c28e04ced02e20325a4ec28a31d#ce58c24cad542c28e04ced02e20325a4ec28a31d"
+version = "9.0.0-rc.4"
+source = "git+https://github.com/zed-industries/notify?rev=faecbc33db4f59313e5225ef766bfd9e54a54cfd#faecbc33db4f59313e5225ef766bfd9e54a54cfd"
dependencies = [
"bitflags 2.10.0",
- "fsevent-sys",
"inotify 0.11.0",
"kqueue",
"libc",
"log",
"mio 1.2.0",
"notify-types",
+ "objc2-core-foundation",
+ "objc2-core-services",
"walkdir",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.2",
+ "xxhash-rust",
]
[[package]]
@@ -11645,8 +11650,11 @@ dependencies = [
[[package]]
name = "notify-types"
-version = "2.0.0"
-source = "git+https://github.com/zed-industries/notify.git?rev=ce58c24cad542c28e04ced02e20325a4ec28a31d#ce58c24cad542c28e04ced02e20325a4ec28a31d"
+version = "2.1.0"
+source = "git+https://github.com/zed-industries/notify?rev=faecbc33db4f59313e5225ef766bfd9e54a54cfd#faecbc33db4f59313e5225ef766bfd9e54a54cfd"
+dependencies = [
+ "bitflags 2.10.0",
+]
[[package]]
name = "ntapi"
@@ -11663,7 +11671,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -12050,6 +12058,16 @@ dependencies = [
"objc2-metal 0.2.2",
]
+[[package]]
+name = "objc2-core-services"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583300ad934cba24ff5292aee751ecc070f7ca6b39a574cc21b7b5e588e06a0b"
+dependencies = [
+ "libc",
+ "objc2-core-foundation",
+]
+
[[package]]
name = "objc2-encode"
version = "4.1.0"
@@ -12533,13 +12551,12 @@ version = "0.1.0"
dependencies = [
"editor",
"futures 0.3.32",
- "fuzzy",
+ "fuzzy_nucleo",
"gpui",
"indoc",
"language",
"lsp",
"menu",
- "ordered-float 2.10.1",
"picker",
"project",
"rope",
@@ -15517,7 +15534,7 @@ dependencies = [
[[package]]
name = "roughr-merman"
version = "0.12.0"
-source = "git+https://github.com/zed-industries/merman?rev=1c765dcca2ef5092fcde7bebe8374819563623ef#1c765dcca2ef5092fcde7bebe8374819563623ef"
+source = "git+https://github.com/zed-industries/merman?tag=v0.6.2-with-patches#06094471f97acb10d0eebf8b92bac19ba2928eea"
dependencies = [
"derive_builder",
"euclid",
@@ -15733,7 +15750,7 @@ dependencies = [
"errno 0.3.14",
"libc",
"linux-raw-sys 0.11.0",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -16684,6 +16701,7 @@ dependencies = [
"agent_skills",
"anyhow",
"audio",
+ "cloud_api_types",
"codestral",
"component",
"copilot",
@@ -16698,6 +16716,7 @@ dependencies = [
"fuzzy",
"gpui",
"heck 0.5.0",
+ "http_client",
"itertools 0.14.0",
"language",
"log",
@@ -16714,6 +16733,7 @@ dependencies = [
"search",
"serde",
"serde_json",
+ "serde_yaml_ng",
"settings",
"shell_command_parser",
"strum 0.27.2",
@@ -16722,6 +16742,7 @@ dependencies = [
"theme_settings",
"title_bar",
"ui",
+ "ui_input",
"util",
"workspace",
"zed_actions",
@@ -16849,6 +16870,7 @@ dependencies = [
"log",
"menu",
"node_runtime",
+ "notifications",
"platform_title_bar",
"pretty_assertions",
"project",
@@ -16995,33 +17017,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
-[[package]]
-name = "skill_creator"
-version = "0.1.0"
-dependencies = [
- "agent_skills",
- "anyhow",
- "editor",
- "fs",
- "futures 0.3.32",
- "gpui",
- "http_client",
- "language",
- "menu",
- "notifications",
- "platform_title_bar",
- "release_channel",
- "serde_json",
- "serde_yaml_ng",
- "settings",
- "theme_settings",
- "ui",
- "ui_input",
- "util",
- "workspace",
- "worktree",
-]
-
[[package]]
name = "skrifa"
version = "0.37.0"
@@ -17188,7 +17183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -18305,7 +18300,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix 1.1.2",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -18742,6 +18737,7 @@ dependencies = [
"chrono",
"client",
"cloud_api_types",
+ "command_palette_hooks",
"db",
"external_websocket_sync",
"feature_flags",
@@ -19766,6 +19762,7 @@ dependencies = [
"markdown",
"menu",
"settings",
+ "theme",
"theme_settings",
"ui",
"workspace",
@@ -21395,7 +21392,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -22601,6 +22598,7 @@ dependencies = [
"theme",
"theme_settings",
"ui",
+ "url",
"util",
"uuid",
"windows 0.61.3",
@@ -22847,6 +22845,12 @@ dependencies = [
"toml_edit 0.22.27",
]
+[[package]]
+name = "xxhash-rust"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
+
[[package]]
name = "y4m"
version = "0.8.0"
@@ -23039,7 +23043,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "1.6.0"
+version = "1.8.0"
dependencies = [
"acp_thread",
"acp_tools",
@@ -23100,7 +23104,6 @@ dependencies = [
"fs",
"futures 0.3.32",
"git",
- "git_graph",
"git_hosting_providers",
"git_ui",
"go_to_line",
@@ -23328,7 +23331,6 @@ dependencies = [
"schemars 1.0.4",
"serde",
"util",
- "uuid",
]
[[package]]
@@ -23385,7 +23387,7 @@ dependencies = [
[[package]]
name = "zed_glsl"
-version = "0.2.3"
+version = "0.2.4"
dependencies = [
"zed_extension_api 0.1.0",
]
diff --git a/Cargo.toml b/Cargo.toml
index 80d648128f4f4d..76130034033422 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ members = [
"crates/auto_update_ui",
"crates/aws_http_client",
"crates/bedrock",
+ "crates/benchmarks",
"crates/breadcrumbs",
"crates/buffer_diff",
"crates/call",
@@ -84,7 +85,6 @@ members = [
"crates/fuzzy",
"crates/fuzzy_nucleo",
"crates/git",
- "crates/git_graph",
"crates/git_hosting_providers",
"crates/git_ui",
"crates/go_to_line",
@@ -172,7 +172,6 @@ members = [
"crates/rope",
"crates/rpc",
"crates/sandbox",
- "crates/skill_creator",
"crates/scheduler",
"crates/schema_generator",
"crates/search",
@@ -341,7 +340,6 @@ fs = { path = "crates/fs" }
fuzzy = { path = "crates/fuzzy" }
fuzzy_nucleo = { path = "crates/fuzzy_nucleo" }
git = { path = "crates/git" }
-git_graph = { path = "crates/git_graph" }
git_hosting_providers = { path = "crates/git_hosting_providers" }
git_ui = { path = "crates/git_ui" }
go_to_line = { path = "crates/go_to_line" }
@@ -432,7 +430,6 @@ reqwest_client = { path = "crates/reqwest_client" }
rodio = { git = "https://github.com/RustAudio/rodio", rev = "e50e726ddd0292f6ef9de0dda6b90af4ed1fb66a", features = ["wav", "playback", "wav_output", "recording"] }
rope = { path = "crates/rope" }
rpc = { path = "crates/rpc" }
-skill_creator = { path = "crates/skill_creator" }
scheduler = { path = "crates/scheduler" }
sandbox = { path = "crates/sandbox" }
search = { path = "crates/search" }
@@ -506,7 +503,7 @@ accesskit = "0.24.0"
accesskit_macos = "0.26.0"
accesskit_unix = "0.21.0"
accesskit_windows = "0.32.1"
-agent-client-protocol = { version = "=0.13.1", features = ["unstable"] }
+agent-client-protocol = { version = "=0.14.0", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "fcf32feacb367b75ec84dd40f041e4fd411d3cc1" }
any_vec = "0.14"
@@ -595,6 +592,7 @@ fork = "0.4.0"
futures = "0.3.32"
futures-concurrency = "7.7.1"
futures-lite = "1.13"
+futures-util = "0.3.32"
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "37f3c0575d379c218a9c455ee67585184e40d43f" }
globset = "0.4"
@@ -889,12 +887,12 @@ features = [
[patch.crates-io]
async-task = { git = "https://github.com/smol-rs/async-task.git", rev = "b4486cd71e4e94fbda54ce6302444de14f4d190e" }
-notify = { git = "https://github.com/zed-industries/notify.git", rev = "ce58c24cad542c28e04ced02e20325a4ec28a31d" }
-notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "ce58c24cad542c28e04ced02e20325a4ec28a31d" }
windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" }
calloop = { git = "https://github.com/zed-industries/calloop" }
livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "c3a55bbc207008f1ca3474b6037fdd3c443cad0f" }
libwebrtc = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "c3a55bbc207008f1ca3474b6037fdd3c443cad0f" }
+notify = { git = "https://github.com/zed-industries/notify", rev = "faecbc33db4f59313e5225ef766bfd9e54a54cfd" }
+notify-types = { git = "https://github.com/zed-industries/notify", rev = "faecbc33db4f59313e5225ef766bfd9e54a54cfd" }
webrtc-sys = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "c3a55bbc207008f1ca3474b6037fdd3c443cad0f" }
[profile.dev]
@@ -940,6 +938,7 @@ cranelift-codegen = { opt-level = 3 }
wasmtime-environ = { opt-level = 3 }
wasmtime-internal-cranelift = { opt-level = 3 }
minidumper = { opt-level = 3 }
+serde_json = { opt-level = 3 }
# Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster
activity_indicator = { codegen-units = 1 }
assets = { codegen-units = 1 }
diff --git a/assets/icons/ai_anthropic_compat.svg b/assets/icons/ai_anthropic_compat.svg
new file mode 100644
index 00000000000000..aae16efa0556ec
--- /dev/null
+++ b/assets/icons/ai_anthropic_compat.svg
@@ -0,0 +1,12 @@
+
diff --git a/assets/icons/compact.svg b/assets/icons/compact.svg
new file mode 100644
index 00000000000000..68f6beb1bec81a
--- /dev/null
+++ b/assets/icons/compact.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/folder_share.svg b/assets/icons/folder_share.svg
new file mode 100644
index 00000000000000..09a232a6898896
--- /dev/null
+++ b/assets/icons/folder_share.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/folder_shared.svg b/assets/icons/folder_shared.svg
new file mode 100644
index 00000000000000..c511390305c5eb
--- /dev/null
+++ b/assets/icons/folder_shared.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/gerrit.svg b/assets/icons/gerrit.svg
new file mode 100644
index 00000000000000..c2149b2c11ad5c
--- /dev/null
+++ b/assets/icons/gerrit.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/icons/sourcehut.svg b/assets/icons/sourcehut.svg
new file mode 100644
index 00000000000000..79f2c53aeeb0ed
--- /dev/null
+++ b/assets/icons/sourcehut.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/this_window.svg b/assets/icons/this_window.svg
new file mode 100644
index 00000000000000..879bb5e9577761
--- /dev/null
+++ b/assets/icons/this_window.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json
index 5fa5c4f575919d..9e735bdd9d5374 100644
--- a/assets/keymaps/default-linux.json
+++ b/assets/keymaps/default-linux.json
@@ -261,7 +261,7 @@
"ctrl-n": "agent::NewThread",
"ctrl--": "pane::GoBack",
"ctrl-alt-p": "agent::ManageProfiles",
- "ctrl-alt-l": "agent::OpenRulesLibrary",
+ "ctrl-alt-l": "agent::ManageSkills",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-tab": "agent::CycleModeSelector",
"ctrl-alt-/": "agent::ToggleModelSelector",
@@ -602,15 +602,15 @@
{
"context": "Workspace",
"bindings": {
- "alt-open": ["projects::OpenRecent", { "create_new_window": false }],
+ "alt-open": "projects::OpenRecent",
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-ctrl-o": ["projects::OpenRecent", { "create_new_window": true }],
- "alt-ctrl-o": ["projects::OpenRecent", { "create_new_window": false }],
- "ctrl-r": ["projects::OpenRecent", { "create_new_window": false }],
- "alt-shift-open": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
+ "alt-ctrl-o": "projects::OpenRecent",
+ "ctrl-r": "projects::OpenRecent",
+ "alt-shift-open": ["projects::OpenRemote", { "from_existing_connection": false }],
// Change to open path modal for existing remote connection by setting the parameter
- // "alt-ctrl-shift-o": "["projects::OpenRemote", { "from_existing_connection": true }]",
- "alt-ctrl-shift-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
+ // "alt-ctrl-shift-o": ["projects::OpenRemote", { "from_existing_connection": true }],
+ "alt-ctrl-shift-o": ["projects::OpenRemote", { "from_existing_connection": false }],
"alt-ctrl-shift-b": "branches::OpenRecent",
"alt-ctrl-shift-w": "git::Worktree",
"alt-shift-enter": "toast::RunAction",
@@ -1011,7 +1011,7 @@
},
},
{
- "context": "GitCommit > Editor",
+ "context": "GitCommit > Editor && mode == auto_height",
"bindings": {
"escape": "menu::Cancel",
"enter": "editor::Newline",
@@ -1516,6 +1516,7 @@
"ctrl--": "image_viewer::ZoomOut",
"ctrl-0": "image_viewer::ResetZoom",
"ctrl-1": "image_viewer::ZoomToActualSize",
+ "ctrl-k r": "editor::RevealInFileManager",
"ctrl-shift-0": "image_viewer::FitToView",
},
},
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index 79b7263639b93f..1a77bdbbcdeda0 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -306,7 +306,7 @@
"bindings": {
"cmd-n": "agent::NewThread",
"ctrl--": "pane::GoBack",
- "cmd-alt-l": "agent::OpenRulesLibrary",
+ "cmd-alt-l": "agent::ManageSkills",
"cmd-alt-p": "agent::ManageProfiles",
"cmd-i": "agent::ToggleProfileSelector",
"shift-tab": "agent::CycleModeSelector",
@@ -314,6 +314,7 @@
"alt-tab": "agent::CycleFavoriteModels",
"shift-alt-escape": "agent::ExpandMessageEditor",
"cmd->": "agent::AddSelectionToThread",
+ "cmd-alt-y": "agent::AllowAlways",
"cmd-y": "agent::AllowOnce",
"cmd-alt-a": "agent::OpenPermissionDropdown",
"cmd-alt-z": "agent::RejectOnce",
@@ -667,10 +668,10 @@
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }],
- "alt-cmd-o": ["projects::OpenRecent", { "create_new_window": false }],
- "ctrl-r": ["projects::OpenRecent", { "create_new_window": false }],
- "ctrl-cmd-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
- "ctrl-cmd-shift-o": ["projects::OpenRemote", { "from_existing_connection": true, "create_new_window": false }],
+ "alt-cmd-o": "projects::OpenRecent",
+ "ctrl-r": "projects::OpenRecent",
+ "ctrl-cmd-o": ["projects::OpenRemote", { "from_existing_connection": false }],
+ "ctrl-cmd-shift-o": ["projects::OpenRemote", { "from_existing_connection": true }],
"cmd-ctrl-b": "branches::OpenRecent",
"cmd-ctrl-w": "git::Worktree",
"ctrl-~": "workspace::NewTerminal",
@@ -1117,7 +1118,7 @@
},
},
{
- "context": "GitCommit > Editor",
+ "context": "GitCommit > Editor && mode == auto_height",
"use_key_equivalents": true,
"bindings": {
"enter": "editor::Newline",
@@ -1570,6 +1571,7 @@
"cmd--": "image_viewer::ZoomOut",
"cmd-0": "image_viewer::ResetZoom",
"cmd-1": "image_viewer::ZoomToActualSize",
+ "cmd-k r": "editor::RevealInFileManager",
"cmd-shift-0": "image_viewer::FitToView",
},
},
diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json
index 1a16a7cc27c380..f12157e68e3f61 100644
--- a/assets/keymaps/default-windows.json
+++ b/assets/keymaps/default-windows.json
@@ -261,7 +261,7 @@
"bindings": {
"ctrl-n": "agent::NewThread",
"ctrl--": "pane::GoBack",
- "shift-alt-l": "agent::OpenRulesLibrary",
+ "shift-alt-l": "agent::ManageSkills",
"shift-alt-p": "agent::ManageProfiles",
"ctrl-i": "agent::ToggleProfileSelector",
"shift-tab": "agent::CycleModeSelector",
@@ -602,10 +602,10 @@
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "ctrl-alt-o": ["projects::OpenRecent", { "create_new_window": true }],
- "ctrl-r": ["projects::OpenRecent", { "create_new_window": false }],
+ "ctrl-r": "projects::OpenRecent",
// Change to open path modal for existing remote connection by setting the parameter
- // "ctrl-shift-alt-o": "["projects::OpenRemote", { "from_existing_connection": true }]",
- "ctrl-shift-alt-o": ["projects::OpenRemote", { "from_existing_connection": false, "create_new_window": false }],
+ // "ctrl-shift-alt-o": ["projects::OpenRemote", { "from_existing_connection": true }],
+ "ctrl-shift-alt-o": ["projects::OpenRemote", { "from_existing_connection": false }],
"shift-alt-b": "branches::OpenRecent",
"shift-alt-w": "git::Worktree",
"shift-alt-enter": "toast::RunAction",
@@ -1003,7 +1003,7 @@
},
},
{
- "context": "GitCommit > Editor",
+ "context": "GitCommit > Editor && mode == auto_height",
"use_key_equivalents": true,
"bindings": {
"escape": "menu::Cancel",
@@ -1496,6 +1496,7 @@
"ctrl--": "image_viewer::ZoomOut",
"ctrl-0": "image_viewer::ResetZoom",
"ctrl-1": "image_viewer::ZoomToActualSize",
+ "ctrl-k r": "editor::RevealInFileManager",
"ctrl-shift-0": "image_viewer::FitToView",
},
},
diff --git a/assets/settings/default.json b/assets/settings/default.json
index 3877eb97bdd7e1..c534ddd2cacf90 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -150,6 +150,14 @@
// that are already part of an open project)
// "cli_default_open_behavior": "new_window"
"cli_default_open_behavior": "existing_window",
+ // The default behavior when opening projects from the UI.
+ //
+ // May take 2 values:
+ // 1. Open projects as a new workspace in the current Zed window's sidebar
+ // "default_open_behavior": "existing_window"
+ // 2. Open projects in a new window
+ // "default_open_behavior": "new_window"
+ "default_open_behavior": "existing_window",
// Whether to attempt to restore previous file's state when opening it again.
// The state is stored per pane.
// When disabled, defaults are applied instead of the state restoration.
@@ -521,14 +529,6 @@
"button_layout": "platform_default",
},
"audio": {
- // Automatically increase or decrease you microphone's volume. This affects how
- // loud you sound to others.
- //
- // Recommended: off (default)
- // Microphones are too quite in zed, until everyone is on experimental
- // audio and has auto speaker volume on this will make you very loud
- // compared to other speakers.
- "experimental.auto_microphone_volume": false,
// Select specific output audio device.
// `null` means use system default.
// Any unrecognized output device will fall back to system default.
@@ -998,8 +998,8 @@
// Maximum length of the commit message title before a warning is shown.
// Set to 0 to disable.
//
- // Default: 72
- "commit_title_max_length": 72,
+ // Default: 0
+ "commit_title_max_length": 0,
},
"message_editor": {
// Whether to automatically replace emoji shortcodes with emoji characters.
@@ -1106,6 +1106,22 @@
},
// When enabled, agent edits will be displayed in single-file editors for review
"single_file_review": false,
+ // Settings for automatic agent context compaction, which summarizes earlier
+ // messages to free up room in the model's context window once it grows too
+ // large.
+ "auto_compact": {
+ // Whether to automatically compact the agent's context near the limit.
+ "enabled": true,
+ // The threshold at which auto-compaction runs. One of:
+ // - A percentage string ending in "%" (e.g. "90%"), measured against
+ // the model's context window. Decimals are allowed (e.g. "95.5%").
+ // - A positive integer: compact after that many tokens have been used
+ // (e.g. 100000 compacts after 100,000 tokens are used).
+ // - A negative integer: compact once that many tokens remain in the
+ // context window (e.g. -20000 compacts once fewer than 20,000 remain).
+ // 0 is not a valid threshold.
+ "threshold": "90%",
+ },
// When enabled, show voting thumbs for feedback on agent edits.
"enable_feedback": true,
"default_profile": "write",
@@ -1136,8 +1152,6 @@
"skill": true,
"spawn_agent": true,
"terminal": true,
- "update_plan": true,
- "update_title": true,
"search_web": true,
},
},
@@ -1159,8 +1173,6 @@
"grep": true,
"skill": true,
"spawn_agent": true,
- "update_plan": true,
- "update_title": true,
"search_web": true,
},
},
@@ -1491,6 +1503,9 @@
"diagnostics": true,
// Send anonymized usage data like what languages you're using Zed with.
"metrics": true,
+ // Allow sending requests to Anthropic models that cannot be offered with
+ // Zero Data Retention
+ "anthropic_retention": false,
},
// Whether to disable all AI features in Zed.
//
@@ -2155,7 +2170,7 @@
},
},
"CSharp": {
- "language_servers": ["roslyn", "!omnisharp", "..."],
+ "language_servers": ["roslyn", "!csharp-ls", "!omnisharp", "..."],
},
"CSS": {
"prettier": {
@@ -2370,6 +2385,7 @@
"anthropic": {
"api_url": "https://api.anthropic.com",
},
+ "anthropic_compatible": {},
"bedrock": {},
"google": {
"api_url": "https://generativelanguage.googleapis.com",
diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs
index be10bc819aada0..fcf05fe84dec16 100644
--- a/crates/acp_thread/src/acp_thread.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -42,7 +42,10 @@ use text::Bias;
use ui::App;
use util::markdown::MarkdownEscaped;
use util::path_list::PathList;
-use util::{ResultExt, get_default_system_shell_preferring_bash, paths::PathStyle};
+use util::{
+ ResultExt, get_default_system_shell_preferring_bash,
+ paths::{PathStyle, is_absolute},
+};
use uuid::Uuid;
/// Returned when the model stops because it exhausted its output token budget.
@@ -74,11 +77,89 @@ pub fn meta_with_tool_name(tool_name: &str) -> acp::Meta {
acp::Meta::from_iter([(TOOL_NAME_META_KEY.into(), tool_name.into())])
}
+/// Key used in ACP `AvailableCommand` meta to record which source produced a
+/// slash command, so the completion popup can group commands by category.
+pub const COMMAND_CATEGORY_META_KEY: &str = "command_category";
+
+/// The source category of a slash command, used to group commands in the
+/// completion popup. Only the native Zed agent annotates its commands; commands
+/// from external ACP agents carry no category and are grouped on their own.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum CommandCategory {
+ /// Built-in Zed agent commands (e.g. `/compact`).
+ Native,
+ /// Commands sourced from MCP server prompts.
+ Mcp,
+}
+
+impl CommandCategory {
+ fn as_str(self) -> &'static str {
+ match self {
+ Self::Native => "native",
+ Self::Mcp => "mcp",
+ }
+ }
+
+ fn from_str(value: &str) -> Option {
+ match value {
+ "native" => Some(Self::Native),
+ "mcp" => Some(Self::Mcp),
+ _ => None,
+ }
+ }
+}
+
+pub fn meta_with_command_category(category: CommandCategory) -> acp::Meta {
+ acp::Meta::from_iter([(COMMAND_CATEGORY_META_KEY.into(), category.as_str().into())])
+}
+
+pub fn command_category_from_meta(meta: &Option) -> Option {
+ meta.as_ref()
+ .and_then(|m| m.get(COMMAND_CATEGORY_META_KEY))
+ .and_then(|v| v.as_str())
+ .and_then(CommandCategory::from_str)
+}
+
/// Key used in ACP ToolCall meta to store the session id and message indexes
pub const SUBAGENT_SESSION_INFO_META_KEY: &str = "subagent_session_info";
pub const SANDBOX_AUTHORIZATION_META_KEY: &str = "sandbox_authorization";
+/// Stable `PermissionOption` ids for the sandbox-escalation approval prompt.
+///
+/// These are shared across the option construction (in the agent), the outcome
+/// dispatch, and the UI so the distinct grant lifetimes stay in sync. Note
+/// that `AllowThread` and `AllowAlways` both use
+/// `PermissionOptionKind::AllowAlways`; the id is what distinguishes them.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum SandboxPermission {
+ AllowOnce,
+ AllowThread,
+ AllowAlways,
+ Deny,
+}
+
+impl SandboxPermission {
+ pub fn as_id(self) -> &'static str {
+ match self {
+ Self::AllowOnce => "allow",
+ Self::AllowThread => "allow_thread",
+ Self::AllowAlways => "allow_always",
+ Self::Deny => "deny",
+ }
+ }
+
+ pub fn from_id(id: &str) -> Option {
+ match id {
+ "allow" => Some(Self::AllowOnce),
+ "allow_thread" => Some(Self::AllowThread),
+ "allow_always" => Some(Self::AllowAlways),
+ "deny" => Some(Self::Deny),
+ _ => None,
+ }
+ }
+}
+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct SandboxAuthorizationDetails {
#[serde(default)]
@@ -220,7 +301,42 @@ pub enum AgentThreadEntry {
AssistantMessage(AssistantMessage),
ToolCall(ToolCall),
CompletedPlan(Vec),
- ContextCompaction,
+ ContextCompaction(ContextCompaction),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ContextCompactionId(pub Arc);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ContextCompactionStatus {
+ InProgress,
+ Completed,
+ Canceled,
+}
+
+/// A point in the thread where the conversation history was compacted to free
+/// up room in the model's context window. The summary can be expanded to inspect
+/// what the model retained.
+#[derive(Debug)]
+pub struct ContextCompaction {
+ pub id: ContextCompactionId,
+ pub status: ContextCompactionStatus,
+ /// The compaction summary, streamed in as the model produces it. This is
+ /// `None` for provider-native compaction, which produces no summary to show.
+ pub summary: Option>,
+}
+
+impl ContextCompaction {
+ pub fn is_in_progress(&self) -> bool {
+ self.status == ContextCompactionStatus::InProgress
+ }
+}
+
+#[derive(Debug)]
+pub struct ContextCompactionUpdate {
+ pub id: ContextCompactionId,
+ pub summary_delta: String,
+ pub status: Option,
}
impl AgentThreadEntry {
@@ -230,7 +346,7 @@ impl AgentThreadEntry {
Self::AssistantMessage(message) => message.indented,
Self::ToolCall(_) => false,
Self::CompletedPlan(_) => false,
- Self::ContextCompaction => false,
+ Self::ContextCompaction(_) => false,
}
}
@@ -247,7 +363,7 @@ impl AgentThreadEntry {
}
md
}
- Self::ContextCompaction => "--- Context Compacted ---\n\n".to_string(),
+ Self::ContextCompaction(_) => "--- Context Compacted ---\n\n".to_string(),
}
}
@@ -400,7 +516,7 @@ impl ToolCall {
}
if let Some(status) = status {
- self.status = status.into();
+ self.update_acp_status(status);
}
if let Some(subagent_session_info) = subagent_session_info_from_meta(&meta) {
@@ -483,6 +599,31 @@ impl ToolCall {
Ok(())
}
+ fn update_status(&mut self, status: ToolCallStatus) {
+ match status {
+ ToolCallStatus::Pending => self.update_acp_status(acp::ToolCallStatus::Pending),
+ ToolCallStatus::InProgress => self.update_acp_status(acp::ToolCallStatus::InProgress),
+ ToolCallStatus::Completed => self.update_acp_status(acp::ToolCallStatus::Completed),
+ ToolCallStatus::Failed => self.update_acp_status(acp::ToolCallStatus::Failed),
+ status @ (ToolCallStatus::WaitingForConfirmation { .. }
+ | ToolCallStatus::Rejected
+ | ToolCallStatus::Canceled) => self.status = status,
+ }
+ }
+
+ fn update_acp_status(&mut self, status: acp::ToolCallStatus) {
+ if let ToolCallStatus::WaitingForConfirmation { current_status, .. } = &mut self.status
+ && matches!(
+ status,
+ acp::ToolCallStatus::Pending | acp::ToolCallStatus::InProgress
+ )
+ {
+ *current_status = status;
+ } else {
+ self.status = status.into();
+ }
+ }
+
pub fn diffs(&self) -> impl Iterator- > {
self.content.iter().filter_map(|content| match content {
ToolCallContent::Diff(diff) => Some(diff),
@@ -535,9 +676,16 @@ impl ToolCall {
) -> Option {
let buffer = project
.update(cx, |project, cx| {
- project
- .project_path_for_absolute_path(&location.path, cx)
- .map(|path| project.open_buffer(path, cx))
+ if let Some(path) = project.project_path_for_absolute_path(&location.path, cx) {
+ Some(project.open_buffer(path, cx))
+ } else if is_absolute(
+ location.path.to_string_lossy().as_ref(),
+ project.path_style(cx),
+ ) {
+ Some(project.open_local_buffer(&location.path, cx))
+ } else {
+ None
+ }
})
.ok()??;
let buffer = buffer.await.log_err()?;
@@ -595,7 +743,7 @@ pub enum SelectedPermissionParams {
Terminal { patterns: Vec },
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct SelectedPermissionOutcome {
pub option_id: acp::PermissionOptionId,
pub option_kind: acp::PermissionOptionKind,
@@ -661,6 +809,7 @@ pub enum ToolCallStatus {
Pending,
/// The tool call is waiting for confirmation from the user.
WaitingForConfirmation {
+ current_status: acp::ToolCallStatus,
options: PermissionOptions,
respond_tx: oneshot::Sender,
kind: AuthorizationKind,
@@ -689,6 +838,26 @@ impl From for ToolCallStatus {
}
}
+impl ToolCallStatus {
+ fn as_acp_status(&self) -> Option {
+ match self {
+ ToolCallStatus::Pending => Some(acp::ToolCallStatus::Pending),
+ ToolCallStatus::WaitingForConfirmation { current_status, .. } => Some(*current_status),
+ ToolCallStatus::InProgress => Some(acp::ToolCallStatus::InProgress),
+ ToolCallStatus::Completed => Some(acp::ToolCallStatus::Completed),
+ ToolCallStatus::Failed => Some(acp::ToolCallStatus::Failed),
+ ToolCallStatus::Rejected | ToolCallStatus::Canceled => None,
+ }
+ }
+
+ fn status_after_permission_grant(status: acp::ToolCallStatus) -> ToolCallStatus {
+ match ToolCallStatus::from(status) {
+ ToolCallStatus::Pending => ToolCallStatus::InProgress,
+ status => status,
+ }
+ }
+}
+
impl Display for ToolCallStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
@@ -790,6 +959,33 @@ impl ContentBlock {
}
}
+ /// Updates a Markdown block in place from a streaming text `block`, reusing
+ /// the existing `Markdown` entity rather than recreating it. Appends only the
+ /// new suffix when the update is a continuation (the common streaming case),
+ /// otherwise re-sets the source. Returns `false` when an in-place update isn't
+ /// applicable, so the caller can fall back to replacing the block wholesale.
+ ///
+ /// Recreating the entity on every streamed snapshot causes the rendered
+ /// element to tear down and rebuild, which flickers badly.
+ pub fn update_text_in_place(&mut self, block: &acp::ContentBlock, cx: &mut App) -> bool {
+ let ContentBlock::Markdown { markdown } = self else {
+ return false;
+ };
+ let acp::ContentBlock::Text(text_content) = block else {
+ return false;
+ };
+ let new_content = &text_content.text;
+ markdown.update(cx, |markdown, cx| {
+ let current = markdown.source().to_string();
+ match new_content.strip_prefix(¤t) {
+ Some("") => {}
+ Some(suffix) => markdown.append(suffix, cx),
+ None => markdown.reset(new_content.clone().into(), cx),
+ }
+ });
+ true
+ }
+
fn decode_image(
image_content: &acp::ImageContent,
) -> Option<(Arc, Option>)> {
@@ -958,6 +1154,17 @@ impl ToolCallContent {
terminals: &HashMap>,
cx: &mut App,
) -> Result {
+ // Update streaming text in place so the rendered markdown element is
+ // reused across snapshots instead of being recreated (which flickers).
+ if let (
+ Self::ContentBlock(block),
+ acp::ToolCallContent::Content(acp::Content { content, .. }),
+ ) = (&mut *self, &new)
+ && block.update_text_in_place(content, cx)
+ {
+ return Ok(true);
+ }
+
let needs_update = match (&self, &new) {
(Self::Diff(old_diff), acp::ToolCallContent::Diff(new_diff)) => {
old_diff.read(cx).needs_update(
@@ -1158,6 +1365,20 @@ pub struct RetryStatus {
pub max_attempts: usize,
pub started_at: Instant,
pub duration: Duration,
+ pub meta: Option,
+}
+
+pub const REFUSAL_FALLBACK_MODEL_META_KEY: &str = "refusal_fallback_model";
+
+pub fn meta_with_refusal_fallback(model_name: &str) -> acp::Meta {
+ acp::Meta::from_iter([(REFUSAL_FALLBACK_MODEL_META_KEY.into(), model_name.into())])
+}
+
+pub fn refusal_fallback_model_from_meta(meta: &Option) -> Option {
+ meta.as_ref()
+ .and_then(|m| m.get(REFUSAL_FALLBACK_MODEL_META_KEY))
+ .and_then(|v| v.as_str())
+ .map(|s| SharedString::from(s.to_owned()))
}
struct RunningTurn {
@@ -1469,6 +1690,15 @@ impl AcpThread {
&self.entries
}
+ pub fn is_compacting(&self) -> bool {
+ self.entries.last().is_some_and(|entry| {
+ matches!(
+ entry,
+ AgentThreadEntry::ContextCompaction(compaction) if compaction.is_in_progress()
+ )
+ })
+ }
+
pub fn invalidate_mermaid_caches(&self, cx: &mut App) {
for entry in &self.entries {
let chunks = match entry {
@@ -1529,7 +1759,7 @@ impl AcpThread {
AgentThreadEntry::ToolCall(_)
| AgentThreadEntry::AssistantMessage(_)
| AgentThreadEntry::CompletedPlan(_)
- | AgentThreadEntry::ContextCompaction => {}
+ | AgentThreadEntry::ContextCompaction(_) => {}
}
}
false
@@ -1558,7 +1788,7 @@ impl AcpThread {
AgentThreadEntry::ToolCall(_)
| AgentThreadEntry::AssistantMessage(_)
| AgentThreadEntry::CompletedPlan(_)
- | AgentThreadEntry::ContextCompaction => {}
+ | AgentThreadEntry::ContextCompaction(_) => {}
}
}
@@ -1578,7 +1808,7 @@ impl AcpThread {
AgentThreadEntry::ToolCall(_)
| AgentThreadEntry::AssistantMessage(_)
| AgentThreadEntry::CompletedPlan(_)
- | AgentThreadEntry::ContextCompaction => {}
+ | AgentThreadEntry::ContextCompaction(_) => {}
}
}
@@ -1591,7 +1821,7 @@ impl AcpThread {
AgentThreadEntry::UserMessage(..) => return false,
AgentThreadEntry::AssistantMessage(..)
| AgentThreadEntry::CompletedPlan(..)
- | AgentThreadEntry::ContextCompaction => continue,
+ | AgentThreadEntry::ContextCompaction(_) => continue,
AgentThreadEntry::ToolCall(..) => return true,
}
}
@@ -1660,7 +1890,7 @@ impl AcpThread {
config_options,
..
}) => cx.emit(AcpThreadEvent::ConfigOptionsUpdated(config_options)),
- acp::SessionUpdate::UsageUpdate(update) if cx.has_flag::() => {
+ acp::SessionUpdate::UsageUpdate(update) => {
let usage = self.token_usage.get_or_insert_with(Default::default);
usage.max_tokens = update.size;
usage.used_tokens = update.used;
@@ -1934,8 +2164,69 @@ impl AcpThread {
cx.emit(AcpThreadEvent::NewEntry);
}
- pub fn push_context_compaction(&mut self, cx: &mut Context) {
- self.push_entry(AgentThreadEntry::ContextCompaction, cx);
+ pub fn push_context_compaction(
+ &mut self,
+ compaction: ContextCompaction,
+ cx: &mut Context,
+ ) {
+ if let Some(ix) =
+ self.entries
+ .iter()
+ .enumerate()
+ .rev()
+ .find_map(|(ix, entry)| match entry {
+ AgentThreadEntry::ContextCompaction(c) if &c.id == &compaction.id => Some(ix),
+ _ => None,
+ })
+ {
+ self.entries[ix] = AgentThreadEntry::ContextCompaction(compaction);
+ cx.emit(AcpThreadEvent::EntryUpdated(ix));
+ } else {
+ self.push_entry(AgentThreadEntry::ContextCompaction(compaction), cx);
+ }
+ }
+
+ pub fn update_context_compaction(
+ &mut self,
+ update: ContextCompactionUpdate,
+ cx: &mut Context,
+ ) {
+ let language_registry = self.project.read(cx).languages().clone();
+ let Some((ix, compaction)) =
+ self.entries
+ .iter_mut()
+ .enumerate()
+ .rev()
+ .find_map(|(ix, entry)| match entry {
+ AgentThreadEntry::ContextCompaction(c) if &c.id == &update.id => Some((ix, c)),
+ _ => None,
+ })
+ else {
+ return;
+ };
+
+ if !update.summary_delta.is_empty() {
+ if compaction.summary.is_none() {
+ compaction.summary = Some(cx.new(|cx| {
+ Markdown::new(
+ update.summary_delta.into(),
+ Some(language_registry),
+ None,
+ cx,
+ )
+ }));
+ } else if let Some(summary) = compaction.summary.clone() {
+ summary.update(cx, |markdown, cx| {
+ markdown.append(&update.summary_delta, cx)
+ });
+ }
+ }
+
+ if let Some(status) = update.status {
+ compaction.status = status;
+ }
+
+ cx.emit(AcpThreadEvent::EntryUpdated(ix));
}
pub fn can_set_title(&mut self, cx: &mut Context) -> bool {
@@ -2106,7 +2397,7 @@ impl AcpThread {
&self.terminals,
cx,
)?;
- call.status = status;
+ call.update_status(status);
cx.emit(AcpThreadEvent::EntryUpdated(ix));
} else {
@@ -2257,7 +2548,13 @@ impl AcpThread {
) -> Result> {
let (tx, rx) = oneshot::channel();
+ let current_status = self
+ .tool_call(&tool_call.tool_call_id)
+ .and_then(|(_, tool_call)| tool_call.status.as_acp_status())
+ .or(tool_call.fields.status)
+ .unwrap_or(acp::ToolCallStatus::Pending);
let status = ToolCallStatus::WaitingForConfirmation {
+ current_status,
options,
respond_tx: tx,
kind,
@@ -2292,24 +2589,30 @@ impl AcpThread {
return;
};
- let is_action_choice = matches!(
- call.status,
- ToolCallStatus::WaitingForConfirmation {
- kind: AuthorizationKind::ActionChoice,
- ..
- }
- );
let new_status =
- if is_action_choice {
- ToolCallStatus::InProgress
- } else {
- match outcome.option_kind {
+ match &call.status {
+ ToolCallStatus::WaitingForConfirmation {
+ kind: AuthorizationKind::ActionChoice,
+ ..
+ } => ToolCallStatus::InProgress,
+ ToolCallStatus::WaitingForConfirmation { current_status, .. } => {
+ match outcome.option_kind {
+ acp::PermissionOptionKind::RejectOnce
+ | acp::PermissionOptionKind::RejectAlways => ToolCallStatus::Rejected,
+ acp::PermissionOptionKind::AllowOnce
+ | acp::PermissionOptionKind::AllowAlways => {
+ ToolCallStatus::status_after_permission_grant(*current_status)
+ }
+ _ => ToolCallStatus::status_after_permission_grant(*current_status),
+ }
+ }
+ _ => match outcome.option_kind {
acp::PermissionOptionKind::RejectOnce
| acp::PermissionOptionKind::RejectAlways => ToolCallStatus::Rejected,
acp::PermissionOptionKind::AllowOnce
| acp::PermissionOptionKind::AllowAlways => ToolCallStatus::InProgress,
_ => ToolCallStatus::InProgress,
- }
+ },
};
let curr_status = mem::replace(&mut call.status, new_status);
@@ -2382,6 +2685,27 @@ impl AcpThread {
&mut self,
message: Vec,
cx: &mut Context,
+ ) -> BoxFuture<'static, Result