Skip to content

Commit 5e397e8

Browse files
agu-zbennetbortfeldmandanilo-leal
authored
acp: Support session modes (e.g. CC plan mode) (#37632)
Adds support for [ACP session modes](zed-industries/agent-client-protocol#67) enabling plan and other permission modes in CC: https://github.com/user-attachments/assets/dea18d82-4da6-465e-983b-02b77c6dcf15 Release Notes: - Claude Code: Add support for plan mode, and all other permission modes --------- Co-authored-by: Bennet Bo Fenner <[email protected]> Co-authored-by: Richard Feldman <[email protected]> Co-authored-by: Danilo Leal <[email protected]>
1 parent ad02f6b commit 5e397e8

26 files changed

+886
-143
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ zlog_settings = { path = "crates/zlog_settings" }
433433
# External crates
434434
#
435435

436-
agent-client-protocol = { version = "0.2.0-alpha.6", features = ["unstable"]}
436+
agent-client-protocol = { version = "0.2.0-alpha.8", features = ["unstable"] }
437437
aho-corasick = "1.1"
438438
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" }
439439
any_vec = "0.14"

assets/keymaps/default-linux.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,14 +328,20 @@
328328
"enter": "agent::AcceptSuggestedContext"
329329
}
330330
},
331+
{
332+
"context": "AcpThread > ModeSelector",
333+
"bindings": {
334+
"ctrl-enter": "menu::Confirm"
335+
}
336+
},
331337
{
332338
"context": "AcpThread > Editor && !use_modifier_to_send",
333339
"use_key_equivalents": true,
334340
"bindings": {
335341
"enter": "agent::Chat",
336342
"shift-ctrl-r": "agent::OpenAgentDiff",
337343
"ctrl-shift-y": "agent::KeepAll",
338-
"ctrl-shift-n": "agent::RejectAll"
344+
"ctrl-shift-n": "agent::RejectAll",
339345
}
340346
},
341347
{
@@ -345,7 +351,8 @@
345351
"ctrl-enter": "agent::Chat",
346352
"shift-ctrl-r": "agent::OpenAgentDiff",
347353
"ctrl-shift-y": "agent::KeepAll",
348-
"ctrl-shift-n": "agent::RejectAll"
354+
"ctrl-shift-n": "agent::RejectAll",
355+
"shift-tab": "agent::CycleModeSelector"
349356
}
350357
},
351358
{

assets/keymaps/default-macos.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,14 +378,21 @@
378378
"ctrl--": "pane::GoBack"
379379
}
380380
},
381+
{
382+
"context": "AcpThread > ModeSelector",
383+
"bindings": {
384+
"cmd-enter": "menu::Confirm"
385+
}
386+
},
381387
{
382388
"context": "AcpThread > Editor && !use_modifier_to_send",
383389
"use_key_equivalents": true,
384390
"bindings": {
385391
"enter": "agent::Chat",
386392
"shift-ctrl-r": "agent::OpenAgentDiff",
387393
"cmd-shift-y": "agent::KeepAll",
388-
"cmd-shift-n": "agent::RejectAll"
394+
"cmd-shift-n": "agent::RejectAll",
395+
"shift-tab": "agent::CycleModeSelector"
389396
}
390397
},
391398
{
@@ -395,7 +402,8 @@
395402
"cmd-enter": "agent::Chat",
396403
"shift-ctrl-r": "agent::OpenAgentDiff",
397404
"cmd-shift-y": "agent::KeepAll",
398-
"cmd-shift-n": "agent::RejectAll"
405+
"cmd-shift-n": "agent::RejectAll",
406+
"shift-tab": "agent::CycleModeSelector"
399407
}
400408
},
401409
{

assets/keymaps/default-windows.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,21 @@
336336
"enter": "agent::AcceptSuggestedContext"
337337
}
338338
},
339+
{
340+
"context": "AcpThread > ModeSelector",
341+
"bindings": {
342+
"ctrl-enter": "menu::Confirm"
343+
}
344+
},
339345
{
340346
"context": "AcpThread > Editor",
341347
"use_key_equivalents": true,
342348
"bindings": {
343349
"enter": "agent::Chat",
344350
"ctrl-shift-r": "agent::OpenAgentDiff",
345351
"ctrl-shift-y": "agent::KeepAll",
346-
"ctrl-shift-n": "agent::RejectAll"
352+
"ctrl-shift-n": "agent::RejectAll",
353+
"shift-tab": "agent::CycleModeSelector"
347354
}
348355
},
349356
{

assets/settings/default.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,9 @@
828828
// }
829829
],
830830
// When enabled, the agent can run potentially destructive actions without asking for your confirmation.
831+
//
832+
// Note: This setting has no effect on external agents that support permission modes, such as Claude Code.
833+
// You can set `agent_servers.claude.default_mode` to `bypassPermissions` to skip all permission requests.
831834
"always_allow_tool_actions": false,
832835
// When enabled, the agent will stream edits.
833836
"stream_edits": false,

