acp: Update agent client protocol crate to 0.15.0 (#59593)
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions

We finally have a cancellation mechanism to use! Made the non
side-effectful handlers stop their work if we get a cancel request
notification.

Release Notes:

- N/A
This commit is contained in:
Ben Brandt 2026-06-22 11:04:29 +02:00 committed by GitHub
parent c0945a8201
commit 8ba35e5eac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 276 additions and 236 deletions

35
Cargo.lock generated
View file

@ -319,9 +319,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efba6592048ef8a9ac97de8d79b2d9933d8ac4d94f7a2de102348fed0c61103"
checksum = "794aea8f191be00ab10233745d49b49f53be6a3f4d7fe540f681aebc535c5415"
dependencies = [
"agent-client-protocol-derive",
"agent-client-protocol-schema",
@ -329,7 +329,6 @@ dependencies = [
"blocking",
"futures 0.3.32",
"futures-concurrency",
"jsonrpcmsg",
"rustc-hash 2.1.1",
"schemars 1.0.4",
"serde",
@ -341,9 +340,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol-derive"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d176a10d4cb06e0262a738c3c5bf21ff0968db13a666e31cbca94a3d3d72e7c"
checksum = "83ffd930c0d668152ce6d591189f324835d46031eabecb51ada3619ad7e2a278"
dependencies = [
"quote",
"syn 2.0.117",
@ -351,9 +350,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol-schema"
version = "0.13.6"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c290bfa00c6b52339db66f8e9cf711d5f08530800529f7d619ff24d6cba253d0"
checksum = "b4e7eb6f1e554741f621ae4abb046d4fbb3cb88541bc599693ae7f9aa30b72eb"
dependencies = [
"anyhow",
"derive_more",
@ -2198,7 +2197,7 @@ dependencies = [
"bitflags 2.10.0",
"cexpr",
"clang-sys",
"itertools 0.10.5",
"itertools 0.11.0",
"log",
"prettyplease",
"proc-macro2",
@ -2218,7 +2217,7 @@ dependencies = [
"bitflags 2.10.0",
"cexpr",
"clang-sys",
"itertools 0.10.5",
"itertools 0.11.0",
"proc-macro2",
"quote",
"regex",
@ -8742,7 +8741,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.56.0",
"windows-core 0.62.2",
]
[[package]]
@ -8996,7 +8995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [
"equivalent",
"hashbrown 0.15.5",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
@ -9413,16 +9412,6 @@ dependencies = [
"util",
]
[[package]]
name = "jsonrpcmsg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d833a15225c779251e13929203518c2ff26e2fe0f322d584b213f4f4dad37bd"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "jsonschema"
version = "0.37.4"
@ -14229,7 +14218,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
dependencies = [
"bytes 1.11.1",
"heck 0.5.0",
"itertools 0.10.5",
"itertools 0.11.0",
"log",
"multimap",
"once_cell",
@ -14262,7 +14251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
dependencies = [
"anyhow",
"itertools 0.10.5",
"itertools 0.11.0",
"proc-macro2",
"quote",
"syn 2.0.117",

View file

@ -503,7 +503,7 @@ accesskit = "0.24.0"
accesskit_macos = "0.26.0"
accesskit_unix = "0.21.0"
accesskit_windows = "0.33.1"
agent-client-protocol = { version = "=0.14.0", features = ["unstable"] }
agent-client-protocol = { version = "=0.15.0", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "4c129667ce56611becdc82de6e28218c80e2e88f" }
any_vec = "0.14"

View file

@ -3,7 +3,7 @@ mod diff;
mod mention;
mod terminal;
use action_log::{ActionLog, ActionLogTelemetry};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::{MaybeUndefined, v1 as acp};
use anyhow::{Context as _, Result, anyhow};
use collections::HashSet;
pub use connection::*;
@ -1935,7 +1935,7 @@ impl AcpThread {
self.update_plan(plan, cx);
}
acp::SessionUpdate::SessionInfoUpdate(info_update) => {
if let acp::MaybeUndefined::Value(title) = info_update.title {
if let MaybeUndefined::Value(title) = info_update.title {
let had_provisional = self.provisional_title.take().is_some();
let title: SharedString = title.into();
if self.title.as_ref() != Some(&title) {
@ -2653,6 +2653,19 @@ impl AcpThread {
}))
}
pub fn cancel_tool_call_authorization(&mut self, id: &acp::ToolCallId, cx: &mut Context<Self>) {
let Some((ix, call)) = self.tool_call_mut(id) else {
return;
};
if !matches!(call.status, ToolCallStatus::WaitingForConfirmation { .. }) {
return;
}
call.status = ToolCallStatus::Canceled;
cx.emit(AcpThreadEvent::EntryUpdated(ix));
cx.emit(AcpThreadEvent::ToolAuthorizationReceived(id.clone()));
}
pub fn authorize_tool_call(
&mut self,
id: acp::ToolCallId,
@ -5139,6 +5152,60 @@ mod tests {
}
}
#[gpui::test]
async fn test_cancel_tool_call_authorization_resolves_permission_request(
cx: &mut TestAppContext,
) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, [], cx).await;
let connection = Rc::new(FakeAgentConnection::new());
let thread = cx
.update(|cx| {
connection.new_session(project, PathList::new(&[Path::new(path!("/test"))]), cx)
})
.await
.unwrap();
let tool_call_id = acp::ToolCallId::new("toolu_01cancelled_permission");
let permission_task = thread
.update(cx, |thread, cx| {
thread.request_tool_call_authorization(
acp::ToolCall::new(tool_call_id.clone(), "Needs permission")
.kind(acp::ToolKind::Execute)
.status(acp::ToolCallStatus::Pending)
.into(),
PermissionOptions::Flat(vec![acp::PermissionOption::new(
acp::PermissionOptionId::new("allow"),
"Allow",
acp::PermissionOptionKind::AllowOnce,
)]),
AuthorizationKind::PermissionGrant,
cx,
)
})
.unwrap();
thread.update(cx, |thread, cx| {
thread.cancel_tool_call_authorization(&tool_call_id, cx);
});
thread.read_with(cx, |thread, _cx| {
let (_, tool_call) = thread
.tool_call(&tool_call_id)
.expect("tool call should exist");
assert!(matches!(tool_call.status, ToolCallStatus::Canceled));
});
match permission_task.await {
RequestPermissionOutcome::Cancelled => {}
RequestPermissionOutcome::Selected(_) => {
panic!("cancelled permission request should not select an outcome")
}
}
}
#[gpui::test]
async fn test_terminal_tool_call_update_closes_open_permission_request(
cx: &mut TestAppContext,

View file

@ -1,5 +1,5 @@
use crate::AcpThread;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use chrono::{DateTime, Utc};
use collections::{HashMap, HashSet, IndexMap};

View file

@ -1,4 +1,4 @@
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Context as _, Result, bail};
use file_icons::FileIcons;
use serde::{Deserialize, Serialize};

View file

@ -1,4 +1,4 @@
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
#[cfg(target_os = "linux")]
use anyhow::Context as _;
use anyhow::Result;

View file

@ -1,6 +1,6 @@
use std::{collections::HashSet, fmt::Display, rc::Rc, sync::Arc};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_servers::{AcpDebugMessage, AcpDebugMessageContent, AcpDebugMessageDirection};
use agent_ui::agent_connection_store::AgentConnectionStatus;
use agent_ui::{Agent, AgentConnectionStore, AgentPanel};

View file

@ -28,7 +28,7 @@ use acp_thread::{
AcpThread, AgentModelId, AgentModelSelector, AgentSessionInfo, AgentSessionList,
AgentSessionListRequest, AgentSessionListResponse, TokenUsageRatio, UserMessageId,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_skills::{
AGENTS_DIR_NAME, MAX_SKILL_DESCRIPTIONS_SIZE, MAX_SKILL_FILE_SIZE, ProjectSkillGroup,
SKILL_FILE_NAME, Skill, SkillIndex, SkillLoadError, SkillLoadWarning, SkillScopeId,

View file

@ -1,6 +1,6 @@
use crate::{AgentMessage, AgentMessageContent, UserMessage, UserMessageContent};
use acp_thread::UserMessageId;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentProfileId;
use anyhow::{Result, anyhow};
use chrono::{DateTime, Utc};

View file

@ -3,7 +3,7 @@ use acp_thread::{
AgentConnection, AgentModelGroupName, AgentModelList, PermissionOptions, ThreadStatus,
UserMessageId,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentProfileId;
use anyhow::Result;
use client::{Client, RefreshLlmTokenListener, UserStore};

View file

@ -12,7 +12,7 @@ use action_log::ActionLog;
use agent_settings::UserAgentsMd;
use crate::sandboxing::{SandboxRequest, ThreadSandboxGrants, sandboxing_enabled_for_project};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::{
AgentProfileId, AgentSettings, AutoCompactThreshold, COMPACTION_PROMPT,
SUMMARIZE_THREAD_DETAILED_PROMPT, SUMMARIZE_THREAD_PROMPT,

View file

@ -1,5 +1,5 @@
use crate::{DbThread, DbThreadMetadata, ThreadsDatabase};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Result, anyhow};
use futures::{FutureExt, future::Shared};
use gpui::{App, Context, Entity, Global, Task, prelude::*};

View file

@ -1,7 +1,7 @@
use std::fmt::Write;
use std::sync::Arc;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use gpui::{App, Entity, SharedString, Task};
use project::Project;
use schemars::JsonSchema;

View file

@ -1,5 +1,5 @@
use crate::{AgentToolOutput, AnyAgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use collections::{BTreeMap, HashMap};
use context_server::{ContextServerId, client::NotificationSubscription};

View file

@ -7,7 +7,7 @@ use crate::{
AgentTool, ToolCallEventStream, ToolInput, ToolPermissionDecision,
authorize_with_sensitive_settings, decide_permission_for_paths,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use futures::FutureExt as _;
use gpui::{App, Entity, Task};

View file

@ -2,7 +2,7 @@ use super::tool_permissions::{
authorize_symlink_access, canonicalize_worktree_roots, detect_symlink_escape,
resolve_creatable_global_skill_path, sensitive_settings_kind,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use futures::FutureExt as _;
use gpui::{App, Entity, SharedString, Task};

View file

@ -1,4 +1,4 @@
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use gpui::{App, SharedString, Task};
use language_model::LanguageModelToolResultContent;

View file

@ -7,7 +7,7 @@ use crate::{
authorize_with_sensitive_settings, decide_permission_for_path,
};
use action_log::ActionLog;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use futures::{FutureExt as _, SinkExt, StreamExt, channel::mpsc};
use gpui::{App, AppContext, Entity, SharedString, Task};

View file

@ -1,5 +1,5 @@
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use futures::{Future, FutureExt as _};
use gpui::{App, AsyncApp, Entity, Task};
use language::{DiagnosticSeverity, OffsetRangeExt};

View file

@ -7,7 +7,7 @@ use super::edit_session::{
};
use crate::{AgentTool, Thread, ToolCallEventStream, ToolInput, ToolInputPayload};
use action_log::ActionLog;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use futures::FutureExt as _;
use gpui::{App, AsyncApp, Entity, Task, WeakEntity};

View file

@ -6,7 +6,7 @@ use super::tool_permissions::resolve_creatable_global_skill_path;
use crate::{Thread, ToolCallEventStream};
use acp_thread::Diff;
use action_log::ActionLog;
use agent_client_protocol::schema::{self as acp, ToolCallLocation, ToolCallUpdateFields};
use agent_client_protocol::schema::v1::{self as acp, ToolCallLocation, ToolCallUpdateFields};
use anyhow::Result;
use collections::HashSet;
use futures::{FutureExt, channel::oneshot};

View file

@ -2,7 +2,7 @@ use std::rc::Rc;
use std::sync::Arc;
use std::{borrow::Cow, cell::RefCell};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Context as _, Result, bail};
use futures::{AsyncReadExt as _, FutureExt as _};
use gpui::{App, AppContext as _, Task};

View file

@ -1,5 +1,5 @@
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Result, anyhow};
use futures::FutureExt as _;
use gpui::{App, AppContext, Entity, SharedString, Task};

View file

@ -3,7 +3,7 @@ use std::sync::Arc;
use super::symbol_locator::{LocationDisplay, SymbolLocator};
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use gpui::{App, Entity, SharedString, Task};
use project::Project;
use schemars::JsonSchema;

View file

@ -1,7 +1,7 @@
use std::fmt::Write;
use std::sync::Arc;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use gpui::{App, Entity, SharedString, Task};
use project::Project;
use schemars::JsonSchema;

View file

@ -3,7 +3,7 @@ use std::sync::Arc;
use super::symbol_locator::{LocationDisplay, SymbolLocator};
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use gpui::{App, Entity, SharedString, Task};
use project::Project;
use schemars::JsonSchema;

View file

@ -1,5 +1,5 @@
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use futures::{FutureExt as _, StreamExt};
use gpui::{App, Entity, SharedString, Task};

View file

@ -1,4 +1,4 @@
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use gpui::{App, SharedString, Task};
use language_model::LanguageModelToolResultContent;

View file

@ -3,7 +3,7 @@ use super::tool_permissions::{
resolve_global_skill_path, resolve_project_path,
};
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Context as _, Result, anyhow};
use fs::Fs;
use futures::StreamExt as _;

View file

@ -7,7 +7,7 @@ use crate::{
AgentTool, ToolCallEventStream, ToolInput, ToolPermissionDecision,
authorize_with_sensitive_settings, decide_permission_for_paths,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use futures::FutureExt as _;
use gpui::{App, Entity, SharedString, Task};

View file

@ -1,5 +1,5 @@
use action_log::ActionLog;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Context as _, Result, anyhow};
use futures::FutureExt as _;
use gpui::{App, Entity, SharedString, Task};

View file

@ -1,7 +1,7 @@
use std::fmt::Write;
use std::sync::Arc;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use collections::HashSet;
use gpui::{App, Entity, SharedString, Task};
use project::Project;

View file

@ -1,4 +1,4 @@
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_skills::Skill;
use anyhow::Result;
use gpui::{App, AsyncApp, SharedString, Task};
@ -704,8 +704,8 @@ mod tests {
// Approve once and confirm the tool then completes successfully.
auth.response
.send(acp_thread::SelectedPermissionOutcome::new(
agent_client_protocol::schema::PermissionOptionId::new("allow"),
agent_client_protocol::schema::PermissionOptionKind::AllowOnce,
agent_client_protocol::schema::v1::PermissionOptionId::new("allow"),
agent_client_protocol::schema::v1::PermissionOptionKind::AllowOnce,
))
.unwrap();

View file

@ -1,5 +1,5 @@
use acp_thread::{SUBAGENT_SESSION_INFO_META_KEY, SubagentSessionInfo};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use gpui::{App, SharedString, Task};
use language_model::LanguageModelToolResultContent;

View file

@ -1,4 +1,4 @@
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use futures::FutureExt as _;
use gpui::{App, AsyncApp, Entity, SharedString, Task};

View file

@ -2,7 +2,7 @@ use crate::{
Thread, ToolCallEventStream, ToolPermissionContext, ToolPermissionDecision,
decide_permission_for_path,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_skills::is_agents_skills_path;
use anyhow::{Result, anyhow};
use fs::Fs;

View file

@ -1,7 +1,7 @@
use std::sync::Arc;
use crate::{AgentTool, ToolCallEventStream, ToolInput};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use cloud_llm_client::WebSearchResponse;
use futures::FutureExt as _;

View file

@ -4,7 +4,7 @@ use super::edit_session::{
};
use crate::{AgentTool, Thread, ToolCallEventStream, ToolInput, ToolInputPayload};
use action_log::ActionLog;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use futures::FutureExt as _;
use gpui::{App, AsyncApp, Entity, Task, WeakEntity};
use language::LanguageRegistry;

View file

@ -3,10 +3,11 @@ use acp_thread::{
AgentSessionListResponse,
};
use action_log::ActionLog;
use agent_client_protocol::schema::{self as acp, ErrorCode};
use agent_client_protocol::{
Agent, Client, ConnectionTo, JsonRpcResponse, Lines, Responder, SentRequest,
use agent_client_protocol::schema::{
ProtocolVersion,
v1::{self as acp, ErrorCode},
};
use agent_client_protocol::{Agent, Client, ConnectionTo, JsonRpcResponse, Lines, Responder};
use anyhow::anyhow;
use async_channel;
use collections::{HashMap, HashSet};
@ -26,7 +27,6 @@ use std::path::PathBuf;
use std::process::{ExitStatus, Stdio};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::{any::Any, cell::RefCell, collections::VecDeque};
use task::{Shell, ShellBuilder, SpawnInTerminal};
use thiserror::Error;
@ -45,8 +45,6 @@ use crate::GEMINI_ID;
pub const GEMINI_TERMINAL_AUTH_METHOD_ID: &str = "spawn-gemini-cli";
const MAX_DEBUG_BACKLOG_MESSAGES: usize = 2000;
const ACP_RESPONSE_CHANNEL_CANCELLED: &str =
"response channel cancelled — connection may have dropped";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AcpDebugMessageDirection {
@ -231,35 +229,6 @@ fn exited_load_error_with_stderr(status: ExitStatus, debug_log: &AcpDebugLog) ->
}
}
/// Awaits the response to an ACP request from a GPUI foreground task.
///
/// The ACP SDK offers two ways to consume a [`SentRequest`]:
/// - [`SentRequest::block_task`]: linear `.await` inside a spawned task.
/// - [`SentRequest::on_receiving_result`]: a callback invoked when the
/// response arrives, with the guarantee that no other inbound messages
/// are processed while the callback runs. This is the recommended form
/// inside SDK handler callbacks, where [`block_task`] would deadlock.
///
/// We use `on_receiving_result` with a oneshot bridge here (rather than
/// [`block_task`]) so that our handler-side code paths can share a single
/// request-awaiting helper. The SDK callback itself is trivial (one channel
/// send) so the extra ordering guarantee it imposes on the dispatch loop is
/// negligible.
fn into_foreground_future<T: JsonRpcResponse>(
sent: SentRequest<T>,
) -> impl Future<Output = Result<T, acp::Error>> {
let (tx, rx) = futures::channel::oneshot::channel();
let spawn_result = sent.on_receiving_result(async move |result| {
tx.send(result).ok();
Ok(())
});
async move {
spawn_result?;
rx.await
.map_err(|_| acp::Error::internal_error().data(ACP_RESPONSE_CHANNEL_CANCELLED))?
}
}
#[derive(Debug, Error)]
#[error("Unsupported version")]
pub struct UnsupportedVersion;
@ -580,7 +549,9 @@ impl AgentSessionList for AcpSessionList {
let acp_request = acp::ListSessionsRequest::new()
.cwd(request.cwd)
.cursor(request.cursor);
let response = into_foreground_future(conn.send_request(acp_request))
let response = conn
.send_request(acp_request)
.block_task()
.await
.map_err(map_acp_error)?;
Ok(AgentSessionListResponse {
@ -622,7 +593,8 @@ impl AgentSessionList for AcpSessionList {
let updates_tx = self.updates_tx.clone();
let session_id = session_id.clone();
cx.foreground_executor().spawn(async move {
into_foreground_future(conn.send_request(acp::DeleteSessionRequest::new(session_id)))
conn.send_request(acp::DeleteSessionRequest::new(session_id))
.block_task()
.await
.map_err(map_acp_error)?;
updates_tx
@ -670,7 +642,7 @@ pub async fn connect(
Ok(Rc::new(conn) as _)
}
const MINIMUM_SUPPORTED_VERSION: acp::ProtocolVersion = acp::ProtocolVersion::V1;
const MINIMUM_SUPPORTED_VERSION: ProtocolVersion = ProtocolVersion::V1;
/// Build a `Client` connection over `transport` with Zed's full
/// agent→client handler set wired up.
@ -947,9 +919,9 @@ impl AcpConnection {
}
});
let initialize_response = into_foreground_future(
connection.send_request(
acp::InitializeRequest::new(acp::ProtocolVersion::V1)
let initialize_response = connection
.send_request(
acp::InitializeRequest::new(ProtocolVersion::V1)
.client_capabilities(
acp::ClientCapabilities::new()
.fs(acp::FileSystemCapabilities::new()
@ -966,23 +938,16 @@ impl AcpConnection {
acp::Implementation::new("zed", version)
.title(release_channel.map(ToOwned::to_owned)),
),
),
)
.boxed_local();
)
.block_task()
.boxed_local();
let (response, status_fut) =
match futures::future::select(initialize_response, status_fut).await {
futures::future::Either::Left((Ok(response), status_fut)) => (response, status_fut),
futures::future::Either::Left((Err(error), status_fut)) => {
let response_channel_cancelled = error.code == ErrorCode::InternalError
&& error.data.as_ref().and_then(|data| data.as_str())
== Some(ACP_RESPONSE_CHANNEL_CANCELLED);
if !response_channel_cancelled {
return Err(error.into());
}
let timer = cx
.background_executor()
.timer(Duration::from_millis(250))
.timer(std::time::Duration::from_millis(250))
.boxed_local();
if let futures::future::Either::Left((load_error, _timer)) =
futures::future::select(status_fut, timer).await
@ -1334,15 +1299,15 @@ impl AcpConnection {
let config_opts = config_options.clone();
let conn = self.connection.clone();
async move |_| {
let result = into_foreground_future(conn.send_request(
acp::SetSessionConfigOptionRequest::new(
let result = conn
.send_request(acp::SetSessionConfigOptionRequest::new(
session_id,
config_id_clone.clone(),
default_value_id,
),
))
.await
.log_err();
))
.block_task()
.await
.log_err();
if result.is_none() {
if let Some(initial) = initial_value {
@ -1544,11 +1509,10 @@ impl AgentConnection for AcpConnection {
let mcp_servers = mcp_servers_for_project(&project, cx);
cx.spawn(async move |cx| {
let response = into_foreground_future(
self.connection.send_request(
directories.into_new_session_request(mcp_servers),
),
)
let response = self
.connection
.send_request(directories.into_new_session_request(mcp_servers))
.block_task()
.await
.map_err(map_acp_error)?;
@ -1572,12 +1536,12 @@ impl AgentConnection for AcpConnection {
let modes = modes.clone();
let conn = self.connection.clone();
async move |_| {
let result = into_foreground_future(
conn.send_request(acp::SetSessionModeRequest::new(
let result = conn
.send_request(acp::SetSessionModeRequest::new(
session_id,
default_mode,
)),
)
))
.block_task()
.await
.log_err();
@ -1681,11 +1645,13 @@ impl AgentConnection for AcpConnection {
title,
move |connection, session_id, directories| {
Box::pin(async move {
let response = into_foreground_future(connection.send_request(
directories.into_load_session_request(session_id.clone(), mcp_servers),
))
.await
.map_err(map_acp_error)?;
let response = connection
.send_request(
directories.into_load_session_request(session_id.clone(), mcp_servers),
)
.block_task()
.await
.map_err(map_acp_error)?;
Ok(SessionConfigResponse {
modes: response.modes,
config_options: response.config_options,
@ -1723,11 +1689,14 @@ impl AgentConnection for AcpConnection {
title,
move |connection, session_id, directories| {
Box::pin(async move {
let response = into_foreground_future(connection.send_request(
directories.into_resume_session_request(session_id.clone(), mcp_servers),
))
.await
.map_err(map_acp_error)?;
let response = connection
.send_request(
directories
.into_resume_session_request(session_id.clone(), mcp_servers),
)
.block_task()
.await
.map_err(map_acp_error)?;
Ok(SessionConfigResponse {
modes: response.modes,
config_options: response.config_options,
@ -1775,10 +1744,9 @@ impl AgentConnection for AcpConnection {
let conn = self.connection.clone();
let session_id = session_id.clone();
return cx.foreground_executor().spawn(async move {
into_foreground_future(
conn.send_request(acp::CloseSessionRequest::new(session_id)),
)
.await?;
conn.send_request(acp::CloseSessionRequest::new(session_id))
.block_task()
.await?;
Ok(())
});
}
@ -1802,10 +1770,9 @@ impl AgentConnection for AcpConnection {
let conn = self.connection.clone();
let session_id = session_id.clone();
cx.foreground_executor().spawn(async move {
into_foreground_future(
conn.send_request(acp::CloseSessionRequest::new(session_id.clone())),
)
.await?;
conn.send_request(acp::CloseSessionRequest::new(session_id.clone()))
.block_task()
.await?;
Ok(())
})
}
@ -1854,7 +1821,8 @@ impl AgentConnection for AcpConnection {
fn authenticate(&self, method_id: acp::AuthMethodId, cx: &mut App) -> Task<Result<()>> {
let conn = self.connection.clone();
cx.foreground_executor().spawn(async move {
into_foreground_future(conn.send_request(acp::AuthenticateRequest::new(method_id)))
conn.send_request(acp::AuthenticateRequest::new(method_id))
.block_task()
.await?;
Ok(())
})
@ -1871,7 +1839,9 @@ impl AgentConnection for AcpConnection {
let conn = self.connection.clone();
cx.foreground_executor().spawn(async move {
into_foreground_future(conn.send_request(acp::LogoutRequest::new())).await?;
conn.send_request(acp::LogoutRequest::new())
.block_task()
.await?;
Ok(())
})
}
@ -1886,7 +1856,7 @@ impl AgentConnection for AcpConnection {
let sessions = self.sessions.clone();
let session_id = params.session_id.clone();
cx.foreground_executor().spawn(async move {
let result = into_foreground_future(conn.send_request(params)).await;
let result = conn.send_request(params).block_task().await;
let mut suppress_abort_err = false;
@ -2410,10 +2380,10 @@ pub mod test_support {
.await
.context("failed to receive fake ACP connection handle")?;
let response = into_foreground_future(
client_conn.send_request(acp::InitializeRequest::new(acp::ProtocolVersion::V1)),
)
.await?;
let response = client_conn
.send_request(acp::InitializeRequest::new(ProtocolVersion::V1))
.block_task()
.await?;
let agent_capabilities = response.agent_capabilities;
@ -3220,11 +3190,11 @@ mod tests {
.await
.expect("failed to receive ACP connection handle");
let response = into_foreground_future(
client_conn.send_request(acp::InitializeRequest::new(acp::ProtocolVersion::V1)),
)
.await
.expect("failed to initialize ACP connection");
let response = client_conn
.send_request(acp::InitializeRequest::new(ProtocolVersion::V1))
.block_task()
.await
.expect("failed to initialize ACP connection");
let agent_capabilities = response.agent_capabilities;
@ -3725,10 +3695,10 @@ impl acp_thread::AgentSessionModes for AcpSessionModes {
};
let state = self.state.clone();
cx.foreground_executor().spawn(async move {
let result = into_foreground_future(
connection.send_request(acp::SetSessionModeRequest::new(session_id, mode_id)),
)
.await;
let result = connection
.send_request(acp::SetSessionModeRequest::new(session_id, mode_id))
.block_task()
.await;
if result.is_err() {
state.borrow_mut().current_mode_id = old_mode_id;
@ -3767,10 +3737,12 @@ impl acp_thread::AgentSessionConfigOptions for AcpSessionConfigOptions {
let watch_tx = self.watch_tx.clone();
cx.foreground_executor().spawn(async move {
let response = into_foreground_future(connection.send_request(
acp::SetSessionConfigOptionRequest::new(session_id, config_id, value),
))
.await?;
let response = connection
.send_request(acp::SetSessionConfigOptionRequest::new(
session_id, config_id, value,
))
.block_task()
.await?;
*state.borrow_mut() = response.config_options.clone();
watch_tx.borrow_mut().send(()).ok();
@ -3810,6 +3782,15 @@ fn respond_err<T: JsonRpcResponse>(responder: Responder<T>, err: acp::Error) {
responder.respond_with_error(err).log_err();
}
fn respond_result<T: JsonRpcResponse>(responder: Responder<T>, result: Result<T, acp::Error>) {
match result {
Ok(response) => {
responder.respond(response).log_err();
}
Err(err) => respond_err(responder, err),
}
}
fn handle_request_permission(
args: acp::RequestPermissionRequest,
responder: Responder<acp::RequestPermissionResponse>,
@ -3821,6 +3802,8 @@ fn handle_request_permission(
Err(e) => return respond_err(responder, e),
};
let cancellation = responder.cancellation();
let tool_call_id = args.tool_call.tool_call_id.clone();
cx.spawn(async move |cx| {
let result: Result<_, acp::Error> = async {
let task = thread
@ -3833,7 +3816,9 @@ fn handle_request_permission(
)
})
.flatten_acp()?;
Ok(task.await)
cancellation
.run_until_cancelled(async { Ok(task.await) })
.await
}
.await;
@ -3843,7 +3828,16 @@ fn handle_request_permission(
.respond(acp::RequestPermissionResponse::new(outcome.into()))
.log_err();
}
Err(e) => respond_err(responder, e),
Err(e) => {
if e.code == ErrorCode::RequestCancelled {
thread
.update(cx, |thread, cx| {
thread.cancel_tool_call_authorization(&tool_call_id, cx)
})
.log_err();
}
respond_err(responder, e)
}
}
})
.detach();
@ -3896,24 +3890,19 @@ fn handle_read_text_file(
};
cx.spawn(async move |cx| {
let result: Result<_, acp::Error> = async {
thread
.update(cx, |thread, cx| {
thread.read_text_file(args.path, args.line, args.limit, false, cx)
})
.map_err(acp::Error::from)?
.await
}
.await;
let cancellation = responder.cancellation();
let result = cancellation
.run_until_cancelled(async {
thread
.update(cx, |thread, cx| {
thread.read_text_file(args.path, args.line, args.limit, false, cx)
})
.map_err(acp::Error::from)?
.await
})
.await;
match result {
Ok(content) => {
responder
.respond(acp::ReadTextFileResponse::new(content))
.log_err();
}
Err(e) => respond_err(responder, e),
}
respond_result(responder, result.map(acp::ReadTextFileResponse::new));
})
.detach();
}
@ -4224,25 +4213,20 @@ fn handle_wait_for_terminal_exit(
};
cx.spawn(async move |cx| {
let result: Result<_, acp::Error> = async {
let exit_status = thread
.update(cx, |thread, cx| {
anyhow::Ok(thread.terminal(args.terminal_id)?.read(cx).wait_for_exit())
})
.flatten_acp()?
.await;
Ok(exit_status)
}
.await;
let cancellation = responder.cancellation();
let result = cancellation
.run_until_cancelled(async {
let exit_status = thread
.update(cx, |thread, cx| {
anyhow::Ok(thread.terminal(args.terminal_id)?.read(cx).wait_for_exit())
})
.flatten_acp()?
.await;
Ok(exit_status)
})
.await;
match result {
Ok(exit_status) => {
responder
.respond(acp::WaitForTerminalExitResponse::new(exit_status))
.log_err();
}
Err(e) => respond_err(responder, e),
}
respond_result(responder, result.map(acp::WaitForTerminalExitResponse::new));
})
.detach();
}

View file

@ -12,7 +12,7 @@ use http_client::read_no_proxy_from_env;
use project::{AgentId, Project, agent_server_store::AgentServerStore};
use acp_thread::AgentConnection;
use agent_client_protocol::schema as acp_schema;
use agent_client_protocol::schema::v1 as acp_schema;
use anyhow::Result;
use gpui::{App, AppContext, Entity, Task};
use settings::SettingsStore;

View file

@ -1,6 +1,6 @@
use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
use acp_thread::AgentConnection;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Context as _, Result};
use collections::HashSet;
use fs::Fs;

View file

@ -1,6 +1,6 @@
use crate::{AgentServer, AgentServerDelegate};
use acp_thread::{AcpThread, AgentThreadEntry, ToolCall, ToolCallStatus};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use client::RefreshLlmTokenListener;
use futures::{FutureExt, StreamExt, channel::mpsc, select};
use gpui::AppContext;
@ -379,7 +379,7 @@ macro_rules! common_e2e_tests {
async fn tool_call_with_permission(cx: &mut ::gpui::TestAppContext) {
$crate::e2e_tests::test_tool_call_with_permission(
$server,
::agent_client_protocol::schema::PermissionOptionId::new($allow_option_id),
::agent_client_protocol::schema::v1::PermissionOptionId::new($allow_option_id),
cx,
)
.await;

View file

@ -12,7 +12,7 @@ use std::{
use acp_thread::{AcpThread, AcpThreadEvent, MentionUri, ThreadStatus, line_range_suffix};
use agent::{ContextServerRegistry, SharedThread, ThreadStore};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_servers::AgentServer;
use agent_settings::UserAgentsMd;
use collections::HashSet;

View file

@ -40,7 +40,7 @@ use std::rc::Rc;
use std::sync::Arc;
use ::ui::IconName;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::{AgentProfileId, AgentSettings};
use command_palette_hooks::CommandPaletteFilter;
use editor::{Editor, SelectionEffects, scroll::Autoscroll};

View file

@ -7,7 +7,7 @@ use std::sync::atomic::AtomicBool;
use crate::DEFAULT_THREAD_TITLE;
use crate::thread_metadata_store::{ThreadMetadata, ThreadMetadataStore};
use acp_thread::MentionUri;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Result;
use editor::{CompletionProvider, Editor, code_context_menus::COMPLETION_MENU_MAX_WIDTH};
use futures::FutureExt as _;

View file

@ -1,7 +1,7 @@
use std::{cmp::Reverse, rc::Rc, sync::Arc};
use acp_thread::AgentSessionConfigOptions;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_servers::AgentServer;
use collections::HashSet;

View file

@ -9,7 +9,7 @@ use action_log::{ActionLog, ActionLogTelemetry, DiffStats};
use agent::{
NativeAgentServer, NativeAgentSessionList, NoModelConfiguredError, SharedThread, ThreadStore,
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
#[cfg(test)]
use agent_servers::AgentServerDelegate;
use agent_servers::{AgentServer, GEMINI_TERMINAL_AUTH_METHOD_ID};

View file

@ -5,7 +5,7 @@ use crate::{
open_abs_path_at_point,
thread_metadata_store::{ThreadId, ThreadMetadataStore},
};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use std::cell::RefCell;
use acp_thread::{

View file

@ -9,7 +9,7 @@
//! the format we persist.
use agent::ZED_AGENT_ID;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Context as _;
use db::kvp::KeyValueStore;
use gpui::{App, AppContext as _, Entity, Task};

View file

@ -2,7 +2,7 @@ use std::ops::Range;
use acp_thread::{AcpThread, AgentThreadEntry, AssistantMessageChunk};
use agent::ThreadStore;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use collections::{HashMap, HashSet};
use editor::{Editor, EditorEvent, EditorMode, MinimapVisibility, SizingBehavior};
@ -690,7 +690,7 @@ mod tests {
use std::sync::Arc;
use acp_thread::{AgentConnection, StubAgentConnection};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
use editor::RowInfo;
use fs::FakeFs;

View file

@ -1,7 +1,7 @@
use crate::diagnostics::{DiagnosticsOptions, codeblock_fence_for_path, collect_diagnostics};
use acp_thread::{MentionUri, selection_name};
use agent::{ThreadStore, outline};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_servers::{AgentServer, AgentServerDelegate};
use anyhow::{Context as _, Result, anyhow};
use collections::{HashMap, HashSet};

View file

@ -11,7 +11,7 @@ use crate::{
};
use acp_thread::MentionUri;
use agent::ThreadStore;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Result, anyhow};
use base64::Engine as _;
use editor::{
@ -2215,7 +2215,7 @@ mod tests {
use acp_thread::MentionUri;
use agent::{ThreadStore, outline};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use base64::Engine as _;
use editor::{
AnchorRangeExt as _, Editor, EditorMode, MultiBufferOffset, SelectionEffects,

View file

@ -1,5 +1,5 @@
use acp_thread::AgentSessionModes;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_servers::AgentServer;
use fs::Fs;

View file

@ -1,5 +1,5 @@
use acp_thread::{AgentConnection, StubAgentConnection};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_servers::{AgentServer, AgentServerDelegate};
use gpui::{
App, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement,

View file

@ -2,7 +2,7 @@ use std::time::Duration;
use acp_thread::AgentSessionListRequest;
use agent::ThreadStore;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use chrono::Utc;
use collections::{HashMap, HashSet};
use db::kvp::Dismissable;

View file

@ -4,7 +4,7 @@ use std::{
};
use agent::{ThreadStore, ZED_AGENT_ID};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::Context as _;
use chrono::{DateTime, Utc};
use collections::{HashMap, HashSet};
@ -1824,7 +1824,7 @@ mod tests {
use acp_thread::StubAgentConnection;
use action_log::ActionLog;
use agent::DbThread;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use gpui::{TestAppContext, VisualTestContext};
use project::FakeFs;
use project::Project;

View file

@ -10,7 +10,7 @@ use crate::thread_metadata_store::{
use crate::{Agent, ArchiveSelectedThread, DEFAULT_THREAD_TITLE, RemoveSelectedThread};
use agent::ThreadStore;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use chrono::{DateTime, Datelike as _, Local, NaiveDate, TimeDelta, Utc};
use collections::HashMap;

View file

@ -1,7 +1,7 @@
use std::{path::PathBuf, time::Duration};
use acp_thread::MentionUri;
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use editor::Editor;
use gpui::{
Animation, AnimationExt, AnyView, Context, IntoElement, TaskExt, WeakEntity, Window,

View file

@ -40,7 +40,7 @@ use std::time::{Duration, Instant};
use acp_thread::AgentConnection as _;
use agent::{NativeAgent, NativeAgentConnection, Templates, ThreadStore};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use anyhow::{Context, Result};
use clap::Parser;
use feature_flags::FeatureFlagAppExt as _;

View file

@ -2473,8 +2473,8 @@ async fn test_adding_remote_skill(cx: &mut TestAppContext, server_cx: &mut TestA
authorization
.response
.send(acp_thread::SelectedPermissionOutcome::new(
agent_client_protocol::schema::PermissionOptionId::new("allow"),
agent_client_protocol::schema::PermissionOptionKind::AllowOnce,
agent_client_protocol::schema::v1::PermissionOptionId::new("allow"),
agent_client_protocol::schema::v1::PermissionOptionKind::AllowOnce,
))
.unwrap();
@ -2523,8 +2523,8 @@ async fn test_adding_remote_skill(cx: &mut TestAppContext, server_cx: &mut TestA
authorization
.response
.send(acp_thread::SelectedPermissionOutcome::new(
agent_client_protocol::schema::PermissionOptionId::new("allow"),
agent_client_protocol::schema::PermissionOptionKind::AllowOnce,
agent_client_protocol::schema::v1::PermissionOptionId::new("allow"),
agent_client_protocol::schema::v1::PermissionOptionKind::AllowOnce,
))
.unwrap();

View file

@ -3,7 +3,7 @@ mod thread_switcher;
use acp_thread::ThreadStatus;
use action_log::DiffStats;
use agent::{ThreadStore, ZED_AGENT_ID};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_settings::AgentSettings;
use agent_ui::terminal_thread_metadata_store::{
TerminalThreadMetadata, TerminalThreadMetadataStore, terminal_title_prefix,

View file

@ -15,7 +15,7 @@ const _: () = assert!(
);
use agent::{SharedThread, ThreadStore};
use agent_client_protocol::schema as acp;
use agent_client_protocol::schema::v1 as acp;
use agent_ui::AgentPanel;
use anyhow::{Context as _, Result};
use clap::Parser;

View file

@ -95,7 +95,7 @@ fn main() {
#[cfg(target_os = "macos")]
use {
acp_thread::{AgentConnection, StubAgentConnection},
agent_client_protocol::schema as acp,
agent_client_protocol::schema::v1 as acp,
agent_servers::{AgentServer, AgentServerDelegate},
anyhow::{Context as _, Result},
assets::Assets,