From 057ea61d2e799d593045db0f08cbfa6964a9b320 Mon Sep 17 00:00:00 2001 From: yishuiliunian Date: Fri, 12 Jun 2026 16:05:30 +0800 Subject: [PATCH] fix(acp): round-trip permission_mode so selector shows current mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit panel.rs translate dropped PermissionModeChanged (fell through _ => None), so AgentsMesh's permission selector showed '—' until a manual switch. Add the arm mirroring ModelChanged/ThinkingChanged → emit _loopal/permission_mode; loopal runtime already emits PermissionModeChanged on cold-start + switch. Also derive the permissionModes capability list from PermissionMode::VARIANTS (strum::VariantNames) instead of a hardcoded array, making the enum the single source of truth. --- crates/loopal-acp/BUILD.bazel | 2 ++ crates/loopal-acp/src/adapter/lifecycle.rs | 8 +++++--- crates/loopal-acp/src/translate/panel.rs | 5 +++++ crates/loopal-tool-api/BUILD.bazel | 1 + crates/loopal-tool-api/src/permission.rs | 5 ++++- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/loopal-acp/BUILD.bazel b/crates/loopal-acp/BUILD.bazel index d756f5f5..0240c961 100644 --- a/crates/loopal-acp/BUILD.bazel +++ b/crates/loopal-acp/BUILD.bazel @@ -9,11 +9,13 @@ rust_library( "//crates/loopal-agent-hub", "//crates/loopal-ipc", "//crates/loopal-protocol", + "//crates/loopal-tool-api", "//crates/loopal-tool-invocation", "@crates//:agent-client-protocol-schema", "@crates//:anyhow", "@crates//:serde", "@crates//:serde_json", + "@crates//:strum", "@crates//:tokio", "@crates//:tracing", "@crates//:uuid", diff --git a/crates/loopal-acp/src/adapter/lifecycle.rs b/crates/loopal-acp/src/adapter/lifecycle.rs index 6092fa5b..2e59206b 100644 --- a/crates/loopal-acp/src/adapter/lifecycle.rs +++ b/crates/loopal-acp/src/adapter/lifecycle.rs @@ -1,6 +1,8 @@ //! ACP lifecycle handlers: initialize, authenticate. +use loopal_tool_api::PermissionMode; use serde_json::Value; +use strum::VariantNames; use tracing::info; use crate::adapter::AcpAdapter; @@ -8,8 +10,8 @@ use crate::types::make_init_response; /// `initialize` result + AgentsMesh extensions: `controlRequest` (runner routes /// control-panel actions via `session/control_request`) and `permissionModes` -/// (AgentsMesh selector renders loopal's modes, not Claude Code's). -/// `permissionModes` must match `loopal_tool_api::PermissionMode` (SSOT). +/// (AgentsMesh selector renders loopal's modes, not Claude Code's), derived from +/// `PermissionMode::VARIANTS` (single source of truth). fn init_response_with_extensions() -> Value { let mut result = serde_json::to_value(make_init_response()).unwrap_or_default(); if let Some(obj) = result.as_object_mut() { @@ -17,7 +19,7 @@ fn init_response_with_extensions() -> Value { "agentsmeshExtensions".into(), serde_json::json!({ "controlRequest": true, - "permissionModes": ["bypass", "ask_dangerous", "ask_any_write"], + "permissionModes": PermissionMode::VARIANTS, }), ); } diff --git a/crates/loopal-acp/src/translate/panel.rs b/crates/loopal-acp/src/translate/panel.rs index c364f9b8..258fa37d 100644 --- a/crates/loopal-acp/src/translate/panel.rs +++ b/crates/loopal-acp/src/translate/panel.rs @@ -72,6 +72,11 @@ pub(crate) fn translate_panel( "thinking", serde_json::json!({ "thinking": thinking_config }), ), + AgentEventPayload::PermissionModeChanged { mode } => ext_notification( + session_id, + "permission_mode", + serde_json::json!({ "permission_mode": mode }), + ), _ => return None, }; Some(AcpNotification::Extension { method, params }) diff --git a/crates/loopal-tool-api/BUILD.bazel b/crates/loopal-tool-api/BUILD.bazel index 01b67bb5..0c0fee44 100644 --- a/crates/loopal-tool-api/BUILD.bazel +++ b/crates/loopal-tool-api/BUILD.bazel @@ -13,6 +13,7 @@ rust_library( "@crates//:schemars", "@crates//:serde", "@crates//:serde_json", + "@crates//:strum", "@crates//:thiserror", ], proc_macro_deps = ["@crates//:async-trait"], diff --git a/crates/loopal-tool-api/src/permission.rs b/crates/loopal-tool-api/src/permission.rs index c7101af0..ef448546 100644 --- a/crates/loopal-tool-api/src/permission.rs +++ b/crates/loopal-tool-api/src/permission.rs @@ -7,8 +7,11 @@ pub enum PermissionLevel { Dangerous, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, strum::VariantNames, +)] #[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] pub enum PermissionMode { #[default] Bypass,