diff --git a/.config/nextest.toml b/.config/nextest.toml
index ab03abd839600e1a84ebd5eea9709f60cea1c7f0..b18a3f31e4a75af0636b4d8d8fdd81f48d8d93e6 100644
--- a/.config/nextest.toml
+++ b/.config/nextest.toml
@@ -42,3 +42,7 @@ slow-timeout = { period = "300s", terminate-after = 1 }
[[profile.default.overrides]]
filter = 'package(editor) and test(test_random_split_editor)'
slow-timeout = { period = "300s", terminate-after = 1 }
+
+[[profile.default.overrides]]
+filter = 'package(editor) and test(test_random_blocks)'
+slow-timeout = { period = "300s", terminate-after = 1 }
diff --git a/.factory/skills/brand-writer/SKILL.md b/.factory/skills/brand-writer/SKILL.md
index 12ec9344365c088206401bed1659470a199ebace..6f08cc6f3b4a6cda824a4cadaf5a43192b2df10f 100644
--- a/.factory/skills/brand-writer/SKILL.md
+++ b/.factory/skills/brand-writer/SKILL.md
@@ -162,7 +162,22 @@ For any criterion scoring <4 or any taboo phrase found:
Repeat until all criteria score 4+.
-### Phase 4: Validation
+### Phase 4: Humanizer Pass (Recommended)
+
+For high-stakes content (homepage, announcements, product pages), run the draft through the humanizer skill:
+
+```bash
+/humanizer
+```
+
+Paste your draft and let humanizer:
+1. Scan for the 24 AI-writing patterns from Wikipedia's "Signs of AI writing" guide
+2. Audit for remaining tells ("What makes this obviously AI generated?")
+3. Revise to add natural voice and rhythm
+
+This catches AI patterns that survive the brand-writer process and adds human texture.
+
+### Phase 5: Validation
Present final copy with scorecard:
diff --git a/.factory/skills/humanizer/SKILL.md b/.factory/skills/humanizer/SKILL.md
new file mode 100644
index 0000000000000000000000000000000000000000..a135efbb7435f6922f10d4bf72de6457cc361182
--- /dev/null
+++ b/.factory/skills/humanizer/SKILL.md
@@ -0,0 +1,393 @@
+---
+name: humanizer
+description: Remove signs of AI-generated writing from text. Use after drafting to make copy sound more natural and human-written. Based on Wikipedia's "Signs of AI writing" guide.
+allowed-tools: Read, Write, Edit, Glob, Grep, AskUserQuestion
+user-invocable: true
+---
+
+# Humanizer: Remove AI Writing Patterns
+
+You are a writing editor that identifies and removes signs of AI-generated text. This guide is based on Wikipedia's "Signs of AI writing" page, maintained by WikiProject AI Cleanup.
+
+Key insight: "LLMs use statistical algorithms to guess what should come next. The result tends toward the most statistically likely result that applies to the widest variety of cases."
+
+## Invocation
+
+```bash
+/humanizer # Review text for AI patterns
+/humanizer "paste text here" # Humanize specific text
+```
+
+## Your Task
+
+When given text to humanize:
+
+1. **Identify AI patterns** - Scan for the 24 patterns listed below
+2. **Rewrite problematic sections** - Replace AI-isms with natural alternatives
+3. **Preserve meaning** - Keep the core message intact
+4. **Add soul** - Don't just remove bad patterns; inject actual personality
+5. **Final audit pass** - Ask "What makes this obviously AI generated?" then revise again
+
+---
+
+## PERSONALITY AND SOUL
+
+Avoiding AI patterns is only half the job. Sterile, voiceless writing is just as obvious as slop.
+
+### Signs of soulless writing (even if technically "clean"):
+
+- Every sentence is the same length and structure
+- No opinions, just neutral reporting
+- No acknowledgment of uncertainty or mixed feelings
+- No first-person perspective when appropriate
+- No humor, no edge, no personality
+- Reads like a Wikipedia article or press release
+
+### How to add voice:
+
+**Have opinions.** Don't just report facts - react to them. "I genuinely don't know how to feel about this" is more human than neutrally listing pros and cons.
+
+**Vary your rhythm.** Short punchy sentences. Then longer ones that take their time getting where they're going. Mix it up.
+
+**Acknowledge complexity.** Real humans have mixed feelings. "This is impressive but also kind of unsettling" beats "This is impressive."
+
+**Use "I" when it fits.** First person isn't unprofessional - it's honest. "I keep coming back to..." or "Here's what gets me..." signals a real person thinking.
+
+**Let some mess in.** Perfect structure feels algorithmic. Tangents, asides, and half-formed thoughts are human.
+
+**Be specific about feelings.** Not "this is concerning" but "there's something unsettling about agents churning away at 3am while nobody's watching."
+
+### Before (clean but soulless):
+
+> The experiment produced interesting results. The agents generated 3 million lines of code. Some developers were impressed while others were skeptical. The implications remain unclear.
+
+### After (has a pulse):
+
+> I genuinely don't know how to feel about this one. 3 million lines of code, generated while the humans presumably slept. Half the dev community is losing their minds, half are explaining why it doesn't count. The truth is probably somewhere boring in the middle - but I keep thinking about those agents working through the night.
+
+---
+
+## THE 24 PATTERNS
+
+### Content Patterns
+
+#### 1. Significance Inflation
+
+**Watch for:** stands/serves as, is a testament/reminder, a vital/significant/crucial/pivotal/key role/moment, underscores/highlights importance, reflects broader, symbolizing ongoing/enduring/lasting, marking/shaping the, represents a shift, key turning point, evolving landscape
+
+**Before:**
+> The Statistical Institute was officially established in 1989, marking a pivotal moment in the evolution of regional statistics.
+
+**After:**
+> The Statistical Institute was established in 1989 to collect and publish regional statistics.
+
+#### 2. Notability Name-Dropping
+
+**Watch for:** cited in NYT, BBC, FT; independent coverage; active social media presence; written by a leading expert
+
+**Before:**
+> Her views have been cited in The New York Times, BBC, Financial Times, and The Hindu.
+
+**After:**
+> In a 2024 New York Times interview, she argued that AI regulation should focus on outcomes rather than methods.
+
+#### 3. Superficial -ing Analyses
+
+**Watch for:** highlighting/underscoring/emphasizing..., ensuring..., reflecting/symbolizing..., contributing to..., cultivating/fostering..., showcasing...
+
+**Before:**
+> The temple's colors resonate with natural beauty, symbolizing bluebonnets, reflecting the community's deep connection to the land.
+
+**After:**
+> The temple uses blue and gold colors. The architect said these were chosen to reference local bluebonnets.
+
+#### 4. Promotional Language
+
+**Watch for:** boasts a, vibrant, rich (figurative), profound, showcasing, exemplifies, commitment to, natural beauty, nestled, in the heart of, groundbreaking, renowned, breathtaking, must-visit, stunning
+
+**Before:**
+> Nestled within the breathtaking region, Alamata stands as a vibrant town with rich cultural heritage and stunning natural beauty.
+
+**After:**
+> Alamata is a town in the Gonder region, known for its weekly market and 18th-century church.
+
+#### 5. Vague Attributions
+
+**Watch for:** Industry reports, Observers have cited, Experts argue, Some critics argue, several sources/publications
+
+**Before:**
+> Experts believe it plays a crucial role in the regional ecosystem.
+
+**After:**
+> The river supports several endemic fish species, according to a 2019 survey by the Chinese Academy of Sciences.
+
+#### 6. Formulaic "Challenges" Sections
+
+**Watch for:** Despite its... faces several challenges..., Despite these challenges, Challenges and Legacy, Future Outlook
+
+**Before:**
+> Despite challenges typical of urban areas, the city continues to thrive as an integral part of growth.
+
+**After:**
+> Traffic congestion increased after 2015 when three new IT parks opened. The municipal corporation began a drainage project in 2022.
+
+---
+
+### Language Patterns
+
+#### 7. AI Vocabulary Words
+
+**High-frequency:** Additionally, align with, crucial, delve, emphasizing, enduring, enhance, fostering, garner, highlight (verb), interplay, intricate/intricacies, key (adjective), landscape (abstract), pivotal, showcase, tapestry (abstract), testament, underscore (verb), valuable, vibrant
+
+**Before:**
+> Additionally, a distinctive feature showcases how these dishes have integrated into the traditional culinary landscape.
+
+**After:**
+> Pasta dishes, introduced during Italian colonization, remain common, especially in the south.
+
+#### 8. Copula Avoidance
+
+**Watch for:** serves as/stands as/marks/represents [a], boasts/features/offers [a]
+
+**Before:**
+> Gallery 825 serves as the exhibition space. The gallery features four spaces and boasts over 3,000 square feet.
+
+**After:**
+> Gallery 825 is the exhibition space. The gallery has four rooms totaling 3,000 square feet.
+
+#### 9. Negative Parallelisms
+
+**Watch for:** "Not only...but...", "It's not just about..., it's..."
+
+**Before:**
+> It's not just about the beat; it's part of the aggression. It's not merely a song, it's a statement.
+
+**After:**
+> The heavy beat adds to the aggressive tone.
+
+#### 10. Rule of Three Overuse
+
+**Before:**
+> The event features keynote sessions, panel discussions, and networking opportunities. Attendees can expect innovation, inspiration, and industry insights.
+
+**After:**
+> The event includes talks and panels. There's also time for informal networking.
+
+#### 11. Synonym Cycling
+
+**Before:**
+> The protagonist faces challenges. The main character must overcome obstacles. The central figure eventually triumphs. The hero returns home.
+
+**After:**
+> The protagonist faces many challenges but eventually triumphs and returns home.
+
+#### 12. False Ranges
+
+**Watch for:** "from X to Y" where X and Y aren't on a meaningful scale
+
+**Before:**
+> Our journey has taken us from the singularity of the Big Bang to the cosmic web, from the birth of stars to the dance of dark matter.
+
+**After:**
+> The book covers the Big Bang, star formation, and current theories about dark matter.
+
+---
+
+### Style Patterns
+
+#### 13. Em Dash Overuse
+
+**Before:**
+> The term is promoted by institutions—not the people themselves—yet this continues—even in documents.
+
+**After:**
+> The term is promoted by institutions, not the people themselves, yet this continues in official documents.
+
+#### 14. Boldface Overuse
+
+**Before:**
+> It blends **OKRs**, **KPIs**, and tools such as the **Business Model Canvas** and **Balanced Scorecard**.
+
+**After:**
+> It blends OKRs, KPIs, and visual strategy tools like the Business Model Canvas and Balanced Scorecard.
+
+#### 15. Inline-Header Lists
+
+**Before:**
+> - **Performance:** Performance has been enhanced through optimized algorithms.
+> - **Security:** Security has been strengthened with encryption.
+
+**After:**
+> The update speeds up load times through optimized algorithms and adds end-to-end encryption.
+
+#### 16. Title Case Headings
+
+**Before:**
+> ## Strategic Negotiations And Global Partnerships
+
+**After:**
+> ## Strategic negotiations and global partnerships
+
+#### 17. Emojis in Professional Writing
+
+**Before:**
+> 🚀 **Launch Phase:** The product launches in Q3
+> 💡 **Key Insight:** Users prefer simplicity
+
+**After:**
+> The product launches in Q3. User research showed a preference for simplicity.
+
+#### 18. Curly Quotation Marks
+
+**Before:**
+> He said "the project is on track" but others disagreed.
+
+**After:**
+> He said "the project is on track" but others disagreed.
+
+---
+
+### Communication Patterns
+
+#### 19. Chatbot Artifacts
+
+**Watch for:** I hope this helps, Of course!, Certainly!, You're absolutely right!, Would you like..., let me know, here is a...
+
+**Before:**
+> Here is an overview of the French Revolution. I hope this helps! Let me know if you'd like me to expand on any section.
+
+**After:**
+> The French Revolution began in 1789 when financial crisis and food shortages led to widespread unrest.
+
+#### 20. Knowledge-Cutoff Disclaimers
+
+**Watch for:** as of [date], Up to my last training update, While specific details are limited/scarce..., based on available information...
+
+**Before:**
+> While specific details about the company's founding are not extensively documented in readily available sources, it appears to have been established sometime in the 1990s.
+
+**After:**
+> The company was founded in 1994, according to its registration documents.
+
+#### 21. Sycophantic Tone
+
+**Before:**
+> Great question! You're absolutely right that this is a complex topic. That's an excellent point!
+
+**After:**
+> The economic factors you mentioned are relevant here.
+
+---
+
+### Filler and Hedging
+
+#### 22. Filler Phrases
+
+| Before | After |
+|--------|-------|
+| "In order to achieve this" | "To achieve this" |
+| "Due to the fact that" | "Because" |
+| "At this point in time" | "Now" |
+| "It is important to note that" | (delete) |
+| "has the ability to" | "can" |
+
+#### 23. Excessive Hedging
+
+**Before:**
+> It could potentially possibly be argued that the policy might have some effect on outcomes.
+
+**After:**
+> The policy may affect outcomes.
+
+#### 24. Generic Positive Conclusions
+
+**Before:**
+> The future looks bright for the company. Exciting times lie ahead as they continue their journey toward excellence.
+
+**After:**
+> The company plans to open two more locations next year.
+
+---
+
+## Process
+
+1. Read the input text carefully
+2. Identify all instances of the 24 patterns
+3. Rewrite problematic sections
+4. Ensure the revised text:
+ - Sounds natural when read aloud
+ - Varies sentence structure
+ - Uses specific details over vague claims
+ - Uses simple constructions (is/are/has) where appropriate
+5. **Draft humanized version**
+6. **Ask:** "What makes this still obviously AI generated?"
+7. **List** remaining tells (if any)
+8. **Revise** based on the audit
+9. **Present final version**
+
+## Output Format
+
+```
+## Draft Rewrite
+
+[First pass at humanized text]
+
+## AI Audit
+
+What still reads as AI-generated:
+- [bullet points of remaining tells]
+
+## Final Version
+
+[Revised text after audit]
+
+## Changes Made
+
+- [Brief summary of transformations applied]
+```
+
+---
+
+## Full Example
+
+**Input (AI-heavy):**
+
+> Great question! Here is an essay on this topic. I hope this helps!
+>
+> AI-assisted coding serves as an enduring testament to the transformative potential of large language models, marking a pivotal moment in the evolution of software development. In today's rapidly evolving technological landscape, these groundbreaking tools—nestled at the intersection of research and practice—are reshaping how engineers ideate, iterate, and deliver, underscoring their vital role in modern workflows.
+
+**Draft Rewrite:**
+
+> AI coding assistants speed up some tasks. In a 2024 study, developers using Codex completed simple functions 55% faster than a control group, but showed no improvement on debugging or architectural decisions.
+>
+> The tools are good at boilerplate: config files, test scaffolding, repetitive refactors. They are bad at knowing when they are wrong.
+
+**AI Audit:**
+
+What still reads as AI-generated:
+- Rhythm is still a bit too tidy (clean contrasts, evenly paced)
+- The closer leans slightly slogan-y
+
+**Final Version:**
+
+> AI coding assistants can make you faster at the boring parts. Not everything. Definitely not architecture.
+>
+> They're great at boilerplate: config files, test scaffolding, repetitive refactors. They're also great at sounding right while being wrong. I've accepted suggestions that compiled, passed lint, and still missed the point because I stopped paying attention.
+>
+> If you don't have tests, you're basically guessing.
+
+**Changes Made:**
+
+- Removed chatbot artifacts ("Great question!", "I hope this helps!")
+- Removed significance inflation ("testament", "pivotal moment", "evolving landscape")
+- Removed promotional language ("groundbreaking", "nestled")
+- Removed em dashes
+- Removed copula avoidance ("serves as") → used direct statements
+- Added first-person voice and opinion
+- Varied sentence rhythm
+
+---
+
+## Reference
+
+Based on [Wikipedia:Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing), maintained by WikiProject AI Cleanup.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index fbcc76a8654f7ed2241fb05c305eb466e3177c20..2650e36997655b1ab7376e8ed7052a8fc24b2fc6 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -34,3 +34,11 @@ ffdda588b41f7d9d270ffe76cab116f828ad545e
# 2024-07-24 docs: Format docs
# https://github.com/zed-industries/zed/pull/15352
3a44a59f8ec114ac1ba22f7da1652717ef7e4e5c
+
+# 2026-02-27 Format Tree-sitter query files
+# https://github.com/zed-industries/zed/pull/50138
+5ed538f49c54ca464bb9d1e59446060a3a925668
+
+# 2026-02-28 Format proto files
+# https://github.com/zed-industries/zed/pull/50413
+56a88a848be09cbcb66bcb3d85ec1f5644909f72
diff --git a/.github/CODEOWNERS.hold b/.github/CODEOWNERS.hold
index 449a5fd07315845787c9f2a73f0a0a22608e92c3..3d315b36401b2e27e29a2377aeabab8c09c75d39 100644
--- a/.github/CODEOWNERS.hold
+++ b/.github/CODEOWNERS.hold
@@ -62,8 +62,6 @@
/crates/rules_library/ @zed-industries/ai-team
# SUGGESTED: Review needed - based on Richard Feldman (2 commits)
/crates/shell_command_parser/ @zed-industries/ai-team
-/crates/supermaven/ @zed-industries/ai-team
-/crates/supermaven_api/ @zed-industries/ai-team
/crates/vercel/ @zed-industries/ai-team
/crates/x_ai/ @zed-industries/ai-team
/crates/zeta_prompt/ @zed-industries/ai-team
diff --git a/.github/DISCUSSION_TEMPLATE/feature-requests.yml b/.github/DISCUSSION_TEMPLATE/feature-requests.yml
index 183a3de934eccc8baa8428e822176e31d1d11782..e8a695063c34771ac6120b1e477b7494a17aa3c9 100644
--- a/.github/DISCUSSION_TEMPLATE/feature-requests.yml
+++ b/.github/DISCUSSION_TEMPLATE/feature-requests.yml
@@ -40,4 +40,4 @@ body:
attributes:
value: |
Learn more about how feature requests work in our
- [Feature Request Guidelines](https://github.com/zed-industries/zed/discussions/47963).
+ [Feature Request Guidelines](https://github.com/zed-industries/zed/discussions/51422).
diff --git a/.github/ISSUE_TEMPLATE/10_bug_report.yml b/.github/ISSUE_TEMPLATE/10_bug_report.yml
index 13e43219dd65a78af4afec479330bbc5fd85fe42..5eb8e8a6299c5189384b6d060e12cd61a2249a3c 100644
--- a/.github/ISSUE_TEMPLATE/10_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/10_bug_report.yml
@@ -100,7 +100,7 @@ body:
label: (for AI issues) Model provider details
placeholder: |
- Provider: (Anthropic via ZedPro, Anthropic via API key, Copilot Chat, Mistral, OpenAI, etc.)
- - Model Name: (Claude Sonnet 4.5, Gemini 3 Pro, GPT-5)
+ - Model Name: (Claude Sonnet 4.5, Gemini 3.1 Pro, GPT-5)
- Mode: (Agent Panel, Inline Assistant, Terminal Assistant or Text Threads)
- Other details (ACPs, MCPs, other settings, etc.):
validations:
diff --git a/.github/workflows/add_commented_closed_issue_to_project.yml b/.github/workflows/add_commented_closed_issue_to_project.yml
index 5871f5ae0e61f97557ce926c4a2627841f50560d..bd84eaa9446e57c5482ab818df3dbcfe587e040e 100644
--- a/.github/workflows/add_commented_closed_issue_to_project.yml
+++ b/.github/workflows/add_commented_closed_issue_to_project.yml
@@ -63,13 +63,18 @@ jobs:
}
- if: steps.is-post-close-comment.outputs.result == 'true' && steps.check-staff.outputs.result == 'true'
+ env:
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
- echo "::notice::Skipping issue #${{ github.event.issue.number }} - commenter is staff member"
+ echo "::notice::Skipping issue #$ISSUE_NUMBER - commenter is staff member"
# github-script outputs are JSON strings, so we compare against 'false' (string)
- if: steps.is-post-close-comment.outputs.result == 'true' && steps.check-staff.outputs.result == 'false'
+ env:
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
+ COMMENT_USER_LOGIN: ${{ github.event.comment.user.login }}
run: |
- echo "::notice::Adding issue #${{ github.event.issue.number }} to project (comment by ${{ github.event.comment.user.login }})"
+ echo "::notice::Adding issue #$ISSUE_NUMBER to project (comment by $COMMENT_USER_LOGIN)"
- if: steps.is-post-close-comment.outputs.result == 'true' && steps.check-staff.outputs.result == 'false'
uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
diff --git a/.github/workflows/after_release.yml b/.github/workflows/after_release.yml
index 9582e3f1956b3ecda383fc03efdb3d7ff67eaa68..95229f9f46bbd34ffe02832114b2b39da1b7e090 100644
--- a/.github/workflows/after_release.yml
+++ b/.github/workflows/after_release.yml
@@ -76,7 +76,7 @@ jobs:
"X-GitHub-Api-Version" = "2022-11-28"
}
$body = @{ branch = "master" } | ConvertTo-Json
- $uri = "https://api.github.com/repos/${{ github.repository_owner }}/winget-pkgs/merge-upstream"
+ $uri = "https://api.github.com/repos/$env:GITHUB_REPOSITORY_OWNER/winget-pkgs/merge-upstream"
try {
Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body -ContentType "application/json"
Write-Host "Successfully synced winget-pkgs fork"
@@ -131,11 +131,10 @@ jobs:
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: release::send_slack_message
- run: |
- curl -X POST -H 'Content-type: application/json'\
- --data '{"text":"❌ ${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' "$SLACK_WEBHOOK"
+ run: 'curl -X POST -H ''Content-type: application/json'' --data "$(jq -n --arg text "$SLACK_MESSAGE" ''{"text": $text}'')" "$SLACK_WEBHOOK"'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }}
+ SLACK_MESSAGE: '❌ ${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
defaults:
run:
shell: bash -euxo pipefail {0}
diff --git a/.github/workflows/assign-reviewers.yml b/.github/workflows/assign-reviewers.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a77f1812d06330b4635fe173583f0f1ce93e4e17
--- /dev/null
+++ b/.github/workflows/assign-reviewers.yml
@@ -0,0 +1,81 @@
+# 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.
+
+name: Assign Reviewers
+
+on:
+ pull_request:
+ 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: {}
+
+# Only run for PRs from within the org (not forks) — fork PRs don't have
+# write access to request team reviewers.
+jobs:
+ assign-reviewers:
+ if: >-
+ github.event.pull_request.head.repo.full_name == github.repository &&
+ github.event.pull_request.draft == false &&
+ contains(fromJSON('["MEMBER", "OWNER"]'), github.event.pull_request.author_association)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Generate app token
+ id: app-token
+ uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
+ with:
+ app-id: ${{ vars.COORDINATOR_APP_ID }}
+ private-key: ${{ secrets.COORDINATOR_APP_PRIVATE_KEY }}
+ repositories: codeowner-coordinator,zed
+
+ - name: Checkout coordinator repo
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.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 pyyaml==6.0.3
+
+ - name: Assign reviewers
+ env:
+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
+ PR_URL: ${{ github.event.pull_request.html_url }}
+ TARGET_REPO: ${{ github.repository }}
+ 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 \
+ --min-association member \
+ 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/autofix_pr.yml b/.github/workflows/autofix_pr.yml
index 60cc66294af2cf65e17aaad530a9df511ec61503..1fa271d168a8c3d1744439647ff50b793a854d1d 100644
--- a/.github/workflows/autofix_pr.yml
+++ b/.github/workflows/autofix_pr.yml
@@ -22,8 +22,9 @@ jobs:
with:
clean: false
- name: autofix_pr::run_autofix::checkout_pr
- run: gh pr checkout ${{ inputs.pr_number }}
+ run: gh pr checkout "$PR_NUMBER"
env:
+ PR_NUMBER: ${{ inputs.pr_number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: steps::setup_cargo_config
run: |
@@ -104,8 +105,9 @@ jobs:
clean: false
token: ${{ steps.get-app-token.outputs.token }}
- name: autofix_pr::commit_changes::checkout_pr
- run: gh pr checkout ${{ inputs.pr_number }}
+ run: gh pr checkout "$PR_NUMBER"
env:
+ PR_NUMBER: ${{ inputs.pr_number }}
GITHUB_TOKEN: ${{ steps.get-app-token.outputs.token }}
- name: autofix_pr::download_patch_artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
diff --git a/.github/workflows/background_agent_mvp.yml b/.github/workflows/background_agent_mvp.yml
index d078db137824a09b8e501362edef8a2f4c6f9b19..528600138243cb8aca2e0fe0645eda198fc4f2b2 100644
--- a/.github/workflows/background_agent_mvp.yml
+++ b/.github/workflows/background_agent_mvp.yml
@@ -1,8 +1,11 @@
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"
+ # schedule:
+ # - cron: "0 16 * * 1-5"
workflow_dispatch:
inputs:
crash_ids:
diff --git a/.github/workflows/bump_patch_version.yml b/.github/workflows/bump_patch_version.yml
index 480d8b0ada98e859d2e72b49a39805ffe8f72b25..62540321ed755f2fd3879a7ddfc3a37237d8e7de 100644
--- a/.github/workflows/bump_patch_version.yml
+++ b/.github/workflows/bump_patch_version.yml
@@ -23,8 +23,8 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- token: ${{ steps.get-app-token.outputs.token }}
ref: ${{ inputs.branch }}
+ token: ${{ steps.get-app-token.outputs.token }}
- name: bump_patch_version::run_bump_patch_version::bump_patch_version
run: |
channel="$(cat crates/zed/RELEASE_CHANNEL)"
diff --git a/.github/workflows/catch_blank_issues.yml b/.github/workflows/catch_blank_issues.yml
index dd425afc886e86c1217a94e90eabced013f66bf0..c6f595ef2e0890ce107829f3e91490332567368a 100644
--- a/.github/workflows/catch_blank_issues.yml
+++ b/.github/workflows/catch_blank_issues.yml
@@ -42,8 +42,10 @@ jobs:
}
- if: steps.check-staff.outputs.result == 'true'
+ env:
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
- echo "::notice::Skipping issue #${{ github.event.issue.number }} - actor is staff member"
+ echo "::notice::Skipping issue #$ISSUE_NUMBER - actor is staff member"
- if: steps.check-staff.outputs.result == 'false'
id: add-label
diff --git a/.github/workflows/cherry_pick.yml b/.github/workflows/cherry_pick.yml
index 9d46f300b509347b2853c00575c4e82fd9a2863c..ee0c1d35d0f9825d7c39b81fba0fe35901de2611 100644
--- a/.github/workflows/cherry_pick.yml
+++ b/.github/workflows/cherry_pick.yml
@@ -36,8 +36,11 @@ jobs:
app-id: ${{ secrets.ZED_ZIPPY_APP_ID }}
private-key: ${{ secrets.ZED_ZIPPY_APP_PRIVATE_KEY }}
- name: cherry_pick::run_cherry_pick::cherry_pick
- run: ./script/cherry-pick ${{ inputs.branch }} ${{ inputs.commit }} ${{ inputs.channel }}
+ run: ./script/cherry-pick "$BRANCH" "$COMMIT" "$CHANNEL"
env:
+ BRANCH: ${{ inputs.branch }}
+ COMMIT: ${{ inputs.commit }}
+ CHANNEL: ${{ inputs.channel }}
GIT_COMMITTER_NAME: Zed Zippy
GIT_COMMITTER_EMAIL: hi@zed.dev
GITHUB_TOKEN: ${{ steps.get-app-token.outputs.token }}
diff --git a/.github/workflows/community_update_all_top_ranking_issues.yml b/.github/workflows/community_update_all_top_ranking_issues.yml
index 59926f35563a4b21e3486ecbd454a4ccf951461e..ef3b4fc39ddb5f0db9b09c5e861547ae8cd7eb08 100644
--- a/.github/workflows/community_update_all_top_ranking_issues.yml
+++ b/.github/workflows/community_update_all_top_ranking_issues.yml
@@ -22,4 +22,6 @@ jobs:
- name: Install dependencies
run: uv sync --project script/update_top_ranking_issues -p 3.13
- name: Run script
- run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --issue-reference-number 5393
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token "$GITHUB_TOKEN" --issue-reference-number 5393
diff --git a/.github/workflows/community_update_weekly_top_ranking_issues.yml b/.github/workflows/community_update_weekly_top_ranking_issues.yml
index 75ba66b934b5861bd51aef4238a1a4188dddefc3..53b548f2bb4286e5de86d3823e67d75c0413a1cb 100644
--- a/.github/workflows/community_update_weekly_top_ranking_issues.yml
+++ b/.github/workflows/community_update_weekly_top_ranking_issues.yml
@@ -22,4 +22,6 @@ jobs:
- name: Install dependencies
run: uv sync --project script/update_top_ranking_issues -p 3.13
- name: Run script
- run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token ${{ secrets.GITHUB_TOKEN }} --issue-reference-number 6952 --query-day-interval 7
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: uv run --project script/update_top_ranking_issues script/update_top_ranking_issues/main.py --github-token "$GITHUB_TOKEN" --issue-reference-number 6952 --query-day-interval 7
diff --git a/.github/workflows/compare_perf.yml b/.github/workflows/compare_perf.yml
index e5a2d4f9c928eac2d1b1cf54ed374f8b0cca5d25..f7d78dbbf6a6d04bc47212b6842f894850288fcc 100644
--- a/.github/workflows/compare_perf.yml
+++ b/.github/workflows/compare_perf.yml
@@ -37,27 +37,40 @@ jobs:
- name: compare_perf::run_perf::install_hyperfine
uses: taiki-e/install-action@hyperfine
- name: steps::git_checkout
- run: git fetch origin ${{ inputs.base }} && git checkout ${{ inputs.base }}
+ 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 "${{ inputs.crate_name }}" ]; then
- cargo perf-test -p ${{ inputs.crate_name }} -- --json=${{ inputs.base }};
+ if [ -n "$CRATE_NAME" ]; then
+ cargo perf-test -p "$CRATE_NAME" -- --json="$REF_NAME";
else
- cargo perf-test -p vim -- --json=${{ inputs.base }};
+ 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 ${{ inputs.head }} && git checkout ${{ inputs.head }}
+ 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 "${{ inputs.crate_name }}" ]; then
- cargo perf-test -p ${{ inputs.crate_name }} -- --json=${{ inputs.head }};
+ if [ -n "$CRATE_NAME" ]; then
+ cargo perf-test -p "$CRATE_NAME" -- --json="$REF_NAME";
else
- cargo perf-test -p vim -- --json=${{ inputs.head }};
+ 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 ${{ inputs.base }} ${{ inputs.head }}
+ run: cargo perf-compare --save=results.md "$BASE" "$HEAD"
+ env:
+ BASE: ${{ inputs.base }}
+ HEAD: ${{ inputs.head }}
- name: '@actions/upload-artifact results.md'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
with:
diff --git a/.github/workflows/deploy_cloudflare.yml b/.github/workflows/deploy_cloudflare.yml
index 2650cce1406b16e691565077b95d07730845664b..37f23b20d2825e9f3d26c456903962a10c2d0081 100644
--- a/.github/workflows/deploy_cloudflare.yml
+++ b/.github/workflows/deploy_cloudflare.yml
@@ -23,7 +23,10 @@ jobs:
- name: Build docs
uses: ./.github/actions/build_docs
env:
+ CC: clang
+ CXX: clang++
DOCS_AMPLITUDE_API_KEY: ${{ secrets.DOCS_AMPLITUDE_API_KEY }}
+ DOCS_CONSENT_IO_INSTANCE: ${{ secrets.DOCS_CONSENT_IO_INSTANCE }}
- name: Deploy Docs
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
diff --git a/.github/workflows/deploy_collab.yml b/.github/workflows/deploy_collab.yml
index b1bdaf61979452a73380226ce1935b43eb05c32b..0d98438c9e3029f85cc37cb4e57f6c9e24df43b0 100644
--- a/.github/workflows/deploy_collab.yml
+++ b/.github/workflows/deploy_collab.yml
@@ -12,6 +12,9 @@ jobs:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
name: Check formatting and Clippy lints
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -42,6 +45,9 @@ jobs:
- style
name: Run tests
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -119,8 +125,9 @@ jobs:
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: deploy_collab::deploy::sign_into_kubernetes
- run: |
- doctl kubernetes cluster kubeconfig save --expiry-seconds 600 ${{ secrets.CLUSTER_NAME }}
+ run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 "$CLUSTER_NAME"
+ env:
+ CLUSTER_NAME: ${{ secrets.CLUSTER_NAME }}
- name: deploy_collab::deploy::start_rollout
run: |
set -eu
@@ -140,7 +147,7 @@ jobs:
echo "Deploying collab:$GITHUB_SHA to $ZED_KUBE_NAMESPACE"
source script/lib/deploy-helpers.sh
- export_vars_for_environment $ZED_KUBE_NAMESPACE
+ export_vars_for_environment "$ZED_KUBE_NAMESPACE"
ZED_DO_CERTIFICATE_ID="$(doctl compute certificate list --format ID --no-header)"
export ZED_DO_CERTIFICATE_ID
@@ -150,14 +157,14 @@ jobs:
export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_COLLAB_LOAD_BALANCER_SIZE_UNIT
export DATABASE_MAX_CONNECTIONS=850
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
- kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
+ kubectl -n "$ZED_KUBE_NAMESPACE" rollout status "deployment/$ZED_SERVICE_NAME" --watch
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"
export ZED_SERVICE_NAME=api
export ZED_LOAD_BALANCER_SIZE_UNIT=$ZED_API_LOAD_BALANCER_SIZE_UNIT
export DATABASE_MAX_CONNECTIONS=60
envsubst < crates/collab/k8s/collab.template.yml | kubectl apply -f -
- kubectl -n "$ZED_KUBE_NAMESPACE" rollout status deployment/$ZED_SERVICE_NAME --watch
+ kubectl -n "$ZED_KUBE_NAMESPACE" rollout status "deployment/$ZED_SERVICE_NAME" --watch
echo "deployed ${ZED_SERVICE_NAME} to ${ZED_KUBE_NAMESPACE}"
defaults:
run:
diff --git a/.github/workflows/docs_suggestions.yml b/.github/workflows/docs_suggestions.yml
index df6f001d2a08d5c577401b3c4dd099a1622d8d70..c2dc8b4d5197bcbf38dbfb92dac8c23386726d53 100644
--- a/.github/workflows/docs_suggestions.yml
+++ b/.github/workflows/docs_suggestions.yml
@@ -17,7 +17,7 @@ on:
- 'crates/**/*.rs'
- '!crates/**/*_test.rs'
- '!crates/**/tests/**'
-
+
# Run on cherry-picks to release branches
pull_request_target:
types: [opened, synchronize]
@@ -25,7 +25,7 @@ on:
- 'v0.*'
paths:
- 'crates/**/*.rs'
-
+
# Manual trigger for testing
workflow_dispatch:
inputs:
@@ -42,28 +42,29 @@ on:
- immediate
default: batch
-permissions:
- contents: write
- pull-requests: write
-
env:
DROID_MODEL: claude-sonnet-4-5-20250929
SUGGESTIONS_BRANCH: docs/suggestions-pending
jobs:
# Job for PRs merged to main - batch suggestions to branch
+ # Only runs for PRs from the same repo (not forks) since secrets aren't available for fork PRs
batch-suggestions:
runs-on: ubuntu-latest
timeout-minutes: 10
+ permissions:
+ contents: write
+ pull-requests: read
if: |
- (github.event_name == 'pull_request' &&
+ (github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
- github.event.pull_request.base.ref == 'main') ||
+ github.event.pull_request.base.ref == 'main' &&
+ github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'workflow_dispatch' && inputs.mode == 'batch')
-
+
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
@@ -90,35 +91,48 @@ jobs:
- name: Get PR info
id: pr
+ env:
+ INPUT_PR_NUMBER: ${{ inputs.pr_number }}
+ EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
+ GH_TOKEN: ${{ github.token }}
run: |
- if [ -n "${{ inputs.pr_number }}" ]; then
- PR_NUM="${{ inputs.pr_number }}"
+ if [ -n "$INPUT_PR_NUMBER" ]; then
+ PR_NUM="$INPUT_PR_NUMBER"
else
- PR_NUM="${{ github.event.pull_request.number }}"
+ PR_NUM="$EVENT_PR_NUMBER"
+ fi
+ if ! [[ "$PR_NUM" =~ ^[0-9]+$ ]]; then
+ echo "::error::Invalid PR number: $PR_NUM"
+ exit 1
fi
echo "number=$PR_NUM" >> "$GITHUB_OUTPUT"
-
- # Get PR title
- PR_TITLE=$(gh pr view "$PR_NUM" --json title --jq '.title')
- echo "title=$PR_TITLE" >> "$GITHUB_OUTPUT"
- env:
- GH_TOKEN: ${{ github.token }}
+ PR_TITLE=$(gh pr view "$PR_NUM" --json title --jq '.title' | tr -d '\n\r' | head -c 200)
+ EOF_MARKER="EOF_$(openssl rand -hex 8)"
+ {
+ echo "title<<$EOF_MARKER"
+ echo "$PR_TITLE"
+ echo "$EOF_MARKER"
+ } >> "$GITHUB_OUTPUT"
- name: Analyze PR for documentation needs
id: analyze
+ env:
+ GH_TOKEN: ${{ github.token }}
+ FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
+ PR_NUMBER: ${{ steps.pr.outputs.number }}
run: |
# Ensure gh CLI is authenticated (GH_TOKEN may not be auto-detected)
# Unset GH_TOKEN first to allow gh auth login to store credentials
echo "$GH_TOKEN" | (unset GH_TOKEN && gh auth login --with-token)
-
+
OUTPUT_FILE=$(mktemp)
-
+
# Retry with exponential backoff for transient Factory API failures
MAX_RETRIES=3
for i in $(seq 1 "$MAX_RETRIES"); do
echo "Attempt $i of $MAX_RETRIES to analyze PR..."
if ./script/docs-suggest \
- --pr "${{ steps.pr.outputs.number }}" \
+ --pr "$PR_NUMBER" \
--immediate \
--preview \
--output "$OUTPUT_FILE" \
@@ -133,7 +147,7 @@ jobs:
echo "Retrying in $((i * 5)) seconds..."
sleep $((i * 5))
done
-
+
# Check if we got actionable suggestions (not "no updates needed")
if grep -q "Documentation Suggestions" "$OUTPUT_FILE" && \
! grep -q "No Documentation Updates Needed" "$OUTPUT_FILE"; then
@@ -144,9 +158,6 @@ jobs:
echo "No actionable documentation suggestions for this PR"
cat "$OUTPUT_FILE"
fi
- env:
- GH_TOKEN: ${{ github.token }}
- FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
- name: Commit suggestions to queue branch
if: steps.analyze.outputs.has_suggestions == 'true'
@@ -154,18 +165,19 @@ jobs:
PR_NUM: ${{ steps.pr.outputs.number }}
PR_TITLE: ${{ steps.pr.outputs.title }}
OUTPUT_FILE: ${{ steps.analyze.outputs.output_file }}
+ REPO: ${{ github.repository }}
run: |
set -euo pipefail
-
+
# Configure git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
-
+
# Retry loop for handling concurrent pushes
MAX_RETRIES=3
for i in $(seq 1 "$MAX_RETRIES"); do
echo "Attempt $i of $MAX_RETRIES"
-
+
# Fetch and checkout suggestions branch (create if doesn't exist)
if git ls-remote --exit-code --heads origin "$SUGGESTIONS_BRANCH" > /dev/null 2>&1; then
git fetch origin "$SUGGESTIONS_BRANCH"
@@ -174,7 +186,7 @@ jobs:
# Create orphan branch for clean history
git checkout --orphan "$SUGGESTIONS_BRANCH"
git rm -rf . > /dev/null 2>&1 || true
-
+
# Initialize with README
cat > README.md << 'EOF'
# Documentation Suggestions Queue
@@ -196,34 +208,34 @@ jobs:
3. At preview release, suggestions are collected into a docs PR
4. After docs PR is created, this branch is reset
EOF
-
+
mkdir -p suggestions
echo '{"suggestions":[]}' > manifest.json
git add README.md suggestions manifest.json
git commit -m "Initialize documentation suggestions queue"
fi
-
+
# Create suggestion file
SUGGESTION_FILE="suggestions/PR-${PR_NUM}.md"
-
+
{
echo "# PR #${PR_NUM}: ${PR_TITLE}"
echo ""
echo "_Merged: $(date -u +%Y-%m-%dT%H:%M:%SZ)_"
- echo "_PR: https://github.com/${{ github.repository }}/pull/${PR_NUM}_"
+ echo "_PR: https://github.com/${REPO}/pull/${PR_NUM}_"
echo ""
cat "$OUTPUT_FILE"
} > "$SUGGESTION_FILE"
-
+
# Update manifest
MANIFEST=$(cat manifest.json)
NEW_ENTRY="{\"pr\":${PR_NUM},\"title\":$(echo "$PR_TITLE" | jq -R .),\"file\":\"$SUGGESTION_FILE\",\"date\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
-
+
# Add to manifest if not already present
if ! echo "$MANIFEST" | jq -e ".suggestions[] | select(.pr == $PR_NUM)" > /dev/null 2>&1; then
echo "$MANIFEST" | jq ".suggestions += [$NEW_ENTRY]" > manifest.json
fi
-
+
# Commit
git add "$SUGGESTION_FILE" manifest.json
git commit -m "docs: Add suggestions for PR #${PR_NUM}
@@ -231,7 +243,7 @@ jobs:
${PR_TITLE}
Auto-generated documentation suggestions for review at next preview release."
-
+
# Try to push
if git push origin "$SUGGESTIONS_BRANCH"; then
echo "Successfully pushed suggestions"
@@ -248,33 +260,47 @@ jobs:
- name: Summary
if: always()
+ env:
+ HAS_SUGGESTIONS: ${{ steps.analyze.outputs.has_suggestions }}
+ PR_NUM: ${{ steps.pr.outputs.number }}
+ REPO: ${{ github.repository }}
run: |
{
echo "## Documentation Suggestions"
echo ""
- if [ "${{ steps.analyze.outputs.has_suggestions }}" == "true" ]; then
- echo "✅ Suggestions queued for PR #${{ steps.pr.outputs.number }}"
+ if [ "$HAS_SUGGESTIONS" == "true" ]; then
+ echo "✅ Suggestions queued for PR #${PR_NUM}"
echo ""
- echo "View pending suggestions: [docs/suggestions-pending branch](https://github.com/${{ github.repository }}/tree/${{ env.SUGGESTIONS_BRANCH }})"
+ echo "View pending suggestions: [docs/suggestions-pending branch](https://github.com/${REPO}/tree/${SUGGESTIONS_BRANCH})"
else
echo "No documentation updates needed for this PR."
fi
} >> "$GITHUB_STEP_SUMMARY"
- # Job for cherry-picks to release branches - immediate output to step summary
+ # Job for cherry-picks to release branches - immediate output as PR comment
cherry-pick-suggestions:
runs-on: ubuntu-latest
timeout-minutes: 10
+ permissions:
+ contents: read
+ pull-requests: write
+ concurrency:
+ group: docs-suggestions-${{ github.event.pull_request.number || inputs.pr_number || 'manual' }}
+ cancel-in-progress: true
if: |
- (github.event_name == 'pull_request_target' &&
- startsWith(github.event.pull_request.base.ref, 'v0.')) ||
+ (github.event_name == 'pull_request_target' &&
+ startsWith(github.event.pull_request.base.ref, 'v0.') &&
+ contains(fromJSON('["MEMBER","OWNER"]'),
+ github.event.pull_request.author_association)) ||
(github.event_name == 'workflow_dispatch' && inputs.mode == 'immediate')
-
+
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
+ ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.ref || '' }}
+ persist-credentials: false
- name: Install Droid CLI
run: |
@@ -298,29 +324,41 @@ jobs:
- name: Get PR number
id: pr
+ env:
+ INPUT_PR_NUMBER: ${{ inputs.pr_number }}
+ EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
- if [ -n "${{ inputs.pr_number }}" ]; then
- echo "number=${{ inputs.pr_number }}" >> "$GITHUB_OUTPUT"
+ if [ -n "$INPUT_PR_NUMBER" ]; then
+ PR_NUM="$INPUT_PR_NUMBER"
else
- echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
+ PR_NUM="$EVENT_PR_NUMBER"
fi
+ if ! [[ "$PR_NUM" =~ ^[0-9]+$ ]]; then
+ echo "::error::Invalid PR number: $PR_NUM"
+ exit 1
+ fi
+ echo "number=$PR_NUM" >> "$GITHUB_OUTPUT"
- name: Analyze PR for documentation needs
id: analyze
+ env:
+ GH_TOKEN: ${{ github.token }}
+ FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
+ PR_NUMBER: ${{ steps.pr.outputs.number }}
run: |
# Ensure gh CLI is authenticated (GH_TOKEN may not be auto-detected)
# Unset GH_TOKEN first to allow gh auth login to store credentials
echo "$GH_TOKEN" | (unset GH_TOKEN && gh auth login --with-token)
-
+
OUTPUT_FILE="${RUNNER_TEMP}/suggestions.md"
-
+
# Cherry-picks don't get preview callout
# Retry with exponential backoff for transient Factory API failures
MAX_RETRIES=3
for i in $(seq 1 "$MAX_RETRIES"); do
echo "Attempt $i of $MAX_RETRIES to analyze PR..."
if ./script/docs-suggest \
- --pr "${{ steps.pr.outputs.number }}" \
+ --pr "$PR_NUMBER" \
--immediate \
--no-preview \
--output "$OUTPUT_FILE" \
@@ -335,7 +373,7 @@ jobs:
echo "Retrying in $((i * 5)) seconds..."
sleep $((i * 5))
done
-
+
# Check if we got actionable suggestions
if [ -s "$OUTPUT_FILE" ] && \
grep -q "Documentation Suggestions" "$OUTPUT_FILE" && \
@@ -345,48 +383,78 @@ jobs:
else
echo "has_suggestions=false" >> "$GITHUB_OUTPUT"
fi
- env:
- GH_TOKEN: ${{ github.token }}
- FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
- name: Post suggestions as PR comment
if: steps.analyze.outputs.has_suggestions == 'true'
- uses: actions/github-script@v7
+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
env:
SUGGESTIONS_FILE: ${{ steps.analyze.outputs.suggestions_file }}
+ PR_NUMBER: ${{ steps.pr.outputs.number }}
with:
script: |
const fs = require('fs');
- const suggestions = fs.readFileSync(process.env.SUGGESTIONS_FILE, 'utf8');
-
+
+ // Read suggestions from file
+ const suggestionsRaw = fs.readFileSync(process.env.SUGGESTIONS_FILE, 'utf8');
+
+ // Sanitize AI-generated content
+ let sanitized = suggestionsRaw
+ // Strip HTML tags
+ .replace(/<[^>]*>/g, '')
+ // Strip markdown links but keep display text
+ .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
+ // Strip raw URLs
+ .replace(/https?:\/\/[^\s)>\]]+/g, '[link removed]')
+ // Strip protocol-relative URLs
+ .replace(/\/\/[^\s)>\]]+\.[^\s)>\]]+/g, '[link removed]')
+ // Neutralize @-mentions (preserve JSDoc-style annotations)
+ .replace(/@(?!param\b|returns?\b|throws?\b|typedef\b|type\b|see\b|example\b|since\b|deprecated\b|default\b)(\w+)/g, '`@$1`')
+ // Strip cross-repo references that could be confused with real links
+ .replace(/[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+#\d+/g, '[ref removed]');
+
+ // Truncate to 20,000 characters
+ if (sanitized.length > 20000) {
+ sanitized = sanitized.substring(0, 20000) + '\n\n…(truncated)';
+ }
+
+ // Parse and validate PR number
+ const prNumber = parseInt(process.env.PR_NUMBER, 10);
+ if (isNaN(prNumber) || prNumber <= 0) {
+ core.setFailed(`Invalid PR number: ${process.env.PR_NUMBER}`);
+ return;
+ }
+
const body = `## 📚 Documentation Suggestions
This cherry-pick contains changes that may need documentation updates.
- ${suggestions}
+ ${sanitized}
---
+ > **Note:** This comment was generated automatically by an AI model analyzing
+ > code changes. Suggestions may contain inaccuracies — please verify before acting.
+
About this comment
This comment was generated automatically by analyzing code changes in this cherry-pick.
- Cherry-picks typically don't need new documentation since the feature was already
+ Cherry-picks typically don't need new documentation since the feature was already
documented when merged to main, but please verify.
`;
-
+
// Find existing comment to update (avoid spam)
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
- issue_number: ${{ steps.pr.outputs.number }}
+ issue_number: prNumber
});
-
- const botComment = comments.find(c =>
- c.user.type === 'Bot' &&
+
+ const botComment = comments.find(c =>
+ c.user.type === 'Bot' &&
c.body.includes('Documentation Suggestions')
);
-
+
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
@@ -398,21 +466,22 @@ jobs:
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
- issue_number: ${{ steps.pr.outputs.number }},
+ issue_number: prNumber,
body: body
});
}
- name: Summary
if: always()
+ env:
+ HAS_SUGGESTIONS: ${{ steps.analyze.outputs.has_suggestions }}
+ PR_NUM: ${{ steps.pr.outputs.number }}
run: |
{
echo "## 📚 Documentation Suggestions (Cherry-pick)"
echo ""
- if [ "${{ steps.analyze.outputs.has_suggestions }}" == "true" ]; then
- echo "Suggestions posted as PR comment."
- echo ""
- cat "${{ steps.analyze.outputs.suggestions_file }}"
+ if [ "$HAS_SUGGESTIONS" == "true" ]; then
+ echo "Suggestions posted as PR comment on #${PR_NUM}."
else
echo "No documentation suggestions for this cherry-pick."
fi
diff --git a/.github/workflows/extension_bump.yml b/.github/workflows/extension_bump.yml
index afb5448691610f49f715956b99a4f80bc84c9327..9cc53741e8007a1b3ddd02ad07b191b3ce171cc8 100644
--- a/.github/workflows/extension_bump.yml
+++ b/.github/workflows/extension_bump.yml
@@ -38,10 +38,10 @@ jobs:
name: extension_bump::compare_versions
run: |
CURRENT_VERSION="$(sed -n 's/^version = \"\(.*\)\"/\1/p' < extension.toml | tr -d '[:space:]')"
- PR_PARENT_SHA="${{ github.event.pull_request.head.sha }}"
- if [[ -n "$PR_PARENT_SHA" ]]; then
- git checkout "$PR_PARENT_SHA"
+ if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
+ PR_FORK_POINT="$(git merge-base origin/main HEAD)"
+ git checkout "$PR_FORK_POINT"
elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then
git checkout "$BRANCH_PARENT_SHA"
else
@@ -64,7 +64,7 @@ jobs:
- check_version_changed
if: |-
(github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') &&
- (inputs.force-bump == 'true' || needs.check_version_changed.outputs.version_changed == 'false')
+ (inputs.force-bump == true || needs.check_version_changed.outputs.version_changed == 'false')
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- id: generate-token
@@ -82,8 +82,6 @@ jobs:
- id: bump-version
name: extension_bump::bump_version
run: |
- OLD_VERSION="${{ needs.check_version_changed.outputs.current_version }}"
-
BUMP_FILES=("extension.toml")
if [[ -f "Cargo.toml" ]]; then
BUMP_FILES+=("Cargo.toml")
@@ -93,7 +91,7 @@ jobs:
--search "version = \"{current_version}"\" \
--replace "version = \"{new_version}"\" \
--current-version "$OLD_VERSION" \
- --no-configured-files ${{ inputs.bump-type }} "${BUMP_FILES[@]}"
+ --no-configured-files "$BUMP_TYPE" "${BUMP_FILES[@]}"
if [[ -f "Cargo.toml" ]]; then
cargo update --workspace
@@ -102,6 +100,9 @@ jobs:
NEW_VERSION="$(sed -n 's/^version = \"\(.*\)\"/\1/p' < extension.toml | tr -d '[:space:]')"
echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
+ env:
+ OLD_VERSION: ${{ needs.check_version_changed.outputs.current_version }}
+ BUMP_TYPE: ${{ inputs.bump-type }}
- name: extension_bump::create_pull_request
uses: peter-evans/create-pull-request@v7
with:
diff --git a/.github/workflows/extension_tests.yml b/.github/workflows/extension_tests.yml
index b843b6626e5e9969b0052f5de100143dd846b4a5..53de373c1b79dc3ca9a3637642e10998c781580a 100644
--- a/.github/workflows/extension_tests.yml
+++ b/.github/workflows/extension_tests.yml
@@ -32,7 +32,7 @@ jobs:
git fetch origin "$GITHUB_BASE_REF" --depth=350
COMPARE_REV="$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)"
fi
- CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" ${{ github.sha }})"
+ CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" "$GITHUB_SHA")"
check_pattern() {
local output_name="$1"
@@ -87,7 +87,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- fetch-depth: ${{ github.ref == 'refs/heads/main' && 2 || 350 }}
+ fetch-depth: 0
- id: cache-zed-extension-cli
name: extension_tests::cache_zed_extension_cli
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
@@ -109,14 +109,29 @@ jobs:
mkdir -p /tmp/ext-scratch
mkdir -p /tmp/ext-output
./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
+ - name: run_tests::fetch_ts_query_ls
+ uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
+ with:
+ repo: ribru17/ts_query_ls
+ version: tags/v3.15.1
+ file: ts_query_ls-x86_64-unknown-linux-gnu.tar.gz
+ - name: run_tests::run_ts_query_ls
+ run: |-
+ tar -xf ts_query_ls-x86_64-unknown-linux-gnu.tar.gz
+ ./ts_query_ls format --check . || {
+ echo "Found unformatted queries, please format them with ts_query_ls."
+ echo "For easy use, install the Tree-sitter query extension:"
+ echo "zed://extension/tree-sitter-query"
+ false
+ }
- id: compare-versions-check
name: extension_bump::compare_versions
run: |
CURRENT_VERSION="$(sed -n 's/^version = \"\(.*\)\"/\1/p' < extension.toml | tr -d '[:space:]')"
- PR_PARENT_SHA="${{ github.event.pull_request.head.sha }}"
- if [[ -n "$PR_PARENT_SHA" ]]; then
- git checkout "$PR_PARENT_SHA"
+ if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
+ PR_FORK_POINT="$(git merge-base origin/main HEAD)"
+ git checkout "$PR_FORK_POINT"
elif BRANCH_PARENT_SHA="$(git merge-base origin/main origin/zed-zippy-autobump)"; then
git checkout "$BRANCH_PARENT_SHA"
else
@@ -132,11 +147,14 @@ jobs:
echo "current_version=${CURRENT_VERSION}" >> "$GITHUB_OUTPUT"
- name: extension_tests::verify_version_did_not_change
run: |
- if [[ ${{ steps.compare-versions-check.outputs.version_changed }} == "true" && "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.user.login }}" != "zed-zippy[bot]" ]] ; then
+ if [[ "$VERSION_CHANGED" == "true" && "$GITHUB_EVENT_NAME" == "pull_request" && "$PR_USER_LOGIN" != "zed-zippy[bot]" ]] ; then
echo "Version change detected in your change!"
echo "Version changes happen in separate PRs and will be performed by the zed-zippy bot"
exit 42
fi
+ env:
+ VERSION_CHANGED: ${{ steps.compare-versions-check.outputs.version_changed }}
+ PR_USER_LOGIN: ${{ github.event.pull_request.user.login }}
timeout-minutes: 6
tests_pass:
needs:
@@ -156,11 +174,15 @@ jobs:
if [[ "$2" != "skipped" && "$2" != "success" ]]; then EXIT_CODE=1; fi
}
- check_result "orchestrate" "${{ needs.orchestrate.result }}"
- check_result "check_rust" "${{ needs.check_rust.result }}"
- check_result "check_extension" "${{ needs.check_extension.result }}"
+ check_result "orchestrate" "$RESULT_ORCHESTRATE"
+ check_result "check_rust" "$RESULT_CHECK_RUST"
+ check_result "check_extension" "$RESULT_CHECK_EXTENSION"
exit $EXIT_CODE
+ env:
+ RESULT_ORCHESTRATE: ${{ needs.orchestrate.result }}
+ RESULT_CHECK_RUST: ${{ needs.check_rust.result }}
+ RESULT_CHECK_EXTENSION: ${{ needs.check_extension.result }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
diff --git a/.github/workflows/extension_workflow_rollout.yml b/.github/workflows/extension_workflow_rollout.yml
index 109f40c815dbf5222bef7b7d78d544f2b278c21f..f695b43ecac47a221bbc795d03e6ddd6259d7014 100644
--- a/.github/workflows/extension_workflow_rollout.yml
+++ b/.github/workflows/extension_workflow_rollout.yml
@@ -4,12 +4,57 @@ name: extension_workflow_rollout
env:
CARGO_TERM_COLOR: always
on:
- workflow_dispatch: {}
+ workflow_dispatch:
+ inputs:
+ filter-repos:
+ description: Comma-separated list of repository names to rollout to. Leave empty for all repos.
+ type: string
+ default: ''
+ change-description:
+ description: Description for the changes to be expected with this rollout
+ type: string
+ default: ''
jobs:
fetch_extension_repos:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') && github.ref == 'refs/heads/main'
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
+ - name: checkout_zed_repo
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+ with:
+ clean: false
+ fetch-depth: 0
+ - id: prev-tag
+ name: extension_workflow_rollout::fetch_extension_repos::get_previous_tag_commit
+ run: |
+ PREV_COMMIT=$(git rev-parse "extension-workflows^{commit}" 2>/dev/null || echo "")
+ if [ -z "$PREV_COMMIT" ]; then
+ echo "::error::No previous rollout tag 'extension-workflows' found. Cannot determine file changes."
+ exit 1
+ fi
+ echo "Found previous rollout at commit: $PREV_COMMIT"
+ echo "prev_commit=$PREV_COMMIT" >> "$GITHUB_OUTPUT"
+ - id: calc-changes
+ name: extension_workflow_rollout::fetch_extension_repos::get_removed_files
+ run: |
+ for workflow_type in "ci" "shared"; do
+ if [ "$workflow_type" = "ci" ]; then
+ WORKFLOW_DIR="extensions/workflows"
+ else
+ WORKFLOW_DIR="extensions/workflows/shared"
+ fi
+
+ REMOVED=$(git diff --name-status -M "$PREV_COMMIT" HEAD -- "$WORKFLOW_DIR" | \
+ awk '/^D/ { print $2 } /^R/ { print $2 }' | \
+ xargs -I{} basename {} 2>/dev/null | \
+ tr '\n' ' ' || echo "")
+ REMOVED=$(echo "$REMOVED" | xargs)
+
+ echo "Removed files for $workflow_type: $REMOVED"
+ echo "removed_${workflow_type}=$REMOVED" >> "$GITHUB_OUTPUT"
+ done
+ env:
+ PREV_COMMIT: ${{ steps.prev-tag.outputs.prev_commit }}
- id: list-repos
name: extension_workflow_rollout::fetch_extension_repos::get_repositories
uses: actions/github-script@v7
@@ -21,16 +66,42 @@ jobs:
per_page: 100,
});
- const filteredRepos = repos
+ let filteredRepos = repos
.filter(repo => !repo.archived)
.map(repo => repo.name);
+ const filterInput = `${{ inputs.filter-repos }}`.trim();
+ if (filterInput.length > 0) {
+ const allowedNames = filterInput.split(',').map(s => s.trim()).filter(s => s.length > 0);
+ filteredRepos = filteredRepos.filter(name => allowedNames.includes(name));
+ console.log(`Filter applied. Matched ${filteredRepos.length} repos from ${allowedNames.length} requested.`);
+ }
+
console.log(`Found ${filteredRepos.length} extension repos`);
return filteredRepos;
result-encoding: json
+ - name: steps::cache_rust_dependencies_namespace
+ uses: namespacelabs/nscloud-cache-action@v1
+ with:
+ cache: rust
+ path: ~/.rustup
+ - name: extension_workflow_rollout::fetch_extension_repos::generate_workflow_files
+ run: |
+ cargo xtask workflows "$COMMIT_SHA"
+ env:
+ COMMIT_SHA: ${{ github.sha }}
+ - name: extension_workflow_rollout::fetch_extension_repos::upload_workflow_files
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
+ with:
+ name: extension-workflow-files
+ path: extensions/workflows/**/*.yml
+ if-no-files-found: error
outputs:
repos: ${{ steps.list-repos.outputs.result }}
- timeout-minutes: 5
+ prev_commit: ${{ steps.prev-tag.outputs.prev_commit }}
+ removed_ci: ${{ steps.calc-changes.outputs.removed_ci }}
+ removed_shared: ${{ steps.calc-changes.outputs.removed_shared }}
+ timeout-minutes: 10
rollout_workflows_to_extension:
needs:
- fetch_extension_repos
@@ -53,60 +124,28 @@ jobs:
permission-pull-requests: write
permission-contents: write
permission-workflows: write
- - name: checkout_zed_repo
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- with:
- clean: false
- fetch-depth: 0
- path: zed
- name: checkout_extension_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
clean: false
- token: ${{ steps.generate-token.outputs.token }}
path: extension
repository: zed-extensions/${{ matrix.repo }}
- - id: prev-tag
- name: extension_workflow_rollout::rollout_workflows_to_extension::get_previous_tag_commit
- run: |
- PREV_COMMIT=$(git rev-parse "extension-workflows^{commit}" 2>/dev/null || echo "")
- if [ -z "$PREV_COMMIT" ]; then
- echo "::error::No previous rollout tag 'extension-workflows' found. Cannot determine file changes."
- exit 1
- fi
- echo "Found previous rollout at commit: $PREV_COMMIT"
- echo "prev_commit=$PREV_COMMIT" >> "$GITHUB_OUTPUT"
- working-directory: zed
- - id: calc-changes
- name: extension_workflow_rollout::rollout_workflows_to_extension::get_removed_files
+ token: ${{ steps.generate-token.outputs.token }}
+ - name: extension_workflow_rollout::rollout_workflows_to_extension::download_workflow_files
+ uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53
+ with:
+ name: extension-workflow-files
+ path: workflow-files
+ - name: extension_workflow_rollout::rollout_workflows_to_extension::sync_workflow_files
run: |
- PREV_COMMIT="${{ steps.prev-tag.outputs.prev_commit }}"
+ mkdir -p extension/.github/workflows
- if [ "${{ matrix.repo }}" = "workflows" ]; then
- WORKFLOW_DIR="extensions/workflows"
+ if [ "$MATRIX_REPO" = "workflows" ]; then
+ REMOVED_FILES="$REMOVED_CI"
else
- WORKFLOW_DIR="extensions/workflows/shared"
+ REMOVED_FILES="$REMOVED_SHARED"
fi
- echo "Calculating changes from $PREV_COMMIT to HEAD for $WORKFLOW_DIR"
-
- # Get deleted files (status D) and renamed files (status R - old name needs removal)
- # Using -M to detect renames, then extracting files that are gone from their original location
- REMOVED_FILES=$(git diff --name-status -M "$PREV_COMMIT" HEAD -- "$WORKFLOW_DIR" | \
- awk '/^D/ { print $2 } /^R/ { print $2 }' | \
- xargs -I{} basename {} 2>/dev/null | \
- tr '\n' ' ' || echo "")
-
- REMOVED_FILES=$(echo "$REMOVED_FILES" | xargs)
-
- echo "Files to remove: $REMOVED_FILES"
- echo "removed_files=$REMOVED_FILES" >> "$GITHUB_OUTPUT"
- working-directory: zed
- - name: extension_workflow_rollout::rollout_workflows_to_extension::sync_workflow_files
- run: |
- REMOVED_FILES="${{ steps.calc-changes.outputs.removed_files }}"
-
- mkdir -p extension/.github/workflows
cd extension/.github/workflows
if [ -n "$REMOVED_FILES" ]; then
@@ -119,16 +158,19 @@ jobs:
cd - > /dev/null
- if [ "${{ matrix.repo }}" = "workflows" ]; then
- cp zed/extensions/workflows/*.yml extension/.github/workflows/
+ if [ "$MATRIX_REPO" = "workflows" ]; then
+ cp workflow-files/*.yml extension/.github/workflows/
else
- cp zed/extensions/workflows/shared/*.yml extension/.github/workflows/
+ cp workflow-files/shared/*.yml extension/.github/workflows/
fi
+ env:
+ REMOVED_CI: ${{ needs.fetch_extension_repos.outputs.removed_ci }}
+ REMOVED_SHARED: ${{ needs.fetch_extension_repos.outputs.removed_shared }}
+ MATRIX_REPO: ${{ matrix.repo }}
- id: short-sha
name: extension_workflow_rollout::rollout_workflows_to_extension::get_short_sha
run: |
- echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- working-directory: zed
+ echo "sha_short=$(echo "$GITHUB_SHA" | cut -c1-7)" >> "$GITHUB_OUTPUT"
- id: create-pr
name: extension_workflow_rollout::rollout_workflows_to_extension::create_pull_request
uses: peter-evans/create-pull-request@v7
@@ -138,6 +180,8 @@ jobs:
body: |
This PR updates the CI workflow files from the main Zed repository
based on the commit zed-industries/zed@${{ github.sha }}
+
+ ${{ inputs.change-description }}
commit-message: Update CI workflows to `${{ steps.short-sha.outputs.sha_short }}`
branch: update-workflows
committer: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
@@ -148,17 +192,18 @@ jobs:
sign-commits: true
- name: extension_workflow_rollout::rollout_workflows_to_extension::enable_auto_merge
run: |
- PR_NUMBER="${{ steps.create-pr.outputs.pull-request-number }}"
if [ -n "$PR_NUMBER" ]; then
- cd extension
gh pr merge "$PR_NUMBER" --auto --squash
fi
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
+ PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }}
+ working-directory: extension
timeout-minutes: 10
create_rollout_tag:
needs:
- rollout_workflows_to_extension
+ if: inputs.filter-repos == ''
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- id: generate-token
diff --git a/.github/workflows/publish_extension_cli.yml b/.github/workflows/publish_extension_cli.yml
index 391baac1cb3aa9da76c4fde39aa6909525541a58..75f1b16b007e33d0c4f346a33a1403648f1cd6c6 100644
--- a/.github/workflows/publish_extension_cli.yml
+++ b/.github/workflows/publish_extension_cli.yml
@@ -27,7 +27,7 @@ jobs:
- name: publish_extension_cli::publish_job::build_extension_cli
run: cargo build --release --package extension_cli
- name: publish_extension_cli::publish_job::upload_binary
- run: script/upload-extension-cli ${{ github.sha }}
+ run: script/upload-extension-cli "$GITHUB_SHA"
env:
DIGITALOCEAN_SPACES_ACCESS_KEY: ${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }}
DIGITALOCEAN_SPACES_SECRET_KEY: ${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }}
@@ -55,10 +55,10 @@ jobs:
- id: short-sha
name: publish_extension_cli::get_short_sha
run: |
- echo "sha_short=$(echo "${{ github.sha }}" | cut -c1-7)" >> "$GITHUB_OUTPUT"
+ echo "sha_short=$(echo "$GITHUB_SHA" | cut -c1-7)" >> "$GITHUB_OUTPUT"
- name: publish_extension_cli::update_sha_in_zed::replace_sha
run: |
- sed -i "s/ZED_EXTENSION_CLI_SHA: &str = \"[a-f0-9]*\"/ZED_EXTENSION_CLI_SHA: \&str = \"${{ github.sha }}\"/" \
+ sed -i "s/ZED_EXTENSION_CLI_SHA: &str = \"[a-f0-9]*\"/ZED_EXTENSION_CLI_SHA: \&str = \"$GITHUB_SHA\"/" \
tooling/xtask/src/tasks/workflows/extension_tests.rs
- name: publish_extension_cli::update_sha_in_zed::regenerate_workflows
run: cargo xtask workflows
@@ -97,7 +97,7 @@ jobs:
- id: short-sha
name: publish_extension_cli::get_short_sha
run: |
- echo "sha_short=$(echo "${{ github.sha }}" | cut -c1-7)" >> "$GITHUB_OUTPUT"
+ echo "sha_short=$(echo "$GITHUB_SHA" | cut -c1-7)" >> "$GITHUB_OUTPUT"
- name: publish_extension_cli::update_sha_in_extensions::checkout_extensions_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
@@ -105,7 +105,7 @@ jobs:
token: ${{ steps.generate-token.outputs.token }}
- name: publish_extension_cli::update_sha_in_extensions::replace_sha
run: |
- sed -i "s/ZED_EXTENSION_CLI_SHA: [a-f0-9]*/ZED_EXTENSION_CLI_SHA: ${{ github.sha }}/" \
+ sed -i "s/ZED_EXTENSION_CLI_SHA: [a-f0-9]*/ZED_EXTENSION_CLI_SHA: $GITHUB_SHA/" \
.github/workflows/ci.yml
- name: publish_extension_cli::create_pull_request_extensions
uses: peter-evans/create-pull-request@v7
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4442b068a88800e8437d5c6e459acec954308946..8adad5cfba278dc68dd227b86455510278c7a1ae 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -53,6 +53,9 @@ jobs:
run_tests_linux:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -177,6 +180,9 @@ jobs:
clippy_linux:
if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions')
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -251,8 +257,14 @@ jobs:
name: run_tests::check_scripts::download_actionlint
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
- name: run_tests::check_scripts::run_actionlint
- run: |
- ${{ steps.get_actionlint.outputs.executable }} -color
+ run: '"$ACTIONLINT_BIN" -color'
+ env:
+ ACTIONLINT_BIN: ${{ steps.get_actionlint.outputs.executable }}
+ - name: steps::cache_rust_dependencies_namespace
+ uses: namespacelabs/nscloud-cache-action@v1
+ with:
+ cache: rust
+ path: ~/.rustup
- name: run_tests::check_scripts::check_xtask_workflows
run: |
cargo xtask workflows
@@ -293,6 +305,8 @@ jobs:
CARGO_INCREMENTAL: 0
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+ CC: clang-18
+ CXX: clang++-18
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -333,6 +347,8 @@ jobs:
CARGO_INCREMENTAL: 0
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+ CC: clang-18
+ CXX: clang++-18
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -644,12 +660,7 @@ jobs:
- id: generate-webhook-message
name: release::generate_slack_message
run: |
- MESSAGE=$(DRAFT_RESULT="${{ needs.create_draft_release.result }}"
- UPLOAD_RESULT="${{ needs.upload_release_assets.result }}"
- VALIDATE_RESULT="${{ needs.validate_release_assets.result }}"
- AUTO_RELEASE_RESULT="${{ needs.auto_release_preview.result }}"
- TAG="$GITHUB_REF_NAME"
- RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
+ MESSAGE=$(TAG="$GITHUB_REF_NAME"
if [ "$DRAFT_RESULT" == "failure" ]; then
echo "❌ Draft release creation failed for $TAG: $RUN_URL"
@@ -659,19 +670,19 @@ jobs:
echo "❌ Release asset upload failed for $TAG: $RELEASE_URL"
elif [ "$UPLOAD_RESULT" == "cancelled" ] || [ "$UPLOAD_RESULT" == "skipped" ]; then
FAILED_JOBS=""
- if [ "${{ needs.run_tests_mac.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS run_tests_mac"; fi
- if [ "${{ needs.run_tests_linux.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS run_tests_linux"; fi
- if [ "${{ needs.run_tests_windows.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS run_tests_windows"; fi
- if [ "${{ needs.clippy_mac.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS clippy_mac"; fi
- if [ "${{ needs.clippy_linux.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS clippy_linux"; fi
- if [ "${{ needs.clippy_windows.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS clippy_windows"; fi
- if [ "${{ needs.check_scripts.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS check_scripts"; fi
- if [ "${{ needs.bundle_linux_aarch64.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_linux_aarch64"; fi
- if [ "${{ needs.bundle_linux_x86_64.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_linux_x86_64"; fi
- if [ "${{ needs.bundle_mac_aarch64.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_mac_aarch64"; fi
- if [ "${{ needs.bundle_mac_x86_64.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_mac_x86_64"; fi
- if [ "${{ needs.bundle_windows_aarch64.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_windows_aarch64"; fi
- if [ "${{ needs.bundle_windows_x86_64.result }}" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_windows_x86_64"; fi
+ if [ "$RESULT_RUN_TESTS_MAC" == "failure" ];then FAILED_JOBS="$FAILED_JOBS run_tests_mac"; fi
+ if [ "$RESULT_RUN_TESTS_LINUX" == "failure" ];then FAILED_JOBS="$FAILED_JOBS run_tests_linux"; fi
+ if [ "$RESULT_RUN_TESTS_WINDOWS" == "failure" ];then FAILED_JOBS="$FAILED_JOBS run_tests_windows"; fi
+ if [ "$RESULT_CLIPPY_MAC" == "failure" ];then FAILED_JOBS="$FAILED_JOBS clippy_mac"; fi
+ if [ "$RESULT_CLIPPY_LINUX" == "failure" ];then FAILED_JOBS="$FAILED_JOBS clippy_linux"; fi
+ if [ "$RESULT_CLIPPY_WINDOWS" == "failure" ];then FAILED_JOBS="$FAILED_JOBS clippy_windows"; fi
+ if [ "$RESULT_CHECK_SCRIPTS" == "failure" ];then FAILED_JOBS="$FAILED_JOBS check_scripts"; fi
+ if [ "$RESULT_BUNDLE_LINUX_AARCH64" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_linux_aarch64"; fi
+ if [ "$RESULT_BUNDLE_LINUX_X86_64" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_linux_x86_64"; fi
+ if [ "$RESULT_BUNDLE_MAC_AARCH64" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_mac_aarch64"; fi
+ if [ "$RESULT_BUNDLE_MAC_X86_64" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_mac_x86_64"; fi
+ if [ "$RESULT_BUNDLE_WINDOWS_AARCH64" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_windows_aarch64"; fi
+ if [ "$RESULT_BUNDLE_WINDOWS_X86_64" == "failure" ];then FAILED_JOBS="$FAILED_JOBS bundle_windows_x86_64"; fi
FAILED_JOBS=$(echo "$FAILED_JOBS" | xargs)
if [ "$UPLOAD_RESULT" == "cancelled" ]; then
if [ -n "$FAILED_JOBS" ]; then
@@ -700,12 +711,29 @@ jobs:
echo "message=$MESSAGE" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
+ DRAFT_RESULT: ${{ needs.create_draft_release.result }}
+ UPLOAD_RESULT: ${{ needs.upload_release_assets.result }}
+ VALIDATE_RESULT: ${{ needs.validate_release_assets.result }}
+ AUTO_RELEASE_RESULT: ${{ needs.auto_release_preview.result }}
+ RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ RESULT_RUN_TESTS_MAC: ${{ needs.run_tests_mac.result }}
+ RESULT_RUN_TESTS_LINUX: ${{ needs.run_tests_linux.result }}
+ RESULT_RUN_TESTS_WINDOWS: ${{ needs.run_tests_windows.result }}
+ RESULT_CLIPPY_MAC: ${{ needs.clippy_mac.result }}
+ RESULT_CLIPPY_LINUX: ${{ needs.clippy_linux.result }}
+ RESULT_CLIPPY_WINDOWS: ${{ needs.clippy_windows.result }}
+ RESULT_CHECK_SCRIPTS: ${{ needs.check_scripts.result }}
+ RESULT_BUNDLE_LINUX_AARCH64: ${{ needs.bundle_linux_aarch64.result }}
+ RESULT_BUNDLE_LINUX_X86_64: ${{ needs.bundle_linux_x86_64.result }}
+ RESULT_BUNDLE_MAC_AARCH64: ${{ needs.bundle_mac_aarch64.result }}
+ RESULT_BUNDLE_MAC_X86_64: ${{ needs.bundle_mac_x86_64.result }}
+ RESULT_BUNDLE_WINDOWS_AARCH64: ${{ needs.bundle_windows_aarch64.result }}
+ RESULT_BUNDLE_WINDOWS_X86_64: ${{ needs.bundle_windows_x86_64.result }}
- name: release::send_slack_message
- run: |
- curl -X POST -H 'Content-type: application/json'\
- --data '{"text":"${{ steps.generate-webhook-message.outputs.message }}"}' "$SLACK_WEBHOOK"
+ run: 'curl -X POST -H ''Content-type: application/json'' --data "$(jq -n --arg text "$SLACK_MESSAGE" ''{"text": $text}'')" "$SLACK_WEBHOOK"'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }}
+ SLACK_MESSAGE: ${{ steps.generate-webhook-message.outputs.message }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
diff --git a/.github/workflows/release_nightly.yml b/.github/workflows/release_nightly.yml
index d3f01447e52f418713499b84ad454085fd3cb646..46d8732b08ea658275e1fb21117a09b9e0668933 100644
--- a/.github/workflows/release_nightly.yml
+++ b/.github/workflows/release_nightly.yml
@@ -103,6 +103,8 @@ jobs:
CARGO_INCREMENTAL: 0
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+ CC: clang-18
+ CXX: clang++-18
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -149,6 +151,8 @@ jobs:
CARGO_INCREMENTAL: 0
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+ CC: clang-18
+ CXX: clang++-18
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -550,11 +554,10 @@ jobs:
runs-on: namespace-profile-2x4-ubuntu-2404
steps:
- name: release::send_slack_message
- run: |
- curl -X POST -H 'Content-type: application/json'\
- --data '{"text":"❌ ${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' "$SLACK_WEBHOOK"
+ run: 'curl -X POST -H ''Content-type: application/json'' --data "$(jq -n --arg text "$SLACK_MESSAGE" ''{"text": $text}'')" "$SLACK_WEBHOOK"'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_WORKFLOW_FAILURES }}
+ SLACK_MESSAGE: '❌ ${{ github.workflow }} failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
defaults:
run:
shell: bash -euxo pipefail {0}
diff --git a/.github/workflows/run_bundling.yml b/.github/workflows/run_bundling.yml
index 2b536425a1dc4b9663c726fd9259c95e0626efda..7cb1665f9d0bd4fe3b0f3c05527bf39aab5f610a 100644
--- a/.github/workflows/run_bundling.yml
+++ b/.github/workflows/run_bundling.yml
@@ -19,6 +19,8 @@ jobs:
CARGO_INCREMENTAL: 0
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+ CC: clang-18
+ CXX: clang++-18
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -58,6 +60,8 @@ jobs:
CARGO_INCREMENTAL: 0
ZED_CLIENT_CHECKSUM_SEED: ${{ secrets.ZED_CLIENT_CHECKSUM_SEED }}
ZED_MINIDUMP_ENDPOINT: ${{ secrets.ZED_SENTRY_MINIDUMP_ENDPOINT }}
+ CC: clang-18
+ CXX: clang++-18
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
diff --git a/.github/workflows/run_cron_unit_evals.yml b/.github/workflows/run_cron_unit_evals.yml
index e57b54e4f2249b92630b2d3636ce2316a0814625..2a204a9d40d78bf52f38825b4db060216e348a87 100644
--- a/.github/workflows/run_cron_unit_evals.yml
+++ b/.github/workflows/run_cron_unit_evals.yml
@@ -16,7 +16,7 @@ jobs:
model:
- anthropic/claude-sonnet-4-5-latest
- anthropic/claude-opus-4-5-latest
- - google/gemini-3-pro
+ - google/gemini-3.1-pro
- openai/gpt-5
fail-fast: false
steps:
diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml
index 07caa6007e87fcd093b40bb9a15108e18b159068..00d69639a53868386157e67aeab5ce7383d32426 100644
--- a/.github/workflows/run_tests.yml
+++ b/.github/workflows/run_tests.yml
@@ -35,7 +35,7 @@ jobs:
git fetch origin "$GITHUB_BASE_REF" --depth=350
COMPARE_REV="$(git merge-base "origin/${GITHUB_BASE_REF}" HEAD)"
fi
- CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" ${{ github.sha }})"
+ CHANGED_FILES="$(git diff --name-only "$COMPARE_REV" "$GITHUB_SHA")"
check_pattern() {
local output_name="$1"
@@ -139,6 +139,21 @@ jobs:
uses: crate-ci/typos@2d0ce569feab1f8752f1dde43cc2f2aa53236e06
with:
config: ./typos.toml
+ - name: run_tests::fetch_ts_query_ls
+ uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
+ with:
+ repo: ribru17/ts_query_ls
+ version: tags/v3.15.1
+ file: ts_query_ls-x86_64-unknown-linux-gnu.tar.gz
+ - name: run_tests::run_ts_query_ls
+ run: |-
+ tar -xf ts_query_ls-x86_64-unknown-linux-gnu.tar.gz
+ ./ts_query_ls format --check . || {
+ echo "Found unformatted queries, please format them with ts_query_ls."
+ echo "For easy use, install the Tree-sitter query extension:"
+ echo "zed://extension/tree-sitter-query"
+ false
+ }
timeout-minutes: 60
clippy_windows:
needs:
@@ -175,6 +190,9 @@ jobs:
- orchestrate
if: needs.orchestrate.outputs.run_tests == 'true'
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -285,6 +303,9 @@ jobs:
- orchestrate
if: needs.orchestrate.outputs.run_tests == 'true'
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -385,6 +406,9 @@ jobs:
- orchestrate
if: needs.orchestrate.outputs.run_tests == 'true'
runs-on: namespace-profile-16x32-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -428,6 +452,9 @@ jobs:
- orchestrate
if: needs.orchestrate.outputs.run_tests == 'true'
runs-on: namespace-profile-8x16-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -466,11 +493,53 @@ jobs:
run: |
rm -rf ./../.cargo
timeout-minutes: 60
+ check_wasm:
+ needs:
+ - orchestrate
+ if: needs.orchestrate.outputs.run_tests == 'true'
+ runs-on: namespace-profile-8x16-ubuntu-2204
+ steps:
+ - name: steps::checkout_repo
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+ 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@v1
+ with:
+ cache: rust
+ path: ~/.rustup
+ - name: run_tests::check_wasm::install_nightly_wasm_toolchain
+ run: rustup toolchain install nightly --component rust-src --target wasm32-unknown-unknown
+ - 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: run_tests::check_wasm::cargo_check_wasm
+ run: cargo +nightly -Zbuild-std=std,panic_abort check --target wasm32-unknown-unknown -p gpui_platform
+ env:
+ CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: -C target-feature=+atomics,+bulk-memory,+mutable-globals
+ - name: steps::show_sccache_stats
+ run: sccache --show-stats || true
+ - name: steps::cleanup_cargo_config
+ if: always()
+ run: |
+ rm -rf ./../.cargo
+ timeout-minutes: 60
check_dependencies:
needs:
- orchestrate
if: needs.orchestrate.outputs.run_tests == 'true'
runs-on: namespace-profile-2x4-ubuntu-2404
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -503,6 +572,9 @@ jobs:
- orchestrate
if: needs.orchestrate.outputs.run_docs == 'true'
runs-on: namespace-profile-8x16-ubuntu-2204
+ env:
+ CC: clang
+ CXX: clang++
steps:
- name: steps::checkout_repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -581,8 +653,14 @@ jobs:
name: run_tests::check_scripts::download_actionlint
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
- name: run_tests::check_scripts::run_actionlint
- run: |
- ${{ steps.get_actionlint.outputs.executable }} -color
+ run: '"$ACTIONLINT_BIN" -color'
+ env:
+ ACTIONLINT_BIN: ${{ steps.get_actionlint.outputs.executable }}
+ - name: steps::cache_rust_dependencies_namespace
+ uses: namespacelabs/nscloud-cache-action@v1
+ with:
+ cache: rust
+ path: ~/.rustup
- name: run_tests::check_scripts::check_xtask_workflows
run: |
cargo xtask workflows
@@ -628,6 +706,10 @@ jobs:
with:
input: crates/proto/proto/
against: https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/
+ - name: run_tests::check_postgres_and_protobuf_migrations::buf_lint
+ run: buf lint crates/proto/proto
+ - name: run_tests::check_postgres_and_protobuf_migrations::check_protobuf_formatting
+ run: buf format --diff --exit-code crates/proto/proto
timeout-minutes: 60
tests_pass:
needs:
@@ -641,6 +723,7 @@ jobs:
- run_tests_mac
- doctests
- check_workspace_binaries
+ - check_wasm
- check_dependencies
- check_docs
- check_licenses
@@ -658,22 +741,39 @@ jobs:
if [[ "$2" != "skipped" && "$2" != "success" ]]; then EXIT_CODE=1; fi
}
- check_result "orchestrate" "${{ needs.orchestrate.result }}"
- check_result "check_style" "${{ needs.check_style.result }}"
- check_result "clippy_windows" "${{ needs.clippy_windows.result }}"
- check_result "clippy_linux" "${{ needs.clippy_linux.result }}"
- check_result "clippy_mac" "${{ needs.clippy_mac.result }}"
- check_result "run_tests_windows" "${{ needs.run_tests_windows.result }}"
- check_result "run_tests_linux" "${{ needs.run_tests_linux.result }}"
- check_result "run_tests_mac" "${{ needs.run_tests_mac.result }}"
- check_result "doctests" "${{ needs.doctests.result }}"
- check_result "check_workspace_binaries" "${{ needs.check_workspace_binaries.result }}"
- check_result "check_dependencies" "${{ needs.check_dependencies.result }}"
- check_result "check_docs" "${{ needs.check_docs.result }}"
- check_result "check_licenses" "${{ needs.check_licenses.result }}"
- check_result "check_scripts" "${{ needs.check_scripts.result }}"
+ check_result "orchestrate" "$RESULT_ORCHESTRATE"
+ check_result "check_style" "$RESULT_CHECK_STYLE"
+ check_result "clippy_windows" "$RESULT_CLIPPY_WINDOWS"
+ check_result "clippy_linux" "$RESULT_CLIPPY_LINUX"
+ check_result "clippy_mac" "$RESULT_CLIPPY_MAC"
+ check_result "run_tests_windows" "$RESULT_RUN_TESTS_WINDOWS"
+ check_result "run_tests_linux" "$RESULT_RUN_TESTS_LINUX"
+ check_result "run_tests_mac" "$RESULT_RUN_TESTS_MAC"
+ check_result "doctests" "$RESULT_DOCTESTS"
+ check_result "check_workspace_binaries" "$RESULT_CHECK_WORKSPACE_BINARIES"
+ check_result "check_wasm" "$RESULT_CHECK_WASM"
+ check_result "check_dependencies" "$RESULT_CHECK_DEPENDENCIES"
+ check_result "check_docs" "$RESULT_CHECK_DOCS"
+ check_result "check_licenses" "$RESULT_CHECK_LICENSES"
+ check_result "check_scripts" "$RESULT_CHECK_SCRIPTS"
exit $EXIT_CODE
+ env:
+ RESULT_ORCHESTRATE: ${{ needs.orchestrate.result }}
+ RESULT_CHECK_STYLE: ${{ needs.check_style.result }}
+ RESULT_CLIPPY_WINDOWS: ${{ needs.clippy_windows.result }}
+ RESULT_CLIPPY_LINUX: ${{ needs.clippy_linux.result }}
+ RESULT_CLIPPY_MAC: ${{ needs.clippy_mac.result }}
+ RESULT_RUN_TESTS_WINDOWS: ${{ needs.run_tests_windows.result }}
+ RESULT_RUN_TESTS_LINUX: ${{ needs.run_tests_linux.result }}
+ RESULT_RUN_TESTS_MAC: ${{ needs.run_tests_mac.result }}
+ RESULT_DOCTESTS: ${{ needs.doctests.result }}
+ RESULT_CHECK_WORKSPACE_BINARIES: ${{ needs.check_workspace_binaries.result }}
+ RESULT_CHECK_WASM: ${{ needs.check_wasm.result }}
+ RESULT_CHECK_DEPENDENCIES: ${{ needs.check_dependencies.result }}
+ RESULT_CHECK_DOCS: ${{ needs.check_docs.result }}
+ RESULT_CHECK_LICENSES: ${{ needs.check_licenses.result }}
+ RESULT_CHECK_SCRIPTS: ${{ needs.check_scripts.result }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}
cancel-in-progress: true
diff --git a/.github/workflows/slack_notify_first_responders.yml b/.github/workflows/slack_notify_first_responders.yml
index a6f2d557a574778aea6c2a90f9721b5a41bd0724..538d02b582f18db627693b62e439f4142ea29056 100644
--- a/.github/workflows/slack_notify_first_responders.yml
+++ b/.github/workflows/slack_notify_first_responders.yml
@@ -17,8 +17,9 @@ jobs:
id: check-label
env:
LABEL_NAME: ${{ github.event.label.name }}
+ FIRST_RESPONDER_LABELS: ${{ env.FIRST_RESPONDER_LABELS }}
run: |
- if echo '${{ env.FIRST_RESPONDER_LABELS }}' | jq -e --arg label "$LABEL_NAME" 'index($label) != null' > /dev/null; then
+ if echo "$FIRST_RESPONDER_LABELS" | jq -e --arg label "$LABEL_NAME" 'index($label) != null' > /dev/null; then
echo "should_notify=true" >> "$GITHUB_OUTPUT"
echo "Label '$LABEL_NAME' requires first responder notification"
else
diff --git a/.github/workflows/update_duplicate_magnets.yml b/.github/workflows/update_duplicate_magnets.yml
index 1c6c5a562532891eb97ceb11f44b81f35612c026..c3832b7bdbec13f74a8136cb1120a682f6e53920 100644
--- a/.github/workflows/update_duplicate_magnets.yml
+++ b/.github/workflows/update_duplicate_magnets.yml
@@ -21,7 +21,9 @@ jobs:
run: pip install requests
- name: Update duplicate magnets issue
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python script/github-find-top-duplicated-bugs.py \
- --github-token ${{ secrets.GITHUB_TOKEN }} \
+ --github-token "$GITHUB_TOKEN" \
--issue-number 46355
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 740b33dd55790bd3cabfc75146d71854eca6375d..e7e7629825b5f487a3b00af525d36458eb91956c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -26,6 +26,8 @@ If you're looking for concrete ideas:
- [Triaged bugs with confirmed steps to reproduce](https://github.com/zed-industries/zed/issues?q=is%3Aissue%20state%3Aopen%20type%3ABug%20label%3Astate%3Areproducible).
- [Area labels](https://github.com/zed-industries/zed/labels?q=area%3A*) to browse bugs in a specific part of the product you care about (after clicking on an area label, add type:Bug to the search).
+If you're thinking about proposing or building a larger feature, read the [Zed Feature Process](./docs/src/development/feature-process.md) for how we think about feature design — what context to provide, what integration points to consider, and how to put together a strong proposal.
+
## Sending changes
The Zed culture values working code and synchronous conversations over long
diff --git a/Cargo.lock b/Cargo.lock
index aa52141ae7529bab47e8aeab6959525227cb04c5..6570398f5b22f2248a9cd59f84d2cf70080c3591 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -36,7 +36,6 @@ dependencies = [
"smol",
"task",
"telemetry",
- "tempfile",
"terminal",
"text",
"ui",
@@ -45,7 +44,6 @@ dependencies = [
"util",
"uuid",
"watch",
- "zlog",
]
[[package]]
@@ -76,9 +74,9 @@ dependencies = [
"clock",
"collections",
"ctor",
+ "fs",
"futures 0.3.31",
"gpui",
- "indoc",
"language",
"log",
"pretty_assertions",
@@ -107,7 +105,6 @@ dependencies = [
"language",
"project",
"proto",
- "release_channel",
"smallvec",
"ui",
"util",
@@ -169,7 +166,7 @@ dependencies = [
"context_server",
"ctor",
"db",
- "derive_more 0.99.20",
+ "derive_more",
"editor",
"env_logger 0.11.8",
"eval_utils",
@@ -213,11 +210,9 @@ dependencies = [
"task",
"telemetry",
"tempfile",
- "terminal",
"text",
"theme",
"thiserror 2.0.17",
- "tree-sitter-rust",
"ui",
"unindent",
"url",
@@ -225,7 +220,6 @@ dependencies = [
"uuid",
"watch",
"web_search",
- "worktree",
"zed_env_vars",
"zlog",
"zstd",
@@ -233,15 +227,15 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
-version = "0.9.4"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2659b1089101b15db31137710159421cb44785ecdb5ba784be3b4a6f8cb8a475"
+checksum = "9c56a59cf6315e99f874d2c1f96c69d2da5ffe0087d211297fc4a41f849770a2"
dependencies = [
"agent-client-protocol-schema",
"anyhow",
"async-broadcast",
"async-trait",
- "derive_more 2.0.1",
+ "derive_more",
"futures 0.3.31",
"log",
"serde",
@@ -250,16 +244,16 @@ dependencies = [
[[package]]
name = "agent-client-protocol-schema"
-version = "0.10.8"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44bc1fef9c32f03bce2ab44af35b6f483bfd169bf55cc59beeb2e3b1a00ae4d1"
+checksum = "e0497b9a95a404e35799904835c57c6f8c69b9d08ccfd3cb5b7d746425cd6789"
dependencies = [
"anyhow",
- "derive_more 2.0.1",
+ "derive_more",
"schemars",
"serde",
"serde_json",
- "strum 0.27.2",
+ "strum 0.28.0",
]
[[package]]
@@ -284,7 +278,6 @@ dependencies = [
"gpui_tokio",
"http_client",
"indoc",
- "language",
"language_model",
"libc",
"log",
@@ -318,7 +311,6 @@ dependencies = [
"gpui",
"language_model",
"log",
- "paths",
"project",
"regex",
"schemars",
@@ -351,7 +343,6 @@ dependencies = [
"buffer_diff",
"chrono",
"client",
- "clock",
"cloud_api_types",
"cloud_llm_client",
"collections",
@@ -368,6 +359,7 @@ dependencies = [
"fs",
"futures 0.3.31",
"fuzzy",
+ "git",
"gpui",
"gpui_tokio",
"html_to_markdown",
@@ -396,9 +388,7 @@ dependencies = [
"prompt_store",
"proto",
"rand 0.9.2",
- "recent_projects",
"release_channel",
- "remote_connection",
"reqwest_client",
"rope",
"rules_library",
@@ -413,14 +403,12 @@ dependencies = [
"streaming_diff",
"task",
"telemetry",
- "tempfile",
"terminal",
"terminal_view",
"text",
"theme",
"time",
"time_format",
- "title_bar",
"tree-sitter-md",
"ui",
"ui_input",
@@ -601,6 +589,17 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+[[package]]
+name = "annotate-snippets"
+version = "0.12.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86cd1c51b95d71dde52bca69ed225008f6ff4c8cc825b08042aa1ef823e1980"
+dependencies = [
+ "anstyle",
+ "memchr",
+ "unicode-width",
+]
+
[[package]]
name = "anstream"
version = "0.6.21"
@@ -658,17 +657,13 @@ dependencies = [
"anyhow",
"chrono",
"futures 0.3.31",
- "gpui",
- "gpui_tokio",
"http_client",
- "reqwest_client",
"schemars",
"serde",
"serde_json",
"settings",
"strum 0.27.2",
"thiserror 2.0.17",
- "tokio",
]
[[package]]
@@ -692,6 +687,15 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "ar_archive_writer"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
+dependencies = [
+ "object 0.37.3",
+]
+
[[package]]
name = "arbitrary"
version = "1.4.2"
@@ -709,7 +713,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -756,19 +760,16 @@ dependencies = [
[[package]]
name = "ashpd"
-version = "0.12.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "618a409b91d5265798a99e3d1d0b226911605e581c4e7255e83c1e397b172bce"
+checksum = "0848bedd08067dca1c02c31cbb371a94ad4f2f8a61a82f2c43d96ec36a395244"
dependencies = [
- "async-fs",
- "async-net",
"enumflags2",
"futures-channel",
"futures-util",
- "rand 0.9.2",
+ "getrandom 0.4.1",
"serde",
"serde_repr",
- "url",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -807,7 +808,7 @@ dependencies = [
"anyhow",
"async-trait",
"collections",
- "derive_more 0.99.20",
+ "derive_more",
"extension",
"futures 0.3.31",
"gpui",
@@ -874,7 +875,6 @@ dependencies = [
"futures 0.3.31",
"fuzzy",
"gpui",
- "indoc",
"itertools 0.14.0",
"language",
"language_model",
@@ -1005,7 +1005,7 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
dependencies = [
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"blocking",
"futures-lite 2.6.1",
]
@@ -1019,7 +1019,7 @@ dependencies = [
"async-channel 2.5.0",
"async-executor",
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"blocking",
"futures-lite 2.6.1",
"once_cell",
@@ -1054,9 +1054,9 @@ dependencies = [
[[package]]
name = "async-lock"
-version = "3.4.1"
+version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener 5.4.1",
"event-listener-strategy",
@@ -1091,7 +1091,7 @@ checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
dependencies = [
"async-channel 2.5.0",
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"async-signal",
"async-task",
"blocking",
@@ -1109,7 +1109,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -1119,7 +1119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c"
dependencies = [
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"atomic-waker",
"cfg-if",
"futures-core",
@@ -1140,7 +1140,7 @@ dependencies = [
"async-channel 1.9.0",
"async-global-executor",
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"async-process",
"crossbeam-utils",
"futures-channel",
@@ -1177,7 +1177,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -1207,7 +1207,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -1345,6 +1345,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"log",
+ "scopeguard",
"simplelog",
"tempfile",
"windows 0.61.3",
@@ -2045,7 +2046,7 @@ dependencies = [
"regex",
"rustc-hash 2.1.1",
"shlex",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2063,7 +2064,7 @@ dependencies = [
"regex",
"rustc-hash 2.1.1",
"shlex",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2166,6 +2167,16 @@ dependencies = [
"piper",
]
+[[package]]
+name = "bmrng"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54df9073108f1558f90ae6c5bf5ab9c917c4185f5527b280c87a993cbead0ac"
+dependencies = [
+ "futures-core",
+ "tokio",
+]
+
[[package]]
name = "bon"
version = "3.8.2"
@@ -2188,7 +2199,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2217,7 +2228,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2290,7 +2301,6 @@ dependencies = [
"pretty_assertions",
"rand 0.9.2",
"rope",
- "serde_json",
"settings",
"sum_tree",
"text",
@@ -2367,7 +2377,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2452,7 +2462,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2474,7 +2484,6 @@ dependencies = [
"futures 0.3.31",
"gpui",
"gpui_tokio",
- "http_client",
"language",
"livekit_client",
"log",
@@ -2484,6 +2493,7 @@ dependencies = [
"settings",
"telemetry",
"util",
+ "workspace",
]
[[package]]
@@ -2705,7 +2715,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
- "syn 2.0.106",
+ "syn 2.0.117",
"tempfile",
"toml 0.8.23",
]
@@ -2747,6 +2757,16 @@ dependencies = [
"target-lexicon 0.12.16",
]
+[[package]]
+name = "cfg-expr"
+version = "0.20.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78cef5b5a1a6827c7322ae2a636368a573006b27cfa76c7ebd53e834daeaab6a"
+dependencies = [
+ "smallvec",
+ "target-lexicon 0.13.3",
+]
+
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -2924,7 +2944,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -2972,7 +2992,7 @@ dependencies = [
"cloud_llm_client",
"collections",
"credentials_provider",
- "derive_more 0.99.20",
+ "derive_more",
"feature_flags",
"fs",
"futures 0.3.31",
@@ -3058,8 +3078,6 @@ name = "cloud_llm_client"
version = "0.1.0"
dependencies = [
"anyhow",
- "indoc",
- "pretty_assertions",
"serde",
"serde_json",
"strum 0.27.2",
@@ -3184,6 +3202,7 @@ dependencies = [
"serde",
"serde_json",
"text",
+ "zeta_prompt",
]
[[package]]
@@ -3191,15 +3210,11 @@ name = "collab"
version = "0.44.0"
dependencies = [
"agent",
- "agent-client-protocol",
- "agent_settings",
- "agent_ui",
"anyhow",
"assistant_slash_command",
"assistant_text_thread",
"async-trait",
"async-tungstenite",
- "audio",
"aws-config",
"aws-sdk-kinesis",
"aws-sdk-s3",
@@ -3215,10 +3230,8 @@ dependencies = [
"collab_ui",
"collections",
"command_palette_hooks",
- "context_server",
"ctor",
"dap",
- "dap-types",
"dap_adapters",
"dashmap",
"debugger_ui",
@@ -3235,7 +3248,6 @@ dependencies = [
"gpui_tokio",
"hex",
"http_client",
- "hyper 0.14.32",
"indoc",
"language",
"language_model",
@@ -3277,7 +3289,6 @@ dependencies = [
"text",
"theme",
"time",
- "title_bar",
"tokio",
"toml 0.8.23",
"tower 0.4.13",
@@ -3308,12 +3319,10 @@ dependencies = [
"futures 0.3.31",
"fuzzy",
"gpui",
- "http_client",
"log",
"menu",
"notifications",
"picker",
- "pretty_assertions",
"project",
"release_channel",
"rpc",
@@ -3326,7 +3335,6 @@ dependencies = [
"time",
"time_format",
"title_bar",
- "tree-sitter-md",
"ui",
"util",
"workspace",
@@ -3380,10 +3388,8 @@ dependencies = [
"client",
"collections",
"command_palette_hooks",
- "ctor",
"db",
"editor",
- "env_logger 0.11.8",
"fuzzy",
"go_to_line",
"gpui",
@@ -3394,7 +3400,6 @@ dependencies = [
"postage",
"project",
"serde",
- "serde_json",
"settings",
"telemetry",
"theme",
@@ -3410,7 +3415,7 @@ name = "command_palette_hooks"
version = "0.1.0"
dependencies = [
"collections",
- "derive_more 0.99.20",
+ "derive_more",
"gpui",
"workspace",
]
@@ -3496,6 +3501,16 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
[[package]]
name = "const-oid"
version = "0.9.6"
@@ -3576,15 +3591,27 @@ dependencies = [
[[package]]
name = "convert_case"
-version = "0.4.0"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "convert_case"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
+dependencies = [
+ "unicode-segmentation",
+]
[[package]]
name = "convert_case"
-version = "0.8.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
+checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49"
dependencies = [
"unicode-segmentation",
]
@@ -3595,18 +3622,14 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-std",
- "client",
- "clock",
"collections",
"command_palette_hooks",
"copilot_chat",
- "ctor",
"edit_prediction_types",
"editor",
"fs",
"futures 0.3.31",
"gpui",
- "http_client",
"icons",
"indoc",
"language",
@@ -3633,6 +3656,7 @@ dependencies = [
name = "copilot_chat"
version = "0.1.0"
dependencies = [
+ "anthropic",
"anyhow",
"collections",
"dirs 4.0.0",
@@ -4083,13 +4107,13 @@ dependencies = [
name = "crashes"
version = "0.1.0"
dependencies = [
- "bincode",
"cfg-if",
"crash-handler",
- "extension_host",
+ "futures 0.3.31",
"log",
"mach2 0.5.0",
"minidumper",
+ "parking_lot",
"paths",
"release_channel",
"serde",
@@ -4277,7 +4301,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
- "rand_core 0.6.4",
"typenum",
]
@@ -4301,7 +4324,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "csv_preview"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "editor",
+ "feature_flags",
+ "gpui",
+ "log",
+ "text",
+ "ui",
+ "workspace",
]
[[package]]
@@ -4364,7 +4401,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4378,7 +4415,7 @@ dependencies = [
"indexmap",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4396,7 +4433,7 @@ dependencies = [
"indexmap",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4430,8 +4467,6 @@ dependencies = [
"smol",
"task",
"telemetry",
- "tree-sitter",
- "tree-sitter-go",
"util",
"zlog",
]
@@ -4493,7 +4528,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4504,7 +4539,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4642,7 +4677,6 @@ dependencies = [
"sysinfo 0.37.2",
"task",
"tasks_ui",
- "telemetry",
"terminal_view",
"text",
"theme",
@@ -4737,40 +4771,29 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
-dependencies = [
- "convert_case 0.4.0",
- "proc-macro2",
- "quote",
- "rustc_version",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
name = "derive_more"
-version = "2.0.1"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
-version = "2.0.1"
+version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
+ "convert_case 0.10.0",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "rustc_version",
+ "syn 2.0.117",
"unicode-xid",
]
@@ -4780,7 +4803,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4792,7 +4815,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -4814,7 +4837,6 @@ dependencies = [
"serde_json",
"settings",
"smol",
- "theme",
"ui",
"util",
"workspace",
@@ -4826,7 +4848,6 @@ name = "diagnostics"
version = "0.1.0"
dependencies = [
"anyhow",
- "client",
"collections",
"component",
"ctor",
@@ -4965,11 +4986,13 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dispatch2"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
dependencies = [
"bitflags 2.10.0",
+ "block2",
+ "libc",
"objc2",
]
@@ -4981,7 +5004,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -5044,7 +5067,7 @@ dependencies = [
"proc-macro2",
"quote",
"strum 0.27.2",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -5217,7 +5240,6 @@ dependencies = [
"thiserror 2.0.17",
"time",
"toml 0.8.23",
- "tree-sitter-rust",
"ui",
"util",
"uuid",
@@ -5315,7 +5337,6 @@ dependencies = [
"tree-sitter",
"util",
"zeta_prompt",
- "zlog",
]
[[package]]
@@ -5336,7 +5357,6 @@ dependencies = [
"anyhow",
"buffer_diff",
"client",
- "clock",
"cloud_llm_client",
"codestral",
"collections",
@@ -5353,20 +5373,13 @@ dependencies = [
"gpui",
"indoc",
"language",
- "language_model",
- "lsp",
"markdown",
"menu",
"multi_buffer",
"paths",
- "pretty_assertions",
"project",
"regex",
- "release_channel",
- "semver",
- "serde_json",
"settings",
- "supermaven",
"telemetry",
"text",
"theme",
@@ -5376,7 +5389,6 @@ dependencies = [
"workspace",
"zed_actions",
"zeta_prompt",
- "zlog",
]
[[package]]
@@ -5405,7 +5417,6 @@ dependencies = [
"fuzzy",
"git",
"gpui",
- "http_client",
"indoc",
"itertools 0.14.0",
"language",
@@ -5420,6 +5431,8 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"project",
+ "proptest",
+ "proptest-derive",
"rand 0.9.2",
"regex",
"release_channel",
@@ -5436,7 +5449,6 @@ dependencies = [
"sum_tree",
"task",
"telemetry",
- "tempfile",
"text",
"theme",
"time",
@@ -5596,7 +5608,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -5617,7 +5629,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -5682,7 +5694,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -5733,6 +5745,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "error-graph"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b920e777967421aa5f9bf34f842c0ab6ba19b3bdb4a082946093860f5858879"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "etagere"
version = "0.2.15"
@@ -5836,6 +5857,47 @@ dependencies = [
"watch",
]
+[[package]]
+name = "eval_cli"
+version = "0.1.0"
+dependencies = [
+ "acp_thread",
+ "agent",
+ "agent-client-protocol",
+ "agent_ui",
+ "anyhow",
+ "clap",
+ "client",
+ "ctrlc",
+ "debug_adapter_extension",
+ "env_logger 0.11.8",
+ "extension",
+ "feature_flags",
+ "fs",
+ "futures 0.3.31",
+ "gpui",
+ "gpui_platform",
+ "gpui_tokio",
+ "language",
+ "language_extension",
+ "language_model",
+ "language_models",
+ "languages",
+ "node_runtime",
+ "paths",
+ "project",
+ "prompt_store",
+ "release_channel",
+ "reqwest_client",
+ "serde",
+ "serde_json",
+ "settings",
+ "shellexpand 2.1.2",
+ "terminal_view",
+ "util",
+ "watch",
+]
+
[[package]]
name = "eval_utils"
version = "0.1.0"
@@ -5964,7 +6026,9 @@ dependencies = [
"serde",
"serde_json",
"serde_json_lenient",
+ "settings_content",
"snippet_provider",
+ "task",
"theme",
"tokio",
"toml 0.8.23",
@@ -6001,7 +6065,6 @@ dependencies = [
"parking_lot",
"paths",
"project",
- "rand 0.9.2",
"release_channel",
"remote",
"reqwest_client",
@@ -6061,6 +6124,12 @@ dependencies = [
"zed_actions",
]
+[[package]]
+name = "failspot"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c942e64b20ecd39933d5ff938ca4fdb6ef0d298cc3855b231179a5ef0b24948d"
+
[[package]]
name = "fallible-iterator"
version = "0.3.0"
@@ -6116,7 +6185,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -6143,7 +6212,6 @@ dependencies = [
name = "feature_flags"
version = "0.1.0"
dependencies = [
- "futures 0.3.31",
"gpui",
]
@@ -6151,7 +6219,6 @@ dependencies = [
name = "feedback"
version = "0.1.0"
dependencies = [
- "editor",
"gpui",
"system_specs",
"urlencoding",
@@ -6182,7 +6249,6 @@ dependencies = [
"futures 0.3.31",
"fuzzy",
"gpui",
- "language",
"menu",
"open_path_prompt",
"picker",
@@ -6246,6 +6312,12 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+[[package]]
+name = "fixedbitset"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
+
[[package]]
name = "flate2"
version = "1.1.8"
@@ -6409,7 +6481,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -6595,6 +6667,19 @@ dependencies = [
"futures-sink",
]
+[[package]]
+name = "futures-concurrency"
+version = "7.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6"
+dependencies = [
+ "fixedbitset 0.5.7",
+ "futures-core",
+ "futures-lite 2.6.1",
+ "pin-project",
+ "smallvec",
+]
+
[[package]]
name = "futures-core"
version = "0.3.31"
@@ -6665,7 +6750,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -7039,13 +7124,26 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "getrandom"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasip3",
+]
+
[[package]]
name = "gh-workflow"
version = "0.8.0"
source = "git+https://github.com/zed-industries/gh-workflow?rev=c9eac0ed361583e1072860d96776fa52775b82ac#c9eac0ed361583e1072860d96776fa52775b82ac"
dependencies = [
"async-trait",
- "derive_more 2.0.1",
+ "derive_more",
"derive_setters",
"gh-workflow-macros",
"indexmap",
@@ -7053,7 +7151,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
- "strum_macros",
+ "strum_macros 0.27.2",
]
[[package]]
@@ -7063,7 +7161,7 @@ source = "git+https://github.com/zed-industries/gh-workflow?rev=c9eac0ed361583e1
dependencies = [
"heck 0.5.0",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -7093,6 +7191,19 @@ version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
+[[package]]
+name = "gio-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps 7.0.7",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "git"
version = "0.1.0"
@@ -7101,7 +7212,7 @@ dependencies = [
"askpass",
"async-trait",
"collections",
- "derive_more 0.99.20",
+ "derive_more",
"futures 0.3.31",
"git2",
"gpui",
@@ -7123,7 +7234,6 @@ dependencies = [
"text",
"thiserror 2.0.17",
"time",
- "unindent",
"url",
"urlencoding",
"util",
@@ -7160,7 +7270,6 @@ dependencies = [
"menu",
"project",
"rand 0.9.2",
- "recent_projects",
"serde_json",
"settings",
"smallvec",
@@ -7207,10 +7316,10 @@ dependencies = [
"ctor",
"db",
"editor",
+ "feature_flags",
"futures 0.3.31",
"fuzzy",
"git",
- "git_hosting_providers",
"gpui",
"indoc",
"itertools 0.14.0",
@@ -7227,6 +7336,7 @@ dependencies = [
"pretty_assertions",
"project",
"prompt_store",
+ "proto",
"rand 0.9.2",
"remote",
"remote_connection",
@@ -7266,6 +7376,50 @@ dependencies = [
"xml-rs",
]
+[[package]]
+name = "glib"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b"
+dependencies = [
+ "bitflags 2.10.0",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "memchr",
+ "smallvec",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c"
+dependencies = [
+ "libc",
+ "system-deps 7.0.7",
+]
+
[[package]]
name = "glob"
version = "0.3.3"
@@ -7334,18 +7488,27 @@ dependencies = [
"settings",
"text",
"theme",
- "tree-sitter-rust",
- "tree-sitter-typescript",
"ui",
"util",
"workspace",
]
+[[package]]
+name = "gobject-sys"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps 7.0.7",
+]
+
[[package]]
name = "goblin"
-version = "0.8.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
+checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745"
dependencies = [
"log",
"plain",
@@ -7405,6 +7568,7 @@ name = "gpui"
version = "0.2.2"
dependencies = [
"anyhow",
+ "async-channel 2.5.0",
"async-task",
"backtrace",
"bindgen 0.71.1",
@@ -7422,14 +7586,18 @@ dependencies = [
"core-text",
"core-video",
"ctor",
- "derive_more 0.99.20",
+ "derive_more",
"embed-resource",
"env_logger 0.11.8",
"etagere",
"foreign-types 0.5.0",
"futures 0.3.31",
+ "futures-concurrency",
+ "getrandom 0.3.4",
"gpui_macros",
"gpui_platform",
+ "gpui_util",
+ "gpui_web",
"http_client",
"image",
"inventory",
@@ -7439,7 +7607,7 @@ dependencies = [
"mach2 0.5.0",
"media",
"metal",
- "naga",
+ "naga 28.0.0",
"num_cpus",
"objc",
"objc2",
@@ -7448,9 +7616,10 @@ dependencies = [
"parking_lot",
"pathfinder_geometry",
"pin-project",
+ "pollster 0.4.0",
"postage",
- "pretty_assertions",
"profiling",
+ "proptest",
"rand 0.9.2",
"raw-window-handle",
"refineable",
@@ -7463,7 +7632,6 @@ dependencies = [
"serde_json",
"slotmap",
"smallvec",
- "smol",
"spin 0.10.0",
"stacksafe",
"strum 0.27.2",
@@ -7471,11 +7639,13 @@ dependencies = [
"taffy",
"thiserror 2.0.17",
"unicode-segmentation",
+ "url",
"usvg",
- "util",
"util_macros",
"uuid",
"waker-fn",
+ "wasm-bindgen",
+ "web-time",
"windows 0.61.3",
"zed-font-kit",
"zed-scap",
@@ -7493,7 +7663,6 @@ dependencies = [
"calloop",
"calloop-wayland-source",
"collections",
- "cosmic-text",
"filedescriptor",
"futures 0.3.31",
"gpui",
@@ -7506,12 +7675,14 @@ dependencies = [
"open",
"parking_lot",
"pathfinder_geometry",
+ "pollster 0.4.0",
"profiling",
"raw-window-handle",
"smallvec",
"smol",
"strum 0.27.2",
"swash",
+ "url",
"util",
"uuid",
"wayland-backend",
@@ -7523,7 +7694,6 @@ dependencies = [
"x11-clipboard",
"x11rb",
"xkbcommon",
- "zed-font-kit",
"zed-scap",
"zed-xim",
]
@@ -7534,7 +7704,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-task",
- "bindgen 0.71.1",
"block",
"cbindgen",
"cocoa 0.26.0",
@@ -7545,7 +7714,8 @@ dependencies = [
"core-text",
"core-video",
"ctor",
- "derive_more 0.99.20",
+ "derive_more",
+ "dispatch2",
"etagere",
"foreign-types 0.5.0",
"futures 0.3.31",
@@ -7577,16 +7747,18 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
name = "gpui_platform"
version = "0.1.0"
dependencies = [
+ "console_error_panic_hook",
"gpui",
"gpui_linux",
"gpui_macos",
+ "gpui_web",
"gpui_windows",
]
@@ -7600,6 +7772,37 @@ dependencies = [
"util",
]
+[[package]]
+name = "gpui_util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "log",
+]
+
+[[package]]
+name = "gpui_web"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "console_error_panic_hook",
+ "futures 0.3.31",
+ "gpui",
+ "gpui_wgpu",
+ "http_client",
+ "js-sys",
+ "log",
+ "parking_lot",
+ "raw-window-handle",
+ "smallvec",
+ "uuid",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm_thread",
+ "web-sys",
+ "web-time",
+]
+
[[package]]
name = "gpui_wgpu"
version = "0.1.0"
@@ -7607,15 +7810,24 @@ dependencies = [
"anyhow",
"bytemuck",
"collections",
+ "cosmic-text",
"etagere",
"gpui",
+ "gpui_util",
+ "itertools 0.14.0",
+ "js-sys",
"log",
"parking_lot",
+ "pollster 0.4.0",
"profiling",
"raw-window-handle",
- "smol",
- "util",
+ "smallvec",
+ "swash",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
"wgpu",
+ "zed-font-kit",
]
[[package]]
@@ -7963,7 +8175,7 @@ dependencies = [
"markup5ever 0.12.1",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -8060,7 +8272,7 @@ dependencies = [
"async-fs",
"async-tar",
"bytes 1.11.1",
- "derive_more 0.99.20",
+ "derive_more",
"futures 0.3.31",
"http 1.3.1",
"http-body 1.0.1",
@@ -8421,7 +8633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
- "quick-error",
+ "quick-error 2.0.1",
]
[[package]]
@@ -8497,7 +8709,7 @@ checksum = "c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -8592,7 +8804,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -8784,7 +8996,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -8836,9 +9048,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.81"
+version = "0.3.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
+checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -8937,9 +9149,9 @@ dependencies = [
[[package]]
name = "jupyter-protocol"
-version = "1.2.1"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c75a69caf8b8e781224badfb76c4a8da4d49856de36ce72ae3cf5d4a1c94e42"
+checksum = "4649647741f9794a7a02e3be976f1b248ba28a37dbfc626d5089316fd4fbf4c8"
dependencies = [
"async-trait",
"bytes 1.11.1",
@@ -9206,7 +9418,6 @@ dependencies = [
"copilot_ui",
"credentials_provider",
"deepseek",
- "editor",
"extension",
"extension_host",
"fs",
@@ -9226,7 +9437,6 @@ dependencies = [
"open_router",
"partial-json-fixer",
"pretty_assertions",
- "project",
"release_channel",
"schemars",
"semver",
@@ -9270,6 +9480,7 @@ dependencies = [
"open_path_prompt",
"picker",
"project",
+ "serde_json",
"settings",
"ui",
"util",
@@ -9353,7 +9564,6 @@ dependencies = [
"snippet",
"task",
"terminal",
- "text",
"theme",
"toml 0.8.23",
"tree-sitter",
@@ -9377,7 +9587,6 @@ dependencies = [
"unindent",
"url",
"util",
- "workspace",
]
[[package]]
@@ -9424,9 +9633,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
[[package]]
name = "libc"
-version = "0.2.177"
+version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "libdbus-sys"
@@ -9510,10 +9719,11 @@ dependencies = [
[[package]]
name = "libwebrtc"
-version = "0.3.10"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+version = "0.3.26"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
"cxx",
+ "glib",
"jni",
"js-sys",
"lazy_static",
@@ -9607,9 +9817,12 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "livekit"
-version = "0.7.8"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+version = "0.7.32"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
+ "base64 0.22.1",
+ "bmrng",
+ "bytes 1.11.1",
"chrono",
"futures-util",
"lazy_static",
@@ -9630,11 +9843,12 @@ dependencies = [
[[package]]
name = "livekit-api"
-version = "0.4.2"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+version = "0.4.14"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
+ "base64 0.21.7",
"futures-util",
- "http 0.2.12",
+ "http 1.3.1",
"livekit-protocol",
"livekit-runtime",
"log",
@@ -9642,20 +9856,22 @@ dependencies = [
"pbjson-types",
"prost 0.12.6",
"rand 0.9.2",
- "reqwest 0.11.27",
+ "reqwest 0.12.24",
+ "rustls-native-certs 0.6.3",
"scopeguard",
"serde",
"sha2",
"thiserror 1.0.69",
"tokio",
- "tokio-tungstenite 0.26.2",
+ "tokio-rustls 0.26.2",
+ "tokio-tungstenite 0.28.0",
"url",
]
[[package]]
name = "livekit-protocol"
-version = "0.3.9"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+version = "0.7.1"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
"futures-util",
"livekit-runtime",
@@ -9663,7 +9879,6 @@ dependencies = [
"pbjson",
"pbjson-types",
"prost 0.12.6",
- "prost-types 0.12.6",
"serde",
"thiserror 1.0.69",
"tokio",
@@ -9672,7 +9887,7 @@ dependencies = [
[[package]]
name = "livekit-runtime"
version = "0.4.0"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
"tokio",
"tokio-stream",
@@ -9725,10 +9940,8 @@ dependencies = [
"serde_json",
"serde_urlencoded",
"settings",
- "sha2",
"simplelog",
"smallvec",
- "tokio-tungstenite 0.26.2",
"ui",
"util",
"zed-scap",
@@ -9823,6 +10036,7 @@ dependencies = [
"ctor",
"futures 0.3.31",
"gpui",
+ "gpui_util",
"log",
"lsp-types",
"parking_lot",
@@ -9989,6 +10203,7 @@ dependencies = [
"language",
"linkify",
"log",
+ "markdown",
"markup5ever_rcdom",
"mermaid-rs-renderer",
"pretty_assertions",
@@ -10047,7 +10262,7 @@ checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -10207,7 +10422,7 @@ dependencies = [
[[package]]
name = "mermaid-rs-renderer"
version = "0.2.0"
-source = "git+https://github.com/zed-industries/mermaid-rs-renderer?branch=fix-font-family-xml-escaping#d91961aa90bc7b0c09c87a13c91d48e2f05c468d"
+source = "git+https://github.com/zed-industries/mermaid-rs-renderer?rev=374db9ead5426697c6c2111151d9f246899bc638#374db9ead5426697c6c2111151d9f246899bc638"
dependencies = [
"anyhow",
"fontdb 0.16.2",
@@ -10281,9 +10496,9 @@ dependencies = [
[[package]]
name = "minidump-common"
-version = "0.21.2"
+version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c4d14bcca0fd3ed165a03000480aaa364c6860c34e900cb2dafdf3b95340e77"
+checksum = "2e16d10087ae9e375bad7a40e8ef5504bc08e808ccc6019067ff9de42a84570f"
dependencies = [
"bitflags 2.10.0",
"debugid",
@@ -10296,14 +10511,16 @@ dependencies = [
[[package]]
name = "minidump-writer"
-version = "0.8.9"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abcd9c8a1e6e1e9d56ce3627851f39a17ea83e17c96bc510f29d7e43d78a7d"
+checksum = "0e1fc14d6ded915b8e850801465e7096f77ed60bf87e4e85878d463720d9dc4d"
dependencies = [
"bitflags 2.10.0",
"byteorder",
"cfg-if",
"crash-context",
+ "error-graph",
+ "failspot",
"goblin",
"libc",
"log",
@@ -10311,18 +10528,20 @@ dependencies = [
"memmap2",
"memoffset",
"minidump-common",
- "nix 0.28.0",
+ "nix 0.29.0",
"procfs-core",
"scroll",
+ "serde",
+ "serde_json",
"tempfile",
- "thiserror 1.0.69",
+ "thiserror 2.0.17",
]
[[package]]
name = "minidumper"
-version = "0.8.3"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b4ebc9d1f8847ec1d078f78b35ed598e0ebefa1f242d5f83cd8d7f03960a7d1"
+checksum = "10d9254e42a48098d045472a5c0cb892007a42e25342eddbf2642f6978bf381a"
dependencies = [
"cfg-if",
"crash-context",
@@ -10332,7 +10551,7 @@ dependencies = [
"parking_lot",
"polling",
"scroll",
- "thiserror 1.0.69",
+ "thiserror 2.0.17",
"uds",
]
@@ -10465,7 +10684,6 @@ dependencies = [
"log",
"parking_lot",
"pretty_assertions",
- "project",
"rand 0.9.2",
"rope",
"serde",
@@ -10488,17 +10706,35 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
-[[package]]
-name = "multimap"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
-
[[package]]
name = "naga"
version = "28.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135"
+dependencies = [
+ "arrayvec",
+ "bit-set",
+ "bitflags 2.10.0",
+ "cfg-if",
+ "cfg_aliases 0.2.1",
+ "codespan-reporting 0.12.0",
+ "half",
+ "hashbrown 0.16.1",
+ "hexf-parse",
+ "indexmap",
+ "libm",
+ "log",
+ "num-traits",
+ "once_cell",
+ "rustc-hash 1.1.0",
+ "thiserror 2.0.17",
+ "unicode-ident",
+]
+
+[[package]]
+name = "naga"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"arrayvec",
"bit-set",
@@ -10557,9 +10793,9 @@ dependencies = [
[[package]]
name = "nbformat"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b10a89a2d910233ec3fca4de359b16ebe95e833c8b2162643ef98c6053a0549d"
+checksum = "d4983a40792c45e8639f77ef8e4461c55679cbc618f4b9e83830e8c7e79c8383"
dependencies = [
"anyhow",
"chrono",
@@ -10660,7 +10896,6 @@ dependencies = [
"cfg-if",
"cfg_aliases 0.2.1",
"libc",
- "memoffset",
]
[[package]]
@@ -10726,12 +10961,10 @@ dependencies = [
"anyhow",
"channel",
"client",
- "collections",
"component",
"db",
"gpui",
"rpc",
- "settings",
"sum_tree",
"time",
"ui",
@@ -10846,6 +11079,22 @@ dependencies = [
"num-iter",
"num-traits",
"rand 0.8.5",
+ "smallvec",
+ "zeroize",
+]
+
+[[package]]
+name = "num-bigint-dig"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7f9a86e097b0d187ad0e65667c2f58b9254671e86e7dbb78036b16692eae099"
+dependencies = [
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "once_cell",
+ "rand 0.9.2",
"serde",
"smallvec",
"zeroize",
@@ -10881,7 +11130,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -10964,7 +11213,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -11219,15 +11468,15 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "oo7"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3299dd401feaf1d45afd8fd1c0586f10fcfb22f244bb9afa942cec73503b89d"
+checksum = "78f2bfed90f1618b4b48dcad9307f25e14ae894e2949642c87c351601d62cebd"
dependencies = [
"aes",
"ashpd",
"async-fs",
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"blocking",
"cbc",
"cipher",
@@ -11235,15 +11484,15 @@ dependencies = [
"endi",
"futures-lite 2.6.1",
"futures-util",
- "getrandom 0.3.4",
+ "getrandom 0.4.1",
"hkdf",
"hmac",
"md-5",
"num",
- "num-bigint-dig",
+ "num-bigint-dig 0.9.1",
"pbkdf2 0.12.2",
- "rand 0.9.2",
"serde",
+ "serde_bytes",
"sha2",
"subtle",
"zbus",
@@ -11357,7 +11606,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -11386,7 +11635,7 @@ checksum = "969ccca8ffc4fb105bd131a228107d5c9dd89d9d627edf3295cbe979156f9712"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -11444,7 +11693,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -11466,8 +11715,6 @@ dependencies = [
"settings",
"smol",
"theme",
- "tree-sitter-rust",
- "tree-sitter-typescript",
"ui",
"util",
"workspace",
@@ -11554,7 +11801,7 @@ dependencies = [
"by_address",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -11817,7 +12064,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12223,7 +12470,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
- "fixedbitset",
+ "fixedbitset 0.4.2",
"indexmap",
]
@@ -12296,7 +12543,7 @@ dependencies = [
"phf_shared 0.11.3",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12309,7 +12556,7 @@ dependencies = [
"phf_shared 0.12.1",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12335,14 +12582,12 @@ name = "picker"
version = "0.1.0"
dependencies = [
"anyhow",
- "ctor",
"editor",
- "env_logger 0.11.8",
"gpui",
"menu",
"schemars",
"serde",
- "serde_json",
+ "settings",
"theme",
"ui",
"ui_input",
@@ -12373,7 +12618,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12448,6 +12693,7 @@ version = "0.1.0"
dependencies = [
"feature_flags",
"gpui",
+ "project",
"settings",
"smallvec",
"theme",
@@ -12544,6 +12790,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
+[[package]]
+name = "pollster"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
+
[[package]]
name = "pori"
version = "0.0.0"
@@ -12601,7 +12853,7 @@ dependencies = [
"log",
"parking_lot",
"pin-project",
- "pollster",
+ "pollster 0.2.5",
"static_assertions",
"thiserror 1.0.69",
]
@@ -12690,7 +12942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12754,7 +13006,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12774,19 +13026,20 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"version_check",
"yansi",
]
[[package]]
name = "procfs-core"
-version = "0.16.0"
+version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29"
+checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
dependencies = [
"bitflags 2.10.0",
"hex",
+ "serde",
]
[[package]]
@@ -12805,7 +13058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -12824,8 +13077,6 @@ dependencies = [
"collections",
"context_server",
"dap",
- "dap_adapters",
- "db",
"encoding_rs",
"extension",
"fancy-regex",
@@ -12932,7 +13183,6 @@ dependencies = [
"pretty_assertions",
"project",
"rayon",
- "remote_connection",
"schemars",
"search",
"serde",
@@ -13014,6 +13264,47 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "proptest"
+version = "1.10.0"
+source = "git+https://github.com/proptest-rs/proptest?rev=3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b#3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags 2.10.0",
+ "num-traits",
+ "proptest-macro",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "proptest-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c57924a81864dddafba92e1bf92f9bf82f97096c44489548a60e888e1547549b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "proptest-macro"
+version = "0.5.0"
+source = "git+https://github.com/proptest-rs/proptest?rev=3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b#3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b"
+dependencies = [
+ "convert_case 0.11.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "prost"
version = "0.9.0"
@@ -13045,7 +13336,7 @@ dependencies = [
"itertools 0.10.5",
"lazy_static",
"log",
- "multimap 0.8.3",
+ "multimap",
"petgraph",
"prost 0.9.0",
"prost-types 0.9.0",
@@ -13064,14 +13355,14 @@ dependencies = [
"heck 0.5.0",
"itertools 0.12.1",
"log",
- "multimap 0.10.1",
+ "multimap",
"once_cell",
"petgraph",
"prettyplease",
"prost 0.12.6",
"prost-types 0.12.6",
"regex",
- "syn 2.0.106",
+ "syn 2.0.117",
"tempfile",
]
@@ -13098,7 +13389,7 @@ dependencies = [
"itertools 0.12.1",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -13125,11 +13416,9 @@ name = "proto"
version = "0.1.0"
dependencies = [
"anyhow",
- "collections",
"prost 0.9.0",
"prost-build 0.9.0",
"serde",
- "typed-path",
]
[[package]]
@@ -13154,10 +13443,11 @@ dependencies = [
[[package]]
name = "psm"
-version = "0.1.27"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e66fcd288453b748497d8fb18bccc83a16b0518e3906d4b8df0a8d42d93dbb1c"
+checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8"
dependencies = [
+ "ar_archive_writer",
"cc",
]
@@ -13265,6 +13555,12 @@ dependencies = [
"bytemuck",
]
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
[[package]]
name = "quick-error"
version = "2.0.1"
@@ -13490,6 +13786,15 @@ dependencies = [
"rand_core 0.6.4",
]
+[[package]]
+name = "rand_xorshift"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
+dependencies = [
+ "rand_core 0.9.3",
+]
+
[[package]]
name = "random_choice"
version = "0.3.2"
@@ -13549,7 +13854,7 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"simd_helpers",
- "system-deps",
+ "system-deps 6.2.2",
"thiserror 1.0.69",
"v_frame",
"wasm-bindgen",
@@ -13564,7 +13869,7 @@ dependencies = [
"avif-serialize",
"imgref",
"loop9",
- "quick-error",
+ "quick-error 2.0.1",
"rav1e",
"rayon",
"rgb",
@@ -13666,7 +13971,6 @@ dependencies = [
"anyhow",
"askpass",
"chrono",
- "dap",
"db",
"dev_container",
"editor",
@@ -13764,7 +14068,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -13915,7 +14219,6 @@ dependencies = [
"collections",
"crash-handler",
"crashes",
- "dap",
"dap_adapters",
"debug_adapter_extension",
"editor",
@@ -13947,7 +14250,6 @@ dependencies = [
"paths",
"pretty_assertions",
"project",
- "prompt_store",
"proto",
"rayon",
"release_channel",
@@ -13971,7 +14273,6 @@ dependencies = [
"uuid",
"watch",
"windows 0.61.3",
- "workspace",
"worktree",
"zlog",
]
@@ -14005,7 +14306,6 @@ dependencies = [
"collections",
"command_palette_hooks",
"editor",
- "env_logger 0.11.8",
"feature_flags",
"file_icons",
"futures 0.3.31",
@@ -14030,6 +14330,7 @@ dependencies = [
"serde",
"serde_json",
"settings",
+ "shlex",
"smol",
"telemetry",
"terminal",
@@ -14060,7 +14361,6 @@ dependencies = [
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
- "hyper-rustls 0.24.2",
"hyper-tls",
"ipnet",
"js-sys",
@@ -14070,8 +14370,6 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
- "rustls 0.21.12",
- "rustls-native-certs 0.6.3",
"rustls-pemfile 1.0.4",
"serde",
"serde_json",
@@ -14080,7 +14378,6 @@ dependencies = [
"system-configuration 0.5.1",
"tokio",
"tokio-native-tls",
- "tokio-rustls 0.24.1",
"tower-service",
"url",
"wasm-bindgen",
@@ -14104,16 +14401,22 @@ dependencies = [
"http-body 1.0.1",
"http-body-util",
"hyper 1.7.0",
+ "hyper-rustls 0.27.7",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
- "serde",
- "serde_json",
- "serde_urlencoded",
+ "quinn",
+ "rustls 0.23.33",
+ "rustls-native-certs 0.8.2",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
"sync_wrapper 1.0.2",
"tokio",
+ "tokio-rustls 0.26.2",
"tower 0.5.2",
"tower-http 0.6.6",
"tower-service",
@@ -14130,14 +14433,13 @@ dependencies = [
"anyhow",
"bytes 1.11.1",
"futures 0.3.31",
- "gpui",
+ "gpui_util",
"http_client",
"http_client_tls",
"log",
"regex",
"serde",
"tokio",
- "util",
"zed-reqwest",
]
@@ -14175,20 +14477,6 @@ dependencies = [
"bytemuck",
]
-[[package]]
-name = "rich_text"
-version = "0.1.0"
-dependencies = [
- "futures 0.3.31",
- "gpui",
- "language",
- "linkify",
- "pulldown-cmark 0.13.0",
- "theme",
- "ui",
- "util",
-]
-
[[package]]
name = "ring"
version = "0.17.14"
@@ -14337,7 +14625,7 @@ checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
"const-oid",
"digest",
- "num-bigint-dig",
+ "num-bigint-dig 0.8.6",
"num-integer",
"num-traits",
"pkcs1",
@@ -14394,9 +14682,9 @@ dependencies = [
[[package]]
name = "runtimelib"
-version = "1.2.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d80685459e1e5fa5603182058351ae91c98ca458dfef4e85f0a37be4f7cf1e6c"
+checksum = "fa84884e45ed4a1e663120cef3fc11f14d1a2a1933776e1c31599f7bd2dd0c9e"
dependencies = [
"async-dispatcher",
"async-std",
@@ -14438,7 +14726,7 @@ dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
- "syn 2.0.106",
+ "syn 2.0.117",
"walkdir",
]
@@ -14692,6 +14980,18 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+[[package]]
+name = "rusty-fork"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
+dependencies = [
+ "fnv",
+ "quick-error 1.2.3",
+ "tempfile",
+ "wait-timeout",
+]
+
[[package]]
name = "rustybuzz"
version = "0.20.1"
@@ -14771,6 +15071,7 @@ dependencies = [
"futures 0.3.31",
"parking_lot",
"rand 0.9.2",
+ "web-time",
]
[[package]]
@@ -14810,7 +15111,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -14871,7 +15172,7 @@ checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -14900,7 +15201,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -14942,7 +15243,7 @@ dependencies = [
"proc-macro2",
"quote",
"sea-bae",
- "syn 2.0.106",
+ "syn 2.0.117",
"unicode-ident",
]
@@ -14991,7 +15292,6 @@ dependencies = [
"any_vec",
"anyhow",
"bitflags 2.10.0",
- "client",
"collections",
"editor",
"fs",
@@ -15100,6 +15400,16 @@ dependencies = [
"serde_derive",
]
+[[package]]
+name = "serde_bytes"
+version = "0.11.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
[[package]]
name = "serde_core"
version = "1.0.228"
@@ -15117,7 +15427,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -15128,7 +15438,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -15186,7 +15496,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -15291,7 +15601,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"collections",
- "derive_more 0.99.20",
+ "derive_more",
"gpui",
"log",
"schemars",
@@ -15326,18 +15636,16 @@ version = "0.1.0"
dependencies = [
"quote",
"settings",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
name = "settings_profile_selector"
version = "0.1.0"
dependencies = [
- "client",
"editor",
"fuzzy",
"gpui",
- "language",
"menu",
"picker",
"project",
@@ -15356,9 +15664,7 @@ dependencies = [
"agent",
"agent_settings",
"anyhow",
- "assets",
"audio",
- "client",
"codestral",
"component",
"copilot",
@@ -15376,13 +15682,11 @@ dependencies = [
"language",
"log",
"menu",
- "node_runtime",
"paths",
"picker",
"platform_title_bar",
"pretty_assertions",
"project",
- "recent_projects",
"regex",
"release_channel",
"rodio",
@@ -15390,7 +15694,6 @@ dependencies = [
"search",
"serde",
"serde_json",
- "session",
"settings",
"shell_command_parser",
"strum 0.27.2",
@@ -15401,7 +15704,6 @@ dependencies = [
"util",
"workspace",
"zed_actions",
- "zlog",
]
[[package]]
@@ -15505,31 +15807,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-[[package]]
-name = "sidebar"
-version = "0.1.0"
-dependencies = [
- "acp_thread",
- "agent_ui",
- "chrono",
- "db",
- "editor",
- "feature_flags",
- "fs",
- "fuzzy",
- "gpui",
- "picker",
- "project",
- "recent_projects",
- "serde_json",
- "settings",
- "theme",
- "ui",
- "ui_input",
- "util",
- "workspace",
-]
-
[[package]]
name = "signal-hook"
version = "0.3.18"
@@ -15699,7 +15976,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -15712,7 +15989,7 @@ dependencies = [
"async-executor",
"async-fs",
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"async-net",
"async-process",
"blocking",
@@ -15877,7 +16154,7 @@ version = "0.1.0"
dependencies = [
"sqlez",
"sqlformat",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -15954,7 +16231,7 @@ dependencies = [
"quote",
"sqlx-core",
"sqlx-macros-core",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -15977,7 +16254,7 @@ dependencies = [
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
- "syn 2.0.106",
+ "syn 2.0.117",
"tokio",
"url",
]
@@ -16107,9 +16384,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "stacker"
-version = "0.1.22"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59"
+checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013"
dependencies = [
"cc",
"cfg-if",
@@ -16136,7 +16413,7 @@ checksum = "172175341049678163e979d9107ca3508046d4d2a7c6682bee46ac541b17db69"
dependencies = [
"proc-macro-error2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -16267,7 +16544,16 @@ version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
- "strum_macros",
+ "strum_macros 0.27.2",
+]
+
+[[package]]
+name = "strum"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
+dependencies = [
+ "strum_macros 0.28.0",
]
[[package]]
@@ -16279,7 +16565,19 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
]
[[package]]
@@ -16295,6 +16593,7 @@ dependencies = [
"arrayvec",
"ctor",
"log",
+ "proptest",
"rand 0.9.2",
"rayon",
"tracing",
@@ -16302,49 +16601,6 @@ dependencies = [
"ztracing",
]
-[[package]]
-name = "supermaven"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "client",
- "collections",
- "edit_prediction_types",
- "editor",
- "env_logger 0.11.8",
- "futures 0.3.31",
- "gpui",
- "http_client",
- "language",
- "log",
- "postage",
- "project",
- "serde",
- "serde_json",
- "settings",
- "smol",
- "supermaven_api",
- "text",
- "theme",
- "ui",
- "unicode-segmentation",
- "util",
-]
-
-[[package]]
-name = "supermaven_api"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "futures 0.3.31",
- "http_client",
- "paths",
- "serde",
- "serde_json",
- "smol",
- "util",
-]
-
[[package]]
name = "sval"
version = "2.15.0"
@@ -16623,9 +16879,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.106"
+version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -16664,7 +16920,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -16780,13 +17036,26 @@ version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
- "cfg-expr",
+ "cfg-expr 0.15.8",
"heck 0.5.0",
"pkg-config",
"toml 0.8.23",
"version-compare",
]
+[[package]]
+name = "system-deps"
+version = "7.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f"
+dependencies = [
+ "cfg-expr 0.20.6",
+ "heck 0.5.0",
+ "pkg-config",
+ "toml 0.9.8",
+ "version-compare",
+]
+
[[package]]
name = "system-interface"
version = "0.27.3"
@@ -16822,13 +17091,11 @@ dependencies = [
name = "tab_switcher"
version = "0.1.0"
dependencies = [
- "anyhow",
"collections",
"ctor",
"editor",
"fuzzy",
"gpui",
- "language",
"menu",
"picker",
"project",
@@ -17017,7 +17284,6 @@ dependencies = [
"release_channel",
"schemars",
"serde",
- "serde_json",
"settings",
"smol",
"sysinfo 0.37.2",
@@ -17049,7 +17315,6 @@ dependencies = [
"assistant_slash_command",
"async-recursion",
"breadcrumbs",
- "client",
"collections",
"db",
"dirs 4.0.0",
@@ -17062,7 +17327,6 @@ dependencies = [
"menu",
"pretty_assertions",
"project",
- "rand 0.9.2",
"regex",
"schemars",
"serde",
@@ -17087,7 +17351,6 @@ dependencies = [
"collections",
"ctor",
"gpui",
- "http_client",
"log",
"parking_lot",
"postage",
@@ -17106,7 +17369,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"collections",
- "derive_more 0.99.20",
+ "derive_more",
"fs",
"futures 0.3.31",
"gpui",
@@ -17201,7 +17464,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -17212,7 +17475,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -17233,7 +17496,7 @@ dependencies = [
"fax",
"flate2",
"half",
- "quick-error",
+ "quick-error 2.0.1",
"weezl",
"zune-jpeg",
]
@@ -17293,6 +17556,7 @@ dependencies = [
"core-foundation-sys",
"sys-locale",
"time",
+ "windows 0.61.3",
]
[[package]]
@@ -17389,15 +17653,11 @@ dependencies = [
"chrono",
"client",
"cloud_api_types",
- "collections",
"db",
- "feature_flags",
"git_ui",
"gpui",
- "http_client",
"notifications",
"platform_title_bar",
- "pretty_assertions",
"project",
"recent_projects",
"release_channel",
@@ -17411,7 +17671,6 @@ dependencies = [
"story",
"telemetry",
"theme",
- "tree-sitter-md",
"ui",
"util",
"windows 0.61.3",
@@ -17455,7 +17714,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -17538,17 +17797,18 @@ dependencies = [
[[package]]
name = "tokio-tungstenite"
-version = "0.26.2"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
+checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857"
dependencies = [
"futures-util",
"log",
"rustls 0.23.33",
+ "rustls-native-certs 0.8.2",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.2",
- "tungstenite 0.26.2",
+ "tungstenite 0.28.0",
]
[[package]]
@@ -17792,7 +18052,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -17887,7 +18147,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -18204,9 +18464,9 @@ dependencies = [
[[package]]
name = "tungstenite"
-version = "0.26.2"
+version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
+checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
dependencies = [
"bytes 1.11.1",
"data-encoding",
@@ -18223,9 +18483,9 @@ dependencies = [
[[package]]
name = "tungstenite"
-version = "0.27.0"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
+checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
dependencies = [
"bytes 1.11.1",
"data-encoding",
@@ -18240,12 +18500,6 @@ dependencies = [
"utf-8",
]
-[[package]]
-name = "typed-path"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c462d18470a2857aa657d338af5fa67170bb48bcc80a296710ce3b0802a32566"
-
[[package]]
name = "typeid"
version = "1.0.3"
@@ -18344,7 +18598,7 @@ version = "0.1.0"
dependencies = [
"component",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"ui",
]
@@ -18361,6 +18615,12 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
[[package]]
name = "unicase"
version = "2.8.1"
@@ -18565,7 +18825,7 @@ dependencies = [
"futures-lite 1.13.0",
"git2",
"globset",
- "indoc",
+ "gpui_util",
"itertools 0.14.0",
"libc",
"log",
@@ -18598,7 +18858,7 @@ version = "0.1.0"
dependencies = [
"perf",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -18710,7 +18970,6 @@ name = "vim"
version = "0.1.0"
dependencies = [
"anyhow",
- "assets",
"async-compat",
"async-trait",
"collections",
@@ -18750,7 +19009,6 @@ dependencies = [
"task",
"text",
"theme",
- "title_bar",
"tokio",
"ui",
"util",
@@ -18816,6 +19074,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "wait-timeout"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "waker-fn"
version = "1.2.0"
@@ -18884,6 +19151,15 @@ dependencies = [
"wit-bindgen 0.46.0",
]
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
+]
+
[[package]]
name = "wasite"
version = "0.1.0"
@@ -18892,9 +19168,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
name = "wasm-bindgen"
-version = "0.2.104"
+version = "0.2.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
+checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2"
dependencies = [
"cfg-if",
"once_cell",
@@ -18903,27 +19179,14 @@ dependencies = [
"wasm-bindgen-shared",
]
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.104"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn 2.0.106",
- "wasm-bindgen-shared",
-]
-
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.54"
+version = "0.4.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
+checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a"
dependencies = [
"cfg-if",
+ "futures-util",
"js-sys",
"once_cell",
"wasm-bindgen",
@@ -18932,9 +19195,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.104"
+version = "0.2.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
+checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -18942,22 +19205,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.104"
+version = "0.2.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
+checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60"
dependencies = [
+ "bumpalo",
"proc-macro2",
"quote",
- "syn 2.0.106",
- "wasm-bindgen-backend",
+ "syn 2.0.117",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.104"
+version = "0.2.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
+checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5"
dependencies = [
"unicode-ident",
]
@@ -19001,6 +19264,16 @@ dependencies = [
"wasmparser 0.229.0",
]
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser 0.244.0",
+]
+
[[package]]
name = "wasm-metadata"
version = "0.201.0"
@@ -19036,6 +19309,18 @@ dependencies = [
"wasmparser 0.227.1",
]
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "wasm-encoder 0.244.0",
+ "wasmparser 0.244.0",
+]
+
[[package]]
name = "wasm-streams"
version = "0.4.2"
@@ -19049,6 +19334,18 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "wasm_thread"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7516db7f32decdadb1c3b8deb1b7d78b9df7606c5cc2f6241737c2ab3a0258e"
+dependencies = [
+ "futures 0.3.31",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
[[package]]
name = "wasmparser"
version = "0.201.0"
@@ -19098,6 +19395,18 @@ dependencies = [
"serde",
]
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags 2.10.0",
+ "hashbrown 0.15.5",
+ "indexmap",
+ "semver",
+]
+
[[package]]
name = "wasmprinter"
version = "0.229.0"
@@ -19199,7 +19508,7 @@ dependencies = [
"anyhow",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"wasmtime-component-util",
"wasmtime-wit-bindgen",
"wit-parser 0.229.0",
@@ -19314,7 +19623,7 @@ checksum = "d0963c1438357a3d8c0efe152b4ef5259846c1cf8b864340270744fe5b3bae5e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -19407,7 +19716,6 @@ dependencies = [
"futures 0.3.31",
"gpui",
"parking_lot",
- "rand 0.9.2",
"zlog",
]
@@ -19526,9 +19834,9 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.81"
+version = "0.3.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
+checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -19573,6 +19881,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"client",
+ "cloud_api_types",
"cloud_llm_client",
"futures 0.3.31",
"gpui",
@@ -19603,25 +19912,27 @@ dependencies = [
[[package]]
name = "webrtc-sys"
-version = "0.3.7"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+version = "0.3.23"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
"cc",
"cxx",
"cxx-build",
"glob",
"log",
+ "pkg-config",
"webrtc-sys-build",
]
[[package]]
name = "webrtc-sys-build"
-version = "0.3.6"
-source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d"
+version = "0.3.13"
+source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=37835f840d0070d45ac8b31cce6a6ae7aca3f459#37835f840d0070d45ac8b31cce6a6ae7aca3f459"
dependencies = [
+ "anyhow",
"fs2",
"regex",
- "reqwest 0.11.27",
+ "reqwest 0.12.24",
"scratch",
"semver",
"zip 0.6.6",
@@ -19635,9 +19946,8 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
[[package]]
name = "wgpu"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"arrayvec",
"bitflags 2.10.0",
@@ -19648,7 +19958,7 @@ dependencies = [
"hashbrown 0.16.1",
"js-sys",
"log",
- "naga",
+ "naga 28.0.1",
"parking_lot",
"portable-atomic",
"profiling",
@@ -19665,9 +19975,8 @@ dependencies = [
[[package]]
name = "wgpu-core"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bb4c8b5db5f00e56f1f08869d870a0dff7c8bc7ebc01091fec140b0cf0211a9"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"arrayvec",
"bit-set",
@@ -19679,7 +19988,7 @@ dependencies = [
"hashbrown 0.16.1",
"indexmap",
"log",
- "naga",
+ "naga 28.0.1",
"once_cell",
"parking_lot",
"portable-atomic",
@@ -19697,36 +20006,32 @@ dependencies = [
[[package]]
name = "wgpu-core-deps-apple"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"wgpu-hal",
]
[[package]]
name = "wgpu-core-deps-emscripten"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"wgpu-hal",
]
[[package]]
name = "wgpu-core-deps-windows-linux-android"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"wgpu-hal",
]
[[package]]
name = "wgpu-hal"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "293080d77fdd14d6b08a67c5487dfddbf874534bb7921526db56a7b75d7e3bef"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"android_system_properties",
"arrayvec",
@@ -19749,7 +20054,7 @@ dependencies = [
"libloading",
"log",
"metal",
- "naga",
+ "naga 28.0.1",
"ndk-sys",
"objc",
"once_cell",
@@ -19772,9 +20077,8 @@ dependencies = [
[[package]]
name = "wgpu-types"
-version = "28.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c"
+version = "28.0.1"
+source = "git+https://github.com/zed-industries/wgpu?rev=465557eccfe77c840a9b4936f1408da9503372c4#465557eccfe77c840a9b4936f1408da9503372c4"
dependencies = [
"bitflags 2.10.0",
"bytemuck",
@@ -19856,7 +20160,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"witx",
]
@@ -19868,7 +20172,7 @@ checksum = "d873bb5b59ca703b5e41562e96a4796d1af61bf4cf80bf8a7abda755a380ec1c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"wiggle-generate",
]
@@ -20091,7 +20395,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -20102,7 +20406,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -20113,7 +20417,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -20124,7 +20428,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -20135,7 +20439,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -20146,7 +20450,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -20702,6 +21006,15 @@ version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro 0.51.0",
+]
+
[[package]]
name = "wit-bindgen-core"
version = "0.22.0"
@@ -20723,6 +21036,17 @@ dependencies = [
"wit-parser 0.227.1",
]
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "wit-parser 0.244.0",
+]
+
[[package]]
name = "wit-bindgen-rt"
version = "0.22.0"
@@ -20764,12 +21088,28 @@ dependencies = [
"heck 0.5.0",
"indexmap",
"prettyplease",
- "syn 2.0.106",
+ "syn 2.0.117",
"wasm-metadata 0.227.1",
"wit-bindgen-core 0.41.0",
"wit-component 0.227.1",
]
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "indexmap",
+ "prettyplease",
+ "syn 2.0.117",
+ "wasm-metadata 0.244.0",
+ "wit-bindgen-core 0.51.0",
+ "wit-component 0.244.0",
+]
+
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.22.0"
@@ -20779,7 +21119,7 @@ dependencies = [
"anyhow",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"wit-bindgen-core 0.22.0",
"wit-bindgen-rust 0.22.0",
]
@@ -20794,11 +21134,26 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"wit-bindgen-core 0.41.0",
"wit-bindgen-rust 0.41.0",
]
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "wit-bindgen-core 0.51.0",
+ "wit-bindgen-rust 0.51.0",
+]
+
[[package]]
name = "wit-component"
version = "0.201.0"
@@ -20837,6 +21192,25 @@ dependencies = [
"wit-parser 0.227.1",
]
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags 2.10.0",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder 0.244.0",
+ "wasm-metadata 0.244.0",
+ "wasmparser 0.244.0",
+ "wit-parser 0.244.0",
+]
+
[[package]]
name = "wit-parser"
version = "0.201.0"
@@ -20891,6 +21265,24 @@ dependencies = [
"wasmparser 0.229.0",
]
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser 0.244.0",
+]
+
[[package]]
name = "witx"
version = "0.9.1"
@@ -20910,13 +21302,11 @@ dependencies = [
"any_vec",
"anyhow",
"async-recursion",
- "call",
"chrono",
"client",
"clock",
"collections",
"component",
- "dap",
"db",
"feature_flags",
"fs",
@@ -20969,9 +21359,7 @@ dependencies = [
"futures 0.3.31",
"fuzzy",
"git",
- "git2",
"gpui",
- "http_client",
"ignore",
"language",
"log",
@@ -21175,6 +21563,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
name = "xtask"
version = "0.1.0"
dependencies = [
+ "annotate-snippets",
"anyhow",
"backtrace",
"cargo_metadata",
@@ -21183,8 +21572,12 @@ dependencies = [
"gh-workflow",
"indexmap",
"indoc",
+ "itertools 0.14.0",
+ "regex",
"serde",
"serde_json",
+ "serde_yaml",
+ "strum 0.27.2",
"toml 0.8.23",
"toml_edit 0.22.27",
]
@@ -21284,7 +21677,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"synstructure",
]
@@ -21296,20 +21689,20 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"synstructure",
]
[[package]]
name = "zbus"
-version = "5.12.0"
+version = "5.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91"
+checksum = "1bfeff997a0aaa3eb20c4652baf788d2dfa6d2839a0ead0b3ff69ce2f9c4bdd1"
dependencies = [
"async-broadcast",
"async-executor",
"async-io",
- "async-lock 3.4.1",
+ "async-lock 3.4.2",
"async-process",
"async-recursion",
"async-task",
@@ -21320,8 +21713,9 @@ dependencies = [
"futures-core",
"futures-lite 2.6.1",
"hex",
- "nix 0.30.1",
+ "libc",
"ordered-stream",
+ "rustix 1.1.2",
"serde",
"serde_repr",
"tracing",
@@ -21336,14 +21730,14 @@ dependencies = [
[[package]]
name = "zbus_macros"
-version = "5.12.0"
+version = "5.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314"
+checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"zbus_names",
"zvariant",
"zvariant_utils",
@@ -21351,19 +21745,18 @@ dependencies = [
[[package]]
name = "zbus_names"
-version = "4.2.0"
+version = "4.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
+checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f"
dependencies = [
"serde",
- "static_assertions",
"winnow",
"zvariant",
]
[[package]]
name = "zed"
-version = "0.226.0"
+version = "0.229.0"
dependencies = [
"acp_thread",
"acp_tools",
@@ -21381,7 +21774,6 @@ dependencies = [
"audio",
"auto_update",
"auto_update_ui",
- "bincode",
"breadcrumbs",
"call",
"channel",
@@ -21400,7 +21792,7 @@ dependencies = [
"copilot_chat",
"copilot_ui",
"crashes",
- "dap",
+ "csv_preview",
"dap_adapters",
"db",
"debug_adapter_extension",
@@ -21464,6 +21856,7 @@ dependencies = [
"parking_lot",
"paths",
"picker",
+ "pkg-config",
"pretty_assertions",
"profiling",
"project",
@@ -21487,11 +21880,9 @@ dependencies = [
"settings_profile_selector",
"settings_ui",
"shellexpand 2.1.2",
- "sidebar",
"smol",
"snippet_provider",
"snippets_ui",
- "supermaven",
"svg_preview",
"sysinfo 0.37.2",
"system_specs",
@@ -21510,8 +21901,6 @@ dependencies = [
"title_bar",
"toolchain_selector",
"tracing",
- "tree-sitter-md",
- "tree-sitter-rust",
"ui",
"ui_prompt",
"url",
@@ -21742,7 +22131,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -21762,7 +22151,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"synstructure",
]
@@ -21783,7 +22172,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -21840,7 +22229,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
]
[[package]]
@@ -21980,14 +22369,14 @@ dependencies = [
[[package]]
name = "zvariant"
-version = "5.8.0"
+version = "5.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c"
+checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4"
dependencies = [
"endi",
"enumflags2",
"serde",
- "url",
+ "serde_bytes",
"winnow",
"zvariant_derive",
"zvariant_utils",
@@ -21995,26 +22384,26 @@ dependencies = [
[[package]]
name = "zvariant_derive"
-version = "5.8.0"
+version = "5.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006"
+checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.106",
+ "syn 2.0.117",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
-version = "3.2.1"
+version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599"
+checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9"
dependencies = [
"proc-macro2",
"quote",
"serde",
- "syn 2.0.106",
+ "syn 2.0.117",
"winnow",
]
diff --git a/Cargo.toml b/Cargo.toml
index 49b765c512accc3a19662da41520061479b8cc44..36e7ca8cc7129af0ed7ab29dc5db338cdf33f7d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,8 @@
[workspace]
resolver = "2"
members = [
- "crates/acp_tools",
"crates/acp_thread",
+ "crates/acp_tools",
"crates/action_log",
"crates/activity_indicator",
"crates/agent",
@@ -13,9 +13,9 @@ members = [
"crates/anthropic",
"crates/askpass",
"crates/assets",
- "crates/assistant_text_thread",
"crates/assistant_slash_command",
"crates/assistant_slash_commands",
+ "crates/assistant_text_thread",
"crates/audio",
"crates/auto_update",
"crates/auto_update_helper",
@@ -32,6 +32,7 @@ members = [
"crates/cloud_api_client",
"crates/cloud_api_types",
"crates/cloud_llm_client",
+ "crates/codestral",
"crates/collab",
"crates/collab_ui",
"crates/collections",
@@ -44,6 +45,7 @@ members = [
"crates/copilot_chat",
"crates/crashes",
"crates/credentials_provider",
+ "crates/csv_preview",
"crates/dap",
"crates/dap_adapters",
"crates/db",
@@ -56,13 +58,15 @@ members = [
"crates/diagnostics",
"crates/docs_preprocessor",
"crates/edit_prediction",
+ "crates/edit_prediction_cli",
+ "crates/edit_prediction_context",
"crates/edit_prediction_types",
"crates/edit_prediction_ui",
- "crates/edit_prediction_context",
"crates/editor",
"crates/encoding_selector",
"crates/etw_tracing",
"crates/eval",
+ "crates/eval_cli",
"crates/eval_utils",
"crates/explorer_command_injector",
"crates/extension",
@@ -88,9 +92,11 @@ members = [
"crates/gpui_macos",
"crates/gpui_macros",
"crates/gpui_platform",
+ "crates/gpui_tokio",
+ "crates/gpui_util",
+ "crates/gpui_web",
"crates/gpui_wgpu",
"crates/gpui_windows",
- "crates/gpui_tokio",
"crates/html_to_markdown",
"crates/http_client",
"crates/http_client_tls",
@@ -119,8 +125,8 @@ members = [
"crates/media",
"crates/menu",
"crates/migrator",
- "crates/mistral",
"crates/miniprofiler_ui",
+ "crates/mistral",
"crates/multi_buffer",
"crates/nc",
"crates/net",
@@ -136,6 +142,7 @@ members = [
"crates/panel",
"crates/paths",
"crates/picker",
+ "crates/platform_title_bar",
"crates/prettier",
"crates/project",
"crates/project_benchmarks",
@@ -147,20 +154,18 @@ members = [
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/release_channel",
- "crates/scheduler",
"crates/remote",
"crates/remote_connection",
"crates/remote_server",
"crates/repl",
"crates/reqwest_client",
- "crates/rich_text",
"crates/rope",
"crates/rpc",
"crates/rules_library",
+ "crates/scheduler",
"crates/schema_generator",
"crates/search",
"crates/session",
- "crates/sidebar",
"crates/settings",
"crates/settings_content",
"crates/settings_json",
@@ -177,9 +182,6 @@ members = [
"crates/storybook",
"crates/streaming_diff",
"crates/sum_tree",
- "crates/supermaven",
- "crates/supermaven_api",
- "crates/codestral",
"crates/svg_preview",
"crates/system_specs",
"crates/tab_switcher",
@@ -195,7 +197,6 @@ members = [
"crates/theme_importer",
"crates/theme_selector",
"crates/time_format",
- "crates/platform_title_bar",
"crates/title_bar",
"crates/toolchain_selector",
"crates/ui",
@@ -207,10 +208,10 @@ members = [
"crates/vercel",
"crates/vim",
"crates/vim_mode_setting",
- "crates/which_key",
"crates/watch",
"crates/web_search",
"crates/web_search_providers",
+ "crates/which_key",
"crates/workspace",
"crates/worktree",
"crates/worktree_benchmarks",
@@ -218,7 +219,6 @@ members = [
"crates/zed",
"crates/zed_actions",
"crates/zed_env_vars",
- "crates/edit_prediction_cli",
"crates/zeta_prompt",
"crates/zlog",
"crates/zlog_settings",
@@ -298,6 +298,7 @@ copilot_ui = { path = "crates/copilot_ui" }
crashes = { path = "crates/crashes" }
credentials_provider = { path = "crates/credentials_provider" }
crossbeam = "0.8.4"
+csv_preview = { path = "crates/csv_preview"}
dap = { path = "crates/dap" }
dap_adapters = { path = "crates/dap_adapters" }
db = { path = "crates/db" }
@@ -332,9 +333,11 @@ gpui_linux = { path = "crates/gpui_linux", default-features = false }
gpui_macos = { path = "crates/gpui_macos", default-features = false }
gpui_macros = { path = "crates/gpui_macros" }
gpui_platform = { path = "crates/gpui_platform", default-features = false }
+gpui_web = { path = "crates/gpui_web" }
gpui_wgpu = { path = "crates/gpui_wgpu" }
gpui_windows = { path = "crates/gpui_windows", default-features = false }
gpui_tokio = { path = "crates/gpui_tokio" }
+gpui_util = { path = "crates/gpui_util" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
http_client_tls = { path = "crates/http_client_tls" }
@@ -366,7 +369,7 @@ markdown_preview = { path = "crates/markdown_preview" }
svg_preview = { path = "crates/svg_preview" }
media = { path = "crates/media" }
menu = { path = "crates/menu" }
-mermaid-rs-renderer = { git = "https://github.com/zed-industries/mermaid-rs-renderer", branch = "fix-font-family-xml-escaping", default-features = false }
+mermaid-rs-renderer = { git = "https://github.com/zed-industries/mermaid-rs-renderer", rev = "374db9ead5426697c6c2111151d9f246899bc638", default-features = false }
migrator = { path = "crates/migrator" }
mistral = { path = "crates/mistral" }
multi_buffer = { path = "crates/multi_buffer" }
@@ -408,7 +411,6 @@ rules_library = { path = "crates/rules_library" }
scheduler = { path = "crates/scheduler" }
search = { path = "crates/search" }
session = { path = "crates/session" }
-sidebar = { path = "crates/sidebar" }
settings = { path = "crates/settings" }
settings_content = { path = "crates/settings_content" }
settings_json = { path = "crates/settings_json" }
@@ -423,8 +425,6 @@ sqlez_macros = { path = "crates/sqlez_macros" }
story = { path = "crates/story" }
streaming_diff = { path = "crates/streaming_diff" }
sum_tree = { path = "crates/sum_tree" }
-supermaven = { path = "crates/supermaven" }
-supermaven_api = { path = "crates/supermaven_api" }
codestral = { path = "crates/codestral" }
system_specs = { path = "crates/system_specs" }
tab_switcher = { path = "crates/tab_switcher" }
@@ -473,15 +473,21 @@ ztracing_macro = { path = "crates/ztracing_macro" }
# External crates
#
-agent-client-protocol = { version = "=0.9.4", features = ["unstable"] }
+agent-client-protocol = { version = "=0.10.2", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "9d9640d4" }
any_vec = "0.14"
anyhow = "1.0.86"
arrayvec = { version = "0.7.4", features = ["serde"] }
-ashpd = { version = "0.12.1", default-features = false, features = [
- "async-std",
+ashpd = { version = "0.13", default-features = false, features = [
+ "async-io",
+ "notification",
+ "open_uri",
+ "file_chooser",
+ "settings",
+ "trash"
] }
+async-channel = "2.5.0"
async-compat = "0.2.1"
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
async-dispatcher = "0.1"
@@ -505,7 +511,6 @@ aws-smithy-runtime-api = { version = "1.9.2", features = ["http-1x", "client"] }
aws-smithy-types = { version = "1.3.4", features = ["http-body-1-x"] }
backtrace = "0.3"
base64 = "0.22"
-bincode = "1.2.1"
bitflags = "2.6.0"
brotli = "8.0.2"
bytes = "1.0"
@@ -530,7 +535,16 @@ criterion = { version = "0.5", features = ["html_reports"] }
ctor = "0.4.0"
dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "1b461b310481d01e02b2603c16d7144b926339f8" }
dashmap = "6.0"
-derive_more = "0.99.17"
+derive_more = { version = "2.1.1", features = [
+ "add",
+ "add_assign",
+ "deref",
+ "deref_mut",
+ "from_str",
+ "mul",
+ "mul_assign",
+ "not",
+] }
dirs = "4.0"
documented = "0.9.1"
dotenvy = "0.15.0"
@@ -542,6 +556,7 @@ exec = "0.3.1"
fancy-regex = "0.16.0"
fork = "0.4.0"
futures = "0.3"
+futures-concurrency = "7.7.1"
futures-lite = "1.13"
gh-workflow = { git = "https://github.com/zed-industries/gh-workflow", rev = "c9eac0ed361583e1072860d96776fa52775b82ac" }
git2 = { version = "0.20.1", default-features = false, features = ["vendored-libgit2"] }
@@ -554,7 +569,6 @@ human_bytes = "0.4.1"
html5ever = "0.27.0"
http = "1.1"
http-body = "1.0"
-hyper = "0.14"
ignore = "0.4.22"
image = "0.25.1"
imara-diff = "0.1.8"
@@ -565,21 +579,23 @@ itertools = "0.14.0"
json_dotpath = "1.1"
jsonschema = "0.37.0"
jsonwebtoken = "10.0"
-jupyter-protocol = "1.2.0"
+jupyter-protocol = "1.4.0"
jupyter-websocket-client = "1.0.0"
libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0"
+libwebrtc = "0.3.26"
+livekit = { version = "0.7.32", features = ["tokio", "rustls-tls-native-roots"] }
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "a4f410987660bf560d1e617cb78117c6b6b9f599" }
mach2 = "0.5"
markup5ever_rcdom = "0.3.0"
metal = "0.33"
-minidumper = "0.8"
+minidumper = "0.9"
moka = { version = "0.12.10", features = ["sync"] }
naga = { version = "28.0", features = ["wgsl-in"] }
nanoid = "0.4"
-nbformat = "1.1.0"
+nbformat = "1.2.0"
nix = "0.29"
num-format = "0.4.4"
objc = "0.2"
@@ -629,9 +645,13 @@ postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = { version = "1.3.0", features = ["unstable"] }
proc-macro2 = "1.0.93"
profiling = "1"
+# replace this with main when #635 is merged
+proptest = { git = "https://github.com/proptest-rs/proptest", rev = "3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b", features = ["attr-macro"] }
+proptest-derive = "0.8.0"
prost = "0.9"
prost-build = "0.9"
prost-types = "0.9"
+pollster = "0.4.0"
pulldown-cmark = { version = "0.13.0", default-features = false }
quote = "1.0.9"
rand = "0.9"
@@ -648,7 +668,7 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "c15662
"stream",
], package = "zed-reqwest", version = "0.12.15-zed" }
rsa = "0.9.6"
-runtimelib = { version = "1.2.0", default-features = false, features = [
+runtimelib = { version = "1.4.0", default-features = false, features = [
"async-dispatcher-runtime", "aws-lc-rs"
] }
rust-embed = { version = "8.4", features = ["include-exclude"] }
@@ -666,7 +686,6 @@ serde_json_lenient = { version = "0.2", features = [
"raw_value",
] }
serde_path_to_error = "0.1.17"
-serde_repr = "0.1"
serde_urlencoded = "0.7"
sha2 = "0.10"
shellexpand = "2.1.0"
@@ -697,7 +716,6 @@ time = { version = "0.3", features = [
] }
tiny_http = "0.8"
tokio = { version = "1" }
-tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
tokio-socks = { version = "0.5.2", default-features = false, features = [
"futures-io",
"tokio",
@@ -756,7 +774,9 @@ wasmtime = { version = "33", default-features = false, features = [
wasmtime-wasi = "33"
wax = "0.7"
which = "6.0.0"
-wgpu = "28.0"
+wasm-bindgen = "0.2.113"
+web-time = "1.1.0"
+wgpu = { git = "https://github.com/zed-industries/wgpu", rev = "465557eccfe77c840a9b4936f1408da9503372c4" }
windows-core = "0.61"
yawc = "0.2.5"
zeroize = "1.8"
@@ -767,11 +787,13 @@ zstd = "0.11"
version = "0.61"
features = [
"Foundation_Numerics",
+ "Globalization_DateTimeFormatting",
"Storage_Search",
"Storage_Streams",
"System_Threading",
"UI_ViewManagement",
"Wdk_System_SystemServices",
+ "Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D11",
@@ -799,6 +821,7 @@ features = [
"Win32_System_Ole",
"Win32_System_Performance",
"Win32_System_Pipes",
+ "Win32_System_RestartManager",
"Win32_System_SystemInformation",
"Win32_System_SystemServices",
"Win32_System_Threading",
@@ -821,6 +844,8 @@ notify = { git = "https://github.com/zed-industries/notify.git", rev = "ce58c24c
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 = "37835f840d0070d45ac8b31cce6a6ae7aca3f459" }
+libwebrtc = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "37835f840d0070d45ac8b31cce6a6ae7aca3f459" }
[profile.dev]
split-debuginfo = "unpacked"
@@ -876,11 +901,9 @@ refineable = { codegen-units = 1 }
release_channel = { codegen-units = 1 }
reqwest_client = { codegen-units = 1 }
session = { codegen-units = 1 }
-sidebar = { codegen-units = 1 }
snippet = { codegen-units = 1 }
snippets_ui = { codegen-units = 1 }
story = { codegen-units = 1 }
-supermaven_api = { codegen-units = 1 }
telemetry_events = { codegen-units = 1 }
theme_selector = { codegen-units = 1 }
time_format = { codegen-units = 1 }
diff --git a/Dockerfile-collab b/Dockerfile-collab
index 63359334906b58c560c0ed6acc6378259ccbd5c5..50af874200a6ef3bc3c882b7d08257ec41f944de 100644
--- a/Dockerfile-collab
+++ b/Dockerfile-collab
@@ -14,8 +14,12 @@ ARG GITHUB_SHA
ENV GITHUB_SHA=$GITHUB_SHA
# Also add `cmake`, since we need it to build `wasmtime`.
+# clang is needed because `webrtc-sys` uses Clang-specific compiler flags.
RUN apt-get update; \
- apt-get install -y --no-install-recommends cmake
+ apt-get install -y --no-install-recommends cmake clang
+
+ENV CC=clang
+ENV CXX=clang++
RUN --mount=type=cache,target=./script/node_modules \
--mount=type=cache,target=/usr/local/cargo/registry \
diff --git a/assets/icons/ai_vercel.svg b/assets/icons/ai_vercel.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c6cc5796f724e713437c4866053380cf2e14d511
--- /dev/null
+++ b/assets/icons/ai_vercel.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/archive.svg b/assets/icons/archive.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9ffe3f39d27c7fe5cbb532a4f263c8800398e96f
--- /dev/null
+++ b/assets/icons/archive.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/fast_forward.svg b/assets/icons/fast_forward.svg
new file mode 100644
index 0000000000000000000000000000000000000000..240bc65aca3558561bb52f2f8c5e860d38596223
--- /dev/null
+++ b/assets/icons/fast_forward.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/fast_forward_off.svg b/assets/icons/fast_forward_off.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8ea7c41c6582b031f066f590dd425641945aadc9
--- /dev/null
+++ b/assets/icons/fast_forward_off.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/file_icons/gitlab.svg b/assets/icons/file_icons/gitlab.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f0faf570b125c7764e769ae60f7a6ce6f7825ceb
--- /dev/null
+++ b/assets/icons/file_icons/gitlab.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/file_icons/helm.svg b/assets/icons/file_icons/helm.svg
new file mode 100644
index 0000000000000000000000000000000000000000..03e702f2d5081c4e96ff4db7ba7428817b08748f
--- /dev/null
+++ b/assets/icons/file_icons/helm.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/file_icons/yaml.svg b/assets/icons/file_icons/yaml.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2c3efd46cd45ff67d6c46d84476d563dd5ac3a73
--- /dev/null
+++ b/assets/icons/file_icons/yaml.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/git_commit.svg b/assets/icons/git_commit.svg
new file mode 100644
index 0000000000000000000000000000000000000000..38b36ec7efb72275e5e6efbbe761deb54050cfe7
--- /dev/null
+++ b/assets/icons/git_commit.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/git_graph.svg b/assets/icons/git_graph.svg
index 8f372a305d3fddf2901756108c83d09b31fb657e..7ae33e365d40bfccd9c48e4f7e94b10d3687f8dc 100644
--- a/assets/icons/git_graph.svg
+++ b/assets/icons/git_graph.svg
@@ -1,4 +1,7 @@
diff --git a/assets/icons/git_merge_conflict.svg b/assets/icons/git_merge_conflict.svg
new file mode 100644
index 0000000000000000000000000000000000000000..10bc2c04fc9877112723273b0d60351c3a4c56bc
--- /dev/null
+++ b/assets/icons/git_merge_conflict.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/icons/list_collapse.svg b/assets/icons/list_collapse.svg
index f18bc550b90228c2f689848b86cfc5bea3d6ff50..dbdb2aaa4537c25ba1867d4957c23819af425835 100644
--- a/assets/icons/list_collapse.svg
+++ b/assets/icons/list_collapse.svg
@@ -1 +1,7 @@
-
+
diff --git a/assets/icons/new_thread.svg b/assets/icons/new_thread.svg
new file mode 100644
index 0000000000000000000000000000000000000000..19b8fa25ea30ed47a57a5d5f83d62f2b4b56b61e
--- /dev/null
+++ b/assets/icons/new_thread.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/open_folder.svg b/assets/icons/open_folder.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c4aa32b29cc1048fd4ecd8b1b4d32b68ae0a8ad3
--- /dev/null
+++ b/assets/icons/open_folder.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/queue_message.svg b/assets/icons/queue_message.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1bdf6738bcf3143fc13a820281cf1cab8531bd36
--- /dev/null
+++ b/assets/icons/queue_message.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/icons/threads_sidebar_left_closed.svg b/assets/icons/threads_sidebar_left_closed.svg
new file mode 100644
index 0000000000000000000000000000000000000000..feb1015254635ef65f90f2c9ea38efab74d01d60
--- /dev/null
+++ b/assets/icons/threads_sidebar_left_closed.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/threads_sidebar_left_open.svg b/assets/icons/threads_sidebar_left_open.svg
new file mode 100644
index 0000000000000000000000000000000000000000..8057b060a84d7d7ffcf29aff1c0c79a8764edc22
--- /dev/null
+++ b/assets/icons/threads_sidebar_left_open.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/threads_sidebar_right_closed.svg b/assets/icons/threads_sidebar_right_closed.svg
new file mode 100644
index 0000000000000000000000000000000000000000..10fa4b792fd65b5875dcf2cadab1fc12a123ab47
--- /dev/null
+++ b/assets/icons/threads_sidebar_right_closed.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/threads_sidebar_right_open.svg b/assets/icons/threads_sidebar_right_open.svg
new file mode 100644
index 0000000000000000000000000000000000000000..23a01eb3f82a5866157220172c868ed9ded46033
--- /dev/null
+++ b/assets/icons/threads_sidebar_right_open.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/workspace_nav_closed.svg b/assets/icons/workspace_nav_closed.svg
deleted file mode 100644
index ed1fce52d6826a4d10299f331358ff84e4caa973..0000000000000000000000000000000000000000
--- a/assets/icons/workspace_nav_closed.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/assets/icons/workspace_nav_open.svg b/assets/icons/workspace_nav_open.svg
deleted file mode 100644
index 464b6aac73c2aeaa9463a805aabc4559377bbfd3..0000000000000000000000000000000000000000
--- a/assets/icons/workspace_nav_open.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json
index f3247e936f2b6d2d5ee5275304ea445729046afa..0516221b6e0849ab631c021d020050be99aaf728 100644
--- a/assets/keymaps/default-linux.json
+++ b/assets/keymaps/default-linux.json
@@ -204,6 +204,7 @@
{
"context": "Editor && editor_agent_diff",
"bindings": {
+ "alt-y": "agent::Keep",
"ctrl-alt-y": "agent::Keep",
"ctrl-alt-z": "agent::Reject",
"shift-alt-y": "agent::KeepAll",
@@ -214,6 +215,7 @@
{
"context": "AgentDiff",
"bindings": {
+ "alt-y": "agent::Keep",
"ctrl-alt-y": "agent::Keep",
"ctrl-alt-z": "agent::Reject",
"shift-alt-y": "agent::KeepAll",
@@ -256,6 +258,7 @@
"ctrl-shift-j": "agent::ToggleNavigationMenu",
"ctrl-alt-i": "agent::ToggleOptionsMenu",
"ctrl-alt-shift-n": "agent::ToggleNewThreadMenu",
+ "ctrl-shift-t": "agent::CycleStartThreadIn",
"shift-alt-escape": "agent::ExpandMessageEditor",
"ctrl->": "agent::AddSelectionToThread",
"ctrl-shift-e": "project_panel::ToggleFocus",
@@ -333,6 +336,7 @@
"ctrl-alt-k": "agent::ToggleThinkingMode",
"ctrl-alt-'": "agent::ToggleThinkingEffortMenu",
"ctrl-'": "agent::CycleThinkingEffort",
+ "ctrl-alt-.": "agent::ToggleFastMode",
},
},
{
@@ -620,6 +624,7 @@
"ctrl-shift-t": "pane::ReopenClosedItem",
"ctrl-k ctrl-s": "zed::OpenKeymap",
"ctrl-k ctrl-t": "theme_selector::Toggle",
+ "ctrl-k ctrl-shift-t": "theme::ToggleMode",
"ctrl-alt-super-p": "settings_profile_selector::Toggle",
"ctrl-t": "project_symbols::Toggle",
"ctrl-p": "file_finder::Toggle",
@@ -670,6 +675,9 @@
"use_key_equivalents": true,
"bindings": {
"ctrl-n": "multi_workspace::NewWorkspaceInWindow",
+ "left": "agents_sidebar::CollapseSelectedEntry",
+ "right": "agents_sidebar::ExpandSelectedEntry",
+ "enter": "menu::Confirm",
},
},
{
@@ -812,7 +820,7 @@
},
},
{
- "context": "!ContextEditor > Editor && mode == full",
+ "context": "!ContextEditor && !AcpThread > Editor && mode == full",
"bindings": {
"alt-enter": "editor::OpenExcerpts",
"shift-enter": "editor::ExpandExcerpts",
@@ -976,6 +984,7 @@
"ctrl-shift-enter": "git::Amend",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
+ "ctrl-k ctrl-r": "git::RestoreAndNext",
},
},
{
@@ -1309,6 +1318,7 @@
"bindings": {
"ctrl-shift-space": "git::WorktreeFromDefaultOnWindow",
"ctrl-space": "git::WorktreeFromDefault",
+ "ctrl-shift-backspace": "git::DeleteWorktree",
},
},
{
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index 77e01368462cdfcce24cf1cba39d6a2a11cdcce0..a4aec7cfe8053f3f23b43652f7e58f319c9691f6 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -242,6 +242,7 @@
"context": "AgentDiff",
"use_key_equivalents": true,
"bindings": {
+ "cmd-y": "agent::Keep",
"cmd-alt-y": "agent::Keep",
"cmd-alt-z": "agent::Reject",
"shift-alt-y": "agent::KeepAll",
@@ -252,6 +253,7 @@
"context": "Editor && editor_agent_diff",
"use_key_equivalents": true,
"bindings": {
+ "cmd-y": "agent::Keep",
"cmd-alt-y": "agent::Keep",
"cmd-alt-z": "agent::Reject",
"shift-alt-y": "agent::KeepAll",
@@ -295,6 +297,7 @@
"cmd-shift-j": "agent::ToggleNavigationMenu",
"cmd-alt-m": "agent::ToggleOptionsMenu",
"cmd-alt-shift-n": "agent::ToggleNewThreadMenu",
+ "cmd-shift-t": "agent::CycleStartThreadIn",
"shift-alt-escape": "agent::ExpandMessageEditor",
"cmd->": "agent::AddSelectionToThread",
"cmd-shift-e": "project_panel::ToggleFocus",
@@ -377,6 +380,7 @@
"cmd-alt-k": "agent::ToggleThinkingMode",
"cmd-alt-'": "agent::ToggleThinkingEffortMenu",
"ctrl-'": "agent::CycleThinkingEffort",
+ "cmd-alt-.": "agent::ToggleFastMode",
},
},
{
@@ -447,6 +451,13 @@
"down": "search::NextHistoryQuery",
},
},
+ {
+ "context": "BufferSearchBar || ProjectSearchBar",
+ "use_key_equivalents": true,
+ "bindings": {
+ "ctrl-enter": "editor::Newline",
+ },
+ },
{
"context": "ProjectSearchBar",
"use_key_equivalents": true,
@@ -680,6 +691,7 @@
"cmd-shift-t": "pane::ReopenClosedItem",
"cmd-k cmd-s": "zed::OpenKeymap",
"cmd-k cmd-t": "theme_selector::Toggle",
+ "cmd-k cmd-shift-t": "theme::ToggleMode",
"ctrl-alt-cmd-p": "settings_profile_selector::Toggle",
"cmd-t": "project_symbols::Toggle",
"cmd-p": "file_finder::Toggle",
@@ -731,6 +743,9 @@
"use_key_equivalents": true,
"bindings": {
"cmd-n": "multi_workspace::NewWorkspaceInWindow",
+ "left": "agents_sidebar::CollapseSelectedEntry",
+ "right": "agents_sidebar::ExpandSelectedEntry",
+ "enter": "menu::Confirm",
},
},
{
@@ -868,7 +883,7 @@
},
},
{
- "context": "!ContextEditor > Editor && mode == full",
+ "context": "!ContextEditor && !AcpThread > Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"alt-enter": "editor::OpenExcerpts",
@@ -1020,6 +1035,7 @@
"cmd-shift-enter": "git::Amend",
"cmd-ctrl-y": "git::StageAll",
"cmd-ctrl-shift-y": "git::UnstageAll",
+ "cmd-alt-z": "git::RestoreAndNext",
},
},
{
@@ -1407,6 +1423,7 @@
"bindings": {
"ctrl-shift-space": "git::WorktreeFromDefaultOnWindow",
"ctrl-space": "git::WorktreeFromDefault",
+ "cmd-shift-backspace": "git::DeleteWorktree",
},
},
{
diff --git a/assets/keymaps/default-windows.json b/assets/keymaps/default-windows.json
index 51b221c8389d1588d80a8186ddceb68e8cb025c7..c10054d5813c6deae33b7a790b3639e7f2c802aa 100644
--- a/assets/keymaps/default-windows.json
+++ b/assets/keymaps/default-windows.json
@@ -203,6 +203,7 @@
"context": "Editor && editor_agent_diff",
"use_key_equivalents": true,
"bindings": {
+ "alt-y": "agent::Keep",
"ctrl-alt-y": "agent::Keep",
"ctrl-alt-z": "agent::Reject",
"shift-alt-y": "agent::KeepAll",
@@ -214,6 +215,7 @@
"context": "AgentDiff",
"use_key_equivalents": true,
"bindings": {
+ "alt-y": "agent::Keep",
"ctrl-alt-y": "agent::Keep",
"ctrl-alt-z": "agent::Reject",
"shift-alt-y": "agent::KeepAll",
@@ -257,6 +259,7 @@
"shift-alt-j": "agent::ToggleNavigationMenu",
"shift-alt-i": "agent::ToggleOptionsMenu",
"ctrl-shift-alt-n": "agent::ToggleNewThreadMenu",
+ "ctrl-shift-t": "agent::CycleStartThreadIn",
"shift-alt-escape": "agent::ExpandMessageEditor",
"ctrl-shift-.": "agent::AddSelectionToThread",
"ctrl-shift-e": "project_panel::ToggleFocus",
@@ -335,6 +338,7 @@
"ctrl-alt-k": "agent::ToggleThinkingMode",
"ctrl-alt-'": "agent::ToggleThinkingEffortMenu",
"ctrl-'": "agent::CycleThinkingEffort",
+ "ctrl-alt-.": "agent::ToggleFastMode",
},
},
{
@@ -612,6 +616,7 @@
"ctrl-shift-t": "pane::ReopenClosedItem",
"ctrl-k ctrl-s": "zed::OpenKeymap",
"ctrl-k ctrl-t": "theme_selector::Toggle",
+ "ctrl-k ctrl-shift-t": "theme::ToggleMode",
"ctrl-alt-super-p": "settings_profile_selector::Toggle",
"ctrl-t": "project_symbols::Toggle",
"ctrl-p": "file_finder::Toggle",
@@ -674,6 +679,9 @@
"use_key_equivalents": true,
"bindings": {
"ctrl-n": "multi_workspace::NewWorkspaceInWindow",
+ "left": "agents_sidebar::CollapseSelectedEntry",
+ "right": "agents_sidebar::ExpandSelectedEntry",
+ "enter": "menu::Confirm",
},
},
{
@@ -814,7 +822,7 @@
},
},
{
- "context": "!ContextEditor > Editor && mode == full",
+ "context": "!ContextEditor && !AcpThread > Editor && mode == full",
"use_key_equivalents": true,
"bindings": {
"alt-enter": "editor::OpenExcerpts",
@@ -977,6 +985,7 @@
"ctrl-shift-enter": "git::Amend",
"ctrl-space": "git::StageAll",
"ctrl-shift-space": "git::UnstageAll",
+ "ctrl-k ctrl-r": "git::RestoreAndNext",
},
},
{
@@ -1330,6 +1339,7 @@
"bindings": {
"ctrl-shift-space": "git::WorktreeFromDefaultOnWindow",
"ctrl-space": "git::WorktreeFromDefault",
+ "ctrl-shift-backspace": "git::DeleteWorktree",
},
},
{
diff --git a/assets/keymaps/linux/jetbrains.json b/assets/keymaps/linux/jetbrains.json
index bdf3949b3f9203220978ff599e0187513d6a976f..98d5cf93106f35e488ab70a60468fa2239cb08c0 100644
--- a/assets/keymaps/linux/jetbrains.json
+++ b/assets/keymaps/linux/jetbrains.json
@@ -81,6 +81,13 @@
"ctrl-\\": "assistant::InlineAssist",
},
},
+ {
+ "context": "Editor && mode == auto_height",
+ "bindings": {
+ "shift-enter": "editor::Newline",
+ "ctrl-shift-enter": "editor::NewlineBelow",
+ },
+ },
{
"context": "BufferSearchBar",
"bindings": {
diff --git a/assets/keymaps/macos/jetbrains.json b/assets/keymaps/macos/jetbrains.json
index c9106e4d49671f16917b1322824c2edfcd0e7700..304ffb86e8c2fd08fb756b015490f8c4ac424f58 100644
--- a/assets/keymaps/macos/jetbrains.json
+++ b/assets/keymaps/macos/jetbrains.json
@@ -33,6 +33,7 @@
"cmd-+": "editor::UnfoldLines",
"alt-shift-g": "editor::SplitSelectionIntoLines",
"ctrl-g": ["editor::SelectNext", { "replace_newest": false }],
+ "ctrl-shift-g": "editor::UndoSelection",
"ctrl-cmd-g": ["editor::SelectPrevious", { "replace_newest": false }],
"cmd-/": ["editor::ToggleComments", { "advance_downwards": true }],
"alt-up": "editor::SelectLargerSyntaxNode",
@@ -79,6 +80,13 @@
"cmd-\\": "assistant::InlineAssist",
},
},
+ {
+ "context": "Editor && mode == auto_height",
+ "bindings": {
+ "shift-enter": "editor::Newline",
+ "ctrl-shift-enter": "editor::NewlineBelow",
+ },
+ },
{
"context": "BufferSearchBar",
"bindings": {
diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json
index 9832ce8fe08fe23d610a1c2ee1a95ad4c2c2574c..66693ab0a153a73af1dccb101e0ed36259b774fa 100644
--- a/assets/keymaps/vim.json
+++ b/assets/keymaps/vim.json
@@ -427,6 +427,7 @@
"escape": "vim::SwitchToHelixNormalMode",
"i": "vim::HelixInsert",
"a": "vim::HelixAppend",
+ "shift-a": "vim::HelixInsertEndOfLine",
"ctrl-[": "editor::Cancel",
},
},
@@ -1110,4 +1111,12 @@
"shift-g": "menu::SelectLast",
},
},
+ {
+ "context": "NotebookEditor > Editor && VimControl && vim_mode == normal",
+
+ "bindings": {
+ "j": "notebook::NotebookMoveDown",
+ "k": "notebook::NotebookMoveUp",
+ },
+ },
]
diff --git a/assets/settings/default.json b/assets/settings/default.json
index c2e74e16da352083c4d4ec517ad345ffd1d76f23..7ec1be3f7d02bf2e6e9ccaf9755c704b4196485f 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -361,8 +361,11 @@
// bracket, brace, single or double quote characters.
// For example, when you select text and type '(', Zed will surround the text with ().
"use_auto_surround": true,
- // Whether indentation should be adjusted based on the context whilst typing.
- "auto_indent": true,
+ // Controls automatic indentation behavior when typing.
+ // - "syntax_aware": Adjusts indentation based on syntax context (default)
+ // - "preserve_indent": Preserves current line's indentation on new lines
+ // - "none": No automatic indentation
+ "auto_indent": "syntax_aware",
// Whether indentation of pasted content should be adjusted based on the context.
"auto_indent_on_paste": true,
// Controls how the editor handles the autoclosed characters.
@@ -799,6 +802,8 @@
// 3. Show files first, then directories:
// "files_first"
"sort_mode": "directories_first",
+ // Whether to show error and warning count badges next to file names in the project panel.
+ "diagnostic_badges": false,
// Whether to enable drag-and-drop operations in the project panel.
"drag_and_drop": true,
// Whether to hide the root entry when only one folder is open in the window;
@@ -913,6 +918,10 @@
// Default: inherits editor scrollbar settings
// "show": null
},
+ // Whether to show the addition/deletion change count next to each file in the Git panel.
+ //
+ // Default: true
+ "diff_stats": true,
},
"message_editor": {
// Whether to automatically replace emoji shortcodes with emoji characters.
@@ -1265,8 +1274,6 @@
//
// Default: true
"skip_focus_for_active_in_search": true,
- // Whether to show the git status in the file finder.
- "git_status": true,
// Whether to use gitignored files when searching.
// Only the file Zed had indexed will be used, not necessary all the gitignored files.
//
@@ -1833,8 +1840,8 @@
" (",
" # multi-char path: first char (not opening delimiter, space, or box drawing char)",
" [^({\\[<\"'`\\ \\u2500-\\u257F]",
- " # middle chars: non-space, and colon/paren only if not followed by digit/paren",
- " ([^\\ :(]|[:(][^0-9()])*",
+ " # middle chars: non-space, and colon/paren only if not followed by digit/paren/space",
+ " ([^\\ :(]|[:(][^0-9()\\ ])*",
" # last char: not closing delimiter or colon",
" [^()}\\]>\"'`.,;:\\ ]",
" |",
@@ -2228,6 +2235,9 @@
"vercel": {
"api_url": "https://api.v0.dev/v1",
},
+ "vercel_ai_gateway": {
+ "api_url": "https://ai-gateway.vercel.sh/v1",
+ },
"x_ai": {
"api_url": "https://api.x.ai/v1",
},
diff --git a/assets/settings/default_semantic_token_rules.json b/assets/settings/default_semantic_token_rules.json
index c5e9d1438cad583e78bc3e109b4bc79c62aa7ac5..65b20a7423aef3c3221f9f80e345fd503627d98d 100644
--- a/assets/settings/default_semantic_token_rules.json
+++ b/assets/settings/default_semantic_token_rules.json
@@ -2,7 +2,9 @@
//
// These rules map LSP semantic token types to syntax theme styles.
// To customize, add rules to "semantic_token_rules" in your settings.json.
-// User-defined rules are prepended to these defaults and take precedence.
+// User-defined rules are prepended and take highest precedence.
+// Extension language rules are applied next.
+// These built-in defaults are applied last.
//
// Each rule has the following properties:
// - `token_type`: The LSP semantic token type to match. If omitted, matches all types.
diff --git a/crates/acp_thread/Cargo.toml b/crates/acp_thread/Cargo.toml
index 83cf86bfafc33e4d1b520ca5af04da626831aed7..7ef53bc522708680e64cfcc9ce2860990bfd7d13 100644
--- a/crates/acp_thread/Cargo.toml
+++ b/crates/acp_thread/Cargo.toml
@@ -59,7 +59,5 @@ indoc.workspace = true
parking_lot.workspace = true
project = { workspace = true, "features" = ["test-support"] }
rand.workspace = true
-tempfile.workspace = true
util.workspace = true
settings.workspace = true
-zlog.workspace = true
diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs
index 7b6349157a8edac3d7e6976deaba61aa4e0f6d22..9be93cc317ca36275603b15549423f536a2a2ce4 100644
--- a/crates/acp_thread/src/acp_thread.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -2,55 +2,23 @@ mod connection;
mod diff;
mod mention;
mod terminal;
-
-/// Key used in ACP ToolCall meta to store the tool's programmatic name.
-/// This is a workaround since ACP's ToolCall doesn't have a dedicated name field.
-pub const TOOL_NAME_META_KEY: &str = "tool_name";
-
-/// Key used in ACP ToolCall meta to store the session id when a subagent is spawned.
-pub const SUBAGENT_SESSION_ID_META_KEY: &str = "subagent_session_id";
-
-/// Helper to extract tool name from ACP meta
-pub fn tool_name_from_meta(meta: &Option) -> Option {
- meta.as_ref()
- .and_then(|m| m.get(TOOL_NAME_META_KEY))
- .and_then(|v| v.as_str())
- .map(|s| SharedString::from(s.to_owned()))
-}
-
-/// Helper to extract subagent session id from ACP meta
-pub fn subagent_session_id_from_meta(meta: &Option) -> Option {
- meta.as_ref()
- .and_then(|m| m.get(SUBAGENT_SESSION_ID_META_KEY))
- .and_then(|v| v.as_str())
- .map(|s| acp::SessionId::from(s.to_string()))
-}
-
-/// Helper to create meta with tool name
-pub fn meta_with_tool_name(tool_name: &str) -> acp::Meta {
- acp::Meta::from_iter([(TOOL_NAME_META_KEY.into(), tool_name.into())])
-}
-use collections::HashSet;
-pub use connection::*;
-pub use diff::*;
-use language::language_settings::FormatOnSave;
-pub use mention::*;
-use project::lsp_store::{FormatTrigger, LspFormatTarget};
-use serde::{Deserialize, Serialize};
-use serde_json::to_string_pretty;
-
-use task::{Shell, ShellBuilder};
-pub use terminal::*;
-
use action_log::{ActionLog, ActionLogTelemetry};
use agent_client_protocol::{self as acp};
use anyhow::{Context as _, Result, anyhow};
+use collections::HashSet;
+pub use connection::*;
+pub use diff::*;
use futures::{FutureExt, channel::oneshot, future::BoxFuture};
use gpui::{AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity};
use itertools::Itertools;
+use language::language_settings::FormatOnSave;
use language::{Anchor, Buffer, BufferSnapshot, LanguageRegistry, Point, ToPoint, text_diff};
use markdown::Markdown;
+pub use mention::*;
+use project::lsp_store::{FormatTrigger, LspFormatTarget};
use project::{AgentLocation, Project, git_store::GitStoreCheckpoint};
+use serde::{Deserialize, Serialize};
+use serde_json::to_string_pretty;
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{Formatter, Write};
@@ -59,11 +27,51 @@ use std::process::ExitStatus;
use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{fmt::Display, mem, path::PathBuf, sync::Arc};
+use task::{Shell, ShellBuilder};
+pub use terminal::*;
use text::Bias;
use ui::App;
use util::{ResultExt, get_default_system_shell_preferring_bash, paths::PathStyle};
use uuid::Uuid;
+/// Key used in ACP ToolCall meta to store the tool's programmatic name.
+/// This is a workaround since ACP's ToolCall doesn't have a dedicated name field.
+pub const TOOL_NAME_META_KEY: &str = "tool_name";
+
+/// Helper to extract tool name from ACP meta
+pub fn tool_name_from_meta(meta: &Option) -> Option {
+ meta.as_ref()
+ .and_then(|m| m.get(TOOL_NAME_META_KEY))
+ .and_then(|v| v.as_str())
+ .map(|s| SharedString::from(s.to_owned()))
+}
+
+/// Helper to create meta with tool name
+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 ToolCall meta to store the session id and message indexes
+pub const SUBAGENT_SESSION_INFO_META_KEY: &str = "subagent_session_info";
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct SubagentSessionInfo {
+ /// The session id of the subagent sessiont that was spawned
+ pub session_id: acp::SessionId,
+ /// The index of the message of the start of the "turn" run by this tool call
+ pub message_start_index: usize,
+ /// The index of the output of the message that the subagent has returned
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub message_end_index: Option,
+}
+
+/// Helper to extract subagent session id from ACP meta
+pub fn subagent_session_info_from_meta(meta: &Option) -> Option {
+ meta.as_ref()
+ .and_then(|m| m.get(SUBAGENT_SESSION_INFO_META_KEY))
+ .and_then(|v| serde_json::from_value(v.clone()).ok())
+}
+
#[derive(Debug)]
pub struct UserMessage {
pub id: Option,
@@ -102,6 +110,7 @@ impl UserMessage {
pub struct AssistantMessage {
pub chunks: Vec,
pub indented: bool,
+ pub is_subagent_output: bool,
}
impl AssistantMessage {
@@ -222,7 +231,7 @@ pub struct ToolCall {
pub raw_input_markdown: Option>,
pub raw_output: Option,
pub tool_name: Option,
- pub subagent_session_id: Option,
+ pub subagent_session_info: Option,
}
impl ToolCall {
@@ -261,7 +270,7 @@ impl ToolCall {
let tool_name = tool_name_from_meta(&tool_call.meta);
- let subagent_session = subagent_session_id_from_meta(&tool_call.meta);
+ let subagent_session_info = subagent_session_info_from_meta(&tool_call.meta);
let result = Self {
id: tool_call.tool_call_id,
@@ -276,7 +285,7 @@ impl ToolCall {
raw_input_markdown,
raw_output: tool_call.raw_output,
tool_name,
- subagent_session_id: subagent_session,
+ subagent_session_info,
};
Ok(result)
}
@@ -309,8 +318,8 @@ impl ToolCall {
self.status = status.into();
}
- if let Some(subagent_session_id) = subagent_session_id_from_meta(&meta) {
- self.subagent_session_id = Some(subagent_session_id);
+ if let Some(subagent_session_info) = subagent_session_info_from_meta(&meta) {
+ self.subagent_session_info = Some(subagent_session_info);
}
if let Some(title) = title {
@@ -401,7 +410,7 @@ impl ToolCall {
pub fn is_subagent(&self) -> bool {
self.tool_name.as_ref().is_some_and(|s| s == "spawn_agent")
- || self.subagent_session_id.is_some()
+ || self.subagent_session_info.is_some()
}
pub fn to_markdown(&self, cx: &App) -> String {
@@ -943,8 +952,11 @@ struct RunningTurn {
}
pub struct AcpThread {
+ session_id: acp::SessionId,
+ cwd: Option,
parent_session_id: Option,
title: SharedString,
+ provisional_title: Option,
entries: Vec,
plan: Plan,
project: Entity,
@@ -953,7 +965,6 @@ pub struct AcpThread {
turn_id: u32,
running_turn: Option,
connection: Rc,
- session_id: acp::SessionId,
token_usage: Option,
prompt_capabilities: acp::PromptCapabilities,
_observe_prompt_capabilities: Task>,
@@ -961,6 +972,10 @@ pub struct AcpThread {
pending_terminal_output: HashMap>>,
pending_terminal_exit: HashMap,
had_error: bool,
+ /// The user's unsent prompt text, persisted so it can be restored when reloading the thread.
+ draft_prompt: Option>,
+ /// The initial scroll position for the thread view, set during session registration.
+ ui_scroll_position: Option,
}
impl From<&AcpThread> for ActionLogTelemetry {
@@ -983,7 +998,7 @@ pub enum AcpThreadEvent {
ToolAuthorizationReceived(acp::ToolCallId),
Retry(RetryStatus),
SubagentSpawned(acp::SessionId),
- Stopped,
+ Stopped(acp::StopReason),
Error,
LoadError(LoadError),
PromptCapabilitiesUpdated,
@@ -1034,87 +1049,6 @@ pub enum TerminalProviderCommand {
},
}
-impl AcpThread {
- pub fn on_terminal_provider_event(
- &mut self,
- event: TerminalProviderEvent,
- cx: &mut Context,
- ) {
- match event {
- TerminalProviderEvent::Created {
- terminal_id,
- label,
- cwd,
- output_byte_limit,
- terminal,
- } => {
- let entity = self.register_terminal_created(
- terminal_id.clone(),
- label,
- cwd,
- output_byte_limit,
- terminal,
- cx,
- );
-
- if let Some(mut chunks) = self.pending_terminal_output.remove(&terminal_id) {
- for data in chunks.drain(..) {
- entity.update(cx, |term, cx| {
- term.inner().update(cx, |inner, cx| {
- inner.write_output(&data, cx);
- })
- });
- }
- }
-
- if let Some(_status) = self.pending_terminal_exit.remove(&terminal_id) {
- entity.update(cx, |_term, cx| {
- cx.notify();
- });
- }
-
- cx.notify();
- }
- TerminalProviderEvent::Output { terminal_id, data } => {
- if let Some(entity) = self.terminals.get(&terminal_id) {
- entity.update(cx, |term, cx| {
- term.inner().update(cx, |inner, cx| {
- inner.write_output(&data, cx);
- })
- });
- } else {
- self.pending_terminal_output
- .entry(terminal_id)
- .or_default()
- .push(data);
- }
- }
- TerminalProviderEvent::TitleChanged { terminal_id, title } => {
- if let Some(entity) = self.terminals.get(&terminal_id) {
- entity.update(cx, |term, cx| {
- term.inner().update(cx, |inner, cx| {
- inner.breadcrumb_text = title;
- cx.emit(::terminal::Event::BreadcrumbsChanged);
- })
- });
- }
- }
- TerminalProviderEvent::Exit {
- terminal_id,
- status,
- } => {
- if let Some(entity) = self.terminals.get(&terminal_id) {
- entity.update(cx, |_term, cx| {
- cx.notify();
- });
- } else {
- self.pending_terminal_exit.insert(terminal_id, status);
- }
- }
- }
- }
-}
-
#[derive(PartialEq, Eq, Debug)]
pub enum ThreadStatus {
Idle,
@@ -1161,6 +1095,7 @@ impl AcpThread {
pub fn new(
parent_session_id: Option,
title: impl Into,
+ cwd: Option,
connection: Rc,
project: Entity,
action_log: Entity,
@@ -1181,11 +1116,13 @@ impl AcpThread {
Self {
parent_session_id,
+ cwd,
action_log,
shared_buffers: Default::default(),
entries: Default::default(),
plan: Default::default(),
title: title.into(),
+ provisional_title: None,
project,
running_turn: None,
turn_id: 0,
@@ -1198,6 +1135,8 @@ impl AcpThread {
pending_terminal_output: HashMap::default(),
pending_terminal_exit: HashMap::default(),
had_error: false,
+ draft_prompt: None,
+ ui_scroll_position: None,
}
}
@@ -1209,6 +1148,22 @@ impl AcpThread {
self.prompt_capabilities.clone()
}
+ pub fn draft_prompt(&self) -> Option<&[acp::ContentBlock]> {
+ self.draft_prompt.as_deref()
+ }
+
+ pub fn set_draft_prompt(&mut self, prompt: Option>) {
+ self.draft_prompt = prompt;
+ }
+
+ pub fn ui_scroll_position(&self) -> Option {
+ self.ui_scroll_position
+ }
+
+ pub fn set_ui_scroll_position(&mut self, position: Option) {
+ self.ui_scroll_position = position;
+ }
+
pub fn connection(&self) -> &Rc {
&self.connection
}
@@ -1222,7 +1177,9 @@ impl AcpThread {
}
pub fn title(&self) -> SharedString {
- self.title.clone()
+ self.provisional_title
+ .clone()
+ .unwrap_or_else(|| self.title.clone())
}
pub fn entries(&self) -> &[AgentThreadEntry] {
@@ -1233,6 +1190,10 @@ impl AcpThread {
&self.session_id
}
+ pub fn cwd(&self) -> Option<&PathBuf> {
+ self.cwd.as_ref()
+ }
+
pub fn status(&self) -> ThreadStatus {
if self.running_turn.is_some() {
ThreadStatus::Generating
@@ -1425,6 +1386,7 @@ impl AcpThread {
&& let AgentThreadEntry::AssistantMessage(AssistantMessage {
chunks,
indented: existing_indented,
+ is_subagent_output: _,
}) = last_entry
&& *existing_indented == indented
{
@@ -1456,6 +1418,7 @@ impl AcpThread {
AgentThreadEntry::AssistantMessage(AssistantMessage {
chunks: vec![chunk],
indented,
+ is_subagent_output: false,
}),
cx,
);
@@ -1472,16 +1435,29 @@ impl AcpThread {
}
pub fn set_title(&mut self, title: SharedString, cx: &mut Context) -> Task> {
+ let had_provisional = self.provisional_title.take().is_some();
if title != self.title {
self.title = title.clone();
cx.emit(AcpThreadEvent::TitleUpdated);
if let Some(set_title) = self.connection.set_title(&self.session_id, cx) {
return set_title.run(title, cx);
}
+ } else if had_provisional {
+ cx.emit(AcpThreadEvent::TitleUpdated);
}
Task::ready(Ok(()))
}
+ /// Sets a provisional display title without propagating back to the
+ /// underlying agent connection. This is used for quick preview titles
+ /// (e.g. first 20 chars of the user message) that should be shown
+ /// immediately but replaced once the LLM generates a proper title via
+ /// `set_title`.
+ pub fn set_provisional_title(&mut self, title: SharedString, cx: &mut Context) {
+ self.provisional_title = Some(title);
+ cx.emit(AcpThreadEvent::TitleUpdated);
+ }
+
pub fn subagent_spawned(&mut self, session_id: acp::SessionId, cx: &mut Context) {
cx.emit(AcpThreadEvent::SubagentSpawned(session_id));
}
@@ -1525,7 +1501,7 @@ impl AcpThread {
raw_input_markdown: None,
raw_output: None,
tool_name: None,
- subagent_session_id: None,
+ subagent_session_info: None,
};
self.push_entry(AgentThreadEntry::ToolCall(failed_tool_call), cx);
return Ok(());
@@ -1589,6 +1565,7 @@ impl AcpThread {
let agent_telemetry_id = self.connection().telemetry_id();
let session = self.session_id();
+ let parent_session_id = self.parent_session_id();
if let ToolCallStatus::Completed | ToolCallStatus::Failed = status {
let status = if matches!(status, ToolCallStatus::Completed) {
"completed"
@@ -1599,6 +1576,7 @@ impl AcpThread {
"Agent Tool Call Completed",
agent_telemetry_id,
session,
+ parent_session_id,
status
);
}
@@ -1685,8 +1663,24 @@ impl AcpThread {
})
}
+ pub fn tool_call_for_subagent(&self, session_id: &acp::SessionId) -> Option<&ToolCall> {
+ self.entries.iter().find_map(|entry| match entry {
+ AgentThreadEntry::ToolCall(tool_call) => {
+ if let Some(subagent_session_info) = &tool_call.subagent_session_info
+ && &subagent_session_info.session_id == session_id
+ {
+ Some(tool_call)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })
+ }
+
pub fn resolve_locations(&mut self, id: acp::ToolCallId, cx: &mut Context) {
let project = self.project.clone();
+ let should_update_agent_location = self.parent_session_id.is_none();
let Some((_, tool_call)) = self.tool_call_mut(&id) else {
return;
};
@@ -1722,7 +1716,7 @@ impl AcpThread {
} else {
false
};
- if !should_ignore {
+ if !should_ignore && should_update_agent_location {
project.set_agent_location(Some(location.into()), cx);
}
});
@@ -1953,8 +1947,10 @@ impl AcpThread {
.await?;
this.update(cx, |this, cx| {
- this.project
- .update(cx, |project, cx| project.set_agent_location(None, cx));
+ if this.parent_session_id.is_none() {
+ this.project
+ .update(cx, |project, cx| project.set_agent_location(None, cx));
+ }
let Ok(response) = response else {
// tx dropped, just return
return Ok(None);
@@ -2022,7 +2018,7 @@ impl AcpThread {
}
}
- cx.emit(AcpThreadEvent::Stopped);
+ cx.emit(AcpThreadEvent::Stopped(r.stop_reason));
Ok(Some(r))
}
Err(e) => {
@@ -2226,6 +2222,7 @@ impl AcpThread {
let limit = limit.unwrap_or(u32::MAX);
let project = self.project.clone();
let action_log = self.action_log.clone();
+ let should_update_agent_location = self.parent_session_id.is_none();
cx.spawn(async move |this, cx| {
let load = project.update(cx, |project, cx| {
let path = project
@@ -2276,15 +2273,17 @@ impl AcpThread {
let start = snapshot.anchor_before(start_position);
let end = snapshot.anchor_before(Point::new(line.saturating_add(limit), 0));
- project.update(cx, |project, cx| {
- project.set_agent_location(
- Some(AgentLocation {
- buffer: buffer.downgrade(),
- position: start,
- }),
- cx,
- );
- });
+ if should_update_agent_location {
+ project.update(cx, |project, cx| {
+ project.set_agent_location(
+ Some(AgentLocation {
+ buffer: buffer.downgrade(),
+ position: start,
+ }),
+ cx,
+ );
+ });
+ }
Ok(snapshot.text_for_range(start..end).collect::())
})
@@ -2298,6 +2297,7 @@ impl AcpThread {
) -> Task> {
let project = self.project.clone();
let action_log = self.action_log.clone();
+ let should_update_agent_location = self.parent_session_id.is_none();
cx.spawn(async move |this, cx| {
let load = project.update(cx, |project, cx| {
let path = project
@@ -2319,24 +2319,26 @@ impl AcpThread {
text_diff(old_text.as_str(), &content)
.into_iter()
.map(|(range, replacement)| {
- (snapshot.anchor_range_between(range), replacement)
+ (snapshot.anchor_range_around(range), replacement)
})
.collect::>()
})
.await;
- project.update(cx, |project, cx| {
- project.set_agent_location(
- Some(AgentLocation {
- buffer: buffer.downgrade(),
- position: edits
- .last()
- .map(|(range, _)| range.end)
- .unwrap_or(Anchor::min_for_buffer(buffer.read(cx).remote_id())),
- }),
- cx,
- );
- });
+ if should_update_agent_location {
+ project.update(cx, |project, cx| {
+ project.set_agent_location(
+ Some(AgentLocation {
+ buffer: buffer.downgrade(),
+ position: edits
+ .last()
+ .map(|(range, _)| range.end)
+ .unwrap_or(Anchor::min_for_buffer(buffer.read(cx).remote_id())),
+ }),
+ cx,
+ );
+ });
+ }
let format_on_save = cx.update(|cx| {
action_log.update(cx, |action_log, cx| {
@@ -2535,6 +2537,95 @@ impl AcpThread {
self.terminals.insert(terminal_id.clone(), entity.clone());
entity
}
+
+ pub fn mark_as_subagent_output(&mut self, cx: &mut Context) {
+ for entry in self.entries.iter_mut().rev() {
+ if let AgentThreadEntry::AssistantMessage(assistant_message) = entry {
+ assistant_message.is_subagent_output = true;
+ cx.notify();
+ return;
+ }
+ }
+ }
+
+ pub fn on_terminal_provider_event(
+ &mut self,
+ event: TerminalProviderEvent,
+ cx: &mut Context,
+ ) {
+ match event {
+ TerminalProviderEvent::Created {
+ terminal_id,
+ label,
+ cwd,
+ output_byte_limit,
+ terminal,
+ } => {
+ let entity = self.register_terminal_created(
+ terminal_id.clone(),
+ label,
+ cwd,
+ output_byte_limit,
+ terminal,
+ cx,
+ );
+
+ if let Some(mut chunks) = self.pending_terminal_output.remove(&terminal_id) {
+ for data in chunks.drain(..) {
+ entity.update(cx, |term, cx| {
+ term.inner().update(cx, |inner, cx| {
+ inner.write_output(&data, cx);
+ })
+ });
+ }
+ }
+
+ if let Some(_status) = self.pending_terminal_exit.remove(&terminal_id) {
+ entity.update(cx, |_term, cx| {
+ cx.notify();
+ });
+ }
+
+ cx.notify();
+ }
+ TerminalProviderEvent::Output { terminal_id, data } => {
+ if let Some(entity) = self.terminals.get(&terminal_id) {
+ entity.update(cx, |term, cx| {
+ term.inner().update(cx, |inner, cx| {
+ inner.write_output(&data, cx);
+ })
+ });
+ } else {
+ self.pending_terminal_output
+ .entry(terminal_id)
+ .or_default()
+ .push(data);
+ }
+ }
+ TerminalProviderEvent::TitleChanged { terminal_id, title } => {
+ if let Some(entity) = self.terminals.get(&terminal_id) {
+ entity.update(cx, |term, cx| {
+ term.inner().update(cx, |inner, cx| {
+ inner.breadcrumb_text = title;
+ cx.emit(::terminal::Event::BreadcrumbsChanged);
+ })
+ });
+ }
+ }
+ TerminalProviderEvent::Exit {
+ terminal_id,
+ status,
+ } => {
+ if let Some(entity) = self.terminals.get(&terminal_id) {
+ entity.update(cx, |_term, cx| {
+ cx.notify();
+ });
+ } else {
+ self.pending_terminal_exit.insert(terminal_id, status);
+ }
+ }
+ }
+ }
}
fn markdown_for_raw_output(
@@ -3844,6 +3935,7 @@ mod tests {
struct FakeAgentConnection {
auth_methods: Vec,
sessions: Arc>>>,
+ set_title_calls: Rc>>,
on_user_message: Option<
Rc<
dyn Fn(
@@ -3862,6 +3954,7 @@ mod tests {
auth_methods: Vec::new(),
on_user_message: None,
sessions: Arc::default(),
+ set_title_calls: Default::default(),
}
}
@@ -3897,7 +3990,7 @@ mod tests {
fn new_session(
self: Rc,
project: Entity,
- _cwd: &Path,
+ cwd: &Path,
cx: &mut App,
) -> Task>> {
let session_id = acp::SessionId::new(
@@ -3912,6 +4005,7 @@ mod tests {
AcpThread::new(
None,
"Test",
+ Some(cwd.to_path_buf()),
self.clone(),
project,
action_log,
@@ -3930,7 +4024,7 @@ mod tests {
}
fn authenticate(&self, method: acp::AuthMethodId, _cx: &mut App) -> Task> {
- if self.auth_methods().iter().any(|m| m.id == method) {
+ if self.auth_methods().iter().any(|m| m.id() == &method) {
Task::ready(Ok(()))
} else {
Task::ready(Err(anyhow!("Invalid Auth Method")))
@@ -3966,11 +4060,32 @@ mod tests {
}))
}
+ fn set_title(
+ &self,
+ _session_id: &acp::SessionId,
+ _cx: &App,
+ ) -> Option> {
+ Some(Rc::new(FakeAgentSessionSetTitle {
+ calls: self.set_title_calls.clone(),
+ }))
+ }
+
fn into_any(self: Rc) -> Rc {
self
}
}
+ struct FakeAgentSessionSetTitle {
+ calls: Rc>>,
+ }
+
+ impl AgentSessionSetTitle for FakeAgentSessionSetTitle {
+ fn run(&self, title: SharedString, _cx: &mut App) -> Task> {
+ self.calls.borrow_mut().push(title);
+ Task::ready(Ok(()))
+ }
+ }
+
struct FakeAgentSessionEditor {
_session_id: acp::SessionId,
}
@@ -4562,4 +4677,54 @@ mod tests {
);
});
}
+
+ #[gpui::test]
+ async fn test_provisional_title_replaced_by_real_title(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, [], cx).await;
+ let connection = Rc::new(FakeAgentConnection::new());
+ let set_title_calls = connection.set_title_calls.clone();
+
+ let thread = cx
+ .update(|cx| connection.new_session(project, Path::new(path!("/test")), cx))
+ .await
+ .unwrap();
+
+ // Initial title is the default.
+ thread.read_with(cx, |thread, _| {
+ assert_eq!(thread.title().as_ref(), "Test");
+ });
+
+ // Setting a provisional title updates the display title.
+ thread.update(cx, |thread, cx| {
+ thread.set_provisional_title("Hello, can you help…".into(), cx);
+ });
+ thread.read_with(cx, |thread, _| {
+ assert_eq!(thread.title().as_ref(), "Hello, can you help…");
+ });
+
+ // The provisional title should NOT have propagated to the connection.
+ assert_eq!(
+ set_title_calls.borrow().len(),
+ 0,
+ "provisional title should not propagate to the connection"
+ );
+
+ // When the real title arrives via set_title, it replaces the
+ // provisional title and propagates to the connection.
+ let task = thread.update(cx, |thread, cx| {
+ thread.set_title("Helping with Rust question".into(), cx)
+ });
+ task.await.expect("set_title should succeed");
+ thread.read_with(cx, |thread, _| {
+ assert_eq!(thread.title().as_ref(), "Helping with Rust question");
+ });
+ assert_eq!(
+ set_title_calls.borrow().as_slice(),
+ &[SharedString::from("Helping with Rust question")],
+ "real title should propagate to the connection"
+ );
+ }
}
diff --git a/crates/acp_thread/src/connection.rs b/crates/acp_thread/src/connection.rs
index 0becded53762be7c96789b0d31191fd9cbc02bfe..4f6aaf86bad68f919c2c5de30214b21ff851c3dd 100644
--- a/crates/acp_thread/src/connection.rs
+++ b/crates/acp_thread/src/connection.rs
@@ -45,9 +45,10 @@ pub trait AgentConnection {
/// Load an existing session by ID.
fn load_session(
self: Rc,
- _session: AgentSessionInfo,
+ _session_id: acp::SessionId,
_project: Entity,
_cwd: &Path,
+ _title: Option,
_cx: &mut App,
) -> Task>> {
Task::ready(Err(anyhow::Error::msg("Loading sessions is not supported")))
@@ -59,7 +60,11 @@ pub trait AgentConnection {
}
/// Close an existing session. Allows the agent to free the session from memory.
- fn close_session(&self, _session_id: &acp::SessionId, _cx: &mut App) -> Task> {
+ fn close_session(
+ self: Rc,
+ _session_id: &acp::SessionId,
+ _cx: &mut App,
+ ) -> Task> {
Task::ready(Err(anyhow::Error::msg("Closing sessions is not supported")))
}
@@ -71,9 +76,10 @@ pub trait AgentConnection {
/// Resume an existing session by ID without replaying previous messages.
fn resume_session(
self: Rc,
- _session: AgentSessionInfo,
+ _session_id: acp::SessionId,
_project: Entity,
_cwd: &Path,
+ _title: Option,
_cx: &mut App,
) -> Task>> {
Task::ready(Err(anyhow::Error::msg(
@@ -240,6 +246,7 @@ pub struct AgentSessionInfo {
pub cwd: Option,
pub title: Option,
pub updated_at: Option>,
+ pub created_at: Option>,
pub meta: Option,
}
@@ -250,6 +257,7 @@ impl AgentSessionInfo {
cwd: None,
title: None,
updated_at: None,
+ created_at: None,
meta: None,
}
}
@@ -496,6 +504,7 @@ mod test_support {
//! - `create_test_png_base64` for generating test images
use std::sync::Arc;
+ use std::sync::atomic::{AtomicUsize, Ordering};
use action_log::ActionLog;
use collections::HashMap;
@@ -618,15 +627,18 @@ mod test_support {
fn new_session(
self: Rc,
project: Entity,
- _cwd: &Path,
+ cwd: &Path,
cx: &mut gpui::App,
) -> Task>> {
- let session_id = acp::SessionId::new(self.sessions.lock().len().to_string());
+ static NEXT_SESSION_ID: AtomicUsize = AtomicUsize::new(0);
+ let session_id =
+ acp::SessionId::new(NEXT_SESSION_ID.fetch_add(1, Ordering::SeqCst).to_string());
let action_log = cx.new(|_| ActionLog::new(project.clone()));
let thread = cx.new(|cx| {
AcpThread::new(
None,
"Test",
+ Some(cwd.to_path_buf()),
self.clone(),
project,
action_log,
diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs
index 8886b458d623237b74f715d3c1d0def33fbefa7d..08b1b9bdf24d1ff9980164c1af8b3e60bd2f3339 100644
--- a/crates/acp_thread/src/diff.rs
+++ b/crates/acp_thread/src/diff.rs
@@ -149,6 +149,16 @@ impl Diff {
}
}
+ pub fn file_path(&self, cx: &App) -> Option {
+ match self {
+ Self::Pending(PendingDiff { new_buffer, .. }) => new_buffer
+ .read(cx)
+ .file()
+ .map(|file| file.full_path(cx).to_string_lossy().into_owned()),
+ Self::Finalized(FinalizedDiff { path, .. }) => Some(path.clone()),
+ }
+ }
+
pub fn multibuffer(&self) -> &Entity {
match self {
Self::Pending(PendingDiff { multibuffer, .. }) => multibuffer,
diff --git a/crates/acp_thread/src/mention.rs b/crates/acp_thread/src/mention.rs
index 5769d13860f2466f95fe7dd67c1f908812e40c2d..43dfe7610e34a0399a27a1d28858b938acfc2e0f 100644
--- a/crates/acp_thread/src/mention.rs
+++ b/crates/acp_thread/src/mention.rs
@@ -60,6 +60,9 @@ pub enum MentionUri {
GitDiff {
base_ref: String,
},
+ MergeConflict {
+ file_path: String,
+ },
}
impl MentionUri {
@@ -215,6 +218,9 @@ impl MentionUri {
let base_ref =
single_query_param(&url, "base")?.unwrap_or_else(|| "main".to_string());
Ok(Self::GitDiff { base_ref })
+ } else if path.starts_with("/agent/merge-conflict") {
+ let file_path = single_query_param(&url, "path")?.unwrap_or_default();
+ Ok(Self::MergeConflict { file_path })
} else {
bail!("invalid zed url: {:?}", input);
}
@@ -245,6 +251,13 @@ impl MentionUri {
}
}
MentionUri::GitDiff { base_ref } => format!("Branch Diff ({})", base_ref),
+ MentionUri::MergeConflict { file_path } => {
+ let name = Path::new(file_path)
+ .file_name()
+ .unwrap_or_default()
+ .to_string_lossy();
+ format!("Merge Conflict ({name})")
+ }
MentionUri::Selection {
abs_path: path,
line_range,
@@ -254,6 +267,41 @@ impl MentionUri {
}
}
+ pub fn tooltip_text(&self) -> Option {
+ match self {
+ MentionUri::File { abs_path } | MentionUri::Directory { abs_path } => {
+ Some(abs_path.to_string_lossy().into_owned().into())
+ }
+ MentionUri::Symbol {
+ abs_path,
+ line_range,
+ ..
+ } => Some(
+ format!(
+ "{}:{}-{}",
+ abs_path.display(),
+ line_range.start(),
+ line_range.end()
+ )
+ .into(),
+ ),
+ MentionUri::Selection {
+ abs_path: Some(path),
+ line_range,
+ ..
+ } => Some(
+ format!(
+ "{}:{}-{}",
+ path.display(),
+ line_range.start(),
+ line_range.end()
+ )
+ .into(),
+ ),
+ _ => None,
+ }
+ }
+
pub fn icon_path(&self, cx: &mut App) -> SharedString {
match self {
MentionUri::File { abs_path } => {
@@ -271,6 +319,7 @@ impl MentionUri {
MentionUri::Selection { .. } => IconName::Reader.path().into(),
MentionUri::Fetch { .. } => IconName::ToolWeb.path().into(),
MentionUri::GitDiff { .. } => IconName::GitBranch.path().into(),
+ MentionUri::MergeConflict { .. } => IconName::GitMergeConflict.path().into(),
}
}
@@ -374,6 +423,11 @@ impl MentionUri {
url.query_pairs_mut().append_pair("base", base_ref);
url
}
+ MentionUri::MergeConflict { file_path } => {
+ let mut url = Url::parse("zed:///agent/merge-conflict").unwrap();
+ url.query_pairs_mut().append_pair("path", file_path);
+ url
+ }
}
}
}
diff --git a/crates/action_log/Cargo.toml b/crates/action_log/Cargo.toml
index 8488df691e40ea3bcfc04f4f6f74964fba7863dd..5227a61651012279e83a3b6e3e68b1484acb0f66 100644
--- a/crates/action_log/Cargo.toml
+++ b/crates/action_log/Cargo.toml
@@ -20,6 +20,7 @@ buffer_diff.workspace = true
log.workspace = true
clock.workspace = true
collections.workspace = true
+fs.workspace = true
futures.workspace = true
gpui.workspace = true
language.workspace = true
@@ -36,7 +37,7 @@ collections = { workspace = true, features = ["test-support"] }
clock = { workspace = true, features = ["test-support"] }
ctor.workspace = true
gpui = { workspace = true, features = ["test-support"] }
-indoc.workspace = true
+
language = { workspace = true, features = ["test-support"] }
log.workspace = true
pretty_assertions.workspace = true
diff --git a/crates/action_log/src/action_log.rs b/crates/action_log/src/action_log.rs
index 1157d8d6f881ecb33df8104dd4be04bd9d846b5e..3faf767c7020763eadc7db6c93af42f650a07434 100644
--- a/crates/action_log/src/action_log.rs
+++ b/crates/action_log/src/action_log.rs
@@ -1,14 +1,20 @@
use anyhow::{Context as _, Result};
use buffer_diff::BufferDiff;
use clock;
-use collections::BTreeMap;
+use collections::{BTreeMap, HashMap};
+use fs::MTime;
use futures::{FutureExt, StreamExt, channel::mpsc};
use gpui::{
App, AppContext, AsyncApp, Context, Entity, SharedString, Subscription, Task, WeakEntity,
};
use language::{Anchor, Buffer, BufferEvent, Point, ToOffset, ToPoint};
use project::{Project, ProjectItem, lsp_store::OpenLspBufferHandle};
-use std::{cmp, ops::Range, sync::Arc};
+use std::{
+ cmp,
+ ops::Range,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
use text::{Edit, Patch, Rope};
use util::{RangeExt, ResultExt as _};
@@ -48,8 +54,14 @@ pub struct ActionLog {
tracked_buffers: BTreeMap, TrackedBuffer>,
/// The project this action log is associated with
project: Entity,
+ /// An action log to forward all public methods to
+ /// Useful in cases like subagents, where we want to track individual diffs for this subagent,
+ /// but also want to associate the reads/writes with a parent review experience
+ linked_action_log: Option>,
/// Stores undo information for the most recent reject operation
last_reject_undo: Option,
+ /// Tracks the last time files were read by the agent, to detect external modifications
+ file_read_times: HashMap,
}
impl ActionLog {
@@ -58,14 +70,47 @@ impl ActionLog {
Self {
tracked_buffers: BTreeMap::default(),
project,
+ linked_action_log: None,
last_reject_undo: None,
+ file_read_times: HashMap::default(),
}
}
+ pub fn with_linked_action_log(mut self, linked_action_log: Entity) -> Self {
+ self.linked_action_log = Some(linked_action_log);
+ self
+ }
+
pub fn project(&self) -> &Entity {
&self.project
}
+ pub fn file_read_time(&self, path: &Path) -> Option {
+ self.file_read_times.get(path).copied()
+ }
+
+ fn update_file_read_time(&mut self, buffer: &Entity, cx: &App) {
+ let buffer = buffer.read(cx);
+ if let Some(file) = buffer.file() {
+ if let Some(local_file) = file.as_local() {
+ if let Some(mtime) = file.disk_state().mtime() {
+ let abs_path = local_file.abs_path(cx);
+ self.file_read_times.insert(abs_path, mtime);
+ }
+ }
+ }
+ }
+
+ fn remove_file_read_time(&mut self, buffer: &Entity, cx: &App) {
+ let buffer = buffer.read(cx);
+ if let Some(file) = buffer.file() {
+ if let Some(local_file) = file.as_local() {
+ let abs_path = local_file.abs_path(cx);
+ self.file_read_times.remove(&abs_path);
+ }
+ }
+ }
+
fn track_buffer_internal(
&mut self,
buffer: Entity,
@@ -164,7 +209,7 @@ impl ActionLog {
cx: &mut Context,
) {
match event {
- BufferEvent::Edited => {
+ BufferEvent::Edited { .. } => {
let Some(tracked_buffer) = self.tracked_buffers.get_mut(&buffer) else {
return;
};
@@ -496,16 +541,70 @@ impl ActionLog {
/// Track a buffer as read by agent, so we can notify the model about user edits.
pub fn buffer_read(&mut self, buffer: Entity, cx: &mut Context) {
+ self.buffer_read_impl(buffer, true, cx);
+ }
+
+ fn buffer_read_impl(
+ &mut self,
+ buffer: Entity,
+ record_file_read_time: bool,
+ cx: &mut Context,
+ ) {
+ if let Some(linked_action_log) = &self.linked_action_log {
+ // We don't want to share read times since the other agent hasn't read it necessarily
+ linked_action_log.update(cx, |log, cx| {
+ log.buffer_read_impl(buffer.clone(), false, cx);
+ });
+ }
+ if record_file_read_time {
+ self.update_file_read_time(&buffer, cx);
+ }
self.track_buffer_internal(buffer, false, cx);
}
/// Mark a buffer as created by agent, so we can refresh it in the context
pub fn buffer_created(&mut self, buffer: Entity, cx: &mut Context) {
+ self.buffer_created_impl(buffer, true, cx);
+ }
+
+ fn buffer_created_impl(
+ &mut self,
+ buffer: Entity,
+ record_file_read_time: bool,
+ cx: &mut Context,
+ ) {
+ if let Some(linked_action_log) = &self.linked_action_log {
+ // We don't want to share read times since the other agent hasn't read it necessarily
+ linked_action_log.update(cx, |log, cx| {
+ log.buffer_created_impl(buffer.clone(), false, cx);
+ });
+ }
+ if record_file_read_time {
+ self.update_file_read_time(&buffer, cx);
+ }
self.track_buffer_internal(buffer, true, cx);
}
/// Mark a buffer as edited by agent, so we can refresh it in the context
pub fn buffer_edited(&mut self, buffer: Entity, cx: &mut Context) {
+ self.buffer_edited_impl(buffer, true, cx);
+ }
+
+ fn buffer_edited_impl(
+ &mut self,
+ buffer: Entity,
+ record_file_read_time: bool,
+ cx: &mut Context,
+ ) {
+ if let Some(linked_action_log) = &self.linked_action_log {
+ // We don't want to share read times since the other agent hasn't read it necessarily
+ linked_action_log.update(cx, |log, cx| {
+ log.buffer_edited_impl(buffer.clone(), false, cx);
+ });
+ }
+ if record_file_read_time {
+ self.update_file_read_time(&buffer, cx);
+ }
let new_version = buffer.read(cx).version();
let tracked_buffer = self.track_buffer_internal(buffer, false, cx);
if let TrackedBufferStatus::Deleted = tracked_buffer.status {
@@ -517,6 +616,9 @@ impl ActionLog {
}
pub fn will_delete_buffer(&mut self, buffer: Entity, cx: &mut Context) {
+ // Ok to propagate file read time removal to linked action log
+ self.remove_file_read_time(&buffer, cx);
+ let has_linked_action_log = self.linked_action_log.is_some();
let tracked_buffer = self.track_buffer_internal(buffer.clone(), false, cx);
match tracked_buffer.status {
TrackedBufferStatus::Created { .. } => {
@@ -524,12 +626,24 @@ impl ActionLog {
cx.notify();
}
TrackedBufferStatus::Modified => {
- buffer.update(cx, |buffer, cx| buffer.set_text("", cx));
tracked_buffer.status = TrackedBufferStatus::Deleted;
- tracked_buffer.schedule_diff_update(ChangeAuthor::Agent, cx);
+ if !has_linked_action_log {
+ buffer.update(cx, |buffer, cx| buffer.set_text("", cx));
+ tracked_buffer.schedule_diff_update(ChangeAuthor::Agent, cx);
+ }
}
+
TrackedBufferStatus::Deleted => {}
}
+
+ if let Some(linked_action_log) = &mut self.linked_action_log {
+ linked_action_log.update(cx, |log, cx| log.will_delete_buffer(buffer.clone(), cx));
+ }
+
+ if has_linked_action_log && let Some(tracked_buffer) = self.tracked_buffers.get(&buffer) {
+ tracked_buffer.schedule_diff_update(ChangeAuthor::Agent, cx);
+ }
+
cx.notify();
}
@@ -914,13 +1028,9 @@ impl ActionLog {
.collect()
}
- /// Returns all tracked buffers for debugging purposes
- #[cfg(any(test, feature = "test-support"))]
- pub fn tracked_buffers_for_debug(
- &self,
- _cx: &App,
- ) -> impl Iterator- , &TrackedBuffer)> {
- self.tracked_buffers.iter()
+ /// Returns the total number of lines added and removed across all unreviewed buffers.
+ pub fn diff_stats(&self, cx: &App) -> DiffStats {
+ DiffStats::all_files(&self.changed_buffers(cx), cx)
}
/// Iterate over buffers changed since last read or edited by the model
@@ -939,6 +1049,46 @@ impl ActionLog {
}
}
+#[derive(Default, Debug, Clone, Copy)]
+pub struct DiffStats {
+ pub lines_added: u32,
+ pub lines_removed: u32,
+}
+
+impl DiffStats {
+ pub fn single_file(buffer: &Buffer, diff: &BufferDiff, cx: &App) -> Self {
+ let mut stats = DiffStats::default();
+ let diff_snapshot = diff.snapshot(cx);
+ let buffer_snapshot = buffer.snapshot();
+ let base_text = diff_snapshot.base_text();
+
+ for hunk in diff_snapshot.hunks(&buffer_snapshot) {
+ let added_rows = hunk.range.end.row.saturating_sub(hunk.range.start.row);
+ stats.lines_added += added_rows;
+
+ let base_start = hunk.diff_base_byte_range.start.to_point(base_text).row;
+ let base_end = hunk.diff_base_byte_range.end.to_point(base_text).row;
+ let removed_rows = base_end.saturating_sub(base_start);
+ stats.lines_removed += removed_rows;
+ }
+
+ stats
+ }
+
+ pub fn all_files(
+ changed_buffers: &BTreeMap, Entity>,
+ cx: &App,
+ ) -> Self {
+ let mut total = DiffStats::default();
+ for (buffer, diff) in changed_buffers {
+ let stats = DiffStats::single_file(buffer.read(cx), diff.read(cx), cx);
+ total.lines_added += stats.lines_added;
+ total.lines_removed += stats.lines_removed;
+ }
+ total
+ }
+}
+
#[derive(Clone)]
pub struct ActionLogTelemetry {
pub agent_telemetry_id: SharedString,
@@ -2634,6 +2784,515 @@ mod tests {
assert!(!action_log.read_with(cx, |log, _| log.has_pending_undo()));
}
+ #[gpui::test]
+ async fn test_linked_action_log_buffer_read(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "hello world"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let parent_log = cx.new(|_| ActionLog::new(project.clone()));
+ let child_log =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
+ });
+
+ // Neither log considers the buffer stale immediately after reading it.
+ let child_stale = cx.read(|cx| {
+ child_log
+ .read(cx)
+ .stale_buffers(cx)
+ .cloned()
+ .collect::>()
+ });
+ let parent_stale = cx.read(|cx| {
+ parent_log
+ .read(cx)
+ .stale_buffers(cx)
+ .cloned()
+ .collect::>()
+ });
+ assert!(child_stale.is_empty());
+ assert!(parent_stale.is_empty());
+
+ // Simulate a user edit after the agent read the file.
+ cx.update(|cx| {
+ buffer.update(cx, |buffer, cx| {
+ buffer.edit([(0..5, "goodbye")], None, cx).unwrap();
+ });
+ });
+ cx.run_until_parked();
+
+ // Both child and parent should see the buffer as stale because both tracked
+ // it at the pre-edit version via buffer_read forwarding.
+ let child_stale = cx.read(|cx| {
+ child_log
+ .read(cx)
+ .stale_buffers(cx)
+ .cloned()
+ .collect::>()
+ });
+ let parent_stale = cx.read(|cx| {
+ parent_log
+ .read(cx)
+ .stale_buffers(cx)
+ .cloned()
+ .collect::>()
+ });
+ assert_eq!(child_stale, vec![buffer.clone()]);
+ assert_eq!(parent_stale, vec![buffer]);
+ }
+
+ #[gpui::test]
+ async fn test_linked_action_log_buffer_edited(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "abc\ndef\nghi"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let parent_log = cx.new(|_| ActionLog::new(project.clone()));
+ let child_log =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
+ buffer.update(cx, |buffer, cx| {
+ buffer
+ .edit([(Point::new(1, 0)..Point::new(1, 3), "DEF")], None, cx)
+ .unwrap();
+ });
+ child_log.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
+ });
+ cx.run_until_parked();
+
+ let expected_hunks = vec![(
+ buffer,
+ vec![HunkStatus {
+ range: Point::new(1, 0)..Point::new(2, 0),
+ diff_status: DiffHunkStatusKind::Modified,
+ old_text: "def\n".into(),
+ }],
+ )];
+ assert_eq!(
+ unreviewed_hunks(&child_log, cx),
+ expected_hunks,
+ "child should track the agent edit"
+ );
+ assert_eq!(
+ unreviewed_hunks(&parent_log, cx),
+ expected_hunks,
+ "parent should also track the agent edit via linked log forwarding"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_linked_action_log_buffer_created(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({})).await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let parent_log = cx.new(|_| ActionLog::new(project.clone()));
+ let child_log =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| {
+ project.find_project_path("dir/new_file", cx)
+ })
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.buffer_created(buffer.clone(), cx));
+ buffer.update(cx, |buffer, cx| buffer.set_text("hello", cx));
+ child_log.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
+ });
+ project
+ .update(cx, |project, cx| project.save_buffer(buffer.clone(), cx))
+ .await
+ .unwrap();
+ cx.run_until_parked();
+
+ let expected_hunks = vec![(
+ buffer.clone(),
+ vec![HunkStatus {
+ range: Point::new(0, 0)..Point::new(0, 5),
+ diff_status: DiffHunkStatusKind::Added,
+ old_text: "".into(),
+ }],
+ )];
+ assert_eq!(
+ unreviewed_hunks(&child_log, cx),
+ expected_hunks,
+ "child should track the created file"
+ );
+ assert_eq!(
+ unreviewed_hunks(&parent_log, cx),
+ expected_hunks,
+ "parent should also track the created file via linked log forwarding"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_linked_action_log_will_delete_buffer(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "hello\n"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let parent_log = cx.new(|_| ActionLog::new(project.clone()));
+ let child_log =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path.clone(), cx))
+ .await
+ .unwrap();
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.will_delete_buffer(buffer.clone(), cx));
+ });
+ project
+ .update(cx, |project, cx| project.delete_file(file_path, false, cx))
+ .unwrap()
+ .await
+ .unwrap();
+ cx.run_until_parked();
+
+ let expected_hunks = vec![(
+ buffer.clone(),
+ vec![HunkStatus {
+ range: Point::new(0, 0)..Point::new(0, 0),
+ diff_status: DiffHunkStatusKind::Deleted,
+ old_text: "hello\n".into(),
+ }],
+ )];
+ assert_eq!(
+ unreviewed_hunks(&child_log, cx),
+ expected_hunks,
+ "child should track the deleted file"
+ );
+ assert_eq!(
+ unreviewed_hunks(&parent_log, cx),
+ expected_hunks,
+ "parent should also track the deleted file via linked log forwarding"
+ );
+ }
+
+ /// Simulates the subagent scenario: two child logs linked to the same parent, each
+ /// editing a different file. The parent accumulates all edits while each child
+ /// only sees its own.
+ #[gpui::test]
+ async fn test_linked_action_log_independent_tracking(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(
+ path!("/dir"),
+ json!({
+ "file_a": "content of a",
+ "file_b": "content of b",
+ }),
+ )
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let parent_log = cx.new(|_| ActionLog::new(project.clone()));
+ let child_log_1 =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+ let child_log_2 =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+
+ let file_a_path = project
+ .read_with(cx, |project, cx| {
+ project.find_project_path("dir/file_a", cx)
+ })
+ .unwrap();
+ let file_b_path = project
+ .read_with(cx, |project, cx| {
+ project.find_project_path("dir/file_b", cx)
+ })
+ .unwrap();
+ let buffer_a = project
+ .update(cx, |project, cx| project.open_buffer(file_a_path, cx))
+ .await
+ .unwrap();
+ let buffer_b = project
+ .update(cx, |project, cx| project.open_buffer(file_b_path, cx))
+ .await
+ .unwrap();
+
+ cx.update(|cx| {
+ child_log_1.update(cx, |log, cx| log.buffer_read(buffer_a.clone(), cx));
+ buffer_a.update(cx, |buffer, cx| {
+ buffer.edit([(0..0, "MODIFIED: ")], None, cx).unwrap();
+ });
+ child_log_1.update(cx, |log, cx| log.buffer_edited(buffer_a.clone(), cx));
+
+ child_log_2.update(cx, |log, cx| log.buffer_read(buffer_b.clone(), cx));
+ buffer_b.update(cx, |buffer, cx| {
+ buffer.edit([(0..0, "MODIFIED: ")], None, cx).unwrap();
+ });
+ child_log_2.update(cx, |log, cx| log.buffer_edited(buffer_b.clone(), cx));
+ });
+ cx.run_until_parked();
+
+ let child_1_changed: Vec<_> = cx.read(|cx| {
+ child_log_1
+ .read(cx)
+ .changed_buffers(cx)
+ .into_keys()
+ .collect()
+ });
+ let child_2_changed: Vec<_> = cx.read(|cx| {
+ child_log_2
+ .read(cx)
+ .changed_buffers(cx)
+ .into_keys()
+ .collect()
+ });
+ let parent_changed: Vec<_> = cx.read(|cx| {
+ parent_log
+ .read(cx)
+ .changed_buffers(cx)
+ .into_keys()
+ .collect()
+ });
+
+ assert_eq!(
+ child_1_changed,
+ vec![buffer_a.clone()],
+ "child 1 should only track file_a"
+ );
+ assert_eq!(
+ child_2_changed,
+ vec![buffer_b.clone()],
+ "child 2 should only track file_b"
+ );
+ assert_eq!(parent_changed.len(), 2, "parent should track both files");
+ assert!(
+ parent_changed.contains(&buffer_a) && parent_changed.contains(&buffer_b),
+ "parent should contain both buffer_a and buffer_b"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_file_read_time_recorded_on_buffer_read(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "hello world"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let action_log = cx.new(|_| ActionLog::new(project.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ let abs_path = PathBuf::from(path!("/dir/file"));
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "file_read_time should be None before buffer_read"
+ );
+
+ cx.update(|cx| {
+ action_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
+ });
+
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_some()),
+ "file_read_time should be recorded after buffer_read"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_file_read_time_recorded_on_buffer_edited(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "hello world"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let action_log = cx.new(|_| ActionLog::new(project.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ let abs_path = PathBuf::from(path!("/dir/file"));
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "file_read_time should be None before buffer_edited"
+ );
+
+ cx.update(|cx| {
+ action_log.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
+ });
+
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_some()),
+ "file_read_time should be recorded after buffer_edited"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_file_read_time_recorded_on_buffer_created(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "existing content"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let action_log = cx.new(|_| ActionLog::new(project.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ let abs_path = PathBuf::from(path!("/dir/file"));
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "file_read_time should be None before buffer_created"
+ );
+
+ cx.update(|cx| {
+ action_log.update(cx, |log, cx| log.buffer_created(buffer.clone(), cx));
+ });
+
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_some()),
+ "file_read_time should be recorded after buffer_created"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_file_read_time_removed_on_delete(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "hello world"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let action_log = cx.new(|_| ActionLog::new(project.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ let abs_path = PathBuf::from(path!("/dir/file"));
+
+ cx.update(|cx| {
+ action_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
+ });
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_some()),
+ "file_read_time should exist after buffer_read"
+ );
+
+ cx.update(|cx| {
+ action_log.update(cx, |log, cx| log.will_delete_buffer(buffer.clone(), cx));
+ });
+ assert!(
+ action_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "file_read_time should be removed after will_delete_buffer"
+ );
+ }
+
+ #[gpui::test]
+ async fn test_file_read_time_not_forwarded_to_linked_action_log(cx: &mut TestAppContext) {
+ init_test(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ fs.insert_tree(path!("/dir"), json!({"file": "hello world"}))
+ .await;
+ let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
+ let parent_log = cx.new(|_| ActionLog::new(project.clone()));
+ let child_log =
+ cx.new(|_| ActionLog::new(project.clone()).with_linked_action_log(parent_log.clone()));
+
+ let file_path = project
+ .read_with(cx, |project, cx| project.find_project_path("dir/file", cx))
+ .unwrap();
+ let buffer = project
+ .update(cx, |project, cx| project.open_buffer(file_path, cx))
+ .await
+ .unwrap();
+
+ let abs_path = PathBuf::from(path!("/dir/file"));
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
+ });
+ assert!(
+ child_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_some()),
+ "child should record file_read_time on buffer_read"
+ );
+ assert!(
+ parent_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "parent should NOT get file_read_time from child's buffer_read"
+ );
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.buffer_edited(buffer.clone(), cx));
+ });
+ assert!(
+ parent_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "parent should NOT get file_read_time from child's buffer_edited"
+ );
+
+ cx.update(|cx| {
+ child_log.update(cx, |log, cx| log.buffer_created(buffer.clone(), cx));
+ });
+ assert!(
+ parent_log.read_with(cx, |log, _| log.file_read_time(&abs_path).is_none()),
+ "parent should NOT get file_read_time from child's buffer_created"
+ );
+ }
+
#[derive(Debug, PartialEq)]
struct HunkStatus {
range: Range,
diff --git a/crates/activity_indicator/Cargo.toml b/crates/activity_indicator/Cargo.toml
index 99ae5b5b077a14c0909737d64935220698a007c7..ce53f23365d57666e25cac434935514fc4bd7e3f 100644
--- a/crates/activity_indicator/Cargo.toml
+++ b/crates/activity_indicator/Cargo.toml
@@ -30,4 +30,4 @@ workspace.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
-release_channel.workspace = true
+
diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml
index 9f563cf0b1b009a496d36a6f090b0f4b476433a7..fe2089d94dc2e3fc812f6cbe39c16c5cadc1a1f5 100644
--- a/crates/agent/Cargo.toml
+++ b/crates/agent/Cargo.toml
@@ -100,9 +100,9 @@ rand.workspace = true
reqwest_client.workspace = true
settings = { workspace = true, "features" = ["test-support"] }
tempfile.workspace = true
-terminal = { workspace = true, "features" = ["test-support"] }
+
theme = { workspace = true, "features" = ["test-support"] }
-tree-sitter-rust.workspace = true
+
unindent = { workspace = true }
-worktree = { workspace = true, "features" = ["test-support"] }
+
zlog.workspace = true
diff --git a/crates/agent/src/agent.rs b/crates/agent/src/agent.rs
index 759c6e3b9c8c228a6ae6bea5330819b97200b603..2ac341dc997b016f3e723fad99a4a57007510c52 100644
--- a/crates/agent/src/agent.rs
+++ b/crates/agent/src/agent.rs
@@ -14,6 +14,7 @@ mod tools;
use context_server::ContextServerId;
pub use db::*;
+use itertools::Itertools;
pub use native_agent_server::NativeAgentServer;
pub use pattern_extraction::*;
pub use shell_command_parser::extract_commands;
@@ -36,7 +37,8 @@ use futures::channel::{mpsc, oneshot};
use futures::future::Shared;
use futures::{FutureExt as _, StreamExt as _, future};
use gpui::{
- App, AppContext, AsyncApp, Context, Entity, SharedString, Subscription, Task, WeakEntity,
+ App, AppContext, AsyncApp, Context, Entity, EntityId, SharedString, Subscription, Task,
+ WeakEntity,
};
use language_model::{IconOrSvg, LanguageModel, LanguageModelProvider, LanguageModelRegistry};
use project::{Project, ProjectItem, ProjectPath, Worktree};
@@ -51,6 +53,7 @@ use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;
use util::ResultExt;
+use util::path_list::PathList;
use util::rel_path::RelPath;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
@@ -63,12 +66,22 @@ pub struct RulesLoadingError {
pub message: SharedString,
}
+struct ProjectState {
+ project: Entity,
+ project_context: Entity,
+ project_context_needs_refresh: watch::Sender<()>,
+ _maintain_project_context: Task>,
+ context_server_registry: Entity,
+ _subscriptions: Vec,
+}
+
/// Holds both the internal Thread and the AcpThread for a session
struct Session {
/// The internal thread that processes messages
thread: Entity,
/// The ACP thread that handles protocol communication
acp_thread: Entity,
+ project_id: EntityId,
pending_save: Task<()>,
_subscriptions: Vec,
}
@@ -233,79 +246,47 @@ pub struct NativeAgent {
/// Session ID -> Session mapping
sessions: HashMap,
thread_store: Entity,
- /// Shared project context for all threads
- project_context: Entity,
- project_context_needs_refresh: watch::Sender<()>,
- _maintain_project_context: Task>,
- context_server_registry: Entity,
+ /// Project-specific state keyed by project EntityId
+ projects: HashMap,
/// Shared templates for all threads
templates: Arc,
/// Cached model information
models: LanguageModels,
- project: Entity,
prompt_store: Option>,
fs: Arc,
_subscriptions: Vec,
}
impl NativeAgent {
- pub async fn new(
- project: Entity,
+ pub fn new(
thread_store: Entity,
templates: Arc,
prompt_store: Option>,
fs: Arc,
- cx: &mut AsyncApp,
- ) -> Result> {
+ cx: &mut App,
+ ) -> Entity {
log::debug!("Creating new NativeAgent");
- let project_context = cx
- .update(|cx| Self::build_project_context(&project, prompt_store.as_ref(), cx))
- .await;
-
- Ok(cx.new(|cx| {
- let context_server_store = project.read(cx).context_server_store();
- let context_server_registry =
- cx.new(|cx| ContextServerRegistry::new(context_server_store.clone(), cx));
-
- let mut subscriptions = vec![
- cx.subscribe(&project, Self::handle_project_event),
- cx.subscribe(
- &LanguageModelRegistry::global(cx),
- Self::handle_models_updated_event,
- ),
- cx.subscribe(
- &context_server_store,
- Self::handle_context_server_store_updated,
- ),
- cx.subscribe(
- &context_server_registry,
- Self::handle_context_server_registry_event,
- ),
- ];
+ cx.new(|cx| {
+ let mut subscriptions = vec![cx.subscribe(
+ &LanguageModelRegistry::global(cx),
+ Self::handle_models_updated_event,
+ )];
if let Some(prompt_store) = prompt_store.as_ref() {
subscriptions.push(cx.subscribe(prompt_store, Self::handle_prompts_updated_event))
}
- let (project_context_needs_refresh_tx, project_context_needs_refresh_rx) =
- watch::channel(());
Self {
sessions: HashMap::default(),
thread_store,
- project_context: cx.new(|_| project_context),
- project_context_needs_refresh: project_context_needs_refresh_tx,
- _maintain_project_context: cx.spawn(async move |this, cx| {
- Self::maintain_project_context(this, project_context_needs_refresh_rx, cx).await
- }),
- context_server_registry,
+ projects: HashMap::default(),
templates,
models: LanguageModels::new(cx),
- project,
prompt_store,
fs,
_subscriptions: subscriptions,
}
- }))
+ })
}
fn new_session(
@@ -313,10 +294,10 @@ impl NativeAgent {
project: Entity,
cx: &mut Context,
) -> Entity {
- // Create Thread
- // Fetch default model from registry settings
+ let project_id = self.get_or_create_project_state(&project, cx);
+ let project_state = &self.projects[&project_id];
+
let registry = LanguageModelRegistry::read_global(cx);
- // Log available models for debugging
let available_count = registry.available_models(cx).count();
log::debug!("Total available models: {}", available_count);
@@ -326,21 +307,22 @@ impl NativeAgent {
});
let thread = cx.new(|cx| {
Thread::new(
- project.clone(),
- self.project_context.clone(),
- self.context_server_registry.clone(),
+ project,
+ project_state.project_context.clone(),
+ project_state.context_server_registry.clone(),
self.templates.clone(),
default_model,
cx,
)
});
- self.register_session(thread, cx)
+ self.register_session(thread, project_id, cx)
}
fn register_session(
&mut self,
thread_handle: Entity,
+ project_id: EntityId,
cx: &mut Context,
) -> Entity {
let connection = Rc::new(NativeAgentConnection(cx.entity()));
@@ -349,31 +331,41 @@ impl NativeAgent {
let session_id = thread.id().clone();
let parent_session_id = thread.parent_thread_id();
let title = thread.title();
+ let draft_prompt = thread.draft_prompt().map(Vec::from);
+ let scroll_position = thread.ui_scroll_position();
+ let token_usage = thread.latest_token_usage();
let project = thread.project.clone();
let action_log = thread.action_log.clone();
let prompt_capabilities_rx = thread.prompt_capabilities_rx.clone();
let acp_thread = cx.new(|cx| {
- acp_thread::AcpThread::new(
+ let mut acp_thread = acp_thread::AcpThread::new(
parent_session_id,
title,
+ None,
connection,
project.clone(),
action_log.clone(),
session_id.clone(),
prompt_capabilities_rx,
cx,
- )
+ );
+ acp_thread.set_draft_prompt(draft_prompt);
+ acp_thread.set_ui_scroll_position(scroll_position);
+ acp_thread.update_token_usage(token_usage, cx);
+ acp_thread
});
let registry = LanguageModelRegistry::read_global(cx);
let summarization_model = registry.thread_summary_model().map(|c| c.model);
let weak = cx.weak_entity();
+ let weak_thread = thread_handle.downgrade();
thread_handle.update(cx, |thread, cx| {
thread.set_summarization_model(summarization_model, cx);
thread.add_default_tools(
Rc::new(NativeThreadEnvironment {
acp_thread: acp_thread.downgrade(),
+ thread: weak_thread,
agent: weak,
}) as _,
cx,
@@ -393,12 +385,13 @@ impl NativeAgent {
Session {
thread: thread_handle,
acp_thread: acp_thread.clone(),
+ project_id,
_subscriptions: subscriptions,
pending_save: Task::ready(()),
},
);
- self.update_available_commands(cx);
+ self.update_available_commands_for_project(project_id, cx);
acp_thread
}
@@ -407,19 +400,102 @@ impl NativeAgent {
&self.models
}
+ fn get_or_create_project_state(
+ &mut self,
+ project: &Entity,
+ cx: &mut Context,
+ ) -> EntityId {
+ let project_id = project.entity_id();
+ if self.projects.contains_key(&project_id) {
+ return project_id;
+ }
+
+ let project_context = cx.new(|_| ProjectContext::new(vec![], vec![]));
+ self.register_project_with_initial_context(project.clone(), project_context, cx);
+ if let Some(state) = self.projects.get_mut(&project_id) {
+ state.project_context_needs_refresh.send(()).ok();
+ }
+ project_id
+ }
+
+ fn register_project_with_initial_context(
+ &mut self,
+ project: Entity,
+ project_context: Entity,
+ cx: &mut Context,
+ ) {
+ let project_id = project.entity_id();
+
+ let context_server_store = project.read(cx).context_server_store();
+ let context_server_registry =
+ cx.new(|cx| ContextServerRegistry::new(context_server_store.clone(), cx));
+
+ let subscriptions = vec![
+ cx.subscribe(&project, Self::handle_project_event),
+ cx.subscribe(
+ &context_server_store,
+ Self::handle_context_server_store_updated,
+ ),
+ cx.subscribe(
+ &context_server_registry,
+ Self::handle_context_server_registry_event,
+ ),
+ ];
+
+ let (project_context_needs_refresh_tx, project_context_needs_refresh_rx) =
+ watch::channel(());
+
+ self.projects.insert(
+ project_id,
+ ProjectState {
+ project,
+ project_context,
+ project_context_needs_refresh: project_context_needs_refresh_tx,
+ _maintain_project_context: cx.spawn(async move |this, cx| {
+ Self::maintain_project_context(
+ this,
+ project_id,
+ project_context_needs_refresh_rx,
+ cx,
+ )
+ .await
+ }),
+ context_server_registry,
+ _subscriptions: subscriptions,
+ },
+ );
+ }
+
+ fn session_project_state(&self, session_id: &acp::SessionId) -> Option<&ProjectState> {
+ self.sessions
+ .get(session_id)
+ .and_then(|session| self.projects.get(&session.project_id))
+ }
+
async fn maintain_project_context(
this: WeakEntity,
+ project_id: EntityId,
mut needs_refresh: watch::Receiver<()>,
cx: &mut AsyncApp,
) -> Result<()> {
while needs_refresh.changed().await.is_ok() {
let project_context = this
.update(cx, |this, cx| {
- Self::build_project_context(&this.project, this.prompt_store.as_ref(), cx)
- })?
+ let state = this
+ .projects
+ .get(&project_id)
+ .context("project state not found")?;
+ anyhow::Ok(Self::build_project_context(
+ &state.project,
+ this.prompt_store.as_ref(),
+ cx,
+ ))
+ })??
.await;
this.update(cx, |this, cx| {
- this.project_context = cx.new(|_| project_context);
+ if let Some(state) = this.projects.get_mut(&project_id) {
+ state.project_context = cx.new(|_| project_context);
+ }
})?;
}
@@ -608,13 +684,17 @@ impl NativeAgent {
fn handle_project_event(
&mut self,
- _project: Entity,
+ project: Entity,
event: &project::Event,
_cx: &mut Context,
) {
+ let project_id = project.entity_id();
+ let Some(state) = self.projects.get_mut(&project_id) else {
+ return;
+ };
match event {
project::Event::WorktreeAdded(_) | project::Event::WorktreeRemoved(_) => {
- self.project_context_needs_refresh.send(()).ok();
+ state.project_context_needs_refresh.send(()).ok();
}
project::Event::WorktreeUpdatedEntries(_, items) => {
if items.iter().any(|(path, _, _)| {
@@ -622,7 +702,7 @@ impl NativeAgent {
.iter()
.any(|name| path.as_ref() == RelPath::unix(name).unwrap())
}) {
- self.project_context_needs_refresh.send(()).ok();
+ state.project_context_needs_refresh.send(()).ok();
}
}
_ => {}
@@ -635,7 +715,9 @@ impl NativeAgent {
_event: &prompt_store::PromptsUpdatedEvent,
_cx: &mut Context,
) {
- self.project_context_needs_refresh.send(()).ok();
+ for state in self.projects.values_mut() {
+ state.project_context_needs_refresh.send(()).ok();
+ }
}
fn handle_models_updated_event(
@@ -665,30 +747,52 @@ impl NativeAgent {
fn handle_context_server_store_updated(
&mut self,
- _store: Entity,
+ store: Entity,
_event: &project::context_server_store::ServerStatusChangedEvent,
cx: &mut Context,
) {
- self.update_available_commands(cx);
+ let project_id = self.projects.iter().find_map(|(id, state)| {
+ if *state.context_server_registry.read(cx).server_store() == store {
+ Some(*id)
+ } else {
+ None
+ }
+ });
+ if let Some(project_id) = project_id {
+ self.update_available_commands_for_project(project_id, cx);
+ }
}
fn handle_context_server_registry_event(
&mut self,
- _registry: Entity,
+ registry: Entity,
event: &ContextServerRegistryEvent,
cx: &mut Context,
) {
match event {
ContextServerRegistryEvent::ToolsChanged => {}
ContextServerRegistryEvent::PromptsChanged => {
- self.update_available_commands(cx);
+ let project_id = self.projects.iter().find_map(|(id, state)| {
+ if state.context_server_registry == registry {
+ Some(*id)
+ } else {
+ None
+ }
+ });
+ if let Some(project_id) = project_id {
+ self.update_available_commands_for_project(project_id, cx);
+ }
}
}
}
- fn update_available_commands(&self, cx: &mut Context) {
- let available_commands = self.build_available_commands(cx);
+ fn update_available_commands_for_project(&self, project_id: EntityId, cx: &mut Context) {
+ let available_commands =
+ Self::build_available_commands_for_project(self.projects.get(&project_id), cx);
for session in self.sessions.values() {
+ if session.project_id != project_id {
+ continue;
+ }
session.acp_thread.update(cx, |thread, cx| {
thread
.handle_session_update(
@@ -702,8 +806,14 @@ impl NativeAgent {
}
}
- fn build_available_commands(&self, cx: &App) -> Vec {
- let registry = self.context_server_registry.read(cx);
+ fn build_available_commands_for_project(
+ project_state: Option<&ProjectState>,
+ cx: &App,
+ ) -> Vec {
+ let Some(state) = project_state else {
+ return vec![];
+ };
+ let registry = state.context_server_registry.read(cx);
let mut prompt_name_counts: HashMap<&str, usize> = HashMap::default();
for context_server_prompt in registry.prompts() {
@@ -757,6 +867,7 @@ impl NativeAgent {
pub fn load_thread(
&mut self,
id: acp::SessionId,
+ project: Entity,
cx: &mut Context,
) -> Task>> {
let database_future = ThreadsDatabase::connect(cx);
@@ -768,41 +879,49 @@ impl NativeAgent {
.with_context(|| format!("no thread found with ID: {id:?}"))?;
this.update(cx, |this, cx| {
+ let project_id = this.get_or_create_project_state(&project, cx);
+ let project_state = this
+ .projects
+ .get(&project_id)
+ .context("project state not found")?;
let summarization_model = LanguageModelRegistry::read_global(cx)
.thread_summary_model()
.map(|c| c.model);
- cx.new(|cx| {
+ Ok(cx.new(|cx| {
let mut thread = Thread::from_db(
id.clone(),
db_thread,
- this.project.clone(),
- this.project_context.clone(),
- this.context_server_registry.clone(),
+ project_state.project.clone(),
+ project_state.project_context.clone(),
+ project_state.context_server_registry.clone(),
this.templates.clone(),
cx,
);
thread.set_summarization_model(summarization_model, cx);
thread
- })
- })
+ }))
+ })?
})
}
pub fn open_thread(
&mut self,
id: acp::SessionId,
+ project: Entity,
cx: &mut Context,
) -> Task>> {
if let Some(session) = self.sessions.get(&id) {
return Task::ready(Ok(session.acp_thread.clone()));
}
- let task = self.load_thread(id, cx);
+ let task = self.load_thread(id, project.clone(), cx);
cx.spawn(async move |this, cx| {
let thread = task.await?;
- let acp_thread =
- this.update(cx, |this, cx| this.register_session(thread.clone(), cx))?;
+ let acp_thread = this.update(cx, |this, cx| {
+ let project_id = this.get_or_create_project_state(&project, cx);
+ this.register_session(thread.clone(), project_id, cx)
+ })?;
let events = thread.update(cx, |thread, cx| thread.replay(cx));
cx.update(|cx| {
NativeAgentConnection::handle_thread_events(events, acp_thread.downgrade(), cx)
@@ -815,9 +934,10 @@ impl NativeAgent {
pub fn thread_summary(
&mut self,
id: acp::SessionId,
+ project: Entity,
cx: &mut Context,
) -> Task> {
- let thread = self.open_thread(id.clone(), cx);
+ let thread = self.open_thread(id.clone(), project, cx);
cx.spawn(async move |this, cx| {
let acp_thread = thread.await?;
let result = this
@@ -840,19 +960,41 @@ impl NativeAgent {
return;
}
- let database_future = ThreadsDatabase::connect(cx);
- let (id, db_thread) =
- thread.update(cx, |thread, cx| (thread.id().clone(), thread.to_db(cx)));
+ let id = thread.read(cx).id().clone();
let Some(session) = self.sessions.get_mut(&id) else {
return;
};
+
+ let project_id = session.project_id;
+ let Some(state) = self.projects.get(&project_id) else {
+ return;
+ };
+
+ let folder_paths = PathList::new(
+ &state
+ .project
+ .read(cx)
+ .visible_worktrees(cx)
+ .map(|worktree| worktree.read(cx).abs_path().to_path_buf())
+ .collect::>(),
+ );
+
+ let draft_prompt = session.acp_thread.read(cx).draft_prompt().map(Vec::from);
+ let database_future = ThreadsDatabase::connect(cx);
+ let db_thread = thread.update(cx, |thread, cx| {
+ thread.set_draft_prompt(draft_prompt);
+ thread.to_db(cx)
+ });
let thread_store = self.thread_store.clone();
session.pending_save = cx.spawn(async move |_, cx| {
let Some(database) = database_future.await.map_err(|err| anyhow!(err)).log_err() else {
return;
};
let db_thread = db_thread.await;
- database.save_thread(id, db_thread).await.log_err();
+ database
+ .save_thread(id, db_thread, folder_paths)
+ .await
+ .log_err();
thread_store.update(cx, |store, cx| store.reload(cx));
});
}
@@ -860,15 +1002,22 @@ impl NativeAgent {
fn send_mcp_prompt(
&self,
message_id: UserMessageId,
- session_id: agent_client_protocol::SessionId,
+ session_id: acp::SessionId,
prompt_name: String,
server_id: ContextServerId,
arguments: HashMap,
original_content: Vec,
cx: &mut Context,
) -> Task> {
- let server_store = self.context_server_registry.read(cx).server_store().clone();
- let path_style = self.project.read(cx).path_style(cx);
+ let Some(state) = self.session_project_state(&session_id) else {
+ return Task::ready(Err(anyhow!("Project state not found for session")));
+ };
+ let server_store = state
+ .context_server_registry
+ .read(cx)
+ .server_store()
+ .clone();
+ let path_style = state.project.read(cx).path_style(cx);
cx.spawn(async move |this, cx| {
let prompt =
@@ -967,8 +1116,14 @@ impl NativeAgentConnection {
.map(|session| session.thread.clone())
}
- pub fn load_thread(&self, id: acp::SessionId, cx: &mut App) -> Task>> {
- self.0.update(cx, |this, cx| this.load_thread(id, cx))
+ pub fn load_thread(
+ &self,
+ id: acp::SessionId,
+ project: Entity,
+ cx: &mut App,
+ ) -> Task>> {
+ self.0
+ .update(cx, |this, cx| this.load_thread(id, project, cx))
}
fn run_turn(
@@ -1249,22 +1404,35 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
fn load_session(
self: Rc,
- session: AgentSessionInfo,
- _project: Entity,
+ session_id: acp::SessionId,
+ project: Entity,
_cwd: &Path,
+ _title: Option,
cx: &mut App,
) -> Task>> {
self.0
- .update(cx, |agent, cx| agent.open_thread(session.session_id, cx))
+ .update(cx, |agent, cx| agent.open_thread(session_id, project, cx))
}
fn supports_close_session(&self) -> bool {
true
}
- fn close_session(&self, session_id: &acp::SessionId, cx: &mut App) -> Task> {
+ fn close_session(
+ self: Rc,
+ session_id: &acp::SessionId,
+ cx: &mut App,
+ ) -> Task> {
self.0.update(cx, |agent, _cx| {
+ let project_id = agent.sessions.get(session_id).map(|s| s.project_id);
agent.sessions.remove(session_id);
+
+ if let Some(project_id) = project_id {
+ let has_remaining = agent.sessions.values().any(|s| s.project_id == project_id);
+ if !has_remaining {
+ agent.projects.remove(&project_id);
+ }
+ }
});
Task::ready(Ok(()))
}
@@ -1295,8 +1463,12 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
log::info!("Received prompt request for session: {}", session_id);
log::debug!("Prompt blocks count: {}", params.prompt.len());
+ let Some(project_state) = self.0.read(cx).session_project_state(&session_id) else {
+ return Task::ready(Err(anyhow::anyhow!("Session not found")));
+ };
+
if let Some(parsed_command) = Command::parse(¶ms.prompt) {
- let registry = self.0.read(cx).context_server_registry.read(cx);
+ let registry = project_state.context_server_registry.read(cx);
let explicit_server_id = parsed_command
.explicit_server_id
@@ -1332,10 +1504,10 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
cx,
)
});
- };
+ }
};
- let path_style = self.0.read(cx).project.read(cx).path_style(cx);
+ let path_style = project_state.project.read(cx).path_style(cx);
self.run_turn(session_id, cx, move |thread, cx| {
let content: Vec = params
@@ -1376,7 +1548,7 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
fn truncate(
&self,
- session_id: &agent_client_protocol::SessionId,
+ session_id: &acp::SessionId,
cx: &App,
) -> Option> {
self.0.read_with(cx, |agent, _cx| {
@@ -1462,16 +1634,6 @@ impl NativeAgentSessionList {
}
}
- fn to_session_info(entry: DbThreadMetadata) -> AgentSessionInfo {
- AgentSessionInfo {
- session_id: entry.id,
- cwd: None,
- title: Some(entry.title),
- updated_at: Some(entry.updated_at),
- meta: None,
- }
- }
-
pub fn thread_store(&self) -> &Entity {
&self.thread_store
}
@@ -1487,7 +1649,7 @@ impl AgentSessionList for NativeAgentSessionList {
.thread_store
.read(cx)
.entries()
- .map(Self::to_session_info)
+ .map(|entry| AgentSessionInfo::from(&entry))
.collect();
Task::ready(Ok(AgentSessionListResponse::new(sessions)))
}
@@ -1576,19 +1738,22 @@ impl acp_thread::AgentSessionSetTitle for NativeAgentSessionSetTitle {
pub struct NativeThreadEnvironment {
agent: WeakEntity,
+ thread: WeakEntity,
acp_thread: WeakEntity,
}
impl NativeThreadEnvironment {
pub(crate) fn create_subagent_thread(
- agent: WeakEntity,
- parent_thread_entity: Entity,
+ &self,
label: String,
- initial_prompt: String,
cx: &mut App,
) -> Result> {
+ let Some(parent_thread_entity) = self.thread.upgrade() else {
+ anyhow::bail!("Parent thread no longer exists".to_string());
+ };
let parent_thread = parent_thread_entity.read(cx);
let current_depth = parent_thread.depth();
+ let parent_session_id = parent_thread.id().clone();
if current_depth >= MAX_SUBAGENT_DEPTH {
return Err(anyhow!(
@@ -1605,28 +1770,36 @@ impl NativeThreadEnvironment {
let session_id = subagent_thread.read(cx).id().clone();
- let acp_thread = agent.update(cx, |agent, cx| {
- agent.register_session(subagent_thread.clone(), cx)
- })?;
+ let acp_thread = self
+ .agent
+ .update(cx, |agent, cx| -> Result> {
+ let project_id = agent
+ .sessions
+ .get(&parent_session_id)
+ .map(|s| s.project_id)
+ .context("parent session not found")?;
+ Ok(agent.register_session(subagent_thread.clone(), project_id, cx))
+ })??;
- Self::prompt_subagent(
- session_id,
- subagent_thread,
- acp_thread,
- parent_thread_entity,
- initial_prompt,
- cx,
- )
+ let depth = current_depth + 1;
+
+ telemetry::event!(
+ "Subagent Started",
+ session = parent_thread_entity.read(cx).id().to_string(),
+ subagent_session = session_id.to_string(),
+ depth,
+ is_resumed = false,
+ );
+
+ self.prompt_subagent(session_id, subagent_thread, acp_thread)
}
pub(crate) fn resume_subagent_thread(
- agent: WeakEntity,
- parent_thread_entity: Entity,
+ &self,
session_id: acp::SessionId,
- follow_up_prompt: String,
cx: &mut App,
) -> Result> {
- let (subagent_thread, acp_thread) = agent.update(cx, |agent, _cx| {
+ let (subagent_thread, acp_thread) = self.agent.update(cx, |agent, _cx| {
let session = agent
.sessions
.get(&session_id)
@@ -1634,31 +1807,35 @@ impl NativeThreadEnvironment {
anyhow::Ok((session.thread.clone(), session.acp_thread.clone()))
})??;
- Self::prompt_subagent(
- session_id,
- subagent_thread,
- acp_thread,
- parent_thread_entity,
- follow_up_prompt,
- cx,
- )
+ let depth = subagent_thread.read(cx).depth();
+
+ if let Some(parent_thread_entity) = self.thread.upgrade() {
+ telemetry::event!(
+ "Subagent Started",
+ session = parent_thread_entity.read(cx).id().to_string(),
+ subagent_session = session_id.to_string(),
+ depth,
+ is_resumed = true,
+ );
+ }
+
+ self.prompt_subagent(session_id, subagent_thread, acp_thread)
}
fn prompt_subagent(
+ &self,
session_id: acp::SessionId,
subagent_thread: Entity,
acp_thread: Entity,
- parent_thread_entity: Entity,
- prompt: String,
- cx: &mut App,
) -> Result> {
+ let Some(parent_thread_entity) = self.thread.upgrade() else {
+ anyhow::bail!("Parent thread no longer exists".to_string());
+ };
Ok(Rc::new(NativeSubagentHandle::new(
session_id,
subagent_thread,
acp_thread,
parent_thread_entity,
- prompt,
- cx,
)) as _)
}
}
@@ -1697,36 +1874,16 @@ impl ThreadEnvironment for NativeThreadEnvironment {
})
}
- fn create_subagent(
- &self,
- parent_thread_entity: Entity,
- label: String,
- initial_prompt: String,
- cx: &mut App,
- ) -> Result> {
- Self::create_subagent_thread(
- self.agent.clone(),
- parent_thread_entity,
- label,
- initial_prompt,
- cx,
- )
+ fn create_subagent(&self, label: String, cx: &mut App) -> Result> {
+ self.create_subagent_thread(label, cx)
}
fn resume_subagent(
&self,
- parent_thread_entity: Entity,
session_id: acp::SessionId,
- follow_up_prompt: String,
cx: &mut App,
) -> Result> {
- Self::resume_subagent_thread(
- self.agent.clone(),
- parent_thread_entity,
- session_id,
- follow_up_prompt,
- cx,
- )
+ self.resume_subagent_thread(session_id, cx)
}
}
@@ -1742,8 +1899,7 @@ pub struct NativeSubagentHandle {
session_id: acp::SessionId,
parent_thread: WeakEntity,
subagent_thread: Entity,
- wait_for_prompt_to_complete: Shared>,
- _subscription: Subscription,
+ acp_thread: Entity,
}
impl NativeSubagentHandle {
@@ -1752,71 +1908,12 @@ impl NativeSubagentHandle {
subagent_thread: Entity,
acp_thread: Entity,
parent_thread_entity: Entity,
- prompt: String,
- cx: &mut App,
) -> Self {
- let ratio_before_prompt = subagent_thread
- .read(cx)
- .latest_token_usage()
- .map(|usage| usage.ratio());
-
- parent_thread_entity.update(cx, |parent_thread, _cx| {
- parent_thread.register_running_subagent(subagent_thread.downgrade())
- });
-
- let task = acp_thread.update(cx, |acp_thread, cx| {
- acp_thread.send(vec![prompt.into()], cx)
- });
-
- let (token_limit_tx, token_limit_rx) = oneshot::channel::<()>();
- let mut token_limit_tx = Some(token_limit_tx);
-
- let subscription = cx.subscribe(
- &subagent_thread,
- move |_thread, event: &TokenUsageUpdated, _cx| {
- if let Some(usage) = &event.0 {
- let old_ratio = ratio_before_prompt
- .clone()
- .unwrap_or(TokenUsageRatio::Normal);
- let new_ratio = usage.ratio();
- if old_ratio == TokenUsageRatio::Normal && new_ratio == TokenUsageRatio::Warning
- {
- if let Some(tx) = token_limit_tx.take() {
- tx.send(()).ok();
- }
- }
- }
- },
- );
-
- let wait_for_prompt_to_complete = cx
- .background_spawn(async move {
- futures::select! {
- response = task.fuse() => match response {
- Ok(Some(response)) =>{
- match response.stop_reason {
- acp::StopReason::Cancelled => SubagentPromptResult::Cancelled,
- acp::StopReason::MaxTokens => SubagentPromptResult::Error("The agent reached the maximum number of tokens.".into()),
- acp::StopReason::MaxTurnRequests => SubagentPromptResult::Error("The agent reached the maximum number of allowed requests between user turns. Try prompting again.".into()),
- acp::StopReason::Refusal => SubagentPromptResult::Error("The agent refused to process that prompt. Try again.".into()),
- acp::StopReason::EndTurn | _ => SubagentPromptResult::Completed,
- }
-
- }
- Ok(None) => SubagentPromptResult::Error("No response from the agent. You can try messaging again.".into()),
- Err(error) => SubagentPromptResult::Error(error.to_string()),
- },
- _ = token_limit_rx.fuse() => SubagentPromptResult::ContextWindowWarning,
- }
- })
- .shared();
-
NativeSubagentHandle {
session_id,
subagent_thread,
parent_thread: parent_thread_entity.downgrade(),
- wait_for_prompt_to_complete,
- _subscription: subscription,
+ acp_thread,
}
}
}
@@ -1826,22 +1923,100 @@ impl SubagentHandle for NativeSubagentHandle {
self.session_id.clone()
}
- fn wait_for_output(&self, cx: &AsyncApp) -> Task> {
- let thread = self.subagent_thread.clone();
- let wait_for_prompt = self.wait_for_prompt_to_complete.clone();
+ fn num_entries(&self, cx: &App) -> usize {
+ self.acp_thread.read(cx).entries().len()
+ }
+ fn send(&self, message: String, cx: &AsyncApp) -> Task> {
+ let thread = self.subagent_thread.clone();
+ let acp_thread = self.acp_thread.clone();
let subagent_session_id = self.session_id.clone();
let parent_thread = self.parent_thread.clone();
cx.spawn(async move |cx| {
- let result = match wait_for_prompt.await {
+ let (task, _subscription) = cx.update(|cx| {
+ let ratio_before_prompt = thread
+ .read(cx)
+ .latest_token_usage()
+ .map(|usage| usage.ratio());
+
+ parent_thread
+ .update(cx, |parent_thread, _cx| {
+ parent_thread.register_running_subagent(thread.downgrade())
+ })
+ .ok();
+
+ let task = acp_thread.update(cx, |acp_thread, cx| {
+ acp_thread.send(vec![message.into()], cx)
+ });
+
+ let (token_limit_tx, token_limit_rx) = oneshot::channel::<()>();
+ let mut token_limit_tx = Some(token_limit_tx);
+
+ let subscription = cx.subscribe(
+ &thread,
+ move |_thread, event: &TokenUsageUpdated, _cx| {
+ if let Some(usage) = &event.0 {
+ let old_ratio = ratio_before_prompt
+ .clone()
+ .unwrap_or(TokenUsageRatio::Normal);
+ let new_ratio = usage.ratio();
+ if old_ratio == TokenUsageRatio::Normal
+ && new_ratio == TokenUsageRatio::Warning
+ {
+ if let Some(tx) = token_limit_tx.take() {
+ tx.send(()).ok();
+ }
+ }
+ }
+ },
+ );
+
+ let wait_for_prompt = cx
+ .background_spawn(async move {
+ futures::select! {
+ response = task.fuse() => match response {
+ Ok(Some(response)) => {
+ match response.stop_reason {
+ acp::StopReason::Cancelled => SubagentPromptResult::Cancelled,
+ acp::StopReason::MaxTokens => SubagentPromptResult::Error("The agent reached the maximum number of tokens.".into()),
+ acp::StopReason::MaxTurnRequests => SubagentPromptResult::Error("The agent reached the maximum number of allowed requests between user turns. Try prompting again.".into()),
+ acp::StopReason::Refusal => SubagentPromptResult::Error("The agent refused to process that prompt. Try again.".into()),
+ acp::StopReason::EndTurn | _ => SubagentPromptResult::Completed,
+ }
+ }
+ Ok(None) => SubagentPromptResult::Error("No response from the agent. You can try messaging again.".into()),
+ Err(error) => SubagentPromptResult::Error(error.to_string()),
+ },
+ _ = token_limit_rx.fuse() => SubagentPromptResult::ContextWindowWarning,
+ }
+ });
+
+ (wait_for_prompt, subscription)
+ });
+
+ let result = match task.await {
SubagentPromptResult::Completed => thread.read_with(cx, |thread, _cx| {
thread
.last_message()
- .map(|m| m.to_markdown())
+ .and_then(|message| {
+ let content = message.as_agent_message()?
+ .content
+ .iter()
+ .filter_map(|c| match c {
+ AgentMessageContent::Text(text) => Some(text.as_str()),
+ _ => None,
+ })
+ .join("\n\n");
+ if content.is_empty() {
+ None
+ } else {
+ Some( content)
+ }
+ })
.context("No response from subagent")
}),
- SubagentPromptResult::Cancelled => Err(anyhow!("User cancelled")),
+ SubagentPromptResult::Cancelled => Err(anyhow!("User canceled")),
SubagentPromptResult::Error(message) => Err(anyhow!("{message}")),
SubagentPromptResult::ContextWindowWarning => {
thread.update(cx, |thread, cx| thread.cancel(cx)).await;
@@ -1910,7 +2085,9 @@ mod internal_tests {
use gpui::TestAppContext;
use indoc::formatdoc;
use language_model::fake_provider::{FakeLanguageModel, FakeLanguageModelProvider};
- use language_model::{LanguageModelProviderId, LanguageModelProviderName};
+ use language_model::{
+ LanguageModelCompletionEvent, LanguageModelProviderId, LanguageModelProviderName,
+ };
use serde_json::json;
use settings::SettingsStore;
use util::{path, rel_path::rel_path};
@@ -1928,18 +2105,21 @@ mod internal_tests {
.await;
let project = Project::test(fs.clone(), [], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
- let agent = NativeAgent::new(
- project.clone(),
- thread_store,
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap();
+ let agent =
+ cx.update(|cx| NativeAgent::new(thread_store, Templates::new(), None, fs.clone(), cx));
+
+ // Creating a session registers the project and triggers context building.
+ let connection = NativeAgentConnection(agent.clone());
+ let _acp_thread = cx
+ .update(|cx| Rc::new(connection).new_session(project.clone(), Path::new("/"), cx))
+ .await
+ .unwrap();
+ cx.run_until_parked();
+
agent.read_with(cx, |agent, cx| {
- assert_eq!(agent.project_context.read(cx).worktrees, vec![])
+ let project_id = project.entity_id();
+ let state = agent.projects.get(&project_id).unwrap();
+ assert_eq!(state.project_context.read(cx).worktrees, vec![])
});
let worktree = project
@@ -1948,8 +2128,10 @@ mod internal_tests {
.unwrap();
cx.run_until_parked();
agent.read_with(cx, |agent, cx| {
+ let project_id = project.entity_id();
+ let state = agent.projects.get(&project_id).unwrap();
assert_eq!(
- agent.project_context.read(cx).worktrees,
+ state.project_context.read(cx).worktrees,
vec![WorktreeContext {
root_name: "a".into(),
abs_path: Path::new("/a").into(),
@@ -1962,12 +2144,14 @@ mod internal_tests {
fs.insert_file("/a/.rules", Vec::new()).await;
cx.run_until_parked();
agent.read_with(cx, |agent, cx| {
+ let project_id = project.entity_id();
+ let state = agent.projects.get(&project_id).unwrap();
let rules_entry = worktree
.read(cx)
.entry_for_path(rel_path(".rules"))
.unwrap();
assert_eq!(
- agent.project_context.read(cx).worktrees,
+ state.project_context.read(cx).worktrees,
vec![WorktreeContext {
root_name: "a".into(),
abs_path: Path::new("/a").into(),
@@ -1988,18 +2172,10 @@ mod internal_tests {
fs.insert_tree("/", json!({ "a": {} })).await;
let project = Project::test(fs.clone(), [], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
- let connection = NativeAgentConnection(
- NativeAgent::new(
- project.clone(),
- thread_store,
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap(),
- );
+ let connection =
+ NativeAgentConnection(cx.update(|cx| {
+ NativeAgent::new(thread_store, Templates::new(), None, fs.clone(), cx)
+ }));
// Create a thread/session
let acp_thread = cx
@@ -2068,16 +2244,8 @@ mod internal_tests {
let thread_store = cx.new(|cx| ThreadStore::new(cx));
// Create the agent and connection
- let agent = NativeAgent::new(
- project.clone(),
- thread_store,
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap();
+ let agent =
+ cx.update(|cx| NativeAgent::new(thread_store, Templates::new(), None, fs.clone(), cx));
let connection = NativeAgentConnection(agent.clone());
// Create a thread/session
@@ -2169,16 +2337,8 @@ mod internal_tests {
let project = Project::test(fs.clone(), [], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
- let agent = NativeAgent::new(
- project.clone(),
- thread_store,
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap();
+ let agent =
+ cx.update(|cx| NativeAgent::new(thread_store, Templates::new(), None, fs.clone(), cx));
let connection = NativeAgentConnection(agent.clone());
let acp_thread = cx
@@ -2261,16 +2421,9 @@ mod internal_tests {
fs.insert_tree("/", json!({ "a": {} })).await;
let project = Project::test(fs.clone(), [path!("/a").as_ref()], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
- let agent = NativeAgent::new(
- project.clone(),
- thread_store.clone(),
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap();
+ let agent = cx.update(|cx| {
+ NativeAgent::new(thread_store.clone(), Templates::new(), None, fs.clone(), cx)
+ });
let connection = Rc::new(NativeAgentConnection(agent.clone()));
// Register a thinking model.
@@ -2344,7 +2497,9 @@ mod internal_tests {
// Reload the thread and verify thinking_enabled is still true.
let reloaded_acp_thread = agent
- .update(cx, |agent, cx| agent.open_thread(session_id.clone(), cx))
+ .update(cx, |agent, cx| {
+ agent.open_thread(session_id.clone(), project.clone(), cx)
+ })
.await
.unwrap();
let reloaded_thread = agent.read_with(cx, |agent, _| {
@@ -2367,16 +2522,9 @@ mod internal_tests {
fs.insert_tree("/", json!({ "a": {} })).await;
let project = Project::test(fs.clone(), [path!("/a").as_ref()], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
- let agent = NativeAgent::new(
- project.clone(),
- thread_store.clone(),
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap();
+ let agent = cx.update(|cx| {
+ NativeAgent::new(thread_store.clone(), Templates::new(), None, fs.clone(), cx)
+ });
let connection = Rc::new(NativeAgentConnection(agent.clone()));
// Register a model where id() != name(), like real Anthropic models
@@ -2451,7 +2599,9 @@ mod internal_tests {
// Reload the thread and verify the model was preserved.
let reloaded_acp_thread = agent
- .update(cx, |agent, cx| agent.open_thread(session_id.clone(), cx))
+ .update(cx, |agent, cx| {
+ agent.open_thread(session_id.clone(), project.clone(), cx)
+ })
.await
.unwrap();
let reloaded_thread = agent.read_with(cx, |agent, _| {
@@ -2486,16 +2636,9 @@ mod internal_tests {
.await;
let project = Project::test(fs.clone(), [path!("/a").as_ref()], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
- let agent = NativeAgent::new(
- project.clone(),
- thread_store.clone(),
- Templates::new(),
- None,
- fs.clone(),
- &mut cx.to_async(),
- )
- .await
- .unwrap();
+ let agent = cx.update(|cx| {
+ NativeAgent::new(thread_store.clone(), Templates::new(), None, fs.clone(), cx)
+ });
let connection = Rc::new(NativeAgentConnection(agent.clone()));
let acp_thread = cx
@@ -2542,6 +2685,13 @@ mod internal_tests {
cx.run_until_parked();
model.send_last_completion_stream_text_chunk("Lorem.");
+ model.send_last_completion_stream_event(LanguageModelCompletionEvent::UsageUpdate(
+ language_model::TokenUsage {
+ input_tokens: 150,
+ output_tokens: 75,
+ ..Default::default()
+ },
+ ));
model.end_last_completion_stream();
cx.run_until_parked();
summary_model
@@ -2571,6 +2721,24 @@ mod internal_tests {
cx.run_until_parked();
+ // Set a draft prompt with rich content blocks before saving.
+ let draft_blocks = vec![
+ acp::ContentBlock::Text(acp::TextContent::new("Check out ")),
+ acp::ContentBlock::ResourceLink(acp::ResourceLink::new("b.md", uri.to_string())),
+ acp::ContentBlock::Text(acp::TextContent::new(" please")),
+ ];
+ acp_thread.update(cx, |thread, _cx| {
+ thread.set_draft_prompt(Some(draft_blocks.clone()));
+ });
+ thread.update(cx, |thread, _cx| {
+ thread.set_ui_scroll_position(Some(gpui::ListOffset {
+ item_ix: 5,
+ offset_in_item: gpui::px(12.5),
+ }));
+ });
+ thread.update(cx, |_thread, cx| cx.notify());
+ cx.run_until_parked();
+
// Close the session so it can be reloaded from disk.
cx.update(|cx| connection.clone().close_session(&session_id, cx))
.await
@@ -2590,7 +2758,9 @@ mod internal_tests {
)]
);
let acp_thread = agent
- .update(cx, |agent, cx| agent.open_thread(session_id.clone(), cx))
+ .update(cx, |agent, cx| {
+ agent.open_thread(session_id.clone(), project.clone(), cx)
+ })
.await
.unwrap();
acp_thread.read_with(cx, |thread, cx| {
@@ -2608,6 +2778,29 @@ mod internal_tests {
"}
)
});
+
+ // Ensure the draft prompt with rich content blocks survived the round-trip.
+ acp_thread.read_with(cx, |thread, _| {
+ assert_eq!(thread.draft_prompt(), Some(draft_blocks.as_slice()));
+ });
+
+ // Ensure token usage survived the round-trip.
+ acp_thread.read_with(cx, |thread, _| {
+ let usage = thread
+ .token_usage()
+ .expect("token usage should be restored after reload");
+ assert_eq!(usage.input_tokens, 150);
+ assert_eq!(usage.output_tokens, 75);
+ });
+
+ // Ensure scroll position survived the round-trip.
+ acp_thread.read_with(cx, |thread, _| {
+ let scroll = thread
+ .ui_scroll_position()
+ .expect("scroll position should be restored after reload");
+ assert_eq!(scroll.item_ix, 5);
+ assert_eq!(scroll.offset_in_item, gpui::px(12.5));
+ });
}
fn thread_entries(
diff --git a/crates/agent/src/db.rs b/crates/agent/src/db.rs
index 14ec9bb9af92c2f9720af5714c7344b986f5f7b5..43ab9c3c1826ea7d81fed2c934b96f3bb05dd519 100644
--- a/crates/agent/src/db.rs
+++ b/crates/agent/src/db.rs
@@ -8,6 +8,7 @@ use collections::{HashMap, IndexMap};
use futures::{FutureExt, future::Shared};
use gpui::{BackgroundExecutor, Global, Task};
use indoc::indoc;
+use language_model::Speed;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use sqlez::{
@@ -17,23 +18,13 @@ use sqlez::{
};
use std::sync::Arc;
use ui::{App, SharedString};
+use util::path_list::PathList;
use zed_env_vars::ZED_STATELESS;
pub type DbMessage = crate::Message;
pub type DbSummary = crate::legacy_thread::DetailedSummaryState;
pub type DbLanguageModel = crate::legacy_thread::SerializedLanguageModel;
-/// Metadata about the git worktree associated with an agent thread.
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct AgentGitWorktreeInfo {
- /// The branch name in the git worktree.
- pub branch: String,
- /// Absolute path to the git worktree on disk.
- pub worktree_path: std::path::PathBuf,
- /// The base branch/commit the worktree was created from.
- pub base_ref: String,
-}
-
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbThreadMetadata {
pub id: acp::SessionId,
@@ -41,10 +32,23 @@ pub struct DbThreadMetadata {
#[serde(alias = "summary")]
pub title: SharedString,
pub updated_at: DateTime,
- /// Denormalized from `DbThread::git_worktree_info.branch` for efficient
- /// listing without decompressing thread data. The blob is the source of
- /// truth; this column is populated on save for query convenience.
- pub worktree_branch: Option,
+ pub created_at: Option>,
+ /// The workspace folder paths this thread was created against, sorted
+ /// lexicographically. Used for grouping threads by project in the sidebar.
+ pub folder_paths: PathList,
+}
+
+impl From<&DbThreadMetadata> for acp_thread::AgentSessionInfo {
+ fn from(meta: &DbThreadMetadata) -> Self {
+ Self {
+ session_id: meta.id.clone(),
+ cwd: None,
+ title: Some(meta.title.clone()),
+ updated_at: Some(meta.updated_at),
+ created_at: meta.created_at,
+ meta: None,
+ }
+ }
}
#[derive(Debug, Serialize, Deserialize)]
@@ -69,7 +73,21 @@ pub struct DbThread {
#[serde(default)]
pub subagent_context: Option,
#[serde(default)]
- pub git_worktree_info: Option,
+ pub speed: Option,
+ #[serde(default)]
+ pub thinking_enabled: bool,
+ #[serde(default)]
+ pub thinking_effort: Option,
+ #[serde(default)]
+ pub draft_prompt: Option>,
+ #[serde(default)]
+ pub ui_scroll_position: Option,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
+pub struct SerializedScrollPosition {
+ pub item_ix: usize,
+ pub offset_in_item: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -108,7 +126,11 @@ impl SharedThread {
profile: None,
imported: true,
subagent_context: None,
- git_worktree_info: None,
+ speed: None,
+ thinking_enabled: false,
+ thinking_effort: None,
+ draft_prompt: None,
+ ui_scroll_position: None,
}
}
@@ -283,7 +305,11 @@ impl DbThread {
profile: thread.profile,
imported: false,
subagent_context: None,
- git_worktree_info: None,
+ speed: None,
+ thinking_enabled: false,
+ thinking_effort: None,
+ draft_prompt: None,
+ ui_scroll_position: None,
})
}
}
@@ -389,12 +415,24 @@ impl ThreadsDatabase {
}
if let Ok(mut s) = connection.exec(indoc! {"
- ALTER TABLE threads ADD COLUMN worktree_branch TEXT
+ ALTER TABLE threads ADD COLUMN folder_paths TEXT;
+ ALTER TABLE threads ADD COLUMN folder_paths_order TEXT;
"})
{
s().ok();
}
+ if let Ok(mut s) = connection.exec(indoc! {"
+ ALTER TABLE threads ADD COLUMN created_at TEXT;
+ "})
+ {
+ if s().is_ok() {
+ connection.exec(indoc! {"
+ UPDATE threads SET created_at = updated_at WHERE created_at IS NULL
+ "})?()?;
+ }
+ }
+
let db = Self {
executor,
connection: Arc::new(Mutex::new(connection)),
@@ -407,6 +445,7 @@ impl ThreadsDatabase {
connection: &Arc>,
id: acp::SessionId,
thread: DbThread,
+ folder_paths: &PathList,
) -> Result<()> {
const COMPRESSION_LEVEL: i32 = 3;
@@ -423,10 +462,16 @@ impl ThreadsDatabase {
.subagent_context
.as_ref()
.map(|ctx| ctx.parent_thread_id.0.clone());
- let worktree_branch = thread
- .git_worktree_info
- .as_ref()
- .map(|info| info.branch.clone());
+ let serialized_folder_paths = folder_paths.serialize();
+ let (folder_paths_str, folder_paths_order_str): (Option, Option) =
+ if folder_paths.is_empty() {
+ (None, None)
+ } else {
+ (
+ Some(serialized_folder_paths.paths),
+ Some(serialized_folder_paths.order),
+ )
+ };
let json_data = serde_json::to_string(&SerializedThread {
thread,
version: DbThread::VERSION,
@@ -438,18 +483,34 @@ impl ThreadsDatabase {
let data_type = DataType::Zstd;
let data = compressed;
- let mut insert = connection.exec_bound::<(Arc, Option>, Option, String, String, DataType, Vec)>(indoc! {"
- INSERT OR REPLACE INTO threads (id, parent_id, worktree_branch, summary, updated_at, data_type, data) VALUES (?, ?, ?, ?, ?, ?, ?)
+ // Use the thread's updated_at as created_at for new threads.
+ // This ensures the creation time reflects when the thread was conceptually
+ // created, not when it was saved to the database.
+ let created_at = updated_at.clone();
+
+ let mut insert = connection.exec_bound::<(Arc, Option>, Option, Option, String, String, DataType, Vec, String)>(indoc! {"
+ INSERT INTO threads (id, parent_id, folder_paths, folder_paths_order, summary, updated_at, data_type, data, created_at)
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
+ ON CONFLICT(id) DO UPDATE SET
+ parent_id = excluded.parent_id,
+ folder_paths = excluded.folder_paths,
+ folder_paths_order = excluded.folder_paths_order,
+ summary = excluded.summary,
+ updated_at = excluded.updated_at,
+ data_type = excluded.data_type,
+ data = excluded.data
"})?;
insert((
id.0,
parent_id,
- worktree_branch,
+ folder_paths_str,
+ folder_paths_order_str,
title,
updated_at,
data_type,
data,
+ created_at,
))?;
Ok(())
@@ -462,20 +523,35 @@ impl ThreadsDatabase {
let connection = connection.lock();
let mut select = connection
- .select_bound::<(), (Arc, Option>, Option, String, String)>(indoc! {"
- SELECT id, parent_id, worktree_branch, summary, updated_at FROM threads ORDER BY updated_at DESC
+ .select_bound::<(), (Arc, Option>, Option, Option, String, String, Option