crates/acp_thread/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ test-support = ["gpui/test-support", "project/test-support", "dep:parking_lot"]
1818
[dependencies]
1919
action_log.workspace = true
2020
agent-client-protocol.workspace = true
21-
anyhow.workspace = true
2221
agent_settings.workspace = true
22+
anyhow.workspace = true
2323
buffer_diff.workspace = true
2424
collections.workspace = true
2525
editor.workspace = true

crates/acp_thread/src/acp_thread.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ pub enum AcpThreadEvent {
805805
PromptCapabilitiesUpdated,
806806
Refusal,
807807
AvailableCommandsUpdated(Vec<acp::AvailableCommand>),
808+
ModeUpdated(acp::SessionModeId),
808809
}
809810

810811
impl EventEmitter<AcpThreadEvent> for AcpThread {}
@@ -1007,6 +1008,9 @@ impl AcpThread {
10071008
acp::SessionUpdate::AvailableCommandsUpdate { available_commands } => {
10081009
cx.emit(AcpThreadEvent::AvailableCommandsUpdated(available_commands))
10091010
}
1011+
acp::SessionUpdate::CurrentModeUpdate { current_mode_id } => {
1012+
cx.emit(AcpThreadEvent::ModeUpdated(current_mode_id))
1013+
}
10101014
}
10111015
Ok(())
10121016
}
@@ -1303,11 +1307,12 @@ impl AcpThread {
13031307
&mut self,
13041308
tool_call: acp::ToolCallUpdate,
13051309
options: Vec<acp::PermissionOption>,
1310+
respect_always_allow_setting: bool,
13061311
cx: &mut Context<Self>,
13071312
) -> Result<BoxFuture<'static, acp::RequestPermissionOutcome>> {
13081313
let (tx, rx) = oneshot::channel();
13091314

1310-
if AgentSettings::get_global(cx).always_allow_tool_actions {
1315+
if respect_always_allow_setting && AgentSettings::get_global(cx).always_allow_tool_actions {
13111316
// Don't use AllowAlways, because then if you were to turn off always_allow_tool_actions,
13121317
// some tools would (incorrectly) continue to auto-accept.
13131318
if let Some(allow_once_option) = options.iter().find_map(|option| {

crates/acp_thread/src/connection.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ pub trait AgentConnection {
7575
fn telemetry(&self) -> Option<Rc<dyn AgentTelemetry>> {
7676
None
7777
}
78+
79+
fn session_modes(
80+
&self,
81+
_session_id: &acp::SessionId,
82+
_cx: &App,
83+
) -> Option<Rc<dyn AgentSessionModes>> {
84+
None
85+
}
86+
7887
fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
7988
}
8089

@@ -109,6 +118,14 @@ pub trait AgentTelemetry {
109118
) -> Task<Result<serde_json::Value>>;
110119
}
111120

121+
pub trait AgentSessionModes {
122+
fn current_mode(&self) -> acp::SessionModeId;
123+
124+
fn all_modes(&self) -> Vec<acp::SessionMode>;
125+
126+
fn set_mode(&self, mode: acp::SessionModeId, cx: &mut App) -> Task<Result<()>>;
127+
}
128+
112129
#[derive(Debug)]
113130
pub struct AuthRequired {
114131
pub description: Option<String>,
@@ -397,6 +414,7 @@ mod test_support {
397414
thread.request_tool_call_authorization(
398415
tool_call.clone().into(),
399416
options.clone(),
417+
false,
400418
cx,
401419
)
402420
})??

crates/agent2/src/agent.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,9 @@ impl NativeAgentConnection {
771771
response,
772772
}) => {
773773
let outcome_task = acp_thread.update(cx, |thread, cx| {
774-
thread.request_tool_call_authorization(tool_call, options, cx)
774+
thread.request_tool_call_authorization(
775+
tool_call, options, true, cx,
776+
)
775777
})??;
776778
cx.background_spawn(async move {
777779
if let acp::RequestPermissionOutcome::Selected { option_id } =

0 commit comments

Comments
 (0)