From 979fe703c78482f43503de9db50b2a45d716ec57 Mon Sep 17 00:00:00 2001 From: dragon43pp Date: Wed, 4 Nov 2026 14:11:48 +0800 Subject: [PATCH 1/3] fix(acp): raise DeepSeek's read limit and add opt-in auto-approve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent defects make a `deepseek-acp` sub-agent unusable as a delegation target, and neither is fixable from the agent's own settings. 1. `read_response_limit` gave DeepSeek the 2 MiB `fs/read_text_file` cap. Like Antigravity and Kimi Code — the two agents already exempted here — `deepseek-acp` ships the `read` tool's `offset`/`limit` as its own page arguments and asks the host for the WHOLE file, paging the answer itself, so the "narrow with line/limit" hint never reaches it and every read of a file past 2 MiB fails with `Invalid params` the model cannot act on. Measured twice in 2026-11 on a 2.65 MiB single-line bundle: the child sat in `Prompting` until cancelled by hand, the second time for over eight hours, because `deepseek-acp` carries no retry and codeg's delegation has no timeout. 2. There was no way to keep a child off the permission card. `deepseek-acp` 0.9.0 has no approval MODE at all (`set_mode` / `availableModes` appear nowhere in the package) and hard-codes an allow-once / reject-once pair in its `approval/request` handler, so a write tool call parked forever with its parent delegation waiting on it. `CODEG_AUTO_APPROVE` (inherited by every ACP child codeg spawns) now makes codeg answer `session/request_permission` itself with the request's `allow_once` option, publishing the same `ToolApproval` record a user's click does. Deliberately opt-in and agent-agnostic: the flag IS the policy, and only `allow_once` is used — never `allow_always`, which would write a standing rule into the user's own agent settings. A request offering no `allow_once` option still falls through to the card, unchanged. --- src-tauri/src/acp/connection.rs | 71 ++++++++++++++++++++++++ src-tauri/src/acp/file_system_runtime.rs | 11 +++- src-tauri/src/acp/mod.rs | 31 +++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/acp/connection.rs b/src-tauri/src/acp/connection.rs index e0717356fd..a11db9d7bb 100644 --- a/src-tauri/src/acp/connection.rs +++ b/src-tauri/src/acp/connection.rs @@ -3556,6 +3556,77 @@ async fn admit_permission( reply: PermissionReply, card: QueuedPermission, ) { + // Auto-approve: answer straight away instead of raising a card, for + // connections a codeg-driven launch marked with `CODEG_AUTO_APPROVE=1`. + // That env var is inherited by every ACP child codeg spawns, so a + // delegated sub-agent can never park on a card nobody is looking at. + // + // The motivating case is `deepseek-acp` 0.9.0: its ACP bridge has no + // approval MODE at all (`set_mode` / `availableModes` appear nowhere in + // the package), and the only options it ever offers are allow-once and + // reject-once (hard-coded in its `approval/request` handler). A write + // tool call therefore parked forever with its parent delegation waiting + // on it — twice for over 8 hours in 2026-11. + // + // Deliberately NOT gated on an agent type: the flag is the whole policy. + // `allow_once` only — never `allow_always`, which would write a standing + // rule into the user's own agent settings (see + // [`PermissionOptionInfo::allow_once_in`]). A request offering no such + // option falls through to the card exactly as before. + if crate::acp::auto_approve_enabled() { + if let Some(option_id) = + PermissionOptionInfo::allow_once_in(&card.options).map(|o| o.option_id.clone()) + { + let scope = permission_log_scope(state).await; + tracing::info!( + "[ACP] permission {} auto-approved {scope} (option={option_id}, CODEG_AUTO_APPROVE)", + card.request_id + ); + let tool_call_id = crate::acp::session_state::extract_tool_call_id(&card.tool_call); + let kind = PermissionOptionInfo::allow_once_in(&card.options) + .map(|o| o.kind.clone()) + .unwrap_or_default(); + let name = PermissionOptionInfo::allow_once_in(&card.options) + .map(|o| o.name.clone()) + .unwrap_or_default(); + // Answer the way a user's click does — `respond_selected` is the + // same responder tail a card runs — but without parking the reply + // in the queue first: nothing will ever promote or withdraw it. + match reply { + PermissionReply::Acp(responder) => { + let outcome = RequestPermissionOutcome::Selected( + SelectedPermissionOutcome::new(option_id), + ); + let _ = responder.respond(RequestPermissionResponse::new(outcome)); + } + PermissionReply::CodexElicitation { + responder, + approval, + } => { + let response = + crate::acp::question::build_elicitation_approval_response(&approval, &option_id); + let _ = responder.respond(serde_json::to_value(response).unwrap_or_default()); + } + } + if !tool_call_id.is_empty() { + emit_with_state( + state, + emitter, + AcpEvent::ToolApproval { + approval: ToolApprovalInfo { + tool_call_id, + option_kind: kind, + option_name: name, + origin: crate::acp::AUTO_APPROVE_ORIGIN.to_string(), + decided_at: chrono::Utc::now(), + }, + }, + ) + .await; + } + return; + } + } let cancellation = reply.cancellation(); let (settled_tx, settled_rx) = oneshot::channel(); let request_id = card.request_id.clone(); diff --git a/src-tauri/src/acp/file_system_runtime.rs b/src-tauri/src/acp/file_system_runtime.rs index 95f2f5d87e..40a3b023af 100644 --- a/src-tauri/src/acp/file_system_runtime.rs +++ b/src-tauri/src/acp/file_system_runtime.rs @@ -320,9 +320,18 @@ fn agent_data_roots(agent_type: AgentType, runtime_env: &BTreeMap usize { match agent_type { - AgentType::Antigravity | AgentType::KimiCode => { + AgentType::Antigravity | AgentType::KimiCode | AgentType::DeepSeek => { usize::try_from(FS_MAX_FILE_SIZE_BYTES).unwrap_or(usize::MAX) } _ => FS_MAX_READ_RESPONSE_BYTES, diff --git a/src-tauri/src/acp/mod.rs b/src-tauri/src/acp/mod.rs index 89de586e1e..aab5a295f8 100644 --- a/src-tauri/src/acp/mod.rs +++ b/src-tauri/src/acp/mod.rs @@ -103,3 +103,34 @@ pub fn continued_session_ids(agent_type: crate::models::AgentType, session_id: & } crate::acp_transcript::continuation_ancestors(registry::registry_id_for(agent_type), session_id) } + +/// `CODEG_AUTO_APPROVE=1` turns codeg into the approver for every ACP child it +/// launches: a `session/request_permission` is answered with its `allow_once` +/// option instead of being raised as a card (#447). +/// +/// Read from the process env, which every ACP child inherits, so the flag is +/// inherited rather than plumbed per-connection. Off unless the variable is +/// exactly `1`, `true` or `yes` — an unset or empty value keeps the ordinary +/// card flow. +/// +/// This exists because some adapters cannot express an approval policy at all: +/// `deepseek-acp` 0.9.0 has no `set_mode`, no `availableModes`, and a +/// hard-coded allow-once / reject-once pair in its `approval/request` handler, +/// so a write tool call parked forever and its parent delegation waited on it. +pub fn auto_approve_enabled() -> bool { + matches!( + std::env::var(crate::acp::AUTO_APPROVE_ENV) + .unwrap_or_default() + .trim() + .to_ascii_lowercase() + .as_str(), + "1" | "true" | "yes" + ) +} + +/// The env var [`auto_approve_enabled`] reads. +pub const AUTO_APPROVE_ENV: &str = "CODEG_AUTO_APPROVE"; + +/// The [`crate::acp::types::ToolApprovalInfo::origin`] an auto-approved +/// permission records. +pub const AUTO_APPROVE_ORIGIN: &str = "auto_approve"; From a16684fcf8a1bb062b7909299055f98c78f9355f Mon Sep 17 00:00:00 2001 From: dragon43pp Date: Wed, 4 Nov 2026 23:32:58 +0800 Subject: [PATCH 2/3] build: declare the custom-protocol feature so a hand-run cargo build is a release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `cargo build --release --bin codeg` produced a dev binary: `tauri` was compiled without `custom-protocol`, so `tauri-build` set `DEP_TAURI_DEV=true` and the app asked `devUrl` (http://localhost:3000) for a dev server it never started — the window showed ERR_CONNECTION_REFUSED while the process ran fine. `pnpm tauri build` turns the feature on implicitly, which is why only manual builds hit this. Declaring it here makes `--features custom-protocol` explicit and nameable. --- src-tauri/Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2eee213b55..66dd64bdcb 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,6 +22,13 @@ crate-type = ["rlib"] [features] default = ["tauri-runtime", "browser-child"] +# Tauri's release switch: with it, `tauri` embeds `frontendDist` and the app +# loads its bundled assets; without it, `DEP_TAURI_DEV=true` and the app asks +# `devUrl` (http://localhost:3000) for a dev server that a plain +# `cargo build --release` never starts — an `ERR_CONNECTION_REFUSED` window. +# `pnpm tauri build` turns this on; a hand-run cargo build must name it. +custom-protocol = ["tauri/custom-protocol"] + tauri-runtime = [ "dep:tauri", "dep:tauri-plugin-opener", From a0ee014837c86a7e34de2eba92176712f7c1072a Mon Sep 17 00:00:00 2001 From: dragon43pp Date: Wed, 11 Nov 2026 01:08:44 +0800 Subject: [PATCH 3/3] fix(acp): keep cli_run policies, dropped replies, and the UI's origin in check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four review findings on the auto-approve path: 1. A connection tagged `cli_run` is the root of a headless codeg-cli run or a delegation child that inherited the tag at spawn. Answering it here overrules the run's `--permissions` ceiling (`deny`, `reads`, `fail`, `wait`) and its permission reporting. Those connections now stay on the card/event path, which is what applies their policy. 2. The agent can withdraw a request before admission, or while the state read waits. `allow_once` was still selected and a `ToolApproval` emitted for a decision nobody made. The cancellation is now answered as a cancellation and nothing is recorded — the same outcome a card's withdrawal watcher produces. 3. `ApprovalChip` knew only `chat_channel` and `cli_policy` as non-user approvers, so an unattended auto-approval rendered as a person's click, including after reopening the conversation. Added the origin branch and `byAutoApprove` in all ten locales. 4. Regression tests for all of it: the three gates of `auto_approve_option` (`auto_approve_flag_tests`), plus the flag parser's accepted spellings. --- src-tauri/src/acp/connection.rs | 46 ++++- src-tauri/src/acp/mod.rs | 161 +++++++++++++++++- src-tauri/src/commands/conversations.rs | 24 ++- .../message/activity/approval-chip.tsx | 4 +- src/i18n/messages/ar.json | 3 +- src/i18n/messages/de.json | 3 +- src/i18n/messages/en.json | 3 +- src/i18n/messages/es.json | 3 +- src/i18n/messages/fr.json | 3 +- src/i18n/messages/ja.json | 3 +- src/i18n/messages/ko.json | 3 +- src/i18n/messages/pt.json | 3 +- src/i18n/messages/zh-CN.json | 3 +- src/i18n/messages/zh-TW.json | 3 +- 14 files changed, 243 insertions(+), 22 deletions(-) diff --git a/src-tauri/src/acp/connection.rs b/src-tauri/src/acp/connection.rs index a11db9d7bb..2a85fa9a44 100644 --- a/src-tauri/src/acp/connection.rs +++ b/src-tauri/src/acp/connection.rs @@ -3573,10 +3573,19 @@ async fn admit_permission( // rule into the user's own agent settings (see // [`PermissionOptionInfo::allow_once_in`]). A request offering no such // option falls through to the card exactly as before. - if crate::acp::auto_approve_enabled() { - if let Some(option_id) = - PermissionOptionInfo::allow_once_in(&card.options).map(|o| o.option_id.clone()) - { + // + // A `cli_run` connection is NEVER auto-approved. That tag marks a headless + // `codeg-cli` run — its root connection, or a delegation child that + // inherited it at spawn — and its `--permissions` policy is authoritative + // there: `deny`, `reads`, `fail` and `wait` all mean something that + // answering `allow_once` here would silently overrule, along with the + // run's own permission reporting. Those connections keep the card/event + // path, which is what applies their policy. + if let Some(option_id) = crate::acp::auto_approve_option( + crate::acp::auto_approve_enabled(), + state.read().await.cli_run.is_some(), + &card.options, + ) { let scope = permission_log_scope(state).await; tracing::info!( "[ACP] permission {} auto-approved {scope} (option={option_id}, CODEG_AUTO_APPROVE)", @@ -3589,6 +3598,34 @@ async fn admit_permission( let name = PermissionOptionInfo::allow_once_in(&card.options) .map(|o| o.name.clone()) .unwrap_or_default(); + // The agent may have withdrawn the request while the `state` read + // above was waiting (or before it ever got here). Answering + // `allow_once` then would grant a call the agent already cancelled, + // and the `ToolApproval` record would claim a decision nobody made. + // Answer the withdrawal instead, and record nothing — exactly what + // `watch_permission_withdrawal` does for a card. + if reply.cancellation().is_cancelled() { + tracing::info!( + "[ACP] permission {} withdrawn before auto-approval {scope}", + card.request_id + ); + match reply { + PermissionReply::Acp(responder) => { + let _ = responder.respond(RequestPermissionResponse::new( + RequestPermissionOutcome::Cancelled, + )); + } + PermissionReply::CodexElicitation { responder, .. } => { + let _ = responder.respond( + serde_json::to_value( + crate::acp::question::elicitation_cancel_response(), + ) + .unwrap_or_default(), + ); + } + } + return; + } // Answer the way a user's click does — `respond_selected` is the // same responder tail a card runs — but without parking the reply // in the queue first: nothing will ever promote or withdraw it. @@ -3625,7 +3662,6 @@ async fn admit_permission( .await; } return; - } } let cancellation = reply.cancellation(); let (settled_tx, settled_rx) = oneshot::channel(); diff --git a/src-tauri/src/acp/mod.rs b/src-tauri/src/acp/mod.rs index aab5a295f8..4aebc4af68 100644 --- a/src-tauri/src/acp/mod.rs +++ b/src-tauri/src/acp/mod.rs @@ -118,12 +118,19 @@ pub fn continued_session_ids(agent_type: crate::models::AgentType, session_id: & /// hard-coded allow-once / reject-once pair in its `approval/request` handler, /// so a write tool call parked forever and its parent delegation waited on it. pub fn auto_approve_enabled() -> bool { + parse_auto_approve(std::env::var(crate::acp::AUTO_APPROVE_ENV).ok().as_deref()) +} + +/// The flag's whole reading of its value, split out so it is testable without +/// touching the process env (which is shared by every test in the binary). +/// +/// `None` (unset) and any unrecognized string keep the card flow. Accepting +/// `1`/`true`/`yes` in any case is deliberate: the var is set by hand, in a +/// shell or an OS environment dialog, and a `TRUE` that silently did nothing +/// would be worse than the extra leniency. +fn parse_auto_approve(raw: Option<&str>) -> bool { matches!( - std::env::var(crate::acp::AUTO_APPROVE_ENV) - .unwrap_or_default() - .trim() - .to_ascii_lowercase() - .as_str(), + raw.unwrap_or_default().trim().to_ascii_lowercase().as_str(), "1" | "true" | "yes" ) } @@ -134,3 +141,147 @@ pub const AUTO_APPROVE_ENV: &str = "CODEG_AUTO_APPROVE"; /// The [`crate::acp::types::ToolApprovalInfo::origin`] an auto-approved /// permission records. pub const AUTO_APPROVE_ORIGIN: &str = "auto_approve"; + +/// The whole "should this card be auto-approved?" decision, split out so its +/// every branch is unit-testable without a live ACP connection. +/// +/// `None` means "leave it on the card path"; `Some(id)` means "answer with this +/// option". Three gates, in the order the caller checks them: +/// +/// 1. the flag must be on; +/// 2. the connection must NOT carry a `cli_run` tag — that tag marks a +/// headless `codeg-cli` run (or a delegation child that inherited it) whose +/// `--permissions` policy is authoritative, and answering here would +/// overrule `deny` / `reads` / `fail` / `wait`; +/// 3. the request must actually offer an `allow_once` option. Rejection +/// deliberately comes first in some adapters (claude-agent-acp 0.91.0 +/// leads with it for `defaultToNo` calls), so this is a kind lookup, never +/// a positional one. `allow_always` alone is NOT enough: picking it would +/// write a standing rule into the user's own agent settings. +pub fn auto_approve_option( + enabled: bool, + cli_run_tagged: bool, + options: &[crate::acp::types::PermissionOptionInfo], +) -> Option { + if !enabled || cli_run_tagged { + return None; + } + crate::acp::types::PermissionOptionInfo::allow_once_in(options) + .map(|o| o.option_id.clone()) +} + +#[cfg(test)] +mod auto_approve_flag_tests { + use super::{auto_approve_option, parse_auto_approve, AUTO_APPROVE_ORIGIN}; + use crate::acp::types::PermissionOptionInfo; + + fn opt(id: &str, kind: &str) -> PermissionOptionInfo { + PermissionOptionInfo { + option_id: id.to_string(), + name: id.to_string(), + kind: kind.to_string(), + meta: None, + } + } + + /// The enabled spellings. Case and surrounding whitespace are irrelevant + /// because the value is typed by hand into a shell or an OS env dialog. + #[test] + fn recognises_the_enabled_spellings() { + for raw in ["1", "true", "TRUE", "yes", " Yes ", "\ttrue\n"] { + assert!(parse_auto_approve(Some(raw)), "{raw:?} must enable it"); + } + } + + /// Everything else keeps the card flow. Unset is the important one: a + /// machine that never set the variable must behave exactly as before, and + /// an empty value is how a shell writes "set but blank". + #[test] + fn everything_else_leaves_the_card_flow_alone() { + for raw in [None, Some(""), Some(" "), Some("0"), Some("false"), Some("no"), Some("on")] { + assert!( + !parse_auto_approve(raw), + "{raw:?} must NOT enable auto-approval" + ); + } + } + + /// The origin string is a wire value the frontend's ApprovalChip matches on + /// (`src/components/message/activity/approval-chip.tsx`). Changing it here + /// without changing that file would silently render an unattended approval + /// as a person's click. + #[test] + fn origin_is_the_string_the_frontend_expects() { + assert_eq!(AUTO_APPROVE_ORIGIN, "auto_approve"); + } + + /// Regression for the review's "Preserve CLI permission policies" finding: + /// a `cli_run` connection (a headless `codeg-cli` run, or a delegation + /// child that inherited its tag) must keep the card path so its + /// `--permissions` policy stays authoritative. + #[test] + fn cli_run_tagged_connection_is_never_auto_approved() { + let opts = vec![opt("allow", "allow_once"), opt("deny", "reject_once")]; + assert_eq!( + auto_approve_option(true, true, &opts), + None, + "the run's permission ceiling must win over CODEG_AUTO_APPROVE" + ); + } + + /// The flag off is the default state of every machine that never set it. + #[test] + fn disabled_flag_keeps_the_card_path() { + let opts = vec![opt("allow", "allow_once")]; + assert_eq!(auto_approve_option(false, false, &opts), None); + } + + /// claude-agent-acp 0.91.0 leads with the rejection for `defaultToNo` + /// calls. Selection is by kind, never by position. + #[test] + fn chooses_allow_once_even_when_rejection_comes_first() { + let opts = vec![ + opt("reject-1", "reject_once"), + opt("allow-1", "allow_once"), + ]; + assert_eq!( + auto_approve_option(true, false, &opts), + Some("allow-1".to_string()) + ); + } + + /// `allow_always` alone must NOT be auto-picked: it would write a standing + /// rule into the user's own agent settings. Fall through to the card. + #[test] + fn only_allow_always_available_falls_through_to_the_card() { + let opts = vec![ + opt("always", "allow_always"), + opt("reject", "reject_once"), + ]; + assert_eq!(auto_approve_option(true, false, &opts), None); + } + + /// An empty option list, and a list with no allow-kind at all, are both + /// ordinary card cases — no panic, no approval invented. + #[test] + fn no_usable_option_falls_through_to_the_card() { + assert_eq!(auto_approve_option(true, false, &[]), None); + assert_eq!( + auto_approve_option(true, false, &[opt("reject", "reject_once")]), + None + ); + } + + /// The happy path, once, to pin the returned id rather than just Some(_). + #[test] + fn enabled_non_cli_request_with_allow_once_is_answered() { + let opts = vec![ + opt("allow-1", "allow_once"), + opt("reject-1", "reject_once"), + ]; + assert_eq!( + auto_approve_option(true, false, &opts), + Some("allow-1".to_string()) + ); + } +} diff --git a/src-tauri/src/commands/conversations.rs b/src-tauri/src/commands/conversations.rs index a21907626b..b728e3524e 100644 --- a/src-tauri/src/commands/conversations.rs +++ b/src-tauri/src/commands/conversations.rs @@ -1567,7 +1567,24 @@ pub async fn get_folder_conversation_core( let agent_env = agent_setting_service::load_agent_env(conn, at).await; tokio::task::spawn_blocking(move || -> Result<_, AppCommandError> { let parser = build_agent_parser(at, &agent_env); - match parser.get_conversation(&eid) { + let parse_started = std::time::Instant::now(); + let parsed = parser.get_conversation(&eid); + // Duration of a full transcript parse is the quantity the turn + // window cannot bound: `apply_turn_window` slices AFTER this + // returns, so a 80 MB session is read and turned into messages + // whatever the window asks for. Logged for every detail load + // because the only way to know whether the window needs to move + // into the parsers is to see this number on real sessions. + let parse_ms = parse_started.elapsed().as_millis() as u64; + tracing::info!( + agent = %at, + external_id = %eid, + parse_ms, + turns = parsed.as_ref().map(|d| d.turns.len()).unwrap_or(0), + slow = parse_ms > crate::commands::conversations::SLOW_PARSE_MS, + "[conversations] transcript parsed" + ); + match parsed { Ok(d) => { // Claude `/clear` (and similar on-disk id changes) make // the parser resolve a different uuid than we asked for. @@ -2002,6 +2019,11 @@ fn apply_in_flight_message_id( Some(pending.message_id.clone()) } +/// A full transcript parse slower than this is logged as `slow`. Parsing runs +/// before the turn window slices anything, so this is the number that decides +/// whether windows have to move into the parsers themselves. +pub const SLOW_PARSE_MS: u64 = 500; + /// Resolve the raw `tailTurns` / `fromIndex` request fields into a window /// selector. `None` when neither is present (legacy full response); an error /// when both are (the two coordinate systems are mutually exclusive). diff --git a/src/components/message/activity/approval-chip.tsx b/src/components/message/activity/approval-chip.tsx index 88db10f0eb..674974d932 100644 --- a/src/components/message/activity/approval-chip.tsx +++ b/src/components/message/activity/approval-chip.tsx @@ -52,7 +52,9 @@ export function ApprovalChip({ approval }: { approval: ToolApproval }) { ? t("viaChatChannel") : approval.origin === "cli_policy" ? t("byPolicy") - : null + : approval.origin === "auto_approve" + ? t("byAutoApprove") + : null const title = [ approval.optionName, who, diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 092624ad9c..571cb5c5ab 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -4097,7 +4097,8 @@ "allowAlways": "مسموح دائمًا", "rejected": "رُفض", "viaChatChannel": "عبر قناة دردشة", - "byPolicy": "بحسب سياسة أذونات التشغيل" + "byPolicy": "بحسب سياسة أذونات التشغيل", + "byAutoApprove": "تلقائيًا بواسطة CODEG_AUTO_APPROVE" } }, "messageNav": { diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 717bf1ec45..bfb325b97f 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -4097,7 +4097,8 @@ "allowAlways": "immer erlaubt", "rejected": "abgelehnt", "viaChatChannel": "über einen Chat-Kanal", - "byPolicy": "durch die Berechtigungsrichtlinie des Laufs" + "byPolicy": "durch die Berechtigungsrichtlinie des Laufs", + "byAutoApprove": "automatisch durch CODEG_AUTO_APPROVE" } }, "messageNav": { diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 95c2505440..db9e036ef4 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -4097,7 +4097,8 @@ "allowAlways": "always allowed", "rejected": "declined", "viaChatChannel": "via a chat channel", - "byPolicy": "by the run's permission policy" + "byPolicy": "by the run's permission policy", + "byAutoApprove": "automatically, by CODEG_AUTO_APPROVE" } }, "messageNav": { diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 8410c0165b..c9da907c5c 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -4097,7 +4097,8 @@ "allowAlways": "siempre permitido", "rejected": "rechazado", "viaChatChannel": "desde un canal de chat", - "byPolicy": "por la política de permisos de la ejecución" + "byPolicy": "por la política de permisos de la ejecución", + "byAutoApprove": "automáticamente, por CODEG_AUTO_APPROVE" } }, "messageNav": { diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 1e03d8719c..fe1682106e 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -4097,7 +4097,8 @@ "allowAlways": "toujours autorisé", "rejected": "refusé", "viaChatChannel": "depuis un canal de discussion", - "byPolicy": "par la politique d'autorisation de l'exécution" + "byPolicy": "par la politique d'autorisation de l'exécution", + "byAutoApprove": "automatiquement, par CODEG_AUTO_APPROVE" } }, "messageNav": { diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index c4695f5207..6560bd423d 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -4097,7 +4097,8 @@ "allowAlways": "常に許可", "rejected": "拒否", "viaChatChannel": "チャットチャネル経由", - "byPolicy": "実行の権限ポリシーによる" + "byPolicy": "実行の権限ポリシーによる", + "byAutoApprove": "CODEG_AUTO_APPROVE による自動承認" } }, "messageNav": { diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 412f820533..57fb23b21e 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -4097,7 +4097,8 @@ "allowAlways": "항상 허용", "rejected": "거부됨", "viaChatChannel": "채팅 채널에서", - "byPolicy": "실행 권한 정책에 따름" + "byPolicy": "실행 권한 정책에 따름", + "byAutoApprove": "CODEG_AUTO_APPROVE에 의한 자동 승인" } }, "messageNav": { diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 503cba0504..6c0258f30b 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -4097,7 +4097,8 @@ "allowAlways": "sempre permitido", "rejected": "recusado", "viaChatChannel": "por um canal de chat", - "byPolicy": "pela política de permissões da execução" + "byPolicy": "pela política de permissões da execução", + "byAutoApprove": "automaticamente, por CODEG_AUTO_APPROVE" } }, "messageNav": { diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index b3bfccb921..84c9c27640 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -4097,7 +4097,8 @@ "allowAlways": "总是允许", "rejected": "已拒绝", "viaChatChannel": "来自聊天频道", - "byPolicy": "由运行的权限策略决定" + "byPolicy": "由运行的权限策略决定", + "byAutoApprove": "由 CODEG_AUTO_APPROVE 自动批准" } }, "messageNav": { diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 5bc94c985c..dac919a63e 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -4097,7 +4097,8 @@ "allowAlways": "一律允許", "rejected": "已拒絕", "viaChatChannel": "來自聊天頻道", - "byPolicy": "由執行的權限原則決定" + "byPolicy": "由執行的權限原則決定", + "byAutoApprove": "由 CODEG_AUTO_APPROVE 自動核准" } }, "messageNav": {