Compare commits
128 Commits
test-test
...
multi-auth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74e80993a3 | ||
|
|
70993b3ce3 | ||
|
|
a47a387186 | ||
|
|
5f6f7edc99 | ||
|
|
4f46ac8c2d | ||
|
|
946c41b4b3 | ||
|
|
7f7312ca8f | ||
|
|
4232b59a59 | ||
|
|
b00901c126 | ||
|
|
e1a83a5fe6 | ||
|
|
59b87d5c71 | ||
|
|
fd4682c8d1 | ||
|
|
f2b966b139 | ||
|
|
4507110981 | ||
|
|
1b43a632dc | ||
|
|
8f3f7232fb | ||
|
|
5f13ce6d7d | ||
|
|
57387812dc | ||
|
|
197d244378 | ||
|
|
6d975984ee | ||
|
|
3aee14378d | ||
|
|
02b15f0062 | ||
|
|
8d48f9cdae | ||
|
|
41994452f2 | ||
|
|
89be2635d4 | ||
|
|
35664461c6 | ||
|
|
219ae05d1f | ||
|
|
e702df21a4 | ||
|
|
3d6722be9a | ||
|
|
47c6ae7b1f | ||
|
|
9984614f3d | ||
|
|
287314f415 | ||
|
|
63e719fadf | ||
|
|
1e69e5d844 | ||
|
|
30c4434b70 | ||
|
|
d7e193caf3 | ||
|
|
438c890816 | ||
|
|
4dd463f8ac | ||
|
|
22fd91d490 | ||
|
|
a660a39ae8 | ||
|
|
60285459e8 | ||
|
|
7e97fcaacb | ||
|
|
ef5b8c6fed | ||
|
|
375a404132 | ||
|
|
27dcdb5841 | ||
|
|
1fbe1e3512 | ||
|
|
62858f6a5c | ||
|
|
3f1319162a | ||
|
|
73e028c01c | ||
|
|
c1e87c8a00 | ||
|
|
1449d1cdb9 | ||
|
|
83bfe2ff7b | ||
|
|
7f9898a90b | ||
|
|
b27fd3b8d7 | ||
|
|
9056d77604 | ||
|
|
b7112320bb | ||
|
|
b59a3bbd49 | ||
|
|
eb3f9b0ea3 | ||
|
|
dd32bb6c74 | ||
|
|
568bb02759 | ||
|
|
ca1f843a0b | ||
|
|
45a0d08535 | ||
|
|
d5a156b774 | ||
|
|
6c3a7f6ddb | ||
|
|
e67065f2a3 | ||
|
|
d99fdc60fd | ||
|
|
038041cc87 | ||
|
|
0cbab311a1 | ||
|
|
908ae95cf8 | ||
|
|
4fa3331bf2 | ||
|
|
cdc9728391 | ||
|
|
aec3c2fbb7 | ||
|
|
620df0c722 | ||
|
|
cd51efad9e | ||
|
|
e85c060625 | ||
|
|
65acf125fb | ||
|
|
2adc023094 | ||
|
|
3780fe3b8e | ||
|
|
59991e9c4d | ||
|
|
e1618994c7 | ||
|
|
c288f9b1e6 | ||
|
|
b26491f570 | ||
|
|
738dcd0c3a | ||
|
|
81425bef72 | ||
|
|
04f0805502 | ||
|
|
58ff46962d | ||
|
|
c5a67d85ab | ||
|
|
3da4cddce2 | ||
|
|
25172f990b | ||
|
|
23a0b6503c | ||
|
|
923e880150 | ||
|
|
de8dd9bea5 | ||
|
|
c77cc9b0eb | ||
|
|
3950f5af29 | ||
|
|
1ee6ef5e1a | ||
|
|
87adc96e0f | ||
|
|
fba7f4d8cc | ||
|
|
ae25baad02 | ||
|
|
cf49194819 | ||
|
|
ea6853d35c | ||
|
|
c37a2f885a | ||
|
|
9c70ba7dcc | ||
|
|
5c4f1e6b85 | ||
|
|
86ce4ef3ab | ||
|
|
9948778e96 | ||
|
|
c2ace408d9 | ||
|
|
e016c05959 | ||
|
|
f2e8d0cc08 | ||
|
|
e406ac6db9 | ||
|
|
546715634c | ||
|
|
db4b86e0c8 | ||
|
|
35f5eb1fe7 | ||
|
|
5939cae6fa | ||
|
|
83f9f9d9e3 | ||
|
|
43baa5d8b8 | ||
|
|
f4609c04eb | ||
|
|
28e14a361d | ||
|
|
77933f83e5 | ||
|
|
186237bb1a | ||
|
|
500acc9511 | ||
|
|
49acfd2602 | ||
|
|
ecb016081a | ||
|
|
bb0cc1059c | ||
|
|
3e2680d650 | ||
|
|
35595fe3c2 | ||
|
|
ce2259ce51 | ||
|
|
6f97d74ff9 | ||
|
|
d6c9d00a4c |
@@ -1,42 +0,0 @@
|
|||||||
# This file contains settings for `cargo hakari`.
|
|
||||||
# See https://docs.rs/cargo-hakari/latest/cargo_hakari/config for a full list of options.
|
|
||||||
|
|
||||||
hakari-package = "workspace-hack"
|
|
||||||
|
|
||||||
resolver = "2"
|
|
||||||
dep-format-version = "4"
|
|
||||||
workspace-hack-line-style = "workspace-dotted"
|
|
||||||
|
|
||||||
# this should be the same list as "targets" in ../rust-toolchain.toml
|
|
||||||
platforms = [
|
|
||||||
"x86_64-apple-darwin",
|
|
||||||
"aarch64-apple-darwin",
|
|
||||||
"x86_64-unknown-linux-gnu",
|
|
||||||
"aarch64-unknown-linux-gnu",
|
|
||||||
"x86_64-pc-windows-msvc",
|
|
||||||
"x86_64-unknown-linux-musl", # remote server
|
|
||||||
]
|
|
||||||
|
|
||||||
[traversal-excludes]
|
|
||||||
workspace-members = [
|
|
||||||
"remote_server",
|
|
||||||
]
|
|
||||||
third-party = [
|
|
||||||
{ name = "reqwest", version = "0.11.27" },
|
|
||||||
# build of remote_server should not include scap / its x11 dependency
|
|
||||||
{ name = "zed-scap", git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", version = "0.0.8-zed" },
|
|
||||||
# build of remote_server should not need to include on libalsa through rodio
|
|
||||||
{ name = "rodio", git = "https://github.com/RustAudio/rodio" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[final-excludes]
|
|
||||||
workspace-members = [
|
|
||||||
"zed_extension_api",
|
|
||||||
|
|
||||||
# exclude all extensions
|
|
||||||
"zed_glsl",
|
|
||||||
"zed_html",
|
|
||||||
"zed_proto",
|
|
||||||
"slash_commands_example",
|
|
||||||
"zed_test_extension",
|
|
||||||
]
|
|
||||||
35
.github/ISSUE_TEMPLATE/06_bug_windows_beta.yml
vendored
35
.github/ISSUE_TEMPLATE/06_bug_windows_beta.yml
vendored
@@ -1,35 +0,0 @@
|
|||||||
name: Bug Report (Windows Beta)
|
|
||||||
description: Zed Windows Beta Related Bugs
|
|
||||||
type: "Bug"
|
|
||||||
labels: ["windows"]
|
|
||||||
title: "Windows Beta: <a short description of the Windows bug>"
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Summary
|
|
||||||
description: Describe the bug with a one-line summary, and provide detailed reproduction steps
|
|
||||||
value: |
|
|
||||||
<!-- Please insert a one-line summary of the issue below -->
|
|
||||||
SUMMARY_SENTENCE_HERE
|
|
||||||
|
|
||||||
### Description
|
|
||||||
<!-- Describe with sufficient detail to reproduce from a clean Zed install. -->
|
|
||||||
Steps to trigger the problem:
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
|
|
||||||
**Expected Behavior**:
|
|
||||||
**Actual Behavior**:
|
|
||||||
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: environment
|
|
||||||
attributes:
|
|
||||||
label: Zed Version and System Specs
|
|
||||||
description: 'Open Zed, and in the command palette select "zed: copy system specs into clipboard"'
|
|
||||||
placeholder: |
|
|
||||||
Output of "zed: copy system specs into clipboard"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
35
.github/workflows/ci.yml
vendored
35
.github/workflows/ci.yml
vendored
@@ -130,39 +130,6 @@ jobs:
|
|||||||
input: "crates/proto/proto/"
|
input: "crates/proto/proto/"
|
||||||
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/"
|
against: "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/"
|
||||||
|
|
||||||
workspace_hack:
|
|
||||||
timeout-minutes: 60
|
|
||||||
name: Check workspace-hack crate
|
|
||||||
needs: [job_spec]
|
|
||||||
if: |
|
|
||||||
github.repository_owner == 'zed-industries' &&
|
|
||||||
needs.job_spec.outputs.run_tests == 'true'
|
|
||||||
runs-on:
|
|
||||||
- namespace-profile-8x16-ubuntu-2204
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
|
||||||
- name: Add Rust to the PATH
|
|
||||||
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
||||||
- name: Install cargo-hakari
|
|
||||||
uses: clechasseur/rs-cargo@8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386 # v2
|
|
||||||
with:
|
|
||||||
command: install
|
|
||||||
args: cargo-hakari@0.9.35
|
|
||||||
|
|
||||||
- name: Check workspace-hack Cargo.toml is up-to-date
|
|
||||||
run: |
|
|
||||||
cargo hakari generate --diff || {
|
|
||||||
echo "To fix, run script/update-workspace-hack or script/update-workspace-hack.ps1";
|
|
||||||
false
|
|
||||||
}
|
|
||||||
- name: Check all crates depend on workspace-hack
|
|
||||||
run: |
|
|
||||||
cargo hakari manage-deps --dry-run || {
|
|
||||||
echo "To fix, run script/update-workspace-hack or script/update-workspace-hack.ps1"
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
style:
|
style:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
name: Check formatting and spelling
|
name: Check formatting and spelling
|
||||||
@@ -507,7 +474,6 @@ jobs:
|
|||||||
- actionlint
|
- actionlint
|
||||||
- migration_checks
|
- migration_checks
|
||||||
# run_tests: If adding required tests, add them here and to script below.
|
# run_tests: If adding required tests, add them here and to script below.
|
||||||
- workspace_hack
|
|
||||||
- linux_tests
|
- linux_tests
|
||||||
- build_remote_server
|
- build_remote_server
|
||||||
- macos_tests
|
- macos_tests
|
||||||
@@ -533,7 +499,6 @@ jobs:
|
|||||||
|
|
||||||
# Only check test jobs if they were supposed to run
|
# Only check test jobs if they were supposed to run
|
||||||
if [[ "${{ needs.job_spec.outputs.run_tests }}" == "true" ]]; then
|
if [[ "${{ needs.job_spec.outputs.run_tests }}" == "true" ]]; then
|
||||||
[[ "${{ needs.workspace_hack.result }}" != 'success' ]] && { RET_CODE=1; echo "Workspace Hack failed"; }
|
|
||||||
[[ "${{ needs.macos_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "macOS tests failed"; }
|
[[ "${{ needs.macos_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "macOS tests failed"; }
|
||||||
[[ "${{ needs.linux_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Linux tests failed"; }
|
[[ "${{ needs.linux_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Linux tests failed"; }
|
||||||
[[ "${{ needs.windows_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows tests failed"; }
|
[[ "${{ needs.windows_tests.result }}" != 'success' ]] && { RET_CODE=1; echo "Windows tests failed"; }
|
||||||
|
|||||||
20
.github/workflows/community_release_actions.yml
vendored
20
.github/workflows/community_release_actions.yml
vendored
@@ -38,6 +38,26 @@ jobs:
|
|||||||
webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}
|
webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}
|
||||||
content: ${{ steps.get-content.outputs.string }}
|
content: ${{ steps.get-content.outputs.string }}
|
||||||
|
|
||||||
|
publish-winget:
|
||||||
|
runs-on:
|
||||||
|
- ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set Package Name
|
||||||
|
id: set-package-name
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event.release.prerelease }}" == "true" ]; then
|
||||||
|
PACKAGE_NAME=ZedIndustries.Zed.Preview
|
||||||
|
else
|
||||||
|
PACKAGE_NAME=ZedIndustries.Zed
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "PACKAGE_NAME=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
|
||||||
|
- uses: vedantmgoyal9/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # v2
|
||||||
|
with:
|
||||||
|
identifier: ${{ steps.set-package-name.outputs.PACKAGE_NAME }}
|
||||||
|
max-versions-to-keep: 5
|
||||||
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
|
|
||||||
send_release_notes_email:
|
send_release_notes_email:
|
||||||
if: false && github.repository_owner == 'zed-industries' && !github.event.release.prerelease
|
if: false && github.repository_owner == 'zed-industries' && !github.event.release.prerelease
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
2
.github/workflows/deploy_cloudflare.yml
vendored
2
.github/workflows/deploy_cloudflare.yml
vendored
@@ -22,6 +22,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
uses: ./.github/actions/build_docs
|
uses: ./.github/actions/build_docs
|
||||||
|
env:
|
||||||
|
DOCS_AMPLITUDE_API_KEY: ${{ secrets.DOCS_AMPLITUDE_API_KEY }}
|
||||||
|
|
||||||
- name: Deploy Docs
|
- name: Deploy Docs
|
||||||
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
|
uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"remove_trailing_whitespace_on_save": true,
|
"remove_trailing_whitespace_on_save": true,
|
||||||
"ensure_final_newline_on_save": true,
|
"ensure_final_newline_on_save": true,
|
||||||
"file_scan_exclusions": [
|
"file_scan_exclusions": [
|
||||||
"crates/assistant_tools/src/edit_agent/evals/fixtures",
|
"crates/agent/src/edit_agent/evals/fixtures",
|
||||||
"crates/eval/worktrees/",
|
"crates/eval/worktrees/",
|
||||||
"crates/eval/repos/",
|
"crates/eval/repos/",
|
||||||
"**/.git",
|
"**/.git",
|
||||||
|
|||||||
4508
Cargo.lock
generated
4508
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
31
Cargo.toml
31
Cargo.toml
@@ -6,7 +6,6 @@ members = [
|
|||||||
"crates/action_log",
|
"crates/action_log",
|
||||||
"crates/activity_indicator",
|
"crates/activity_indicator",
|
||||||
"crates/agent",
|
"crates/agent",
|
||||||
"crates/agent2",
|
|
||||||
"crates/agent_servers",
|
"crates/agent_servers",
|
||||||
"crates/agent_settings",
|
"crates/agent_settings",
|
||||||
"crates/agent_ui",
|
"crates/agent_ui",
|
||||||
@@ -17,8 +16,6 @@ members = [
|
|||||||
"crates/assistant_context",
|
"crates/assistant_context",
|
||||||
"crates/assistant_slash_command",
|
"crates/assistant_slash_command",
|
||||||
"crates/assistant_slash_commands",
|
"crates/assistant_slash_commands",
|
||||||
"crates/assistant_tool",
|
|
||||||
"crates/assistant_tools",
|
|
||||||
"crates/audio",
|
"crates/audio",
|
||||||
"crates/auto_update",
|
"crates/auto_update",
|
||||||
"crates/auto_update_helper",
|
"crates/auto_update_helper",
|
||||||
@@ -61,7 +58,7 @@ members = [
|
|||||||
"crates/edit_prediction_context",
|
"crates/edit_prediction_context",
|
||||||
"crates/zeta2_tools",
|
"crates/zeta2_tools",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
"crates/eval",
|
# "crates/eval",
|
||||||
"crates/explorer_command_injector",
|
"crates/explorer_command_injector",
|
||||||
"crates/extension",
|
"crates/extension",
|
||||||
"crates/extension_api",
|
"crates/extension_api",
|
||||||
@@ -221,7 +218,6 @@ members = [
|
|||||||
#
|
#
|
||||||
|
|
||||||
"tooling/perf",
|
"tooling/perf",
|
||||||
"tooling/workspace-hack",
|
|
||||||
"tooling/xtask",
|
"tooling/xtask",
|
||||||
]
|
]
|
||||||
default-members = ["crates/zed"]
|
default-members = ["crates/zed"]
|
||||||
@@ -240,7 +236,6 @@ acp_tools = { path = "crates/acp_tools" }
|
|||||||
acp_thread = { path = "crates/acp_thread" }
|
acp_thread = { path = "crates/acp_thread" }
|
||||||
action_log = { path = "crates/action_log" }
|
action_log = { path = "crates/action_log" }
|
||||||
agent = { path = "crates/agent" }
|
agent = { path = "crates/agent" }
|
||||||
agent2 = { path = "crates/agent2" }
|
|
||||||
activity_indicator = { path = "crates/activity_indicator" }
|
activity_indicator = { path = "crates/activity_indicator" }
|
||||||
agent_ui = { path = "crates/agent_ui" }
|
agent_ui = { path = "crates/agent_ui" }
|
||||||
agent_settings = { path = "crates/agent_settings" }
|
agent_settings = { path = "crates/agent_settings" }
|
||||||
@@ -253,8 +248,6 @@ assets = { path = "crates/assets" }
|
|||||||
assistant_context = { path = "crates/assistant_context" }
|
assistant_context = { path = "crates/assistant_context" }
|
||||||
assistant_slash_command = { path = "crates/assistant_slash_command" }
|
assistant_slash_command = { path = "crates/assistant_slash_command" }
|
||||||
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
|
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
|
||||||
assistant_tool = { path = "crates/assistant_tool" }
|
|
||||||
assistant_tools = { path = "crates/assistant_tools" }
|
|
||||||
audio = { path = "crates/audio" }
|
audio = { path = "crates/audio" }
|
||||||
auto_update = { path = "crates/auto_update" }
|
auto_update = { path = "crates/auto_update" }
|
||||||
auto_update_helper = { path = "crates/auto_update_helper" }
|
auto_update_helper = { path = "crates/auto_update_helper" }
|
||||||
@@ -378,7 +371,7 @@ remote_server = { path = "crates/remote_server" }
|
|||||||
repl = { path = "crates/repl" }
|
repl = { path = "crates/repl" }
|
||||||
reqwest_client = { path = "crates/reqwest_client" }
|
reqwest_client = { path = "crates/reqwest_client" }
|
||||||
rich_text = { path = "crates/rich_text" }
|
rich_text = { path = "crates/rich_text" }
|
||||||
rodio = { git = "https://github.com/RustAudio/rodio" }
|
rodio = { git = "https://github.com/RustAudio/rodio", rev ="e2074c6c2acf07b57cf717e076bdda7a9ac6e70b", features = ["wav", "playback", "wav_output", "recording"] }
|
||||||
rope = { path = "crates/rope" }
|
rope = { path = "crates/rope" }
|
||||||
rpc = { path = "crates/rpc" }
|
rpc = { path = "crates/rpc" }
|
||||||
rules_library = { path = "crates/rules_library" }
|
rules_library = { path = "crates/rules_library" }
|
||||||
@@ -444,7 +437,7 @@ zlog_settings = { path = "crates/zlog_settings" }
|
|||||||
# External crates
|
# External crates
|
||||||
#
|
#
|
||||||
|
|
||||||
agent-client-protocol = { version = "0.4.3", features = ["unstable"] }
|
agent-client-protocol = { version = "=0.4.3", features = ["unstable"] }
|
||||||
aho-corasick = "1.1"
|
aho-corasick = "1.1"
|
||||||
alacritty_terminal = "0.25.1-rc1"
|
alacritty_terminal = "0.25.1-rc1"
|
||||||
any_vec = "0.14"
|
any_vec = "0.14"
|
||||||
@@ -455,12 +448,13 @@ async-compat = "0.2.1"
|
|||||||
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
|
async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
|
||||||
async-dispatcher = "0.1"
|
async-dispatcher = "0.1"
|
||||||
async-fs = "2.1"
|
async-fs = "2.1"
|
||||||
|
async-lock = "2.1"
|
||||||
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
|
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
|
||||||
async-recursion = "1.0.0"
|
async-recursion = "1.0.0"
|
||||||
async-tar = "0.5.0"
|
async-tar = "0.5.0"
|
||||||
async-task = "4.7"
|
async-task = "4.7"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
async-tungstenite = "0.29.1"
|
async-tungstenite = "0.31.0"
|
||||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||||
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
|
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
|
||||||
aws-credential-types = { version = "1.2.2", features = [
|
aws-credential-types = { version = "1.2.2", features = [
|
||||||
@@ -486,10 +480,10 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||||||
ciborium = "0.2"
|
ciborium = "0.2"
|
||||||
circular-buffer = "1.0"
|
circular-buffer = "1.0"
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
cocoa = "0.26"
|
cocoa = "=0.26.0"
|
||||||
cocoa-foundation = "0.2.0"
|
cocoa-foundation = "=0.2.0"
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
core-foundation = "0.10.0"
|
core-foundation = "=0.10.0"
|
||||||
core-foundation-sys = "0.8.6"
|
core-foundation-sys = "0.8.6"
|
||||||
core-video = { version = "0.4.3", features = ["metal"] }
|
core-video = { version = "0.4.3", features = ["metal"] }
|
||||||
cpal = "0.16"
|
cpal = "0.16"
|
||||||
@@ -552,7 +546,7 @@ nix = "0.29"
|
|||||||
num-format = "0.4.4"
|
num-format = "0.4.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
objc = "0.2"
|
objc = "0.2"
|
||||||
objc2-foundation = { version = "0.3", default-features = false, features = [
|
objc2-foundation = { version = "=0.3.1", default-features = false, features = [
|
||||||
"NSArray",
|
"NSArray",
|
||||||
"NSAttributedString",
|
"NSAttributedString",
|
||||||
"NSBundle",
|
"NSBundle",
|
||||||
@@ -650,7 +644,7 @@ sqlformat = "0.2"
|
|||||||
stacksafe = "0.1"
|
stacksafe = "0.1"
|
||||||
streaming-iterator = "0.1"
|
streaming-iterator = "0.1"
|
||||||
strsim = "0.11"
|
strsim = "0.11"
|
||||||
strum = { version = "0.27.0", features = ["derive"] }
|
strum = { version = "0.27.2", features = ["derive"] }
|
||||||
subtle = "2.5.0"
|
subtle = "2.5.0"
|
||||||
syn = { version = "2.0.101", features = ["full", "extra-traits", "visit-mut"] }
|
syn = { version = "2.0.101", features = ["full", "extra-traits", "visit-mut"] }
|
||||||
sys-locale = "0.3.1"
|
sys-locale = "0.3.1"
|
||||||
@@ -718,7 +712,6 @@ wasmtime-wasi = "29"
|
|||||||
which = "6.0.0"
|
which = "6.0.0"
|
||||||
windows-core = "0.61"
|
windows-core = "0.61"
|
||||||
wit-component = "0.221"
|
wit-component = "0.221"
|
||||||
workspace-hack = "0.1.0"
|
|
||||||
yawc = "0.2.5"
|
yawc = "0.2.5"
|
||||||
zeroize = "1.8"
|
zeroize = "1.8"
|
||||||
zstd = "0.11"
|
zstd = "0.11"
|
||||||
@@ -779,9 +772,6 @@ notify = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5a
|
|||||||
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
|
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "bbb9ea5ae52b253e095737847e367c30653a2e96" }
|
||||||
windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" }
|
windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" }
|
||||||
|
|
||||||
# Makes the workspace hack crate refer to the local one, but only when you're building locally
|
|
||||||
workspace-hack = { path = "tooling/workspace-hack" }
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
split-debuginfo = "unpacked"
|
split-debuginfo = "unpacked"
|
||||||
codegen-units = 16
|
codegen-units = 16
|
||||||
@@ -909,5 +899,4 @@ ignored = [
|
|||||||
"serde",
|
"serde",
|
||||||
"component",
|
"component",
|
||||||
"documented",
|
"documented",
|
||||||
"workspace-hack",
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ Welcome to Zed, a high-performance, multiplayer code editor from the creators of
|
|||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
On macOS and Linux you can [download Zed directly](https://zed.dev/download) or [install Zed via your local package manager](https://zed.dev/docs/linux#installing-via-a-package-manager).
|
On macOS, Linux, and Windows you can [download Zed directly](https://zed.dev/download) or [install Zed via your local package manager](https://zed.dev/docs/linux#installing-via-a-package-manager).
|
||||||
|
|
||||||
Other platforms are not yet available:
|
Other platforms are not yet available:
|
||||||
|
|
||||||
- Windows ([tracking issue](https://github.com/zed-industries/zed/issues/5394))
|
|
||||||
- Web ([tracking issue](https://github.com/zed-industries/zed/issues/5396))
|
- Web ([tracking issue](https://github.com/zed-industries/zed/issues/5396))
|
||||||
|
|
||||||
### Developing Zed
|
### Developing Zed
|
||||||
|
|||||||
@@ -269,14 +269,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AgentPanel && prompt_editor",
|
"context": "AgentPanel && text_thread",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-n": "agent::NewTextThread",
|
"ctrl-n": "agent::NewTextThread",
|
||||||
"ctrl-alt-t": "agent::NewThread"
|
"ctrl-alt-t": "agent::NewThread"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AgentPanel && external_agent_thread",
|
"context": "AgentPanel && acp_thread",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-n": "agent::NewExternalAgentThread",
|
"ctrl-n": "agent::NewExternalAgentThread",
|
||||||
@@ -491,8 +491,8 @@
|
|||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-[": "editor::Outdent",
|
"ctrl-[": "editor::Outdent",
|
||||||
"ctrl-]": "editor::Indent",
|
"ctrl-]": "editor::Indent",
|
||||||
"shift-alt-up": "editor::AddSelectionAbove", // Insert Cursor Above
|
"shift-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }], // Insert Cursor Above
|
||||||
"shift-alt-down": "editor::AddSelectionBelow", // Insert Cursor Below
|
"shift-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }], // Insert Cursor Below
|
||||||
"ctrl-shift-k": "editor::DeleteLine",
|
"ctrl-shift-k": "editor::DeleteLine",
|
||||||
"alt-up": "editor::MoveLineUp",
|
"alt-up": "editor::MoveLineUp",
|
||||||
"alt-down": "editor::MoveLineDown",
|
"alt-down": "editor::MoveLineDown",
|
||||||
@@ -539,7 +539,7 @@
|
|||||||
"ctrl-k ctrl-0": "editor::FoldAll",
|
"ctrl-k ctrl-0": "editor::FoldAll",
|
||||||
"ctrl-k ctrl-j": "editor::UnfoldAll",
|
"ctrl-k ctrl-j": "editor::UnfoldAll",
|
||||||
"ctrl-space": "editor::ShowCompletions",
|
"ctrl-space": "editor::ShowCompletions",
|
||||||
"ctrl-shift-space": "editor::ShowWordCompletions",
|
"ctrl-shift-space": "editor::ShowSignatureHelp",
|
||||||
"ctrl-.": "editor::ToggleCodeActions",
|
"ctrl-.": "editor::ToggleCodeActions",
|
||||||
"ctrl-k r": "editor::RevealInFileManager",
|
"ctrl-k r": "editor::RevealInFileManager",
|
||||||
"ctrl-k p": "editor::CopyPath",
|
"ctrl-k p": "editor::CopyPath",
|
||||||
@@ -799,7 +799,7 @@
|
|||||||
"ctrl-shift-e": "pane::RevealInProjectPanel",
|
"ctrl-shift-e": "pane::RevealInProjectPanel",
|
||||||
"ctrl-f8": "editor::GoToHunk",
|
"ctrl-f8": "editor::GoToHunk",
|
||||||
"ctrl-shift-f8": "editor::GoToPreviousHunk",
|
"ctrl-shift-f8": "editor::GoToPreviousHunk",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"ctrl-i": "assistant::InlineAssist",
|
||||||
"ctrl-:": "editor::ToggleInlayHints"
|
"ctrl-:": "editor::ToggleInlayHints"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1093,7 +1093,7 @@
|
|||||||
"paste": "terminal::Paste",
|
"paste": "terminal::Paste",
|
||||||
"shift-insert": "terminal::Paste",
|
"shift-insert": "terminal::Paste",
|
||||||
"ctrl-shift-v": "terminal::Paste",
|
"ctrl-shift-v": "terminal::Paste",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"ctrl-i": "assistant::InlineAssist",
|
||||||
"alt-b": ["terminal::SendText", "\u001bb"],
|
"alt-b": ["terminal::SendText", "\u001bb"],
|
||||||
"alt-f": ["terminal::SendText", "\u001bf"],
|
"alt-f": ["terminal::SendText", "\u001bf"],
|
||||||
"alt-.": ["terminal::SendText", "\u001b."],
|
"alt-.": ["terminal::SendText", "\u001b."],
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
"cmd-\"": "editor::ExpandAllDiffHunks",
|
"cmd-\"": "editor::ExpandAllDiffHunks",
|
||||||
"cmd-alt-g b": "git::Blame",
|
"cmd-alt-g b": "git::Blame",
|
||||||
"cmd-alt-g m": "git::OpenModifiedFiles",
|
"cmd-alt-g m": "git::OpenModifiedFiles",
|
||||||
"cmd-i": "editor::ShowSignatureHelp",
|
"cmd-shift-space": "editor::ShowSignatureHelp",
|
||||||
"f9": "editor::ToggleBreakpoint",
|
"f9": "editor::ToggleBreakpoint",
|
||||||
"shift-f9": "editor::EditLogBreakpoint",
|
"shift-f9": "editor::EditLogBreakpoint",
|
||||||
"ctrl-f12": "editor::GoToDeclaration",
|
"ctrl-f12": "editor::GoToDeclaration",
|
||||||
@@ -307,7 +307,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AgentPanel && prompt_editor",
|
"context": "AgentPanel && text_thread",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-n": "agent::NewTextThread",
|
"cmd-n": "agent::NewTextThread",
|
||||||
@@ -315,7 +315,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AgentPanel && external_agent_thread",
|
"context": "AgentPanel && acp_thread",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-n": "agent::NewExternalAgentThread",
|
"cmd-n": "agent::NewExternalAgentThread",
|
||||||
@@ -539,10 +539,10 @@
|
|||||||
"bindings": {
|
"bindings": {
|
||||||
"cmd-[": "editor::Outdent",
|
"cmd-[": "editor::Outdent",
|
||||||
"cmd-]": "editor::Indent",
|
"cmd-]": "editor::Indent",
|
||||||
"cmd-ctrl-p": "editor::AddSelectionAbove", // Insert cursor above
|
"cmd-ctrl-p": ["editor::AddSelectionAbove", { "skip_soft_wrap": false }], // Insert cursor above
|
||||||
"cmd-alt-up": "editor::AddSelectionAbove",
|
"cmd-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }],
|
||||||
"cmd-ctrl-n": "editor::AddSelectionBelow", // Insert cursor below
|
"cmd-ctrl-n": ["editor::AddSelectionBelow", { "skip_soft_wrap": false }], // Insert cursor below
|
||||||
"cmd-alt-down": "editor::AddSelectionBelow",
|
"cmd-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }],
|
||||||
"cmd-shift-k": "editor::DeleteLine",
|
"cmd-shift-k": "editor::DeleteLine",
|
||||||
"alt-up": "editor::MoveLineUp",
|
"alt-up": "editor::MoveLineUp",
|
||||||
"alt-down": "editor::MoveLineDown",
|
"alt-down": "editor::MoveLineDown",
|
||||||
@@ -864,7 +864,7 @@
|
|||||||
"cmd-shift-e": "pane::RevealInProjectPanel",
|
"cmd-shift-e": "pane::RevealInProjectPanel",
|
||||||
"cmd-f8": "editor::GoToHunk",
|
"cmd-f8": "editor::GoToHunk",
|
||||||
"cmd-shift-f8": "editor::GoToPreviousHunk",
|
"cmd-shift-f8": "editor::GoToPreviousHunk",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"cmd-i": "assistant::InlineAssist",
|
||||||
"ctrl-:": "editor::ToggleInlayHints"
|
"ctrl-:": "editor::ToggleInlayHints"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1167,7 +1167,7 @@
|
|||||||
"cmd-a": "editor::SelectAll",
|
"cmd-a": "editor::SelectAll",
|
||||||
"cmd-k": "terminal::Clear",
|
"cmd-k": "terminal::Clear",
|
||||||
"cmd-n": "workspace::NewTerminal",
|
"cmd-n": "workspace::NewTerminal",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"cmd-i": "assistant::InlineAssist",
|
||||||
"ctrl-_": null, // emacs undo
|
"ctrl-_": null, // emacs undo
|
||||||
// Some nice conveniences
|
// Some nice conveniences
|
||||||
"cmd-backspace": ["terminal::SendText", "\u0015"], // ctrl-u: clear line
|
"cmd-backspace": ["terminal::SendText", "\u0015"], // ctrl-u: clear line
|
||||||
|
|||||||
@@ -270,7 +270,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AgentPanel && prompt_editor",
|
"context": "AgentPanel && text_thread",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-n": "agent::NewTextThread",
|
"ctrl-n": "agent::NewTextThread",
|
||||||
@@ -278,7 +278,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "AgentPanel && external_agent_thread",
|
"context": "AgentPanel && acp_thread",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-n": "agent::NewExternalAgentThread",
|
"ctrl-n": "agent::NewExternalAgentThread",
|
||||||
@@ -500,8 +500,8 @@
|
|||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-[": "editor::Outdent",
|
"ctrl-[": "editor::Outdent",
|
||||||
"ctrl-]": "editor::Indent",
|
"ctrl-]": "editor::Indent",
|
||||||
"ctrl-shift-alt-up": "editor::AddSelectionAbove", // Insert Cursor Above
|
"ctrl-shift-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }], // Insert Cursor Above
|
||||||
"ctrl-shift-alt-down": "editor::AddSelectionBelow", // Insert Cursor Below
|
"ctrl-shift-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }], // Insert Cursor Below
|
||||||
"ctrl-shift-k": "editor::DeleteLine",
|
"ctrl-shift-k": "editor::DeleteLine",
|
||||||
"alt-up": "editor::MoveLineUp",
|
"alt-up": "editor::MoveLineUp",
|
||||||
"alt-down": "editor::MoveLineDown",
|
"alt-down": "editor::MoveLineDown",
|
||||||
@@ -548,7 +548,7 @@
|
|||||||
"ctrl-k ctrl-0": "editor::FoldAll",
|
"ctrl-k ctrl-0": "editor::FoldAll",
|
||||||
"ctrl-k ctrl-j": "editor::UnfoldAll",
|
"ctrl-k ctrl-j": "editor::UnfoldAll",
|
||||||
"ctrl-space": "editor::ShowCompletions",
|
"ctrl-space": "editor::ShowCompletions",
|
||||||
"ctrl-shift-space": "editor::ShowWordCompletions",
|
"ctrl-shift-space": "editor::ShowSignatureHelp",
|
||||||
"ctrl-.": "editor::ToggleCodeActions",
|
"ctrl-.": "editor::ToggleCodeActions",
|
||||||
"ctrl-k r": "editor::RevealInFileManager",
|
"ctrl-k r": "editor::RevealInFileManager",
|
||||||
"ctrl-k p": "editor::CopyPath",
|
"ctrl-k p": "editor::CopyPath",
|
||||||
@@ -812,7 +812,7 @@
|
|||||||
"ctrl-shift-e": "pane::RevealInProjectPanel",
|
"ctrl-shift-e": "pane::RevealInProjectPanel",
|
||||||
"ctrl-f8": "editor::GoToHunk",
|
"ctrl-f8": "editor::GoToHunk",
|
||||||
"ctrl-shift-f8": "editor::GoToPreviousHunk",
|
"ctrl-shift-f8": "editor::GoToPreviousHunk",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"ctrl-i": "assistant::InlineAssist",
|
||||||
"ctrl-shift-;": "editor::ToggleInlayHints"
|
"ctrl-shift-;": "editor::ToggleInlayHints"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1117,8 +1117,9 @@
|
|||||||
"ctrl-insert": "terminal::Copy",
|
"ctrl-insert": "terminal::Copy",
|
||||||
"ctrl-shift-c": "terminal::Copy",
|
"ctrl-shift-c": "terminal::Copy",
|
||||||
"shift-insert": "terminal::Paste",
|
"shift-insert": "terminal::Paste",
|
||||||
|
"ctrl-v": "terminal::Paste",
|
||||||
"ctrl-shift-v": "terminal::Paste",
|
"ctrl-shift-v": "terminal::Paste",
|
||||||
"ctrl-enter": "assistant::InlineAssist",
|
"ctrl-i": "assistant::InlineAssist",
|
||||||
"alt-b": ["terminal::SendText", "\u001bb"],
|
"alt-b": ["terminal::SendText", "\u001bb"],
|
||||||
"alt-f": ["terminal::SendText", "\u001bf"],
|
"alt-f": ["terminal::SendText", "\u001bf"],
|
||||||
"alt-.": ["terminal::SendText", "\u001b."],
|
"alt-.": ["terminal::SendText", "\u001b."],
|
||||||
@@ -1153,6 +1154,12 @@
|
|||||||
"alt-t": "terminal::RerunTask"
|
"alt-t": "terminal::RerunTask"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"context": "Terminal && selection",
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-c": "terminal::Copy"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"context": "ZedPredictModal",
|
"context": "ZedPredictModal",
|
||||||
"use_key_equivalents": true,
|
"use_key_equivalents": true,
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
"ctrl-<": "editor::ScrollCursorCenter", // editor:scroll-to-cursor
|
"ctrl-<": "editor::ScrollCursorCenter", // editor:scroll-to-cursor
|
||||||
"f3": ["editor::SelectNext", { "replace_newest": true }], // find-and-replace:find-next
|
"f3": ["editor::SelectNext", { "replace_newest": true }], // find-and-replace:find-next
|
||||||
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
|
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
|
||||||
"alt-shift-down": "editor::AddSelectionBelow", // editor:add-selection-below
|
"alt-shift-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }], // editor:add-selection-below
|
||||||
"alt-shift-up": "editor::AddSelectionAbove", // editor:add-selection-above
|
"alt-shift-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }], // editor:add-selection-above
|
||||||
"ctrl-j": "editor::JoinLines", // editor:join-lines
|
"ctrl-j": "editor::JoinLines", // editor:join-lines
|
||||||
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
|
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
|
||||||
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
|
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"alt-x": "command_palette::Toggle",
|
||||||
"ctrl-g": "editor::Cancel",
|
"ctrl-g": "editor::Cancel",
|
||||||
"ctrl-x b": "tab_switcher::Toggle", // switch-to-buffer
|
"ctrl-x b": "tab_switcher::Toggle", // switch-to-buffer
|
||||||
|
"ctrl-x ctrl-b": "tab_switcher::Toggle", // list-buffers
|
||||||
"alt-g g": "go_to_line::Toggle", // goto-line
|
"alt-g g": "go_to_line::Toggle", // goto-line
|
||||||
"alt-g alt-g": "go_to_line::Toggle", // goto-line
|
"alt-g alt-g": "go_to_line::Toggle", // goto-line
|
||||||
"ctrl-space": "editor::SetMark", // set-mark
|
"ctrl-space": "editor::SetMark", // set-mark
|
||||||
@@ -29,6 +31,8 @@
|
|||||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
||||||
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
||||||
"alt-m": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }], // back-to-indentation
|
"alt-m": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }], // back-to-indentation
|
||||||
|
"alt-left": "editor::MoveToPreviousWordStart", // left-word
|
||||||
|
"alt-right": "editor::MoveToNextWordEnd", // right-word
|
||||||
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
||||||
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
||||||
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
||||||
@@ -43,6 +47,8 @@
|
|||||||
"ctrl-x h": "editor::SelectAll", // mark-whole-buffer
|
"ctrl-x h": "editor::SelectAll", // mark-whole-buffer
|
||||||
"ctrl-d": "editor::Delete", // delete-char
|
"ctrl-d": "editor::Delete", // delete-char
|
||||||
"alt-d": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }], // kill-word
|
"alt-d": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }], // kill-word
|
||||||
|
"alt-backspace": "editor::DeleteToPreviousWordStart", // backward-kill-word
|
||||||
|
"alt-delete": "editor::DeleteToPreviousWordStart", // backward-kill-word
|
||||||
"ctrl-k": "editor::KillRingCut", // kill-line
|
"ctrl-k": "editor::KillRingCut", // kill-line
|
||||||
"ctrl-w": "editor::Cut", // kill-region
|
"ctrl-w": "editor::Cut", // kill-region
|
||||||
"alt-w": "editor::Copy", // kill-ring-save
|
"alt-w": "editor::Copy", // kill-ring-save
|
||||||
@@ -52,14 +58,19 @@
|
|||||||
"ctrl-x u": "editor::Undo", // undo
|
"ctrl-x u": "editor::Undo", // undo
|
||||||
"alt-{": "editor::MoveToStartOfParagraph", // backward-paragraph
|
"alt-{": "editor::MoveToStartOfParagraph", // backward-paragraph
|
||||||
"alt-}": "editor::MoveToEndOfParagraph", // forward-paragraph
|
"alt-}": "editor::MoveToEndOfParagraph", // forward-paragraph
|
||||||
|
"ctrl-up": "editor::MoveToStartOfParagraph", // backward-paragraph
|
||||||
|
"ctrl-down": "editor::MoveToEndOfParagraph", // forward-paragraph
|
||||||
"ctrl-v": "editor::MovePageDown", // scroll-up
|
"ctrl-v": "editor::MovePageDown", // scroll-up
|
||||||
"alt-v": "editor::MovePageUp", // scroll-down
|
"alt-v": "editor::MovePageUp", // scroll-down
|
||||||
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
"ctrl-x ]": "editor::MoveToEnd", // end-of-buffer
|
"ctrl-x ]": "editor::MoveToEnd", // end-of-buffer
|
||||||
"alt-<": "editor::MoveToBeginning", // beginning-of-buffer
|
"alt-<": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
"alt->": "editor::MoveToEnd", // end-of-buffer
|
"alt->": "editor::MoveToEnd", // end-of-buffer
|
||||||
|
"ctrl-home": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
|
"ctrl-end": "editor::MoveToEnd", // end-of-buffer
|
||||||
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
|
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
|
||||||
"ctrl-s": "buffer_search::Deploy", // isearch-forward
|
"ctrl-s": "buffer_search::Deploy", // isearch-forward
|
||||||
|
"ctrl-r": "buffer_search::Deploy", // isearch-backward
|
||||||
"alt-^": "editor::JoinLines", // join-line
|
"alt-^": "editor::JoinLines", // join-line
|
||||||
"alt-q": "editor::Rewrap" // fill-paragraph
|
"alt-q": "editor::Rewrap" // fill-paragraph
|
||||||
}
|
}
|
||||||
@@ -85,10 +96,19 @@
|
|||||||
"end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
"end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
||||||
"ctrl-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }],
|
"ctrl-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }],
|
||||||
"ctrl-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
"ctrl-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
||||||
|
"alt-m": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }],
|
||||||
"alt-f": "editor::SelectToNextWordEnd",
|
"alt-f": "editor::SelectToNextWordEnd",
|
||||||
"alt-b": "editor::SelectToPreviousSubwordStart",
|
"alt-b": "editor::SelectToPreviousSubwordStart",
|
||||||
|
"alt-{": "editor::SelectToStartOfParagraph",
|
||||||
|
"alt-}": "editor::SelectToEndOfParagraph",
|
||||||
|
"ctrl-up": "editor::SelectToStartOfParagraph",
|
||||||
|
"ctrl-down": "editor::SelectToEndOfParagraph",
|
||||||
|
"ctrl-x [": "editor::SelectToBeginning",
|
||||||
|
"ctrl-x ]": "editor::SelectToEnd",
|
||||||
"alt-<": "editor::SelectToBeginning",
|
"alt-<": "editor::SelectToBeginning",
|
||||||
"alt->": "editor::SelectToEnd",
|
"alt->": "editor::SelectToEnd",
|
||||||
|
"ctrl-home": "editor::SelectToBeginning",
|
||||||
|
"ctrl-end": "editor::SelectToEnd",
|
||||||
"ctrl-g": "editor::Cancel"
|
"ctrl-g": "editor::Cancel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-alt-up": "editor::AddSelectionAbove",
|
"ctrl-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": false }],
|
||||||
"ctrl-alt-down": "editor::AddSelectionBelow",
|
"ctrl-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": false }],
|
||||||
"ctrl-shift-up": "editor::MoveLineUp",
|
"ctrl-shift-up": "editor::MoveLineUp",
|
||||||
"ctrl-shift-down": "editor::MoveLineDown",
|
"ctrl-shift-down": "editor::MoveLineDown",
|
||||||
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
"cmd-<": "editor::ScrollCursorCenter",
|
"cmd-<": "editor::ScrollCursorCenter",
|
||||||
"cmd-g": ["editor::SelectNext", { "replace_newest": true }],
|
"cmd-g": ["editor::SelectNext", { "replace_newest": true }],
|
||||||
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
|
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
|
||||||
"ctrl-shift-down": "editor::AddSelectionBelow",
|
"ctrl-shift-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }],
|
||||||
"ctrl-shift-up": "editor::AddSelectionAbove",
|
"ctrl-shift-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }],
|
||||||
"alt-enter": "editor::Newline",
|
"alt-enter": "editor::Newline",
|
||||||
"cmd-shift-d": "editor::DuplicateLineDown",
|
"cmd-shift-d": "editor::DuplicateLineDown",
|
||||||
"ctrl-cmd-up": "editor::MoveLineUp",
|
"ctrl-cmd-up": "editor::MoveLineUp",
|
||||||
|
|||||||
@@ -12,8 +12,10 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"alt-x": "command_palette::Toggle",
|
||||||
"ctrl-g": "editor::Cancel",
|
"ctrl-g": "editor::Cancel",
|
||||||
"ctrl-x b": "tab_switcher::Toggle", // switch-to-buffer
|
"ctrl-x b": "tab_switcher::Toggle", // switch-to-buffer
|
||||||
|
"ctrl-x ctrl-b": "tab_switcher::Toggle", // list-buffers
|
||||||
"alt-g g": "go_to_line::Toggle", // goto-line
|
"alt-g g": "go_to_line::Toggle", // goto-line
|
||||||
"alt-g alt-g": "go_to_line::Toggle", // goto-line
|
"alt-g alt-g": "go_to_line::Toggle", // goto-line
|
||||||
"ctrl-space": "editor::SetMark", // set-mark
|
"ctrl-space": "editor::SetMark", // set-mark
|
||||||
@@ -30,6 +32,8 @@
|
|||||||
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
"shift-home": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }], // move-beginning-of-line
|
||||||
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
"shift-end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }], // move-end-of-line
|
||||||
"alt-m": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }], // back-to-indentation
|
"alt-m": ["editor::MoveToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }], // back-to-indentation
|
||||||
|
"alt-left": "editor::MoveToPreviousWordStart", // left-word
|
||||||
|
"alt-right": "editor::MoveToNextWordEnd", // right-word
|
||||||
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
"alt-f": "editor::MoveToNextSubwordEnd", // forward-word
|
||||||
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
"alt-b": "editor::MoveToPreviousSubwordStart", // backward-word
|
||||||
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
"alt-u": "editor::ConvertToUpperCase", // upcase-word
|
||||||
@@ -44,6 +48,8 @@
|
|||||||
"ctrl-x h": "editor::SelectAll", // mark-whole-buffer
|
"ctrl-x h": "editor::SelectAll", // mark-whole-buffer
|
||||||
"ctrl-d": "editor::Delete", // delete-char
|
"ctrl-d": "editor::Delete", // delete-char
|
||||||
"alt-d": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }], // kill-word
|
"alt-d": ["editor::DeleteToNextWordEnd", { "ignore_newlines": false, "ignore_brackets": false }], // kill-word
|
||||||
|
"alt-backspace": "editor::DeleteToPreviousWordStart", // backward-kill-word
|
||||||
|
"alt-delete": "editor::DeleteToPreviousWordStart", // backward-kill-word
|
||||||
"ctrl-k": "editor::KillRingCut", // kill-line
|
"ctrl-k": "editor::KillRingCut", // kill-line
|
||||||
"ctrl-w": "editor::Cut", // kill-region
|
"ctrl-w": "editor::Cut", // kill-region
|
||||||
"alt-w": "editor::Copy", // kill-ring-save
|
"alt-w": "editor::Copy", // kill-ring-save
|
||||||
@@ -53,14 +59,19 @@
|
|||||||
"ctrl-x u": "editor::Undo", // undo
|
"ctrl-x u": "editor::Undo", // undo
|
||||||
"alt-{": "editor::MoveToStartOfParagraph", // backward-paragraph
|
"alt-{": "editor::MoveToStartOfParagraph", // backward-paragraph
|
||||||
"alt-}": "editor::MoveToEndOfParagraph", // forward-paragraph
|
"alt-}": "editor::MoveToEndOfParagraph", // forward-paragraph
|
||||||
|
"ctrl-up": "editor::MoveToStartOfParagraph", // backward-paragraph
|
||||||
|
"ctrl-down": "editor::MoveToEndOfParagraph", // forward-paragraph
|
||||||
"ctrl-v": "editor::MovePageDown", // scroll-up
|
"ctrl-v": "editor::MovePageDown", // scroll-up
|
||||||
"alt-v": "editor::MovePageUp", // scroll-down
|
"alt-v": "editor::MovePageUp", // scroll-down
|
||||||
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
"ctrl-x [": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
"ctrl-x ]": "editor::MoveToEnd", // end-of-buffer
|
"ctrl-x ]": "editor::MoveToEnd", // end-of-buffer
|
||||||
"alt-<": "editor::MoveToBeginning", // beginning-of-buffer
|
"alt-<": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
"alt->": "editor::MoveToEnd", // end-of-buffer
|
"alt->": "editor::MoveToEnd", // end-of-buffer
|
||||||
|
"ctrl-home": "editor::MoveToBeginning", // beginning-of-buffer
|
||||||
|
"ctrl-end": "editor::MoveToEnd", // end-of-buffer
|
||||||
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
|
"ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
|
||||||
"ctrl-s": "buffer_search::Deploy", // isearch-forward
|
"ctrl-s": "buffer_search::Deploy", // isearch-forward
|
||||||
|
"ctrl-r": "buffer_search::Deploy", // isearch-backward
|
||||||
"alt-^": "editor::JoinLines", // join-line
|
"alt-^": "editor::JoinLines", // join-line
|
||||||
"alt-q": "editor::Rewrap" // fill-paragraph
|
"alt-q": "editor::Rewrap" // fill-paragraph
|
||||||
}
|
}
|
||||||
@@ -86,10 +97,19 @@
|
|||||||
"end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
"end": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
||||||
"ctrl-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }],
|
"ctrl-a": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false }],
|
||||||
"ctrl-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
"ctrl-e": ["editor::SelectToEndOfLine", { "stop_at_soft_wraps": false }],
|
||||||
|
"alt-m": ["editor::SelectToBeginningOfLine", { "stop_at_soft_wraps": false, "stop_at_indent": true }],
|
||||||
"alt-f": "editor::SelectToNextWordEnd",
|
"alt-f": "editor::SelectToNextWordEnd",
|
||||||
"alt-b": "editor::SelectToPreviousSubwordStart",
|
"alt-b": "editor::SelectToPreviousSubwordStart",
|
||||||
|
"alt-{": "editor::SelectToStartOfParagraph",
|
||||||
|
"alt-}": "editor::SelectToEndOfParagraph",
|
||||||
|
"ctrl-up": "editor::SelectToStartOfParagraph",
|
||||||
|
"ctrl-down": "editor::SelectToEndOfParagraph",
|
||||||
|
"ctrl-x [": "editor::SelectToBeginning",
|
||||||
|
"ctrl-x ]": "editor::SelectToEnd",
|
||||||
"alt-<": "editor::SelectToBeginning",
|
"alt-<": "editor::SelectToBeginning",
|
||||||
"alt->": "editor::SelectToEnd",
|
"alt->": "editor::SelectToEnd",
|
||||||
|
"ctrl-home": "editor::SelectToBeginning",
|
||||||
|
"ctrl-end": "editor::SelectToEnd",
|
||||||
"ctrl-g": "editor::Cancel"
|
"ctrl-g": "editor::Cancel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"ctrl-shift-up": "editor::AddSelectionAbove",
|
"ctrl-shift-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": false }],
|
||||||
"ctrl-shift-down": "editor::AddSelectionBelow",
|
"ctrl-shift-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": false }],
|
||||||
"cmd-ctrl-up": "editor::MoveLineUp",
|
"cmd-ctrl-up": "editor::MoveLineUp",
|
||||||
"cmd-ctrl-down": "editor::MoveLineDown",
|
"cmd-ctrl-down": "editor::MoveLineDown",
|
||||||
"cmd-shift-space": "editor::SelectAll",
|
"cmd-shift-space": "editor::SelectAll",
|
||||||
|
|||||||
@@ -95,8 +95,6 @@
|
|||||||
"g g": "vim::StartOfDocument",
|
"g g": "vim::StartOfDocument",
|
||||||
"g h": "editor::Hover",
|
"g h": "editor::Hover",
|
||||||
"g B": "editor::BlameHover",
|
"g B": "editor::BlameHover",
|
||||||
"g t": "vim::GoToTab",
|
|
||||||
"g shift-t": "vim::GoToPreviousTab",
|
|
||||||
"g d": "editor::GoToDefinition",
|
"g d": "editor::GoToDefinition",
|
||||||
"g shift-d": "editor::GoToDeclaration",
|
"g shift-d": "editor::GoToDeclaration",
|
||||||
"g y": "editor::GoToTypeDefinition",
|
"g y": "editor::GoToTypeDefinition",
|
||||||
@@ -499,9 +497,11 @@
|
|||||||
"shift-u": "editor::Redo",
|
"shift-u": "editor::Redo",
|
||||||
"ctrl-c": "editor::ToggleComments",
|
"ctrl-c": "editor::ToggleComments",
|
||||||
"d": "vim::HelixDelete",
|
"d": "vim::HelixDelete",
|
||||||
"c": "vim::Substitute",
|
"c": "vim::HelixSubstitute",
|
||||||
"shift-c": "editor::AddSelectionBelow",
|
"alt-c": "vim::HelixSubstituteNoYank",
|
||||||
"alt-shift-c": "editor::AddSelectionAbove"
|
"shift-c": "vim::HelixDuplicateBelow",
|
||||||
|
"alt-shift-c": "vim::HelixDuplicateAbove",
|
||||||
|
",": "vim::HelixKeepNewestSelection"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -811,7 +811,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "VimControl || !Editor && !Terminal",
|
"context": "VimControl && !menu || !Editor && !Terminal",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
// window related commands (ctrl-w X)
|
// window related commands (ctrl-w X)
|
||||||
"ctrl-w": null,
|
"ctrl-w": null,
|
||||||
@@ -831,10 +831,10 @@
|
|||||||
"ctrl-w shift-right": "workspace::SwapPaneRight",
|
"ctrl-w shift-right": "workspace::SwapPaneRight",
|
||||||
"ctrl-w shift-up": "workspace::SwapPaneUp",
|
"ctrl-w shift-up": "workspace::SwapPaneUp",
|
||||||
"ctrl-w shift-down": "workspace::SwapPaneDown",
|
"ctrl-w shift-down": "workspace::SwapPaneDown",
|
||||||
"ctrl-w shift-h": "workspace::SwapPaneLeft",
|
"ctrl-w shift-h": "workspace::MovePaneLeft",
|
||||||
"ctrl-w shift-l": "workspace::SwapPaneRight",
|
"ctrl-w shift-l": "workspace::MovePaneRight",
|
||||||
"ctrl-w shift-k": "workspace::SwapPaneUp",
|
"ctrl-w shift-k": "workspace::MovePaneUp",
|
||||||
"ctrl-w shift-j": "workspace::SwapPaneDown",
|
"ctrl-w shift-j": "workspace::MovePaneDown",
|
||||||
"ctrl-w >": "vim::ResizePaneRight",
|
"ctrl-w >": "vim::ResizePaneRight",
|
||||||
"ctrl-w <": "vim::ResizePaneLeft",
|
"ctrl-w <": "vim::ResizePaneLeft",
|
||||||
"ctrl-w -": "vim::ResizePaneDown",
|
"ctrl-w -": "vim::ResizePaneDown",
|
||||||
@@ -865,7 +865,9 @@
|
|||||||
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
|
"ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
|
||||||
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
|
"ctrl-w o": "workspace::CloseInactiveTabsAndPanes",
|
||||||
"ctrl-w ctrl-n": "workspace::NewFileSplitHorizontal",
|
"ctrl-w ctrl-n": "workspace::NewFileSplitHorizontal",
|
||||||
"ctrl-w n": "workspace::NewFileSplitHorizontal"
|
"ctrl-w n": "workspace::NewFileSplitHorizontal",
|
||||||
|
"g t": "vim::GoToTab",
|
||||||
|
"g shift-t": "vim::GoToPreviousTab"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -311,11 +311,11 @@
|
|||||||
"use_on_type_format": true,
|
"use_on_type_format": true,
|
||||||
// Whether to automatically add matching closing characters when typing
|
// Whether to automatically add matching closing characters when typing
|
||||||
// opening parenthesis, bracket, brace, single or double quote characters.
|
// opening parenthesis, bracket, brace, single or double quote characters.
|
||||||
// For example, when you type (, Zed will add a closing ) at the correct position.
|
// For example, when you type '(', Zed will add a closing ) at the correct position.
|
||||||
"use_autoclose": true,
|
"use_autoclose": true,
|
||||||
// Whether to automatically surround selected text when typing opening parenthesis,
|
// Whether to automatically surround selected text when typing opening parenthesis,
|
||||||
// bracket, brace, single or double quote characters.
|
// bracket, brace, single or double quote characters.
|
||||||
// For example, when you select text and type (, Zed will surround the text with ().
|
// For example, when you select text and type '(', Zed will surround the text with ().
|
||||||
"use_auto_surround": true,
|
"use_auto_surround": true,
|
||||||
// Whether indentation should be adjusted based on the context whilst typing.
|
// Whether indentation should be adjusted based on the context whilst typing.
|
||||||
"auto_indent": true,
|
"auto_indent": true,
|
||||||
@@ -724,7 +724,9 @@
|
|||||||
// Whether to hide the root entry when only one folder is open in the window.
|
// Whether to hide the root entry when only one folder is open in the window.
|
||||||
"hide_root": false,
|
"hide_root": false,
|
||||||
// Whether to hide the hidden entries in the project panel.
|
// Whether to hide the hidden entries in the project panel.
|
||||||
"hide_hidden": false
|
"hide_hidden": false,
|
||||||
|
// Whether to automatically open files when pasting them in the project panel.
|
||||||
|
"open_file_on_paste": true
|
||||||
},
|
},
|
||||||
"outline_panel": {
|
"outline_panel": {
|
||||||
// Whether to show the outline panel button in the status bar
|
// Whether to show the outline panel button in the status bar
|
||||||
@@ -882,8 +884,6 @@
|
|||||||
// Note: This setting has no effect on external agents that support permission modes, such as Claude Code.
|
// Note: This setting has no effect on external agents that support permission modes, such as Claude Code.
|
||||||
// You can set `agent_servers.claude.default_mode` to `bypassPermissions` to skip all permission requests.
|
// You can set `agent_servers.claude.default_mode` to `bypassPermissions` to skip all permission requests.
|
||||||
"always_allow_tool_actions": false,
|
"always_allow_tool_actions": false,
|
||||||
// When enabled, the agent will stream edits.
|
|
||||||
"stream_edits": false,
|
|
||||||
// When enabled, agent edits will be displayed in single-file editors for review
|
// When enabled, agent edits will be displayed in single-file editors for review
|
||||||
"single_file_review": true,
|
"single_file_review": true,
|
||||||
// When enabled, show voting thumbs for feedback on agent edits.
|
// When enabled, show voting thumbs for feedback on agent edits.
|
||||||
@@ -1527,6 +1527,7 @@
|
|||||||
// A value of 45 preserves colorful themes while ensuring legibility.
|
// A value of 45 preserves colorful themes while ensuring legibility.
|
||||||
"minimum_contrast": 45
|
"minimum_contrast": 45
|
||||||
},
|
},
|
||||||
|
"code_actions_on_format": {},
|
||||||
// Settings related to running tasks.
|
// Settings related to running tasks.
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"variables": {},
|
"variables": {},
|
||||||
@@ -1556,6 +1557,7 @@
|
|||||||
//
|
//
|
||||||
"file_types": {
|
"file_types": {
|
||||||
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json", "tsconfig*.json"],
|
"JSONC": ["**/.zed/**/*.json", "**/zed/**/*.json", "**/Zed/**/*.json", "**/.vscode/**/*.json", "tsconfig*.json"],
|
||||||
|
"Markdown": [".rules", ".cursorrules", ".windsurfrules", ".clinerules"],
|
||||||
"Shell Script": [".env.*"]
|
"Shell Script": [".env.*"]
|
||||||
},
|
},
|
||||||
// Settings for which version of Node.js and NPM to use when installing
|
// Settings for which version of Node.js and NPM to use when installing
|
||||||
@@ -1696,7 +1698,9 @@
|
|||||||
"preferred_line_length": 72
|
"preferred_line_length": 72
|
||||||
},
|
},
|
||||||
"Go": {
|
"Go": {
|
||||||
"formatter": [{ "code_action": "source.organizeImports" }, "language_server"],
|
"code_actions_on_format": {
|
||||||
|
"source.organizeImports": true
|
||||||
|
},
|
||||||
"debuggers": ["Delve"]
|
"debuggers": ["Delve"]
|
||||||
},
|
},
|
||||||
"GraphQL": {
|
"GraphQL": {
|
||||||
@@ -1771,7 +1775,8 @@
|
|||||||
"name": "ruff"
|
"name": "ruff"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debuggers": ["Debugpy"]
|
"debuggers": ["Debugpy"],
|
||||||
|
"language_servers": ["basedpyright", "ruff", "!ty", "!pyrefly", "!pyright", "!pylsp", "..."]
|
||||||
},
|
},
|
||||||
"Ruby": {
|
"Ruby": {
|
||||||
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."]
|
"language_servers": ["solargraph", "!ruby-lsp", "!rubocop", "!sorbet", "!steep", "..."]
|
||||||
@@ -1920,6 +1925,11 @@
|
|||||||
// DAP Specific settings.
|
// DAP Specific settings.
|
||||||
"dap": {
|
"dap": {
|
||||||
// Specify the DAP name as a key here.
|
// Specify the DAP name as a key here.
|
||||||
|
"CodeLLDB": {
|
||||||
|
"env": {
|
||||||
|
"RUST_LOG": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// Common language server settings.
|
// Common language server settings.
|
||||||
"global_lsp_settings": {
|
"global_lsp_settings": {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ avoid-breaking-exported-api = false
|
|||||||
ignore-interior-mutability = [
|
ignore-interior-mutability = [
|
||||||
# Suppresses clippy::mutable_key_type, which is a false positive as the Eq
|
# Suppresses clippy::mutable_key_type, which is a false positive as the Eq
|
||||||
# and Hash impls do not use fields with interior mutability.
|
# and Hash impls do not use fields with interior mutability.
|
||||||
"agent::context::AgentContextKey"
|
"agent_ui::context::AgentContextKey"
|
||||||
]
|
]
|
||||||
disallowed-methods = [
|
disallowed-methods = [
|
||||||
{ path = "std::process::Command::spawn", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::spawn" },
|
{ path = "std::process::Command::spawn", reason = "Spawning `std::process::Command` can block the current thread for an unknown duration", replacement = "smol::process::Command::spawn" },
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ url.workspace = true
|
|||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
watch.workspace = true
|
watch.workspace = true
|
||||||
workspace-hack.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
|||||||
@@ -26,5 +26,4 @@ settings.workspace = true
|
|||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace-hack.workspace = true
|
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ project.workspace = true
|
|||||||
text.workspace = true
|
text.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
watch.workspace = true
|
watch.workspace = true
|
||||||
workspace-hack.workspace = true
|
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ proto.workspace = true
|
|||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
workspace-hack.workspace = true
|
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -5,74 +5,99 @@ edition.workspace = true
|
|||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/agent.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-support = ["db/test-support"]
|
||||||
|
e2e = []
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/agent.rs"
|
|
||||||
doctest = false
|
|
||||||
|
|
||||||
[features]
|
|
||||||
test-support = [
|
|
||||||
"gpui/test-support",
|
|
||||||
"language/test-support",
|
|
||||||
]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
acp_thread.workspace = true
|
||||||
action_log.workspace = true
|
action_log.workspace = true
|
||||||
|
agent-client-protocol.workspace = true
|
||||||
|
agent_servers.workspace = true
|
||||||
agent_settings.workspace = true
|
agent_settings.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assistant_context.workspace = true
|
assistant_context.workspace = true
|
||||||
assistant_tool.workspace = true
|
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
cloud_llm_client.workspace = true
|
cloud_llm_client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
component.workspace = true
|
|
||||||
context_server.workspace = true
|
context_server.workspace = true
|
||||||
convert_case.workspace = true
|
db.workspace = true
|
||||||
|
derive_more.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
git.workspace = true
|
git.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
heed.workspace = true
|
handlebars = { workspace = true, features = ["rust-embed"] }
|
||||||
|
html_to_markdown.workspace = true
|
||||||
http_client.workspace = true
|
http_client.workspace = true
|
||||||
icons.workspace = true
|
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
|
itertools.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
language_model.workspace = true
|
language_model.workspace = true
|
||||||
|
language_models.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
open.workspace = true
|
||||||
|
parking_lot.workspace = true
|
||||||
paths.workspace = true
|
paths.workspace = true
|
||||||
postage.workspace = true
|
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
prompt_store.workspace = true
|
prompt_store.workspace = true
|
||||||
ref-cast.workspace = true
|
regex.workspace = true
|
||||||
rope.workspace = true
|
rust-embed.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
|
smallvec.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
sqlez.workspace = true
|
sqlez.workspace = true
|
||||||
|
streaming_diff.workspace = true
|
||||||
|
strsim.workspace = true
|
||||||
|
task.workspace = true
|
||||||
telemetry.workspace = true
|
telemetry.workspace = true
|
||||||
|
terminal.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
theme.workspace = true
|
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
time.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
workspace-hack.workspace = true
|
watch.workspace = true
|
||||||
|
web_search.workspace = true
|
||||||
zed_env_vars.workspace = true
|
zed_env_vars.workspace = true
|
||||||
zstd.workspace = true
|
zstd.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assistant_tools.workspace = true
|
agent_servers = { workspace = true, "features" = ["test-support"] }
|
||||||
|
assistant_context = { workspace = true, "features" = ["test-support"] }
|
||||||
|
client = { workspace = true, "features" = ["test-support"] }
|
||||||
|
clock = { workspace = true, "features" = ["test-support"] }
|
||||||
|
context_server = { workspace = true, "features" = ["test-support"] }
|
||||||
|
ctor.workspace = true
|
||||||
|
db = { workspace = true, "features" = ["test-support"] }
|
||||||
|
editor = { workspace = true, "features" = ["test-support"] }
|
||||||
|
env_logger.workspace = true
|
||||||
|
fs = { workspace = true, "features" = ["test-support"] }
|
||||||
|
git = { workspace = true, "features" = ["test-support"] }
|
||||||
gpui = { workspace = true, "features" = ["test-support"] }
|
gpui = { workspace = true, "features" = ["test-support"] }
|
||||||
indoc.workspace = true
|
gpui_tokio.workspace = true
|
||||||
language = { workspace = true, "features" = ["test-support"] }
|
language = { workspace = true, "features" = ["test-support"] }
|
||||||
language_model = { workspace = true, "features" = ["test-support"] }
|
language_model = { workspace = true, "features" = ["test-support"] }
|
||||||
parking_lot.workspace = true
|
lsp = { workspace = true, "features" = ["test-support"] }
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
project = { workspace = true, features = ["test-support"] }
|
project = { workspace = true, "features" = ["test-support"] }
|
||||||
workspace = { workspace = true, features = ["test-support"] }
|
|
||||||
rand.workspace = true
|
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
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,341 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use agent_settings::{AgentProfileId, AgentProfileSettings, AgentSettings};
|
|
||||||
use assistant_tool::{Tool, ToolSource, ToolWorkingSet, UniqueToolName};
|
|
||||||
use collections::IndexMap;
|
|
||||||
use convert_case::{Case, Casing};
|
|
||||||
use fs::Fs;
|
|
||||||
use gpui::{App, Entity, SharedString};
|
|
||||||
use settings::{Settings, update_settings_file};
|
|
||||||
use util::ResultExt;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct AgentProfile {
|
|
||||||
id: AgentProfileId,
|
|
||||||
tool_set: Entity<ToolWorkingSet>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type AvailableProfiles = IndexMap<AgentProfileId, SharedString>;
|
|
||||||
|
|
||||||
impl AgentProfile {
|
|
||||||
pub fn new(id: AgentProfileId, tool_set: Entity<ToolWorkingSet>) -> Self {
|
|
||||||
Self { id, tool_set }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Saves a new profile to the settings.
|
|
||||||
pub fn create(
|
|
||||||
name: String,
|
|
||||||
base_profile_id: Option<AgentProfileId>,
|
|
||||||
fs: Arc<dyn Fs>,
|
|
||||||
cx: &App,
|
|
||||||
) -> AgentProfileId {
|
|
||||||
let id = AgentProfileId(name.to_case(Case::Kebab).into());
|
|
||||||
|
|
||||||
let base_profile =
|
|
||||||
base_profile_id.and_then(|id| AgentSettings::get_global(cx).profiles.get(&id).cloned());
|
|
||||||
|
|
||||||
let profile_settings = AgentProfileSettings {
|
|
||||||
name: name.into(),
|
|
||||||
tools: base_profile
|
|
||||||
.as_ref()
|
|
||||||
.map(|profile| profile.tools.clone())
|
|
||||||
.unwrap_or_default(),
|
|
||||||
enable_all_context_servers: base_profile
|
|
||||||
.as_ref()
|
|
||||||
.map(|profile| profile.enable_all_context_servers)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
context_servers: base_profile
|
|
||||||
.map(|profile| profile.context_servers)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
update_settings_file(fs, cx, {
|
|
||||||
let id = id.clone();
|
|
||||||
move |settings, _cx| {
|
|
||||||
profile_settings.save_to_settings(id, settings).log_err();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a map of AgentProfileIds to their names
|
|
||||||
pub fn available_profiles(cx: &App) -> AvailableProfiles {
|
|
||||||
let mut profiles = AvailableProfiles::default();
|
|
||||||
for (id, profile) in AgentSettings::get_global(cx).profiles.iter() {
|
|
||||||
profiles.insert(id.clone(), profile.name.clone());
|
|
||||||
}
|
|
||||||
profiles
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> &AgentProfileId {
|
|
||||||
&self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enabled_tools(&self, cx: &App) -> Vec<(UniqueToolName, Arc<dyn Tool>)> {
|
|
||||||
let Some(settings) = AgentSettings::get_global(cx).profiles.get(&self.id) else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tool_set
|
|
||||||
.read(cx)
|
|
||||||
.tools(cx)
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(_, tool)| Self::is_enabled(settings, tool.source(), tool.name()))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_tool_enabled(&self, source: ToolSource, tool_name: String, cx: &App) -> bool {
|
|
||||||
let Some(settings) = AgentSettings::get_global(cx).profiles.get(&self.id) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::is_enabled(settings, source, tool_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_enabled(settings: &AgentProfileSettings, source: ToolSource, name: String) -> bool {
|
|
||||||
match source {
|
|
||||||
ToolSource::Native => *settings.tools.get(name.as_str()).unwrap_or(&false),
|
|
||||||
ToolSource::ContextServer { id } => settings
|
|
||||||
.context_servers
|
|
||||||
.get(id.as_ref())
|
|
||||||
.and_then(|preset| preset.tools.get(name.as_str()).copied())
|
|
||||||
.unwrap_or(settings.enable_all_context_servers),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use agent_settings::ContextServerPreset;
|
|
||||||
use assistant_tool::ToolRegistry;
|
|
||||||
use collections::IndexMap;
|
|
||||||
use gpui::SharedString;
|
|
||||||
use gpui::{AppContext, TestAppContext};
|
|
||||||
use http_client::FakeHttpClient;
|
|
||||||
use project::Project;
|
|
||||||
use settings::{Settings, SettingsStore};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_enabled_built_in_tools_for_profile(cx: &mut TestAppContext) {
|
|
||||||
init_test_settings(cx);
|
|
||||||
|
|
||||||
let id = AgentProfileId::default();
|
|
||||||
let profile_settings = cx.read(|cx| {
|
|
||||||
AgentSettings::get_global(cx)
|
|
||||||
.profiles
|
|
||||||
.get(&id)
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
});
|
|
||||||
let tool_set = default_tool_set(cx);
|
|
||||||
|
|
||||||
let profile = AgentProfile::new(id, tool_set);
|
|
||||||
|
|
||||||
let mut enabled_tools = cx
|
|
||||||
.read(|cx| profile.enabled_tools(cx))
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, tool)| tool.name())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
enabled_tools.sort();
|
|
||||||
|
|
||||||
let mut expected_tools = profile_settings
|
|
||||||
.tools
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(tool, enabled)| enabled.then_some(tool.to_string()))
|
|
||||||
// Provider dependent
|
|
||||||
.filter(|tool| tool != "web_search")
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
// Plus all registered MCP tools
|
|
||||||
expected_tools.extend(["enabled_mcp_tool".into(), "disabled_mcp_tool".into()]);
|
|
||||||
expected_tools.sort();
|
|
||||||
|
|
||||||
assert_eq!(enabled_tools, expected_tools);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_custom_mcp_settings(cx: &mut TestAppContext) {
|
|
||||||
init_test_settings(cx);
|
|
||||||
|
|
||||||
let id = AgentProfileId("custom_mcp".into());
|
|
||||||
let profile_settings = cx.read(|cx| {
|
|
||||||
AgentSettings::get_global(cx)
|
|
||||||
.profiles
|
|
||||||
.get(&id)
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
});
|
|
||||||
let tool_set = default_tool_set(cx);
|
|
||||||
|
|
||||||
let profile = AgentProfile::new(id, tool_set);
|
|
||||||
|
|
||||||
let mut enabled_tools = cx
|
|
||||||
.read(|cx| profile.enabled_tools(cx))
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, tool)| tool.name())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
enabled_tools.sort();
|
|
||||||
|
|
||||||
let mut expected_tools = profile_settings.context_servers["mcp"]
|
|
||||||
.tools
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(key, enabled)| enabled.then(|| key.to_string()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
expected_tools.sort();
|
|
||||||
|
|
||||||
assert_eq!(enabled_tools, expected_tools);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_only_built_in(cx: &mut TestAppContext) {
|
|
||||||
init_test_settings(cx);
|
|
||||||
|
|
||||||
let id = AgentProfileId("write_minus_mcp".into());
|
|
||||||
let profile_settings = cx.read(|cx| {
|
|
||||||
AgentSettings::get_global(cx)
|
|
||||||
.profiles
|
|
||||||
.get(&id)
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
});
|
|
||||||
let tool_set = default_tool_set(cx);
|
|
||||||
|
|
||||||
let profile = AgentProfile::new(id, tool_set);
|
|
||||||
|
|
||||||
let mut enabled_tools = cx
|
|
||||||
.read(|cx| profile.enabled_tools(cx))
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, tool)| tool.name())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
enabled_tools.sort();
|
|
||||||
|
|
||||||
let mut expected_tools = profile_settings
|
|
||||||
.tools
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(tool, enabled)| enabled.then_some(tool.to_string()))
|
|
||||||
// Provider dependent
|
|
||||||
.filter(|tool| tool != "web_search")
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
expected_tools.sort();
|
|
||||||
|
|
||||||
assert_eq!(enabled_tools, expected_tools);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_test_settings(cx: &mut TestAppContext) {
|
|
||||||
cx.update(|cx| {
|
|
||||||
let settings_store = SettingsStore::test(cx);
|
|
||||||
cx.set_global(settings_store);
|
|
||||||
Project::init_settings(cx);
|
|
||||||
AgentSettings::register(cx);
|
|
||||||
language_model::init_settings(cx);
|
|
||||||
ToolRegistry::default_global(cx);
|
|
||||||
assistant_tools::init(FakeHttpClient::with_404_response(), cx);
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.update(|cx| {
|
|
||||||
let mut agent_settings = AgentSettings::get_global(cx).clone();
|
|
||||||
agent_settings.profiles.insert(
|
|
||||||
AgentProfileId("write_minus_mcp".into()),
|
|
||||||
AgentProfileSettings {
|
|
||||||
name: "write_minus_mcp".into(),
|
|
||||||
enable_all_context_servers: false,
|
|
||||||
..agent_settings.profiles[&AgentProfileId::default()].clone()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
agent_settings.profiles.insert(
|
|
||||||
AgentProfileId("custom_mcp".into()),
|
|
||||||
AgentProfileSettings {
|
|
||||||
name: "mcp".into(),
|
|
||||||
tools: IndexMap::default(),
|
|
||||||
enable_all_context_servers: false,
|
|
||||||
context_servers: IndexMap::from_iter([("mcp".into(), context_server_preset())]),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
AgentSettings::override_global(agent_settings, cx);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context_server_preset() -> ContextServerPreset {
|
|
||||||
ContextServerPreset {
|
|
||||||
tools: IndexMap::from_iter([
|
|
||||||
("enabled_mcp_tool".into(), true),
|
|
||||||
("disabled_mcp_tool".into(), false),
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_tool_set(cx: &mut TestAppContext) -> Entity<ToolWorkingSet> {
|
|
||||||
cx.new(|cx| {
|
|
||||||
let mut tool_set = ToolWorkingSet::default();
|
|
||||||
tool_set.insert(Arc::new(FakeTool::new("enabled_mcp_tool", "mcp")), cx);
|
|
||||||
tool_set.insert(Arc::new(FakeTool::new("disabled_mcp_tool", "mcp")), cx);
|
|
||||||
tool_set
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FakeTool {
|
|
||||||
name: String,
|
|
||||||
source: SharedString,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FakeTool {
|
|
||||||
fn new(name: impl Into<String>, source: impl Into<SharedString>) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.into(),
|
|
||||||
source: source.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tool for FakeTool {
|
|
||||||
fn name(&self) -> String {
|
|
||||||
self.name.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source(&self) -> ToolSource {
|
|
||||||
ToolSource::ContextServer {
|
|
||||||
id: self.source.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> String {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn icon(&self) -> icons::IconName {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn needs_confirmation(
|
|
||||||
&self,
|
|
||||||
_input: &serde_json::Value,
|
|
||||||
_project: &Entity<Project>,
|
|
||||||
_cx: &App,
|
|
||||||
) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ui_text(&self, _input: &serde_json::Value) -> String {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
self: Arc<Self>,
|
|
||||||
_input: serde_json::Value,
|
|
||||||
_request: Arc<language_model::LanguageModelRequest>,
|
|
||||||
_project: Entity<Project>,
|
|
||||||
_action_log: Entity<action_log::ActionLog>,
|
|
||||||
_model: Arc<dyn language_model::LanguageModel>,
|
|
||||||
_window: Option<gpui::AnyWindowHandle>,
|
|
||||||
_cx: &mut App,
|
|
||||||
) -> assistant_tool::ToolResult {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn may_perform_edits(&self) -> bool {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use action_log::ActionLog;
|
|
||||||
use anyhow::{Result, anyhow, bail};
|
|
||||||
use assistant_tool::{Tool, ToolResult, ToolSource};
|
|
||||||
use context_server::{ContextServerId, types};
|
|
||||||
use gpui::{AnyWindowHandle, App, Entity, Task};
|
|
||||||
use icons::IconName;
|
|
||||||
use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat};
|
|
||||||
use project::{Project, context_server_store::ContextServerStore};
|
|
||||||
|
|
||||||
pub struct ContextServerTool {
|
|
||||||
store: Entity<ContextServerStore>,
|
|
||||||
server_id: ContextServerId,
|
|
||||||
tool: types::Tool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextServerTool {
|
|
||||||
pub fn new(
|
|
||||||
store: Entity<ContextServerStore>,
|
|
||||||
server_id: ContextServerId,
|
|
||||||
tool: types::Tool,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
store,
|
|
||||||
server_id,
|
|
||||||
tool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tool for ContextServerTool {
|
|
||||||
fn name(&self) -> String {
|
|
||||||
self.tool.name.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> String {
|
|
||||||
self.tool.description.clone().unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn icon(&self) -> IconName {
|
|
||||||
IconName::ToolHammer
|
|
||||||
}
|
|
||||||
|
|
||||||
fn source(&self) -> ToolSource {
|
|
||||||
ToolSource::ContextServer {
|
|
||||||
id: self.server_id.clone().0.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn needs_confirmation(&self, _: &serde_json::Value, _: &Entity<Project>, _: &App) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn may_perform_edits(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> {
|
|
||||||
let mut schema = self.tool.input_schema.clone();
|
|
||||||
assistant_tool::adapt_schema_to_format(&mut schema, format)?;
|
|
||||||
Ok(match schema {
|
|
||||||
serde_json::Value::Null => {
|
|
||||||
serde_json::json!({ "type": "object", "properties": [] })
|
|
||||||
}
|
|
||||||
serde_json::Value::Object(map) if map.is_empty() => {
|
|
||||||
serde_json::json!({ "type": "object", "properties": [] })
|
|
||||||
}
|
|
||||||
_ => schema,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ui_text(&self, _input: &serde_json::Value) -> String {
|
|
||||||
format!("Run MCP tool `{}`", self.tool.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
self: Arc<Self>,
|
|
||||||
input: serde_json::Value,
|
|
||||||
_request: Arc<LanguageModelRequest>,
|
|
||||||
_project: Entity<Project>,
|
|
||||||
_action_log: Entity<ActionLog>,
|
|
||||||
_model: Arc<dyn LanguageModel>,
|
|
||||||
_window: Option<AnyWindowHandle>,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> ToolResult {
|
|
||||||
if let Some(server) = self.store.read(cx).get_running_server(&self.server_id) {
|
|
||||||
let tool_name = self.tool.name.clone();
|
|
||||||
|
|
||||||
cx.spawn(async move |_cx| {
|
|
||||||
let Some(protocol) = server.client() else {
|
|
||||||
bail!("Context server not initialized");
|
|
||||||
};
|
|
||||||
|
|
||||||
let arguments = if let serde_json::Value::Object(map) = input {
|
|
||||||
Some(map.into_iter().collect())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Running tool: {} with arguments: {:?}",
|
|
||||||
tool_name,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
let response = protocol
|
|
||||||
.request::<context_server::types::requests::CallTool>(
|
|
||||||
context_server::types::CallToolParams {
|
|
||||||
name: tool_name,
|
|
||||||
arguments,
|
|
||||||
meta: None,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut result = String::new();
|
|
||||||
for content in response.content {
|
|
||||||
match content {
|
|
||||||
types::ToolResponseContent::Text { text } => {
|
|
||||||
result.push_str(&text);
|
|
||||||
}
|
|
||||||
types::ToolResponseContent::Image { .. } => {
|
|
||||||
log::warn!("Ignoring image content from tool response");
|
|
||||||
}
|
|
||||||
types::ToolResponseContent::Audio { .. } => {
|
|
||||||
log::warn!("Ignoring audio content from tool response");
|
|
||||||
}
|
|
||||||
types::ToolResponseContent::Resource { .. } => {
|
|
||||||
log::warn!("Ignoring resource content from tool response");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result.into())
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
Task::ready(Err(anyhow!("Context server not found"))).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::{AgentMessage, AgentMessageContent, UserMessage, UserMessageContent};
|
use crate::{AgentMessage, AgentMessageContent, UserMessage, UserMessageContent};
|
||||||
use acp_thread::UserMessageId;
|
use acp_thread::UserMessageId;
|
||||||
use agent::{thread::DetailedSummaryState, thread_store};
|
|
||||||
use agent_client_protocol as acp;
|
use agent_client_protocol as acp;
|
||||||
use agent_settings::{AgentProfileId, CompletionMode};
|
use agent_settings::{AgentProfileId, CompletionMode};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
@@ -21,8 +20,8 @@ use ui::{App, SharedString};
|
|||||||
use zed_env_vars::ZED_STATELESS;
|
use zed_env_vars::ZED_STATELESS;
|
||||||
|
|
||||||
pub type DbMessage = crate::Message;
|
pub type DbMessage = crate::Message;
|
||||||
pub type DbSummary = DetailedSummaryState;
|
pub type DbSummary = crate::legacy_thread::DetailedSummaryState;
|
||||||
pub type DbLanguageModel = thread_store::SerializedLanguageModel;
|
pub type DbLanguageModel = crate::legacy_thread::SerializedLanguageModel;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct DbThreadMetadata {
|
pub struct DbThreadMetadata {
|
||||||
@@ -40,7 +39,7 @@ pub struct DbThread {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub detailed_summary: Option<SharedString>,
|
pub detailed_summary: Option<SharedString>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub initial_project_snapshot: Option<Arc<agent::thread::ProjectSnapshot>>,
|
pub initial_project_snapshot: Option<Arc<crate::ProjectSnapshot>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub cumulative_token_usage: language_model::TokenUsage,
|
pub cumulative_token_usage: language_model::TokenUsage,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -61,13 +60,17 @@ impl DbThread {
|
|||||||
match saved_thread_json.get("version") {
|
match saved_thread_json.get("version") {
|
||||||
Some(serde_json::Value::String(version)) => match version.as_str() {
|
Some(serde_json::Value::String(version)) => match version.as_str() {
|
||||||
Self::VERSION => Ok(serde_json::from_value(saved_thread_json)?),
|
Self::VERSION => Ok(serde_json::from_value(saved_thread_json)?),
|
||||||
_ => Self::upgrade_from_agent_1(agent::SerializedThread::from_json(json)?),
|
_ => Self::upgrade_from_agent_1(crate::legacy_thread::SerializedThread::from_json(
|
||||||
|
json,
|
||||||
|
)?),
|
||||||
},
|
},
|
||||||
_ => Self::upgrade_from_agent_1(agent::SerializedThread::from_json(json)?),
|
_ => {
|
||||||
|
Self::upgrade_from_agent_1(crate::legacy_thread::SerializedThread::from_json(json)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_from_agent_1(thread: agent::SerializedThread) -> Result<Self> {
|
fn upgrade_from_agent_1(thread: crate::legacy_thread::SerializedThread) -> Result<Self> {
|
||||||
let mut messages = Vec::new();
|
let mut messages = Vec::new();
|
||||||
let mut request_token_usage = HashMap::default();
|
let mut request_token_usage = HashMap::default();
|
||||||
|
|
||||||
@@ -80,14 +83,19 @@ impl DbThread {
|
|||||||
// Convert segments to content
|
// Convert segments to content
|
||||||
for segment in msg.segments {
|
for segment in msg.segments {
|
||||||
match segment {
|
match segment {
|
||||||
thread_store::SerializedMessageSegment::Text { text } => {
|
crate::legacy_thread::SerializedMessageSegment::Text { text } => {
|
||||||
content.push(UserMessageContent::Text(text));
|
content.push(UserMessageContent::Text(text));
|
||||||
}
|
}
|
||||||
thread_store::SerializedMessageSegment::Thinking { text, .. } => {
|
crate::legacy_thread::SerializedMessageSegment::Thinking {
|
||||||
|
text,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// User messages don't have thinking segments, but handle gracefully
|
// User messages don't have thinking segments, but handle gracefully
|
||||||
content.push(UserMessageContent::Text(text));
|
content.push(UserMessageContent::Text(text));
|
||||||
}
|
}
|
||||||
thread_store::SerializedMessageSegment::RedactedThinking { .. } => {
|
crate::legacy_thread::SerializedMessageSegment::RedactedThinking {
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// User messages don't have redacted thinking, skip.
|
// User messages don't have redacted thinking, skip.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,16 +121,18 @@ impl DbThread {
|
|||||||
// Convert segments to content
|
// Convert segments to content
|
||||||
for segment in msg.segments {
|
for segment in msg.segments {
|
||||||
match segment {
|
match segment {
|
||||||
thread_store::SerializedMessageSegment::Text { text } => {
|
crate::legacy_thread::SerializedMessageSegment::Text { text } => {
|
||||||
content.push(AgentMessageContent::Text(text));
|
content.push(AgentMessageContent::Text(text));
|
||||||
}
|
}
|
||||||
thread_store::SerializedMessageSegment::Thinking {
|
crate::legacy_thread::SerializedMessageSegment::Thinking {
|
||||||
text,
|
text,
|
||||||
signature,
|
signature,
|
||||||
} => {
|
} => {
|
||||||
content.push(AgentMessageContent::Thinking { text, signature });
|
content.push(AgentMessageContent::Thinking { text, signature });
|
||||||
}
|
}
|
||||||
thread_store::SerializedMessageSegment::RedactedThinking { data } => {
|
crate::legacy_thread::SerializedMessageSegment::RedactedThinking {
|
||||||
|
data,
|
||||||
|
} => {
|
||||||
content.push(AgentMessageContent::RedactedThinking(data));
|
content.push(AgentMessageContent::RedactedThinking(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,10 +197,9 @@ impl DbThread {
|
|||||||
messages,
|
messages,
|
||||||
updated_at: thread.updated_at,
|
updated_at: thread.updated_at,
|
||||||
detailed_summary: match thread.detailed_summary_state {
|
detailed_summary: match thread.detailed_summary_state {
|
||||||
DetailedSummaryState::NotGenerated | DetailedSummaryState::Generating { .. } => {
|
crate::legacy_thread::DetailedSummaryState::NotGenerated
|
||||||
None
|
| crate::legacy_thread::DetailedSummaryState::Generating => None,
|
||||||
}
|
crate::legacy_thread::DetailedSummaryState::Generated { text, .. } => Some(text),
|
||||||
DetailedSummaryState::Generated { text, .. } => Some(text),
|
|
||||||
},
|
},
|
||||||
initial_project_snapshot: thread.initial_project_snapshot,
|
initial_project_snapshot: thread.initial_project_snapshot,
|
||||||
cumulative_token_usage: thread.cumulative_token_usage,
|
cumulative_token_usage: thread.cumulative_token_usage,
|
||||||
@@ -414,84 +423,3 @@ impl ThreadsDatabase {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use agent::MessageSegment;
|
|
||||||
use agent::context::LoadedContext;
|
|
||||||
use client::Client;
|
|
||||||
use fs::{FakeFs, Fs};
|
|
||||||
use gpui::AppContext;
|
|
||||||
use gpui::TestAppContext;
|
|
||||||
use http_client::FakeHttpClient;
|
|
||||||
use language_model::Role;
|
|
||||||
use project::Project;
|
|
||||||
use settings::SettingsStore;
|
|
||||||
|
|
||||||
fn init_test(fs: Arc<dyn Fs>, cx: &mut TestAppContext) {
|
|
||||||
env_logger::try_init().ok();
|
|
||||||
cx.update(|cx| {
|
|
||||||
let settings_store = SettingsStore::test(cx);
|
|
||||||
cx.set_global(settings_store);
|
|
||||||
Project::init_settings(cx);
|
|
||||||
language::init(cx);
|
|
||||||
|
|
||||||
let http_client = FakeHttpClient::with_404_response();
|
|
||||||
let clock = Arc::new(clock::FakeSystemClock::new());
|
|
||||||
let client = Client::new(clock, http_client, cx);
|
|
||||||
agent::init(fs, cx);
|
|
||||||
agent_settings::init(cx);
|
|
||||||
language_model::init(client, cx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[gpui::test]
|
|
||||||
async fn test_retrieving_old_thread(cx: &mut TestAppContext) {
|
|
||||||
let fs = FakeFs::new(cx.executor());
|
|
||||||
init_test(fs.clone(), cx);
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
|
||||||
|
|
||||||
// Save a thread using the old agent.
|
|
||||||
let thread_store = cx.new(|cx| agent::ThreadStore::fake(project, cx));
|
|
||||||
let thread = thread_store.update(cx, |thread_store, cx| thread_store.create_thread(cx));
|
|
||||||
thread.update(cx, |thread, cx| {
|
|
||||||
thread.insert_message(
|
|
||||||
Role::User,
|
|
||||||
vec![MessageSegment::Text("Hey!".into())],
|
|
||||||
LoadedContext::default(),
|
|
||||||
vec![],
|
|
||||||
false,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
thread.insert_message(
|
|
||||||
Role::Assistant,
|
|
||||||
vec![MessageSegment::Text("How're you doing?".into())],
|
|
||||||
LoadedContext::default(),
|
|
||||||
vec![],
|
|
||||||
false,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
thread_store
|
|
||||||
.update(cx, |thread_store, cx| thread_store.save_thread(&thread, cx))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Open that same thread using the new agent.
|
|
||||||
let db = cx.update(ThreadsDatabase::connect).await.unwrap();
|
|
||||||
let threads = db.list_threads().await.unwrap();
|
|
||||||
assert_eq!(threads.len(), 1);
|
|
||||||
let thread = db
|
|
||||||
.load_thread(threads[0].id.clone())
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(thread.messages[0].to_markdown(), "## User\n\nHey!\n");
|
|
||||||
assert_eq!(
|
|
||||||
thread.messages[1].to_markdown(),
|
|
||||||
"## Assistant\n\nHow're you doing?\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
ReadFileToolInput,
|
EditFileMode, EditFileToolInput, GrepToolInput, ListDirectoryToolInput, ReadFileToolInput,
|
||||||
edit_file_tool::{EditFileMode, EditFileToolInput},
|
|
||||||
grep_tool::GrepToolInput,
|
|
||||||
list_directory_tool::ListDirectoryToolInput,
|
|
||||||
};
|
};
|
||||||
use Role::*;
|
use Role::*;
|
||||||
use assistant_tool::ToolRegistry;
|
|
||||||
use client::{Client, UserStore};
|
use client::{Client, UserStore};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
@@ -15,11 +11,11 @@ use gpui::{AppContext, TestAppContext, Timer};
|
|||||||
use http_client::StatusCode;
|
use http_client::StatusCode;
|
||||||
use indoc::{formatdoc, indoc};
|
use indoc::{formatdoc, indoc};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModelRegistry, LanguageModelRequestTool, LanguageModelToolResult,
|
LanguageModelRegistry, LanguageModelToolResult, LanguageModelToolResultContent,
|
||||||
LanguageModelToolResultContent, LanguageModelToolUse, LanguageModelToolUseId, SelectedModel,
|
LanguageModelToolUse, LanguageModelToolUseId, SelectedModel,
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use prompt_store::{ModelContext, ProjectContext, PromptBuilder, WorktreeContext};
|
use prompt_store::{ProjectContext, WorktreeContext};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use reqwest_client::ReqwestClient;
|
use reqwest_client::ReqwestClient;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@@ -121,6 +117,7 @@ fn eval_delete_run_git_blame() {
|
|||||||
// gemini-2.5-pro-06-05 | 1.0 (2025-06-16)
|
// gemini-2.5-pro-06-05 | 1.0 (2025-06-16)
|
||||||
// gemini-2.5-flash |
|
// gemini-2.5-flash |
|
||||||
// gpt-4.1 |
|
// gpt-4.1 |
|
||||||
|
|
||||||
let input_file_path = "root/blame.rs";
|
let input_file_path = "root/blame.rs";
|
||||||
let input_file_content = include_str!("evals/fixtures/delete_run_git_blame/before.rs");
|
let input_file_content = include_str!("evals/fixtures/delete_run_git_blame/before.rs");
|
||||||
let output_file_content = include_str!("evals/fixtures/delete_run_git_blame/after.rs");
|
let output_file_content = include_str!("evals/fixtures/delete_run_git_blame/after.rs");
|
||||||
@@ -184,6 +181,7 @@ fn eval_translate_doc_comments() {
|
|||||||
// gemini-2.5-pro-preview-03-25 | 1.0 (2025-05-22)
|
// gemini-2.5-pro-preview-03-25 | 1.0 (2025-05-22)
|
||||||
// gemini-2.5-flash-preview-04-17 |
|
// gemini-2.5-flash-preview-04-17 |
|
||||||
// gpt-4.1 |
|
// gpt-4.1 |
|
||||||
|
|
||||||
let input_file_path = "root/canvas.rs";
|
let input_file_path = "root/canvas.rs";
|
||||||
let input_file_content = include_str!("evals/fixtures/translate_doc_comments/before.rs");
|
let input_file_content = include_str!("evals/fixtures/translate_doc_comments/before.rs");
|
||||||
let edit_description = "Translate all doc comments to Italian";
|
let edit_description = "Translate all doc comments to Italian";
|
||||||
@@ -246,6 +244,7 @@ fn eval_use_wasi_sdk_in_compile_parser_to_wasm() {
|
|||||||
// gemini-2.5-pro-preview-latest | 0.99 (2025-06-16)
|
// gemini-2.5-pro-preview-latest | 0.99 (2025-06-16)
|
||||||
// gemini-2.5-flash-preview-04-17 |
|
// gemini-2.5-flash-preview-04-17 |
|
||||||
// gpt-4.1 |
|
// gpt-4.1 |
|
||||||
|
|
||||||
let input_file_path = "root/lib.rs";
|
let input_file_path = "root/lib.rs";
|
||||||
let input_file_content =
|
let input_file_content =
|
||||||
include_str!("evals/fixtures/use_wasi_sdk_in_compile_parser_to_wasm/before.rs");
|
include_str!("evals/fixtures/use_wasi_sdk_in_compile_parser_to_wasm/before.rs");
|
||||||
@@ -371,6 +370,7 @@ fn eval_disable_cursor_blinking() {
|
|||||||
// gemini-2.5-pro | 0.95 (2025-07-14)
|
// gemini-2.5-pro | 0.95 (2025-07-14)
|
||||||
// gemini-2.5-flash-preview-04-17 | 0.78 (2025-07-14)
|
// gemini-2.5-flash-preview-04-17 | 0.78 (2025-07-14)
|
||||||
// gpt-4.1 | 0.00 (2025-07-14) (follows edit_description too literally)
|
// gpt-4.1 | 0.00 (2025-07-14) (follows edit_description too literally)
|
||||||
|
|
||||||
let input_file_path = "root/editor.rs";
|
let input_file_path = "root/editor.rs";
|
||||||
let input_file_content = include_str!("evals/fixtures/disable_cursor_blinking/before.rs");
|
let input_file_content = include_str!("evals/fixtures/disable_cursor_blinking/before.rs");
|
||||||
let edit_description = "Comment out the call to `BlinkManager::enable`";
|
let edit_description = "Comment out the call to `BlinkManager::enable`";
|
||||||
@@ -463,6 +463,7 @@ fn eval_from_pixels_constructor() {
|
|||||||
// claude-3.7-sonnet | 2025-06-14 | 0.88
|
// claude-3.7-sonnet | 2025-06-14 | 0.88
|
||||||
// gemini-2.5-pro-preview-06-05 | 2025-06-16 | 0.98
|
// gemini-2.5-pro-preview-06-05 | 2025-06-16 | 0.98
|
||||||
// gpt-4.1 |
|
// gpt-4.1 |
|
||||||
|
|
||||||
let input_file_path = "root/canvas.rs";
|
let input_file_path = "root/canvas.rs";
|
||||||
let input_file_content = include_str!("evals/fixtures/from_pixels_constructor/before.rs");
|
let input_file_content = include_str!("evals/fixtures/from_pixels_constructor/before.rs");
|
||||||
let edit_description = "Implement from_pixels constructor and add tests.";
|
let edit_description = "Implement from_pixels constructor and add tests.";
|
||||||
@@ -665,6 +666,7 @@ fn eval_zode() {
|
|||||||
// gemini-2.5-pro-preview-03-25 | 1.0 (2025-05-22)
|
// gemini-2.5-pro-preview-03-25 | 1.0 (2025-05-22)
|
||||||
// gemini-2.5-flash-preview-04-17 | 1.0 (2025-05-22)
|
// gemini-2.5-flash-preview-04-17 | 1.0 (2025-05-22)
|
||||||
// gpt-4.1 | 1.0 (2025-05-22)
|
// gpt-4.1 | 1.0 (2025-05-22)
|
||||||
|
|
||||||
let input_file_path = "root/zode.py";
|
let input_file_path = "root/zode.py";
|
||||||
let input_content = None;
|
let input_content = None;
|
||||||
let edit_description = "Create the main Zode CLI script";
|
let edit_description = "Create the main Zode CLI script";
|
||||||
@@ -771,6 +773,7 @@ fn eval_add_overwrite_test() {
|
|||||||
// gemini-2.5-pro-preview-03-25 | 0.35 (2025-05-22)
|
// gemini-2.5-pro-preview-03-25 | 0.35 (2025-05-22)
|
||||||
// gemini-2.5-flash-preview-04-17 |
|
// gemini-2.5-flash-preview-04-17 |
|
||||||
// gpt-4.1 |
|
// gpt-4.1 |
|
||||||
|
|
||||||
let input_file_path = "root/action_log.rs";
|
let input_file_path = "root/action_log.rs";
|
||||||
let input_file_content = include_str!("evals/fixtures/add_overwrite_test/before.rs");
|
let input_file_content = include_str!("evals/fixtures/add_overwrite_test/before.rs");
|
||||||
let edit_description = "Add a new test for overwriting a file in action_log.rs";
|
let edit_description = "Add a new test for overwriting a file in action_log.rs";
|
||||||
@@ -1010,7 +1013,7 @@ fn eval_create_empty_file() {
|
|||||||
//
|
//
|
||||||
// TODO: gpt-4.1-mini errored 38 times:
|
// TODO: gpt-4.1-mini errored 38 times:
|
||||||
// "data did not match any variant of untagged enum ResponseStreamResult"
|
// "data did not match any variant of untagged enum ResponseStreamResult"
|
||||||
//
|
|
||||||
let input_file_content = None;
|
let input_file_content = None;
|
||||||
let expected_output_content = String::new();
|
let expected_output_content = String::new();
|
||||||
eval(
|
eval(
|
||||||
@@ -1475,19 +1478,16 @@ impl EditAgentTest {
|
|||||||
language::init(cx);
|
language::init(cx);
|
||||||
language_model::init(client.clone(), cx);
|
language_model::init(client.clone(), cx);
|
||||||
language_models::init(user_store, client.clone(), cx);
|
language_models::init(user_store, client.clone(), cx);
|
||||||
crate::init(client.http_client(), cx);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.insert_tree("/root", json!({})).await;
|
fs.insert_tree("/root", json!({})).await;
|
||||||
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
|
let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
|
||||||
let agent_model = SelectedModel::from_str(
|
let agent_model = SelectedModel::from_str(
|
||||||
&std::env::var("ZED_AGENT_MODEL")
|
&std::env::var("ZED_AGENT_MODEL").unwrap_or("anthropic/claude-4-sonnet-latest".into()),
|
||||||
.unwrap_or("anthropic/claude-3-7-sonnet-latest".into()),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let judge_model = SelectedModel::from_str(
|
let judge_model = SelectedModel::from_str(
|
||||||
&std::env::var("ZED_JUDGE_MODEL")
|
&std::env::var("ZED_JUDGE_MODEL").unwrap_or("anthropic/claude-4-sonnet-latest".into()),
|
||||||
.unwrap_or("anthropic/claude-3-7-sonnet-latest".into()),
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (agent_model, judge_model) = cx
|
let (agent_model, judge_model) = cx
|
||||||
@@ -1553,39 +1553,27 @@ impl EditAgentTest {
|
|||||||
.update(cx, |project, cx| project.open_buffer(path, cx))
|
.update(cx, |project, cx| project.open_buffer(path, cx))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tools = cx.update(|cx| {
|
|
||||||
ToolRegistry::default_global(cx)
|
let tools = crate::built_in_tools().collect::<Vec<_>>();
|
||||||
.tools()
|
|
||||||
.into_iter()
|
let system_prompt = {
|
||||||
.filter_map(|tool| {
|
let worktrees = vec![WorktreeContext {
|
||||||
let input_schema = tool
|
root_name: "root".to_string(),
|
||||||
.input_schema(self.agent.model.tool_input_format())
|
abs_path: Path::new("/path/to/root").into(),
|
||||||
.ok()?;
|
rules_file: None,
|
||||||
Some(LanguageModelRequestTool {
|
}];
|
||||||
name: tool.name(),
|
let project_context = ProjectContext::new(worktrees, Vec::default());
|
||||||
description: tool.description(),
|
let tool_names = tools
|
||||||
input_schema,
|
.iter()
|
||||||
})
|
.map(|tool| tool.name.clone().into())
|
||||||
})
|
.collect::<Vec<_>>();
|
||||||
.collect::<Vec<_>>()
|
let template = crate::SystemPromptTemplate {
|
||||||
});
|
project: &project_context,
|
||||||
let tool_names = tools
|
|
||||||
.iter()
|
|
||||||
.map(|tool| tool.name.clone())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let worktrees = vec![WorktreeContext {
|
|
||||||
root_name: "root".to_string(),
|
|
||||||
abs_path: Path::new("/path/to/root").into(),
|
|
||||||
rules_file: None,
|
|
||||||
}];
|
|
||||||
let prompt_builder = PromptBuilder::new(None)?;
|
|
||||||
let project_context = ProjectContext::new(worktrees, Vec::default());
|
|
||||||
let system_prompt = prompt_builder.generate_assistant_system_prompt(
|
|
||||||
&project_context,
|
|
||||||
&ModelContext {
|
|
||||||
available_tools: tool_names,
|
available_tools: tool_names,
|
||||||
},
|
};
|
||||||
)?;
|
let templates = Templates::new();
|
||||||
|
template.render(&templates).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let has_system_prompt = eval
|
let has_system_prompt = eval
|
||||||
.conversation
|
.conversation
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{DbThreadMetadata, ThreadsDatabase};
|
use crate::{DbThread, DbThreadMetadata, ThreadsDatabase};
|
||||||
use acp_thread::MentionUri;
|
use acp_thread::MentionUri;
|
||||||
use agent_client_protocol as acp;
|
use agent_client_protocol as acp;
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
@@ -8,8 +8,9 @@ use db::kvp::KEY_VALUE_STORE;
|
|||||||
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
|
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::contexts_dir;
|
use paths::contexts_dir;
|
||||||
|
use project::Project;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::VecDeque, path::Path, sync::Arc, time::Duration};
|
use std::{collections::VecDeque, path::Path, rc::Rc, sync::Arc, time::Duration};
|
||||||
use ui::ElementId;
|
use ui::ElementId;
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
|
||||||
@@ -19,6 +20,33 @@ const SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE: Duration = Duration::from_millis(50
|
|||||||
|
|
||||||
const DEFAULT_TITLE: &SharedString = &SharedString::new_static("New Thread");
|
const DEFAULT_TITLE: &SharedString = &SharedString::new_static("New Thread");
|
||||||
|
|
||||||
|
//todo: We should remove this function once we support loading all acp thread
|
||||||
|
pub fn load_agent_thread(
|
||||||
|
session_id: acp::SessionId,
|
||||||
|
history_store: Entity<HistoryStore>,
|
||||||
|
project: Entity<Project>,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> Task<Result<Entity<crate::Thread>>> {
|
||||||
|
use agent_servers::{AgentServer, AgentServerDelegate};
|
||||||
|
|
||||||
|
let server = Rc::new(crate::NativeAgentServer::new(
|
||||||
|
project.read(cx).fs().clone(),
|
||||||
|
history_store,
|
||||||
|
));
|
||||||
|
let delegate = AgentServerDelegate::new(
|
||||||
|
project.read(cx).agent_server_store().clone(),
|
||||||
|
project.clone(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let connection = server.connect(None, delegate, cx);
|
||||||
|
cx.spawn(async move |cx| {
|
||||||
|
let (agent, _) = connection.await?;
|
||||||
|
let agent = agent.downcast::<crate::NativeAgentConnection>().unwrap();
|
||||||
|
cx.update(|cx| agent.load_thread(session_id, cx))?.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum HistoryEntry {
|
pub enum HistoryEntry {
|
||||||
AcpThread(DbThreadMetadata),
|
AcpThread(DbThreadMetadata),
|
||||||
@@ -55,8 +83,13 @@ impl HistoryEntry {
|
|||||||
|
|
||||||
pub fn title(&self) -> &SharedString {
|
pub fn title(&self) -> &SharedString {
|
||||||
match self {
|
match self {
|
||||||
HistoryEntry::AcpThread(thread) if thread.title.is_empty() => DEFAULT_TITLE,
|
HistoryEntry::AcpThread(thread) => {
|
||||||
HistoryEntry::AcpThread(thread) => &thread.title,
|
if thread.title.is_empty() {
|
||||||
|
DEFAULT_TITLE
|
||||||
|
} else {
|
||||||
|
&thread.title
|
||||||
|
}
|
||||||
|
}
|
||||||
HistoryEntry::TextThread(context) => &context.title,
|
HistoryEntry::TextThread(context) => &context.title,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +120,7 @@ enum SerializedRecentOpen {
|
|||||||
pub struct HistoryStore {
|
pub struct HistoryStore {
|
||||||
threads: Vec<DbThreadMetadata>,
|
threads: Vec<DbThreadMetadata>,
|
||||||
entries: Vec<HistoryEntry>,
|
entries: Vec<HistoryEntry>,
|
||||||
context_store: Entity<assistant_context::ContextStore>,
|
text_thread_store: Entity<assistant_context::ContextStore>,
|
||||||
recently_opened_entries: VecDeque<HistoryEntryId>,
|
recently_opened_entries: VecDeque<HistoryEntryId>,
|
||||||
_subscriptions: Vec<gpui::Subscription>,
|
_subscriptions: Vec<gpui::Subscription>,
|
||||||
_save_recently_opened_entries_task: Task<()>,
|
_save_recently_opened_entries_task: Task<()>,
|
||||||
@@ -95,10 +128,11 @@ pub struct HistoryStore {
|
|||||||
|
|
||||||
impl HistoryStore {
|
impl HistoryStore {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
context_store: Entity<assistant_context::ContextStore>,
|
text_thread_store: Entity<assistant_context::ContextStore>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let subscriptions = vec![cx.observe(&context_store, |this, _, cx| this.update_entries(cx))];
|
let subscriptions =
|
||||||
|
vec![cx.observe(&text_thread_store, |this, _, cx| this.update_entries(cx))];
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let entries = Self::load_recently_opened_entries(cx).await;
|
let entries = Self::load_recently_opened_entries(cx).await;
|
||||||
@@ -114,7 +148,7 @@ impl HistoryStore {
|
|||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
context_store,
|
text_thread_store,
|
||||||
recently_opened_entries: VecDeque::default(),
|
recently_opened_entries: VecDeque::default(),
|
||||||
threads: Vec::default(),
|
threads: Vec::default(),
|
||||||
entries: Vec::default(),
|
entries: Vec::default(),
|
||||||
@@ -127,6 +161,18 @@ impl HistoryStore {
|
|||||||
self.threads.iter().find(|thread| &thread.id == session_id)
|
self.threads.iter().find(|thread| &thread.id == session_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_thread(
|
||||||
|
&mut self,
|
||||||
|
id: acp::SessionId,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) -> Task<Result<Option<DbThread>>> {
|
||||||
|
let database_future = ThreadsDatabase::connect(cx);
|
||||||
|
cx.background_spawn(async move {
|
||||||
|
let database = database_future.await.map_err(|err| anyhow!(err))?;
|
||||||
|
database.load_thread(id).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delete_thread(
|
pub fn delete_thread(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: acp::SessionId,
|
id: acp::SessionId,
|
||||||
@@ -145,9 +191,8 @@ impl HistoryStore {
|
|||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
self.context_store.update(cx, |context_store, cx| {
|
self.text_thread_store
|
||||||
context_store.delete_local_context(path, cx)
|
.update(cx, |store, cx| store.delete_local_context(path, cx))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_text_thread(
|
pub fn load_text_thread(
|
||||||
@@ -155,9 +200,8 @@ impl HistoryStore {
|
|||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Entity<AssistantContext>>> {
|
) -> Task<Result<Entity<AssistantContext>>> {
|
||||||
self.context_store.update(cx, |context_store, cx| {
|
self.text_thread_store
|
||||||
context_store.open_local_context(path, cx)
|
.update(cx, |store, cx| store.open_local_context(path, cx))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload(&self, cx: &mut Context<Self>) {
|
pub fn reload(&self, cx: &mut Context<Self>) {
|
||||||
@@ -197,7 +241,7 @@ impl HistoryStore {
|
|||||||
let mut history_entries = Vec::new();
|
let mut history_entries = Vec::new();
|
||||||
history_entries.extend(self.threads.iter().cloned().map(HistoryEntry::AcpThread));
|
history_entries.extend(self.threads.iter().cloned().map(HistoryEntry::AcpThread));
|
||||||
history_entries.extend(
|
history_entries.extend(
|
||||||
self.context_store
|
self.text_thread_store
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.unordered_contexts()
|
.unordered_contexts()
|
||||||
.cloned()
|
.cloned()
|
||||||
@@ -231,21 +275,21 @@ impl HistoryStore {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let context_entries =
|
let context_entries = self
|
||||||
self.context_store
|
.text_thread_store
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.unordered_contexts()
|
.unordered_contexts()
|
||||||
.flat_map(|context| {
|
.flat_map(|context| {
|
||||||
self.recently_opened_entries
|
self.recently_opened_entries
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(index, entry)| match entry {
|
.flat_map(|(index, entry)| match entry {
|
||||||
HistoryEntryId::TextThread(path) if &context.path == path => {
|
HistoryEntryId::TextThread(path) if &context.path == path => {
|
||||||
Some((index, HistoryEntry::TextThread(context.clone())))
|
Some((index, HistoryEntry::TextThread(context.clone())))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
thread_entries
|
thread_entries
|
||||||
.chain(context_entries)
|
.chain(context_entries)
|
||||||
402
crates/agent/src/legacy_thread.rs
Normal file
402
crates/agent/src/legacy_thread.rs
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
use crate::ProjectSnapshot;
|
||||||
|
use agent_settings::{AgentProfileId, CompletionMode};
|
||||||
|
use anyhow::Result;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use gpui::SharedString;
|
||||||
|
use language_model::{LanguageModelToolResultContent, LanguageModelToolUseId, Role, TokenUsage};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub enum DetailedSummaryState {
|
||||||
|
#[default]
|
||||||
|
NotGenerated,
|
||||||
|
Generating,
|
||||||
|
Generated {
|
||||||
|
text: SharedString,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct MessageId(pub usize);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
|
pub struct SerializedThread {
|
||||||
|
pub version: String,
|
||||||
|
pub summary: SharedString,
|
||||||
|
pub updated_at: DateTime<Utc>,
|
||||||
|
pub messages: Vec<SerializedMessage>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub initial_project_snapshot: Option<Arc<ProjectSnapshot>>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub cumulative_token_usage: TokenUsage,
|
||||||
|
#[serde(default)]
|
||||||
|
pub request_token_usage: Vec<TokenUsage>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub detailed_summary_state: DetailedSummaryState,
|
||||||
|
#[serde(default)]
|
||||||
|
pub model: Option<SerializedLanguageModel>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub completion_mode: Option<CompletionMode>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tool_use_limit_reached: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub profile: Option<AgentProfileId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
|
pub struct SerializedLanguageModel {
|
||||||
|
pub provider: String,
|
||||||
|
pub model: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerializedThread {
|
||||||
|
pub const VERSION: &'static str = "0.2.0";
|
||||||
|
|
||||||
|
pub fn from_json(json: &[u8]) -> Result<Self> {
|
||||||
|
let saved_thread_json = serde_json::from_slice::<serde_json::Value>(json)?;
|
||||||
|
match saved_thread_json.get("version") {
|
||||||
|
Some(serde_json::Value::String(version)) => match version.as_str() {
|
||||||
|
SerializedThreadV0_1_0::VERSION => {
|
||||||
|
let saved_thread =
|
||||||
|
serde_json::from_value::<SerializedThreadV0_1_0>(saved_thread_json)?;
|
||||||
|
Ok(saved_thread.upgrade())
|
||||||
|
}
|
||||||
|
SerializedThread::VERSION => Ok(serde_json::from_value::<SerializedThread>(
|
||||||
|
saved_thread_json,
|
||||||
|
)?),
|
||||||
|
_ => anyhow::bail!("unrecognized serialized thread version: {version:?}"),
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let saved_thread =
|
||||||
|
serde_json::from_value::<LegacySerializedThread>(saved_thread_json)?;
|
||||||
|
Ok(saved_thread.upgrade())
|
||||||
|
}
|
||||||
|
version => anyhow::bail!("unrecognized serialized thread version: {version:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct SerializedThreadV0_1_0(
|
||||||
|
// The structure did not change, so we are reusing the latest SerializedThread.
|
||||||
|
// When making the next version, make sure this points to SerializedThreadV0_2_0
|
||||||
|
SerializedThread,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl SerializedThreadV0_1_0 {
|
||||||
|
pub const VERSION: &'static str = "0.1.0";
|
||||||
|
|
||||||
|
pub fn upgrade(self) -> SerializedThread {
|
||||||
|
debug_assert_eq!(SerializedThread::VERSION, "0.2.0");
|
||||||
|
|
||||||
|
let mut messages: Vec<SerializedMessage> = Vec::with_capacity(self.0.messages.len());
|
||||||
|
|
||||||
|
for message in self.0.messages {
|
||||||
|
if message.role == Role::User
|
||||||
|
&& !message.tool_results.is_empty()
|
||||||
|
&& let Some(last_message) = messages.last_mut()
|
||||||
|
{
|
||||||
|
debug_assert!(last_message.role == Role::Assistant);
|
||||||
|
|
||||||
|
last_message.tool_results = message.tool_results;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedThread {
|
||||||
|
messages,
|
||||||
|
version: SerializedThread::VERSION.to_string(),
|
||||||
|
..self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct SerializedMessage {
|
||||||
|
pub id: MessageId,
|
||||||
|
pub role: Role,
|
||||||
|
#[serde(default)]
|
||||||
|
pub segments: Vec<SerializedMessageSegment>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tool_uses: Vec<SerializedToolUse>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tool_results: Vec<SerializedToolResult>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub context: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub creases: Vec<SerializedCrease>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub is_hidden: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum SerializedMessageSegment {
|
||||||
|
#[serde(rename = "text")]
|
||||||
|
Text {
|
||||||
|
text: String,
|
||||||
|
},
|
||||||
|
#[serde(rename = "thinking")]
|
||||||
|
Thinking {
|
||||||
|
text: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
signature: Option<String>,
|
||||||
|
},
|
||||||
|
RedactedThinking {
|
||||||
|
data: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct SerializedToolUse {
|
||||||
|
pub id: LanguageModelToolUseId,
|
||||||
|
pub name: SharedString,
|
||||||
|
pub input: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct SerializedToolResult {
|
||||||
|
pub tool_use_id: LanguageModelToolUseId,
|
||||||
|
pub is_error: bool,
|
||||||
|
pub content: LanguageModelToolResultContent,
|
||||||
|
pub output: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct LegacySerializedThread {
|
||||||
|
pub summary: SharedString,
|
||||||
|
pub updated_at: DateTime<Utc>,
|
||||||
|
pub messages: Vec<LegacySerializedMessage>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub initial_project_snapshot: Option<Arc<ProjectSnapshot>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LegacySerializedThread {
|
||||||
|
pub fn upgrade(self) -> SerializedThread {
|
||||||
|
SerializedThread {
|
||||||
|
version: SerializedThread::VERSION.to_string(),
|
||||||
|
summary: self.summary,
|
||||||
|
updated_at: self.updated_at,
|
||||||
|
messages: self.messages.into_iter().map(|msg| msg.upgrade()).collect(),
|
||||||
|
initial_project_snapshot: self.initial_project_snapshot,
|
||||||
|
cumulative_token_usage: TokenUsage::default(),
|
||||||
|
request_token_usage: Vec::new(),
|
||||||
|
detailed_summary_state: DetailedSummaryState::default(),
|
||||||
|
model: None,
|
||||||
|
completion_mode: None,
|
||||||
|
tool_use_limit_reached: false,
|
||||||
|
profile: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct LegacySerializedMessage {
|
||||||
|
pub id: MessageId,
|
||||||
|
pub role: Role,
|
||||||
|
pub text: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tool_uses: Vec<SerializedToolUse>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tool_results: Vec<SerializedToolResult>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LegacySerializedMessage {
|
||||||
|
fn upgrade(self) -> SerializedMessage {
|
||||||
|
SerializedMessage {
|
||||||
|
id: self.id,
|
||||||
|
role: self.role,
|
||||||
|
segments: vec![SerializedMessageSegment::Text { text: self.text }],
|
||||||
|
tool_uses: self.tool_uses,
|
||||||
|
tool_results: self.tool_results,
|
||||||
|
context: String::new(),
|
||||||
|
creases: Vec::new(),
|
||||||
|
is_hidden: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct SerializedCrease {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub icon_path: SharedString,
|
||||||
|
pub label: SharedString,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use chrono::Utc;
|
||||||
|
use language_model::{Role, TokenUsage};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_legacy_serialized_thread_upgrade() {
|
||||||
|
let updated_at = Utc::now();
|
||||||
|
let legacy_thread = LegacySerializedThread {
|
||||||
|
summary: "Test conversation".into(),
|
||||||
|
updated_at,
|
||||||
|
messages: vec![LegacySerializedMessage {
|
||||||
|
id: MessageId(1),
|
||||||
|
role: Role::User,
|
||||||
|
text: "Hello, world!".to_string(),
|
||||||
|
tool_uses: vec![],
|
||||||
|
tool_results: vec![],
|
||||||
|
}],
|
||||||
|
initial_project_snapshot: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let upgraded = legacy_thread.upgrade();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
upgraded,
|
||||||
|
SerializedThread {
|
||||||
|
summary: "Test conversation".into(),
|
||||||
|
updated_at,
|
||||||
|
messages: vec![SerializedMessage {
|
||||||
|
id: MessageId(1),
|
||||||
|
role: Role::User,
|
||||||
|
segments: vec![SerializedMessageSegment::Text {
|
||||||
|
text: "Hello, world!".to_string()
|
||||||
|
}],
|
||||||
|
tool_uses: vec![],
|
||||||
|
tool_results: vec![],
|
||||||
|
context: "".to_string(),
|
||||||
|
creases: vec![],
|
||||||
|
is_hidden: false
|
||||||
|
}],
|
||||||
|
version: SerializedThread::VERSION.to_string(),
|
||||||
|
initial_project_snapshot: None,
|
||||||
|
cumulative_token_usage: TokenUsage::default(),
|
||||||
|
request_token_usage: vec![],
|
||||||
|
detailed_summary_state: DetailedSummaryState::default(),
|
||||||
|
model: None,
|
||||||
|
completion_mode: None,
|
||||||
|
tool_use_limit_reached: false,
|
||||||
|
profile: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialized_threadv0_1_0_upgrade() {
|
||||||
|
let updated_at = Utc::now();
|
||||||
|
let thread_v0_1_0 = SerializedThreadV0_1_0(SerializedThread {
|
||||||
|
summary: "Test conversation".into(),
|
||||||
|
updated_at,
|
||||||
|
messages: vec![
|
||||||
|
SerializedMessage {
|
||||||
|
id: MessageId(1),
|
||||||
|
role: Role::User,
|
||||||
|
segments: vec![SerializedMessageSegment::Text {
|
||||||
|
text: "Use tool_1".to_string(),
|
||||||
|
}],
|
||||||
|
tool_uses: vec![],
|
||||||
|
tool_results: vec![],
|
||||||
|
context: "".to_string(),
|
||||||
|
creases: vec![],
|
||||||
|
is_hidden: false,
|
||||||
|
},
|
||||||
|
SerializedMessage {
|
||||||
|
id: MessageId(2),
|
||||||
|
role: Role::Assistant,
|
||||||
|
segments: vec![SerializedMessageSegment::Text {
|
||||||
|
text: "I want to use a tool".to_string(),
|
||||||
|
}],
|
||||||
|
tool_uses: vec![SerializedToolUse {
|
||||||
|
id: "abc".into(),
|
||||||
|
name: "tool_1".into(),
|
||||||
|
input: serde_json::Value::Null,
|
||||||
|
}],
|
||||||
|
tool_results: vec![],
|
||||||
|
context: "".to_string(),
|
||||||
|
creases: vec![],
|
||||||
|
is_hidden: false,
|
||||||
|
},
|
||||||
|
SerializedMessage {
|
||||||
|
id: MessageId(1),
|
||||||
|
role: Role::User,
|
||||||
|
segments: vec![SerializedMessageSegment::Text {
|
||||||
|
text: "Here is the tool result".to_string(),
|
||||||
|
}],
|
||||||
|
tool_uses: vec![],
|
||||||
|
tool_results: vec![SerializedToolResult {
|
||||||
|
tool_use_id: "abc".into(),
|
||||||
|
is_error: false,
|
||||||
|
content: LanguageModelToolResultContent::Text("abcdef".into()),
|
||||||
|
output: Some(serde_json::Value::Null),
|
||||||
|
}],
|
||||||
|
context: "".to_string(),
|
||||||
|
creases: vec![],
|
||||||
|
is_hidden: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: SerializedThreadV0_1_0::VERSION.to_string(),
|
||||||
|
initial_project_snapshot: None,
|
||||||
|
cumulative_token_usage: TokenUsage::default(),
|
||||||
|
request_token_usage: vec![],
|
||||||
|
detailed_summary_state: DetailedSummaryState::default(),
|
||||||
|
model: None,
|
||||||
|
completion_mode: None,
|
||||||
|
tool_use_limit_reached: false,
|
||||||
|
profile: None,
|
||||||
|
});
|
||||||
|
let upgraded = thread_v0_1_0.upgrade();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
upgraded,
|
||||||
|
SerializedThread {
|
||||||
|
summary: "Test conversation".into(),
|
||||||
|
updated_at,
|
||||||
|
messages: vec![
|
||||||
|
SerializedMessage {
|
||||||
|
id: MessageId(1),
|
||||||
|
role: Role::User,
|
||||||
|
segments: vec![SerializedMessageSegment::Text {
|
||||||
|
text: "Use tool_1".to_string()
|
||||||
|
}],
|
||||||
|
tool_uses: vec![],
|
||||||
|
tool_results: vec![],
|
||||||
|
context: "".to_string(),
|
||||||
|
creases: vec![],
|
||||||
|
is_hidden: false
|
||||||
|
},
|
||||||
|
SerializedMessage {
|
||||||
|
id: MessageId(2),
|
||||||
|
role: Role::Assistant,
|
||||||
|
segments: vec![SerializedMessageSegment::Text {
|
||||||
|
text: "I want to use a tool".to_string(),
|
||||||
|
}],
|
||||||
|
tool_uses: vec![SerializedToolUse {
|
||||||
|
id: "abc".into(),
|
||||||
|
name: "tool_1".into(),
|
||||||
|
input: serde_json::Value::Null,
|
||||||
|
}],
|
||||||
|
tool_results: vec![SerializedToolResult {
|
||||||
|
tool_use_id: "abc".into(),
|
||||||
|
is_error: false,
|
||||||
|
content: LanguageModelToolResultContent::Text("abcdef".into()),
|
||||||
|
output: Some(serde_json::Value::Null),
|
||||||
|
}],
|
||||||
|
context: "".to_string(),
|
||||||
|
creases: vec![],
|
||||||
|
is_hidden: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
version: SerializedThread::VERSION.to_string(),
|
||||||
|
initial_project_snapshot: None,
|
||||||
|
cumulative_token_usage: TokenUsage::default(),
|
||||||
|
request_token_usage: vec![],
|
||||||
|
detailed_summary_state: DetailedSummaryState::default(),
|
||||||
|
model: None,
|
||||||
|
completion_mode: None,
|
||||||
|
tool_use_limit_reached: false,
|
||||||
|
profile: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use std::{any::Any, path::Path, rc::Rc, sync::Arc};
|
|||||||
|
|
||||||
use agent_servers::{AgentServer, AgentServerDelegate};
|
use agent_servers::{AgentServer, AgentServerDelegate};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use collections::HashMap;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{App, Entity, SharedString, Task};
|
use gpui::{App, Entity, SharedString, Task};
|
||||||
use prompt_store::PromptStore;
|
use prompt_store::PromptStore;
|
||||||
@@ -41,7 +42,7 @@ impl AgentServer for NativeAgentServer {
|
|||||||
) -> Task<
|
) -> Task<
|
||||||
Result<(
|
Result<(
|
||||||
Rc<dyn acp_thread::AgentConnection>,
|
Rc<dyn acp_thread::AgentConnection>,
|
||||||
Option<task::SpawnInTerminal>,
|
HashMap<String, task::SpawnInTerminal>,
|
||||||
)>,
|
)>,
|
||||||
> {
|
> {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@@ -67,7 +68,7 @@ impl AgentServer for NativeAgentServer {
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Rc::new(connection) as Rc<dyn acp_thread::AgentConnection>,
|
Rc::new(connection) as Rc<dyn acp_thread::AgentConnection>,
|
||||||
None,
|
HashMap::default(),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
use action_log::ActionLog;
|
use anyhow::Result;
|
||||||
use anyhow::{Context as _, Result};
|
|
||||||
use gpui::{AsyncApp, Entity};
|
use gpui::{AsyncApp, Entity};
|
||||||
use language::{Buffer, OutlineItem, ParseStatus};
|
use language::{Buffer, OutlineItem, ParseStatus};
|
||||||
use project::Project;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use text::Point;
|
use text::Point;
|
||||||
@@ -11,51 +9,66 @@ use text::Point;
|
|||||||
/// we automatically provide the file's symbol outline instead, with line numbers.
|
/// we automatically provide the file's symbol outline instead, with line numbers.
|
||||||
pub const AUTO_OUTLINE_SIZE: usize = 16384;
|
pub const AUTO_OUTLINE_SIZE: usize = 16384;
|
||||||
|
|
||||||
pub async fn file_outline(
|
/// Result of getting buffer content, which can be either full content or an outline.
|
||||||
project: Entity<Project>,
|
pub struct BufferContent {
|
||||||
path: String,
|
/// The actual content (either full text or outline)
|
||||||
action_log: Entity<ActionLog>,
|
pub text: String,
|
||||||
regex: Option<Regex>,
|
/// Whether this is an outline (true) or full content (false)
|
||||||
cx: &mut AsyncApp,
|
pub is_outline: bool,
|
||||||
) -> anyhow::Result<String> {
|
|
||||||
let buffer = {
|
|
||||||
let project_path = project.read_with(cx, |project, cx| {
|
|
||||||
project
|
|
||||||
.find_project_path(&path, cx)
|
|
||||||
.with_context(|| format!("Path {path} not found in project"))
|
|
||||||
})??;
|
|
||||||
|
|
||||||
project
|
|
||||||
.update(cx, |project, cx| project.open_buffer(project_path, cx))?
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
action_log.update(cx, |action_log, cx| {
|
|
||||||
action_log.buffer_read(buffer.clone(), cx);
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Wait until the buffer has been fully parsed, so that we can read its outline.
|
|
||||||
let mut parse_status = buffer.read_with(cx, |buffer, _| buffer.parse_status())?;
|
|
||||||
while *parse_status.borrow() != ParseStatus::Idle {
|
|
||||||
parse_status.changed().await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
|
|
||||||
let outline = snapshot.outline(None);
|
|
||||||
|
|
||||||
render_outline(
|
|
||||||
outline
|
|
||||||
.items
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| item.to_point(&snapshot)),
|
|
||||||
regex,
|
|
||||||
0,
|
|
||||||
usize::MAX,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_outline(
|
/// Returns either the full content of a buffer or its outline, depending on size.
|
||||||
|
/// For files larger than AUTO_OUTLINE_SIZE, returns an outline with a header.
|
||||||
|
/// For smaller files, returns the full content.
|
||||||
|
pub async fn get_buffer_content_or_outline(
|
||||||
|
buffer: Entity<Buffer>,
|
||||||
|
path: Option<&str>,
|
||||||
|
cx: &AsyncApp,
|
||||||
|
) -> Result<BufferContent> {
|
||||||
|
let file_size = buffer.read_with(cx, |buffer, _| buffer.text().len())?;
|
||||||
|
|
||||||
|
if file_size > AUTO_OUTLINE_SIZE {
|
||||||
|
// For large files, use outline instead of full content
|
||||||
|
// Wait until the buffer has been fully parsed, so we can read its outline
|
||||||
|
let mut parse_status = buffer.read_with(cx, |buffer, _| buffer.parse_status())?;
|
||||||
|
while *parse_status.borrow() != ParseStatus::Idle {
|
||||||
|
parse_status.changed().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let outline_items = buffer.read_with(cx, |buffer, _| {
|
||||||
|
let snapshot = buffer.snapshot();
|
||||||
|
snapshot
|
||||||
|
.outline(None)
|
||||||
|
.items
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| item.to_point(&snapshot))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let outline_text = render_outline(outline_items, None, 0, usize::MAX).await?;
|
||||||
|
|
||||||
|
let text = if let Some(path) = path {
|
||||||
|
format!(
|
||||||
|
"# File outline for {path} (file too large to show full content)\n\n{outline_text}",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("# File outline (file too large to show full content)\n\n{outline_text}",)
|
||||||
|
};
|
||||||
|
Ok(BufferContent {
|
||||||
|
text,
|
||||||
|
is_outline: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// File is small enough, return full content
|
||||||
|
let text = buffer.read_with(cx, |buffer, _| buffer.text())?;
|
||||||
|
Ok(BufferContent {
|
||||||
|
text,
|
||||||
|
is_outline: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn render_outline(
|
||||||
items: impl IntoIterator<Item = OutlineItem<Point>>,
|
items: impl IntoIterator<Item = OutlineItem<Point>>,
|
||||||
regex: Option<Regex>,
|
regex: Option<Regex>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
@@ -128,62 +141,3 @@ fn render_entries(
|
|||||||
|
|
||||||
entries_rendered
|
entries_rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of getting buffer content, which can be either full content or an outline.
|
|
||||||
pub struct BufferContent {
|
|
||||||
/// The actual content (either full text or outline)
|
|
||||||
pub text: String,
|
|
||||||
/// Whether this is an outline (true) or full content (false)
|
|
||||||
pub is_outline: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns either the full content of a buffer or its outline, depending on size.
|
|
||||||
/// For files larger than AUTO_OUTLINE_SIZE, returns an outline with a header.
|
|
||||||
/// For smaller files, returns the full content.
|
|
||||||
pub async fn get_buffer_content_or_outline(
|
|
||||||
buffer: Entity<Buffer>,
|
|
||||||
path: Option<&str>,
|
|
||||||
cx: &AsyncApp,
|
|
||||||
) -> Result<BufferContent> {
|
|
||||||
let file_size = buffer.read_with(cx, |buffer, _| buffer.text().len())?;
|
|
||||||
|
|
||||||
if file_size > AUTO_OUTLINE_SIZE {
|
|
||||||
// For large files, use outline instead of full content
|
|
||||||
// Wait until the buffer has been fully parsed, so we can read its outline
|
|
||||||
let mut parse_status = buffer.read_with(cx, |buffer, _| buffer.parse_status())?;
|
|
||||||
while *parse_status.borrow() != ParseStatus::Idle {
|
|
||||||
parse_status.changed().await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let outline_items = buffer.read_with(cx, |buffer, _| {
|
|
||||||
let snapshot = buffer.snapshot();
|
|
||||||
snapshot
|
|
||||||
.outline(None)
|
|
||||||
.items
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| item.to_point(&snapshot))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let outline_text = render_outline(outline_items, None, 0, usize::MAX).await?;
|
|
||||||
|
|
||||||
let text = if let Some(path) = path {
|
|
||||||
format!(
|
|
||||||
"# File outline for {path} (file too large to show full content)\n\n{outline_text}",
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!("# File outline (file too large to show full content)\n\n{outline_text}",)
|
|
||||||
};
|
|
||||||
Ok(BufferContent {
|
|
||||||
text,
|
|
||||||
is_outline: true,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// File is small enough, return full content
|
|
||||||
let text = buffer.read_with(cx, |buffer, _| buffer.text())?;
|
|
||||||
Ok(BufferContent {
|
|
||||||
text,
|
|
||||||
is_outline: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[The following is an auto-generated notification; do not reply]
|
|
||||||
|
|
||||||
These files have changed since the last read:
|
|
||||||
@@ -975,9 +975,9 @@ async fn test_mcp_tools(cx: &mut TestAppContext) {
|
|||||||
vec![context_server::types::Tool {
|
vec![context_server::types::Tool {
|
||||||
name: "echo".into(),
|
name: "echo".into(),
|
||||||
description: None,
|
description: None,
|
||||||
input_schema: serde_json::to_value(
|
input_schema: serde_json::to_value(EchoTool::input_schema(
|
||||||
EchoTool.input_schema(LanguageModelToolSchemaFormat::JsonSchema),
|
LanguageModelToolSchemaFormat::JsonSchema,
|
||||||
)
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
output_schema: None,
|
output_schema: None,
|
||||||
annotations: None,
|
annotations: None,
|
||||||
@@ -1149,9 +1149,9 @@ async fn test_mcp_tool_truncation(cx: &mut TestAppContext) {
|
|||||||
context_server::types::Tool {
|
context_server::types::Tool {
|
||||||
name: "echo".into(), // Conflicts with native EchoTool
|
name: "echo".into(), // Conflicts with native EchoTool
|
||||||
description: None,
|
description: None,
|
||||||
input_schema: serde_json::to_value(
|
input_schema: serde_json::to_value(EchoTool::input_schema(
|
||||||
EchoTool.input_schema(LanguageModelToolSchemaFormat::JsonSchema),
|
LanguageModelToolSchemaFormat::JsonSchema,
|
||||||
)
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
output_schema: None,
|
output_schema: None,
|
||||||
annotations: None,
|
annotations: None,
|
||||||
@@ -1174,9 +1174,9 @@ async fn test_mcp_tool_truncation(cx: &mut TestAppContext) {
|
|||||||
context_server::types::Tool {
|
context_server::types::Tool {
|
||||||
name: "echo".into(), // Also conflicts with native EchoTool
|
name: "echo".into(), // Also conflicts with native EchoTool
|
||||||
description: None,
|
description: None,
|
||||||
input_schema: serde_json::to_value(
|
input_schema: serde_json::to_value(EchoTool::input_schema(
|
||||||
EchoTool.input_schema(LanguageModelToolSchemaFormat::JsonSchema),
|
LanguageModelToolSchemaFormat::JsonSchema,
|
||||||
)
|
))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
output_schema: None,
|
output_schema: None,
|
||||||
annotations: None,
|
annotations: None,
|
||||||
@@ -1864,7 +1864,7 @@ async fn test_agent_connection(cx: &mut TestAppContext) {
|
|||||||
let selector_opt = connection.model_selector(&session_id);
|
let selector_opt = connection.model_selector(&session_id);
|
||||||
assert!(
|
assert!(
|
||||||
selector_opt.is_some(),
|
selector_opt.is_some(),
|
||||||
"agent2 should always support ModelSelector"
|
"agent should always support ModelSelector"
|
||||||
);
|
);
|
||||||
let selector = selector_opt.unwrap();
|
let selector = selector_opt.unwrap();
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,48 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use language_model::LanguageModelToolSchemaFormat;
|
||||||
|
use schemars::{
|
||||||
|
JsonSchema, Schema,
|
||||||
|
generate::SchemaSettings,
|
||||||
|
transform::{Transform, transform_subschemas},
|
||||||
|
};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::LanguageModelToolSchemaFormat;
|
pub(crate) fn root_schema_for<T: JsonSchema>(format: LanguageModelToolSchemaFormat) -> Schema {
|
||||||
|
let mut generator = match format {
|
||||||
|
LanguageModelToolSchemaFormat::JsonSchema => SchemaSettings::draft07().into_generator(),
|
||||||
|
LanguageModelToolSchemaFormat::JsonSchemaSubset => SchemaSettings::openapi3()
|
||||||
|
.with(|settings| {
|
||||||
|
settings.meta_schema = None;
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
})
|
||||||
|
.with_transform(ToJsonSchemaSubsetTransform)
|
||||||
|
.into_generator(),
|
||||||
|
};
|
||||||
|
generator.root_schema_for::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ToJsonSchemaSubsetTransform;
|
||||||
|
|
||||||
|
impl Transform for ToJsonSchemaSubsetTransform {
|
||||||
|
fn transform(&mut self, schema: &mut Schema) {
|
||||||
|
// Ensure that the type field is not an array, this happens when we use
|
||||||
|
// Option<T>, the type will be [T, "null"].
|
||||||
|
if let Some(type_field) = schema.get_mut("type")
|
||||||
|
&& let Some(types) = type_field.as_array()
|
||||||
|
&& let Some(first_type) = types.first()
|
||||||
|
{
|
||||||
|
*type_field = first_type.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// oneOf is not supported, use anyOf instead
|
||||||
|
if let Some(one_of) = schema.remove("oneOf") {
|
||||||
|
schema.insert("anyOf".to_string(), one_of);
|
||||||
|
}
|
||||||
|
|
||||||
|
transform_subschemas(self, schema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to adapt a JSON schema representation to be compatible with the specified format.
|
/// Tries to adapt a JSON schema representation to be compatible with the specified format.
|
||||||
///
|
///
|
||||||
@@ -1,575 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
thread::{MessageId, PromptId, ThreadId},
|
|
||||||
thread_store::SerializedMessage,
|
|
||||||
};
|
|
||||||
use agent_settings::CompletionMode;
|
|
||||||
use anyhow::Result;
|
|
||||||
use assistant_tool::{
|
|
||||||
AnyToolCard, Tool, ToolResultContent, ToolResultOutput, ToolUseStatus, ToolWorkingSet,
|
|
||||||
};
|
|
||||||
use collections::HashMap;
|
|
||||||
use futures::{FutureExt as _, future::Shared};
|
|
||||||
use gpui::{App, Entity, SharedString, Task, Window};
|
|
||||||
use icons::IconName;
|
|
||||||
use language_model::{
|
|
||||||
ConfiguredModel, LanguageModel, LanguageModelExt, LanguageModelRequest,
|
|
||||||
LanguageModelToolResult, LanguageModelToolResultContent, LanguageModelToolUse,
|
|
||||||
LanguageModelToolUseId, Role,
|
|
||||||
};
|
|
||||||
use project::Project;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use util::truncate_lines_to_byte_limit;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ToolUse {
|
|
||||||
pub id: LanguageModelToolUseId,
|
|
||||||
pub name: SharedString,
|
|
||||||
pub ui_text: SharedString,
|
|
||||||
pub status: ToolUseStatus,
|
|
||||||
pub input: serde_json::Value,
|
|
||||||
pub icon: icons::IconName,
|
|
||||||
pub needs_confirmation: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ToolUseState {
|
|
||||||
tools: Entity<ToolWorkingSet>,
|
|
||||||
tool_uses_by_assistant_message: HashMap<MessageId, Vec<LanguageModelToolUse>>,
|
|
||||||
tool_results: HashMap<LanguageModelToolUseId, LanguageModelToolResult>,
|
|
||||||
pending_tool_uses_by_id: HashMap<LanguageModelToolUseId, PendingToolUse>,
|
|
||||||
tool_result_cards: HashMap<LanguageModelToolUseId, AnyToolCard>,
|
|
||||||
tool_use_metadata_by_id: HashMap<LanguageModelToolUseId, ToolUseMetadata>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToolUseState {
|
|
||||||
pub fn new(tools: Entity<ToolWorkingSet>) -> Self {
|
|
||||||
Self {
|
|
||||||
tools,
|
|
||||||
tool_uses_by_assistant_message: HashMap::default(),
|
|
||||||
tool_results: HashMap::default(),
|
|
||||||
pending_tool_uses_by_id: HashMap::default(),
|
|
||||||
tool_result_cards: HashMap::default(),
|
|
||||||
tool_use_metadata_by_id: HashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a [`ToolUseState`] from the given list of [`SerializedMessage`]s.
|
|
||||||
///
|
|
||||||
/// Accepts a function to filter the tools that should be used to populate the state.
|
|
||||||
///
|
|
||||||
/// If `window` is `None` (e.g., when in headless mode or when running evals),
|
|
||||||
/// tool cards won't be deserialized
|
|
||||||
pub fn from_serialized_messages(
|
|
||||||
tools: Entity<ToolWorkingSet>,
|
|
||||||
messages: &[SerializedMessage],
|
|
||||||
project: Entity<Project>,
|
|
||||||
window: Option<&mut Window>, // None in headless mode
|
|
||||||
cx: &mut App,
|
|
||||||
) -> Self {
|
|
||||||
let mut this = Self::new(tools);
|
|
||||||
let mut tool_names_by_id = HashMap::default();
|
|
||||||
let mut window = window;
|
|
||||||
|
|
||||||
for message in messages {
|
|
||||||
match message.role {
|
|
||||||
Role::Assistant => {
|
|
||||||
if !message.tool_uses.is_empty() {
|
|
||||||
let tool_uses = message
|
|
||||||
.tool_uses
|
|
||||||
.iter()
|
|
||||||
.map(|tool_use| LanguageModelToolUse {
|
|
||||||
id: tool_use.id.clone(),
|
|
||||||
name: tool_use.name.clone().into(),
|
|
||||||
raw_input: tool_use.input.to_string(),
|
|
||||||
input: tool_use.input.clone(),
|
|
||||||
is_input_complete: true,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
tool_names_by_id.extend(
|
|
||||||
tool_uses
|
|
||||||
.iter()
|
|
||||||
.map(|tool_use| (tool_use.id.clone(), tool_use.name.clone())),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.tool_uses_by_assistant_message
|
|
||||||
.insert(message.id, tool_uses);
|
|
||||||
|
|
||||||
for tool_result in &message.tool_results {
|
|
||||||
let tool_use_id = tool_result.tool_use_id.clone();
|
|
||||||
let Some(tool_use) = tool_names_by_id.get(&tool_use_id) else {
|
|
||||||
log::warn!("no tool name found for tool use: {tool_use_id:?}");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
tool_name: tool_use.clone(),
|
|
||||||
is_error: tool_result.is_error,
|
|
||||||
content: tool_result.content.clone(),
|
|
||||||
output: tool_result.output.clone(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(window) = &mut window
|
|
||||||
&& let Some(tool) = this.tools.read(cx).tool(tool_use, cx)
|
|
||||||
&& let Some(output) = tool_result.output.clone()
|
|
||||||
&& let Some(card) =
|
|
||||||
tool.deserialize_card(output, project.clone(), window, cx)
|
|
||||||
{
|
|
||||||
this.tool_result_cards.insert(tool_use_id, card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Role::System | Role::User => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cancel_pending(&mut self) -> Vec<PendingToolUse> {
|
|
||||||
let mut canceled_tool_uses = Vec::new();
|
|
||||||
self.pending_tool_uses_by_id
|
|
||||||
.retain(|tool_use_id, tool_use| {
|
|
||||||
if matches!(tool_use.status, PendingToolUseStatus::Error { .. }) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = "Tool canceled by user".into();
|
|
||||||
self.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
tool_name: tool_use.name.clone(),
|
|
||||||
content,
|
|
||||||
output: None,
|
|
||||||
is_error: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
canceled_tool_uses.push(tool_use.clone());
|
|
||||||
false
|
|
||||||
});
|
|
||||||
canceled_tool_uses
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pending_tool_uses(&self) -> Vec<&PendingToolUse> {
|
|
||||||
self.pending_tool_uses_by_id.values().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_uses_for_message(
|
|
||||||
&self,
|
|
||||||
id: MessageId,
|
|
||||||
project: &Entity<Project>,
|
|
||||||
cx: &App,
|
|
||||||
) -> Vec<ToolUse> {
|
|
||||||
let Some(tool_uses_for_message) = &self.tool_uses_by_assistant_message.get(&id) else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tool_uses = Vec::new();
|
|
||||||
|
|
||||||
for tool_use in tool_uses_for_message.iter() {
|
|
||||||
let tool_result = self.tool_results.get(&tool_use.id);
|
|
||||||
|
|
||||||
let status = (|| {
|
|
||||||
if let Some(tool_result) = tool_result {
|
|
||||||
let content = tool_result
|
|
||||||
.content
|
|
||||||
.to_str()
|
|
||||||
.map(|str| str.to_owned().into())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
return if tool_result.is_error {
|
|
||||||
ToolUseStatus::Error(content)
|
|
||||||
} else {
|
|
||||||
ToolUseStatus::Finished(content)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pending_tool_use) = self.pending_tool_uses_by_id.get(&tool_use.id) {
|
|
||||||
match pending_tool_use.status {
|
|
||||||
PendingToolUseStatus::Idle => ToolUseStatus::Pending,
|
|
||||||
PendingToolUseStatus::NeedsConfirmation { .. } => {
|
|
||||||
ToolUseStatus::NeedsConfirmation
|
|
||||||
}
|
|
||||||
PendingToolUseStatus::Running { .. } => ToolUseStatus::Running,
|
|
||||||
PendingToolUseStatus::Error(ref err) => {
|
|
||||||
ToolUseStatus::Error(err.clone().into())
|
|
||||||
}
|
|
||||||
PendingToolUseStatus::InputStillStreaming => {
|
|
||||||
ToolUseStatus::InputStillStreaming
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ToolUseStatus::Pending
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
let (icon, needs_confirmation) =
|
|
||||||
if let Some(tool) = self.tools.read(cx).tool(&tool_use.name, cx) {
|
|
||||||
(
|
|
||||||
tool.icon(),
|
|
||||||
tool.needs_confirmation(&tool_use.input, project, cx),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(IconName::Cog, false)
|
|
||||||
};
|
|
||||||
|
|
||||||
tool_uses.push(ToolUse {
|
|
||||||
id: tool_use.id.clone(),
|
|
||||||
name: tool_use.name.clone().into(),
|
|
||||||
ui_text: self.tool_ui_label(
|
|
||||||
&tool_use.name,
|
|
||||||
&tool_use.input,
|
|
||||||
tool_use.is_input_complete,
|
|
||||||
cx,
|
|
||||||
),
|
|
||||||
input: tool_use.input.clone(),
|
|
||||||
status,
|
|
||||||
icon,
|
|
||||||
needs_confirmation,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tool_uses
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_ui_label(
|
|
||||||
&self,
|
|
||||||
tool_name: &str,
|
|
||||||
input: &serde_json::Value,
|
|
||||||
is_input_complete: bool,
|
|
||||||
cx: &App,
|
|
||||||
) -> SharedString {
|
|
||||||
if let Some(tool) = self.tools.read(cx).tool(tool_name, cx) {
|
|
||||||
if is_input_complete {
|
|
||||||
tool.ui_text(input).into()
|
|
||||||
} else {
|
|
||||||
tool.still_streaming_ui_text(input).into()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
format!("Unknown tool {tool_name:?}").into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_results_for_message(
|
|
||||||
&self,
|
|
||||||
assistant_message_id: MessageId,
|
|
||||||
) -> Vec<&LanguageModelToolResult> {
|
|
||||||
let Some(tool_uses) = self
|
|
||||||
.tool_uses_by_assistant_message
|
|
||||||
.get(&assistant_message_id)
|
|
||||||
else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
|
|
||||||
tool_uses
|
|
||||||
.iter()
|
|
||||||
.filter_map(|tool_use| self.tool_results.get(&tool_use.id))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn message_has_tool_results(&self, assistant_message_id: MessageId) -> bool {
|
|
||||||
self.tool_uses_by_assistant_message
|
|
||||||
.get(&assistant_message_id)
|
|
||||||
.is_some_and(|results| !results.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_result(
|
|
||||||
&self,
|
|
||||||
tool_use_id: &LanguageModelToolUseId,
|
|
||||||
) -> Option<&LanguageModelToolResult> {
|
|
||||||
self.tool_results.get(tool_use_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_result_card(&self, tool_use_id: &LanguageModelToolUseId) -> Option<&AnyToolCard> {
|
|
||||||
self.tool_result_cards.get(tool_use_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_tool_result_card(
|
|
||||||
&mut self,
|
|
||||||
tool_use_id: LanguageModelToolUseId,
|
|
||||||
card: AnyToolCard,
|
|
||||||
) {
|
|
||||||
self.tool_result_cards.insert(tool_use_id, card);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_tool_use(
|
|
||||||
&mut self,
|
|
||||||
assistant_message_id: MessageId,
|
|
||||||
tool_use: LanguageModelToolUse,
|
|
||||||
metadata: ToolUseMetadata,
|
|
||||||
cx: &App,
|
|
||||||
) -> Arc<str> {
|
|
||||||
let tool_uses = self
|
|
||||||
.tool_uses_by_assistant_message
|
|
||||||
.entry(assistant_message_id)
|
|
||||||
.or_default();
|
|
||||||
|
|
||||||
let mut existing_tool_use_found = false;
|
|
||||||
|
|
||||||
for existing_tool_use in tool_uses.iter_mut() {
|
|
||||||
if existing_tool_use.id == tool_use.id {
|
|
||||||
*existing_tool_use = tool_use.clone();
|
|
||||||
existing_tool_use_found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !existing_tool_use_found {
|
|
||||||
tool_uses.push(tool_use.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = if tool_use.is_input_complete {
|
|
||||||
self.tool_use_metadata_by_id
|
|
||||||
.insert(tool_use.id.clone(), metadata);
|
|
||||||
|
|
||||||
PendingToolUseStatus::Idle
|
|
||||||
} else {
|
|
||||||
PendingToolUseStatus::InputStillStreaming
|
|
||||||
};
|
|
||||||
|
|
||||||
let ui_text: Arc<str> = self
|
|
||||||
.tool_ui_label(
|
|
||||||
&tool_use.name,
|
|
||||||
&tool_use.input,
|
|
||||||
tool_use.is_input_complete,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let may_perform_edits = self
|
|
||||||
.tools
|
|
||||||
.read(cx)
|
|
||||||
.tool(&tool_use.name, cx)
|
|
||||||
.is_some_and(|tool| tool.may_perform_edits());
|
|
||||||
|
|
||||||
self.pending_tool_uses_by_id.insert(
|
|
||||||
tool_use.id.clone(),
|
|
||||||
PendingToolUse {
|
|
||||||
assistant_message_id,
|
|
||||||
id: tool_use.id,
|
|
||||||
name: tool_use.name.clone(),
|
|
||||||
ui_text: ui_text.clone(),
|
|
||||||
input: tool_use.input,
|
|
||||||
may_perform_edits,
|
|
||||||
status,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ui_text
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_pending_tool(
|
|
||||||
&mut self,
|
|
||||||
tool_use_id: LanguageModelToolUseId,
|
|
||||||
ui_text: SharedString,
|
|
||||||
task: Task<()>,
|
|
||||||
) {
|
|
||||||
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
|
||||||
tool_use.ui_text = ui_text.into();
|
|
||||||
tool_use.status = PendingToolUseStatus::Running {
|
|
||||||
_task: task.shared(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn confirm_tool_use(
|
|
||||||
&mut self,
|
|
||||||
tool_use_id: LanguageModelToolUseId,
|
|
||||||
ui_text: impl Into<Arc<str>>,
|
|
||||||
input: serde_json::Value,
|
|
||||||
request: Arc<LanguageModelRequest>,
|
|
||||||
tool: Arc<dyn Tool>,
|
|
||||||
) {
|
|
||||||
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
|
||||||
let ui_text = ui_text.into();
|
|
||||||
tool_use.ui_text = ui_text.clone();
|
|
||||||
let confirmation = Confirmation {
|
|
||||||
tool_use_id,
|
|
||||||
input,
|
|
||||||
request,
|
|
||||||
tool,
|
|
||||||
ui_text,
|
|
||||||
};
|
|
||||||
tool_use.status = PendingToolUseStatus::NeedsConfirmation(Arc::new(confirmation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_tool_output(
|
|
||||||
&mut self,
|
|
||||||
tool_use_id: LanguageModelToolUseId,
|
|
||||||
tool_name: Arc<str>,
|
|
||||||
output: Result<ToolResultOutput>,
|
|
||||||
configured_model: Option<&ConfiguredModel>,
|
|
||||||
completion_mode: CompletionMode,
|
|
||||||
) -> Option<PendingToolUse> {
|
|
||||||
let metadata = self.tool_use_metadata_by_id.remove(&tool_use_id);
|
|
||||||
|
|
||||||
telemetry::event!(
|
|
||||||
"Agent Tool Finished",
|
|
||||||
model = metadata
|
|
||||||
.as_ref()
|
|
||||||
.map(|metadata| metadata.model.telemetry_id()),
|
|
||||||
model_provider = metadata
|
|
||||||
.as_ref()
|
|
||||||
.map(|metadata| metadata.model.provider_id().to_string()),
|
|
||||||
thread_id = metadata.as_ref().map(|metadata| metadata.thread_id.clone()),
|
|
||||||
prompt_id = metadata.as_ref().map(|metadata| metadata.prompt_id.clone()),
|
|
||||||
tool_name,
|
|
||||||
success = output.is_ok()
|
|
||||||
);
|
|
||||||
|
|
||||||
match output {
|
|
||||||
Ok(output) => {
|
|
||||||
let tool_result = output.content;
|
|
||||||
const BYTES_PER_TOKEN_ESTIMATE: usize = 3;
|
|
||||||
|
|
||||||
let old_use = self.pending_tool_uses_by_id.remove(&tool_use_id);
|
|
||||||
|
|
||||||
// Protect from overly large output
|
|
||||||
let tool_output_limit = configured_model
|
|
||||||
.map(|model| {
|
|
||||||
model.model.max_token_count_for_mode(completion_mode.into()) as usize
|
|
||||||
* BYTES_PER_TOKEN_ESTIMATE
|
|
||||||
})
|
|
||||||
.unwrap_or(usize::MAX);
|
|
||||||
|
|
||||||
let content = match tool_result {
|
|
||||||
ToolResultContent::Text(text) => {
|
|
||||||
let text = if text.len() < tool_output_limit {
|
|
||||||
text
|
|
||||||
} else {
|
|
||||||
let truncated = truncate_lines_to_byte_limit(&text, tool_output_limit);
|
|
||||||
format!(
|
|
||||||
"Tool result too long. The first {} bytes:\n\n{}",
|
|
||||||
truncated.len(),
|
|
||||||
truncated
|
|
||||||
)
|
|
||||||
};
|
|
||||||
LanguageModelToolResultContent::Text(text.into())
|
|
||||||
}
|
|
||||||
ToolResultContent::Image(language_model_image) => {
|
|
||||||
if language_model_image.estimate_tokens() < tool_output_limit {
|
|
||||||
LanguageModelToolResultContent::Image(language_model_image)
|
|
||||||
} else {
|
|
||||||
self.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
tool_name,
|
|
||||||
content: "Tool responded with an image that would exceeded the remaining tokens".into(),
|
|
||||||
is_error: true,
|
|
||||||
output: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return old_use;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
tool_name,
|
|
||||||
content,
|
|
||||||
is_error: false,
|
|
||||||
output: output.output,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
old_use
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
self.tool_results.insert(
|
|
||||||
tool_use_id.clone(),
|
|
||||||
LanguageModelToolResult {
|
|
||||||
tool_use_id: tool_use_id.clone(),
|
|
||||||
tool_name,
|
|
||||||
content: LanguageModelToolResultContent::Text(err.to_string().into()),
|
|
||||||
is_error: true,
|
|
||||||
output: None,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(tool_use) = self.pending_tool_uses_by_id.get_mut(&tool_use_id) {
|
|
||||||
tool_use.status = PendingToolUseStatus::Error(err.to_string().into());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.pending_tool_uses_by_id.get(&tool_use_id).cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_tool_results(&self, assistant_message_id: MessageId) -> bool {
|
|
||||||
self.tool_uses_by_assistant_message
|
|
||||||
.contains_key(&assistant_message_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tool_results(
|
|
||||||
&self,
|
|
||||||
assistant_message_id: MessageId,
|
|
||||||
) -> impl Iterator<Item = (&LanguageModelToolUse, Option<&LanguageModelToolResult>)> {
|
|
||||||
self.tool_uses_by_assistant_message
|
|
||||||
.get(&assistant_message_id)
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.map(|tool_use| (tool_use, self.tool_results.get(&tool_use.id)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PendingToolUse {
|
|
||||||
pub id: LanguageModelToolUseId,
|
|
||||||
/// The ID of the Assistant message in which the tool use was requested.
|
|
||||||
#[allow(unused)]
|
|
||||||
pub assistant_message_id: MessageId,
|
|
||||||
pub name: Arc<str>,
|
|
||||||
pub ui_text: Arc<str>,
|
|
||||||
pub input: serde_json::Value,
|
|
||||||
pub status: PendingToolUseStatus,
|
|
||||||
pub may_perform_edits: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Confirmation {
|
|
||||||
pub tool_use_id: LanguageModelToolUseId,
|
|
||||||
pub input: serde_json::Value,
|
|
||||||
pub ui_text: Arc<str>,
|
|
||||||
pub request: Arc<LanguageModelRequest>,
|
|
||||||
pub tool: Arc<dyn Tool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PendingToolUseStatus {
|
|
||||||
InputStillStreaming,
|
|
||||||
Idle,
|
|
||||||
NeedsConfirmation(Arc<Confirmation>),
|
|
||||||
Running { _task: Shared<Task<()>> },
|
|
||||||
Error(#[allow(unused)] Arc<str>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PendingToolUseStatus {
|
|
||||||
pub fn is_idle(&self) -> bool {
|
|
||||||
matches!(self, PendingToolUseStatus::Idle)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_error(&self) -> bool {
|
|
||||||
matches!(self, PendingToolUseStatus::Error(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn needs_confirmation(&self) -> bool {
|
|
||||||
matches!(self, PendingToolUseStatus::NeedsConfirmation { .. })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ToolUseMetadata {
|
|
||||||
pub model: Arc<dyn LanguageModel>,
|
|
||||||
pub thread_id: ThreadId,
|
|
||||||
pub prompt_id: PromptId,
|
|
||||||
}
|
|
||||||
88
crates/agent/src/tools.rs
Normal file
88
crates/agent/src/tools.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
mod context_server_registry;
|
||||||
|
mod copy_path_tool;
|
||||||
|
mod create_directory_tool;
|
||||||
|
mod delete_path_tool;
|
||||||
|
mod diagnostics_tool;
|
||||||
|
mod edit_file_tool;
|
||||||
|
mod fetch_tool;
|
||||||
|
mod find_path_tool;
|
||||||
|
mod grep_tool;
|
||||||
|
mod list_directory_tool;
|
||||||
|
mod move_path_tool;
|
||||||
|
mod now_tool;
|
||||||
|
mod open_tool;
|
||||||
|
mod read_file_tool;
|
||||||
|
mod terminal_tool;
|
||||||
|
mod thinking_tool;
|
||||||
|
mod web_search_tool;
|
||||||
|
|
||||||
|
use crate::AgentTool;
|
||||||
|
use language_model::{LanguageModelRequestTool, LanguageModelToolSchemaFormat};
|
||||||
|
|
||||||
|
pub use context_server_registry::*;
|
||||||
|
pub use copy_path_tool::*;
|
||||||
|
pub use create_directory_tool::*;
|
||||||
|
pub use delete_path_tool::*;
|
||||||
|
pub use diagnostics_tool::*;
|
||||||
|
pub use edit_file_tool::*;
|
||||||
|
pub use fetch_tool::*;
|
||||||
|
pub use find_path_tool::*;
|
||||||
|
pub use grep_tool::*;
|
||||||
|
pub use list_directory_tool::*;
|
||||||
|
pub use move_path_tool::*;
|
||||||
|
pub use now_tool::*;
|
||||||
|
pub use open_tool::*;
|
||||||
|
pub use read_file_tool::*;
|
||||||
|
pub use terminal_tool::*;
|
||||||
|
pub use thinking_tool::*;
|
||||||
|
pub use web_search_tool::*;
|
||||||
|
|
||||||
|
macro_rules! tools {
|
||||||
|
($($tool:ty),* $(,)?) => {
|
||||||
|
/// A list of all built-in tool names
|
||||||
|
pub fn built_in_tool_names() -> impl Iterator<Item = String> {
|
||||||
|
[
|
||||||
|
$(
|
||||||
|
<$tool>::name().to_string(),
|
||||||
|
)*
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of all built-in tools
|
||||||
|
pub fn built_in_tools() -> impl Iterator<Item = LanguageModelRequestTool> {
|
||||||
|
fn language_model_tool<T: AgentTool>() -> LanguageModelRequestTool {
|
||||||
|
LanguageModelRequestTool {
|
||||||
|
name: T::name().to_string(),
|
||||||
|
description: T::description().to_string(),
|
||||||
|
input_schema: T::input_schema(LanguageModelToolSchemaFormat::JsonSchema).to_value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[
|
||||||
|
$(
|
||||||
|
language_model_tool::<$tool>(),
|
||||||
|
)*
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tools! {
|
||||||
|
CopyPathTool,
|
||||||
|
CreateDirectoryTool,
|
||||||
|
DeletePathTool,
|
||||||
|
DiagnosticsTool,
|
||||||
|
EditFileTool,
|
||||||
|
FetchTool,
|
||||||
|
FindPathTool,
|
||||||
|
GrepTool,
|
||||||
|
ListDirectoryTool,
|
||||||
|
MovePathTool,
|
||||||
|
NowTool,
|
||||||
|
OpenTool,
|
||||||
|
ReadFileTool,
|
||||||
|
TerminalTool,
|
||||||
|
ThinkingTool,
|
||||||
|
WebSearchTool,
|
||||||
|
}
|
||||||
@@ -32,6 +32,17 @@ impl ContextServerRegistry {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tools_for_server(
|
||||||
|
&self,
|
||||||
|
server_id: &ContextServerId,
|
||||||
|
) -> impl Iterator<Item = &Arc<dyn AnyAgentTool>> {
|
||||||
|
self.registered_servers
|
||||||
|
.get(server_id)
|
||||||
|
.map(|server| server.tools.values())
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn servers(
|
pub fn servers(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<
|
) -> impl Iterator<
|
||||||
@@ -154,7 +165,7 @@ impl AnyAgentTool for ContextServerTool {
|
|||||||
format: language_model::LanguageModelToolSchemaFormat,
|
format: language_model::LanguageModelToolSchemaFormat,
|
||||||
) -> Result<serde_json::Value> {
|
) -> Result<serde_json::Value> {
|
||||||
let mut schema = self.tool.input_schema.clone();
|
let mut schema = self.tool.input_schema.clone();
|
||||||
assistant_tool::adapt_schema_to_format(&mut schema, format)?;
|
crate::tool_schema::adapt_schema_to_format(&mut schema, format)?;
|
||||||
Ok(match schema {
|
Ok(match schema {
|
||||||
serde_json::Value::Null => {
|
serde_json::Value::Null => {
|
||||||
serde_json::json!({ "type": "object", "properties": [] })
|
serde_json::json!({ "type": "object", "properties": [] })
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
use crate::{AgentTool, Thread, ToolCallEventStream};
|
use crate::{
|
||||||
|
AgentTool, Templates, Thread, ToolCallEventStream,
|
||||||
|
edit_agent::{EditAgent, EditAgentOutput, EditAgentOutputEvent, EditFormat},
|
||||||
|
};
|
||||||
use acp_thread::Diff;
|
use acp_thread::Diff;
|
||||||
use agent_client_protocol::{self as acp, ToolCallLocation, ToolCallUpdateFields};
|
use agent_client_protocol::{self as acp, ToolCallLocation, ToolCallUpdateFields};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use assistant_tools::edit_agent::{EditAgent, EditAgentOutput, EditAgentOutputEvent, EditFormat};
|
|
||||||
use cloud_llm_client::CompletionIntent;
|
use cloud_llm_client::CompletionIntent;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use gpui::{App, AppContext, AsyncApp, Entity, Task, WeakEntity};
|
use gpui::{App, AppContext, AsyncApp, Entity, Task, WeakEntity};
|
||||||
@@ -34,7 +36,7 @@ const DEFAULT_UI_TEXT: &str = "Editing file";
|
|||||||
///
|
///
|
||||||
/// 2. Verify the directory path is correct (only applicable when creating new files):
|
/// 2. Verify the directory path is correct (only applicable when creating new files):
|
||||||
/// - Use the `list_directory` tool to verify the parent directory exists and is the correct location
|
/// - Use the `list_directory` tool to verify the parent directory exists and is the correct location
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct EditFileToolInput {
|
pub struct EditFileToolInput {
|
||||||
/// A one-line, user-friendly markdown description of the edit. This will be shown in the UI and also passed to another model to perform the edit.
|
/// A one-line, user-friendly markdown description of the edit. This will be shown in the UI and also passed to another model to perform the edit.
|
||||||
///
|
///
|
||||||
@@ -75,7 +77,7 @@ pub struct EditFileToolInput {
|
|||||||
pub mode: EditFileMode,
|
pub mode: EditFileMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
struct EditFileToolPartialInput {
|
struct EditFileToolPartialInput {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
path: String,
|
path: String,
|
||||||
@@ -123,6 +125,7 @@ pub struct EditFileTool {
|
|||||||
thread: WeakEntity<Thread>,
|
thread: WeakEntity<Thread>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
|
templates: Arc<Templates>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditFileTool {
|
impl EditFileTool {
|
||||||
@@ -130,11 +133,13 @@ impl EditFileTool {
|
|||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
thread: WeakEntity<Thread>,
|
thread: WeakEntity<Thread>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
templates: Arc<Templates>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
project,
|
project,
|
||||||
thread,
|
thread,
|
||||||
language_registry,
|
language_registry,
|
||||||
|
templates,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,8 +299,7 @@ impl AgentTool for EditFileTool {
|
|||||||
model,
|
model,
|
||||||
project.clone(),
|
project.clone(),
|
||||||
action_log.clone(),
|
action_log.clone(),
|
||||||
// TODO: move edit agent to this crate so we can use our templates
|
self.templates.clone(),
|
||||||
assistant_tools::templates::Templates::new(),
|
|
||||||
edit_format,
|
edit_format,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -599,6 +603,7 @@ mod tests {
|
|||||||
project,
|
project,
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
))
|
))
|
||||||
.run(input, ToolCallEventStream::test().0, cx)
|
.run(input, ToolCallEventStream::test().0, cx)
|
||||||
})
|
})
|
||||||
@@ -807,6 +812,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry.clone(),
|
language_registry.clone(),
|
||||||
|
Templates::new(),
|
||||||
))
|
))
|
||||||
.run(input, ToolCallEventStream::test().0, cx)
|
.run(input, ToolCallEventStream::test().0, cx)
|
||||||
});
|
});
|
||||||
@@ -865,6 +871,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
))
|
))
|
||||||
.run(input, ToolCallEventStream::test().0, cx)
|
.run(input, ToolCallEventStream::test().0, cx)
|
||||||
});
|
});
|
||||||
@@ -951,6 +958,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry.clone(),
|
language_registry.clone(),
|
||||||
|
Templates::new(),
|
||||||
))
|
))
|
||||||
.run(input, ToolCallEventStream::test().0, cx)
|
.run(input, ToolCallEventStream::test().0, cx)
|
||||||
});
|
});
|
||||||
@@ -1005,6 +1013,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
))
|
))
|
||||||
.run(input, ToolCallEventStream::test().0, cx)
|
.run(input, ToolCallEventStream::test().0, cx)
|
||||||
});
|
});
|
||||||
@@ -1057,6 +1066,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
fs.insert_tree("/root", json!({})).await;
|
fs.insert_tree("/root", json!({})).await;
|
||||||
|
|
||||||
@@ -1197,6 +1207,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Test global config paths - these should require confirmation if they exist and are outside the project
|
// Test global config paths - these should require confirmation if they exist and are outside the project
|
||||||
@@ -1309,6 +1320,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Test files in different worktrees
|
// Test files in different worktrees
|
||||||
@@ -1393,6 +1405,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Test edge cases
|
// Test edge cases
|
||||||
@@ -1482,6 +1495,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Test different EditFileMode values
|
// Test different EditFileMode values
|
||||||
@@ -1566,6 +1580,7 @@ mod tests {
|
|||||||
project,
|
project,
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
language_registry,
|
language_registry,
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
@@ -1653,6 +1668,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
let (stream_tx, mut stream_rx) = ToolCallEventStream::test();
|
let (stream_tx, mut stream_rx) = ToolCallEventStream::test();
|
||||||
let edit = cx.update(|cx| {
|
let edit = cx.update(|cx| {
|
||||||
@@ -1682,6 +1698,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
let (stream_tx, mut stream_rx) = ToolCallEventStream::test();
|
let (stream_tx, mut stream_rx) = ToolCallEventStream::test();
|
||||||
let edit = cx.update(|cx| {
|
let edit = cx.update(|cx| {
|
||||||
@@ -1709,6 +1726,7 @@ mod tests {
|
|||||||
project.clone(),
|
project.clone(),
|
||||||
thread.downgrade(),
|
thread.downgrade(),
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
|
Templates::new(),
|
||||||
));
|
));
|
||||||
let (stream_tx, mut stream_rx) = ToolCallEventStream::test();
|
let (stream_tx, mut stream_rx) = ToolCallEventStream::test();
|
||||||
let edit = cx.update(|cx| {
|
let edit = cx.update(|cx| {
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use action_log::ActionLog;
|
use action_log::ActionLog;
|
||||||
use agent_client_protocol::{self as acp, ToolCallUpdateFields};
|
use agent_client_protocol::{self as acp, ToolCallUpdateFields};
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use assistant_tool::outline;
|
|
||||||
use gpui::{App, Entity, SharedString, Task};
|
use gpui::{App, Entity, SharedString, Task};
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
use language::Point;
|
use language::Point;
|
||||||
@@ -13,7 +12,7 @@ use settings::Settings;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::markdown::MarkdownCodeBlock;
|
use util::markdown::MarkdownCodeBlock;
|
||||||
|
|
||||||
use crate::{AgentTool, ToolCallEventStream};
|
use crate::{AgentTool, ToolCallEventStream, outline};
|
||||||
|
|
||||||
/// Reads the content of the given file in the project.
|
/// Reads the content of the given file in the project.
|
||||||
///
|
///
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "agent2"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition.workspace = true
|
|
||||||
publish.workspace = true
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
path = "src/agent2.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
test-support = ["db/test-support"]
|
|
||||||
e2e = []
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
acp_thread.workspace = true
|
|
||||||
action_log.workspace = true
|
|
||||||
agent.workspace = true
|
|
||||||
agent-client-protocol.workspace = true
|
|
||||||
agent_servers.workspace = true
|
|
||||||
agent_settings.workspace = true
|
|
||||||
anyhow.workspace = true
|
|
||||||
assistant_context.workspace = true
|
|
||||||
assistant_tool.workspace = true
|
|
||||||
assistant_tools.workspace = true
|
|
||||||
chrono.workspace = true
|
|
||||||
client.workspace = true
|
|
||||||
cloud_llm_client.workspace = true
|
|
||||||
collections.workspace = true
|
|
||||||
context_server.workspace = true
|
|
||||||
db.workspace = true
|
|
||||||
fs.workspace = true
|
|
||||||
futures.workspace = true
|
|
||||||
git.workspace = true
|
|
||||||
gpui.workspace = true
|
|
||||||
handlebars = { workspace = true, features = ["rust-embed"] }
|
|
||||||
html_to_markdown.workspace = true
|
|
||||||
http_client.workspace = true
|
|
||||||
indoc.workspace = true
|
|
||||||
itertools.workspace = true
|
|
||||||
language.workspace = true
|
|
||||||
language_model.workspace = true
|
|
||||||
language_models.workspace = true
|
|
||||||
log.workspace = true
|
|
||||||
open.workspace = true
|
|
||||||
parking_lot.workspace = true
|
|
||||||
paths.workspace = true
|
|
||||||
project.workspace = true
|
|
||||||
prompt_store.workspace = true
|
|
||||||
rust-embed.workspace = true
|
|
||||||
schemars.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
settings.workspace = true
|
|
||||||
smol.workspace = true
|
|
||||||
sqlez.workspace = true
|
|
||||||
task.workspace = true
|
|
||||||
telemetry.workspace = true
|
|
||||||
terminal.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
text.workspace = true
|
|
||||||
ui.workspace = true
|
|
||||||
util.workspace = true
|
|
||||||
uuid.workspace = true
|
|
||||||
watch.workspace = true
|
|
||||||
web_search.workspace = true
|
|
||||||
workspace-hack.workspace = true
|
|
||||||
zed_env_vars.workspace = true
|
|
||||||
zstd.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
agent = { workspace = true, "features" = ["test-support"] }
|
|
||||||
agent_servers = { workspace = true, "features" = ["test-support"] }
|
|
||||||
assistant_context = { workspace = true, "features" = ["test-support"] }
|
|
||||||
ctor.workspace = true
|
|
||||||
client = { workspace = true, "features" = ["test-support"] }
|
|
||||||
clock = { workspace = true, "features" = ["test-support"] }
|
|
||||||
context_server = { workspace = true, "features" = ["test-support"] }
|
|
||||||
db = { workspace = true, "features" = ["test-support"] }
|
|
||||||
editor = { workspace = true, "features" = ["test-support"] }
|
|
||||||
env_logger.workspace = true
|
|
||||||
fs = { workspace = true, "features" = ["test-support"] }
|
|
||||||
git = { workspace = true, "features" = ["test-support"] }
|
|
||||||
gpui = { workspace = true, "features" = ["test-support"] }
|
|
||||||
gpui_tokio.workspace = true
|
|
||||||
language = { workspace = true, "features" = ["test-support"] }
|
|
||||||
language_model = { workspace = true, "features" = ["test-support"] }
|
|
||||||
lsp = { workspace = true, "features" = ["test-support"] }
|
|
||||||
pretty_assertions.workspace = true
|
|
||||||
project = { workspace = true, "features" = ["test-support"] }
|
|
||||||
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
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
mod agent;
|
|
||||||
mod db;
|
|
||||||
mod history_store;
|
|
||||||
mod native_agent_server;
|
|
||||||
mod templates;
|
|
||||||
mod thread;
|
|
||||||
mod tool_schema;
|
|
||||||
mod tools;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
pub use agent::*;
|
|
||||||
pub use db::*;
|
|
||||||
pub use history_store::*;
|
|
||||||
pub use native_agent_server::NativeAgentServer;
|
|
||||||
pub use templates::*;
|
|
||||||
pub use thread::*;
|
|
||||||
pub use tools::*;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,43 +0,0 @@
|
|||||||
use language_model::LanguageModelToolSchemaFormat;
|
|
||||||
use schemars::{
|
|
||||||
JsonSchema, Schema,
|
|
||||||
generate::SchemaSettings,
|
|
||||||
transform::{Transform, transform_subschemas},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn root_schema_for<T: JsonSchema>(format: LanguageModelToolSchemaFormat) -> Schema {
|
|
||||||
let mut generator = match format {
|
|
||||||
LanguageModelToolSchemaFormat::JsonSchema => SchemaSettings::draft07().into_generator(),
|
|
||||||
LanguageModelToolSchemaFormat::JsonSchemaSubset => SchemaSettings::openapi3()
|
|
||||||
.with(|settings| {
|
|
||||||
settings.meta_schema = None;
|
|
||||||
settings.inline_subschemas = true;
|
|
||||||
})
|
|
||||||
.with_transform(ToJsonSchemaSubsetTransform)
|
|
||||||
.into_generator(),
|
|
||||||
};
|
|
||||||
generator.root_schema_for::<T>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct ToJsonSchemaSubsetTransform;
|
|
||||||
|
|
||||||
impl Transform for ToJsonSchemaSubsetTransform {
|
|
||||||
fn transform(&mut self, schema: &mut Schema) {
|
|
||||||
// Ensure that the type field is not an array, this happens when we use
|
|
||||||
// Option<T>, the type will be [T, "null"].
|
|
||||||
if let Some(type_field) = schema.get_mut("type")
|
|
||||||
&& let Some(types) = type_field.as_array()
|
|
||||||
&& let Some(first_type) = types.first()
|
|
||||||
{
|
|
||||||
*type_field = first_type.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
// oneOf is not supported, use anyOf instead
|
|
||||||
if let Some(one_of) = schema.remove("oneOf") {
|
|
||||||
schema.insert("anyOf".to_string(), one_of);
|
|
||||||
}
|
|
||||||
|
|
||||||
transform_subschemas(self, schema);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
mod context_server_registry;
|
|
||||||
mod copy_path_tool;
|
|
||||||
mod create_directory_tool;
|
|
||||||
mod delete_path_tool;
|
|
||||||
mod diagnostics_tool;
|
|
||||||
mod edit_file_tool;
|
|
||||||
mod fetch_tool;
|
|
||||||
mod find_path_tool;
|
|
||||||
mod grep_tool;
|
|
||||||
mod list_directory_tool;
|
|
||||||
mod move_path_tool;
|
|
||||||
mod now_tool;
|
|
||||||
mod open_tool;
|
|
||||||
mod read_file_tool;
|
|
||||||
mod terminal_tool;
|
|
||||||
mod thinking_tool;
|
|
||||||
mod web_search_tool;
|
|
||||||
|
|
||||||
/// A list of all built in tool names, for use in deduplicating MCP tool names
|
|
||||||
pub fn default_tool_names() -> impl Iterator<Item = &'static str> {
|
|
||||||
[
|
|
||||||
CopyPathTool::name(),
|
|
||||||
CreateDirectoryTool::name(),
|
|
||||||
DeletePathTool::name(),
|
|
||||||
DiagnosticsTool::name(),
|
|
||||||
EditFileTool::name(),
|
|
||||||
FetchTool::name(),
|
|
||||||
FindPathTool::name(),
|
|
||||||
GrepTool::name(),
|
|
||||||
ListDirectoryTool::name(),
|
|
||||||
MovePathTool::name(),
|
|
||||||
NowTool::name(),
|
|
||||||
OpenTool::name(),
|
|
||||||
ReadFileTool::name(),
|
|
||||||
TerminalTool::name(),
|
|
||||||
ThinkingTool::name(),
|
|
||||||
WebSearchTool::name(),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use context_server_registry::*;
|
|
||||||
pub use copy_path_tool::*;
|
|
||||||
pub use create_directory_tool::*;
|
|
||||||
pub use delete_path_tool::*;
|
|
||||||
pub use diagnostics_tool::*;
|
|
||||||
pub use edit_file_tool::*;
|
|
||||||
pub use fetch_tool::*;
|
|
||||||
pub use find_path_tool::*;
|
|
||||||
pub use grep_tool::*;
|
|
||||||
pub use list_directory_tool::*;
|
|
||||||
pub use move_path_tool::*;
|
|
||||||
pub use now_tool::*;
|
|
||||||
pub use open_tool::*;
|
|
||||||
pub use read_file_tool::*;
|
|
||||||
pub use terminal_tool::*;
|
|
||||||
pub use thinking_tool::*;
|
|
||||||
pub use web_search_tool::*;
|
|
||||||
|
|
||||||
use crate::AgentTool;
|
|
||||||
@@ -51,7 +51,6 @@ terminal.workspace = true
|
|||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
watch.workspace = true
|
watch.workspace = true
|
||||||
workspace-hack.workspace = true
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user