优化: 设置页UX与AI自动执行三档
This commit is contained in:
@@ -427,17 +427,43 @@ pub(crate) async fn process_tool_calls(
|
|||||||
// 若 LLM 重试同命令(同 tool_name + 同 args,键序无关),命中已落定的旧 tool_result,
|
// 若 LLM 重试同命令(同 tool_name + 同 args,键序无关),命中已落定的旧 tool_result,
|
||||||
// 把缓存结果作为新 tool_call_id 的 tool_result 回传 LLM,跳过 insert pending + 跳过审批,
|
// 把缓存结果作为新 tool_call_id 的 tool_result 回传 LLM,跳过 insert pending + 跳过审批,
|
||||||
// 断「超时→重试→重新审批」循环。Med 不去重(去重易误伤),Low 无审批本就不进此分支。
|
// 断「超时→重试→重新审批」循环。Med 不去重(去重易误伤),Low 无审批本就不进此分支。
|
||||||
let mut low_risk: Vec<(ToolCallDraft, serde_json::Value)> = Vec::new();
|
// F-#97:low_risk 向量携带 risk_level(治 securityReview blocker :710 审计失真)。
|
||||||
|
// 原 Vec 仅 (draft,args),回填审计硬编码 RiskLevel::Low;#97 让 Med/High(all/medium 模式)
|
||||||
|
// 也进此向量,需透传真实等级,审计方可追溯「all 模式执行了多少高危命令」。
|
||||||
|
let mut low_risk: Vec<(ToolCallDraft, serde_json::Value, RiskLevel)> = Vec::new();
|
||||||
|
// F-#97 自动执行范围三档(2026-06-22):KV df-ai-auto-execute-mode 取值 low/medium/all,默认 low(=现状)。
|
||||||
|
// low:仅 Low 自动(等价旧行为);medium:Low+Medium 自动;all:全自动无审批(完全 AI 接管)。
|
||||||
|
// 锁内 await KV 快读,不竞争 session 锁(settings 独立 repo)。
|
||||||
|
let auto_exec_mode = app_handle
|
||||||
|
.state::<crate::state::AppState>()
|
||||||
|
.settings
|
||||||
|
.get("df-ai-auto-execute-mode")
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.unwrap_or_else(|| "low".to_string());
|
||||||
// trust_hits 收集:AE-04 会话信任命中(同会话已批准同类操作),真实执行但移到锁外 spawn
|
// trust_hits 收集:AE-04 会话信任命中(同会话已批准同类操作),真实执行但移到锁外 spawn
|
||||||
// (对齐 Low risk L673-720 不持锁模式)。命中时此处只 emit toast + 收集,不 .await execute。
|
// (对齐 Low risk L673-720 不持锁模式)。命中时此处只 emit toast + 收集,不 .await execute。
|
||||||
// 元组携带 risk_level: 回填审计需原等级(Med/High 才进此分支),避免闭包内再查 registry
|
// 元组携带 risk_level: 回填审计需原等级(Med/High 才进此分支),避免闭包内再查 registry
|
||||||
let mut trust_hits: Vec<(ToolCallDraft, serde_json::Value, String, RiskLevel)> = Vec::new();
|
let mut trust_hits: Vec<(ToolCallDraft, serde_json::Value, String, RiskLevel)> = Vec::new();
|
||||||
for (_, draft, args) in drafts {
|
for (_, draft, args) in drafts {
|
||||||
let risk_level = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
let risk_level = tools_arc.get(&draft.name).map(|t| t.risk_level).unwrap_or(RiskLevel::High);
|
||||||
match risk_level {
|
// F-#97:按 auto_exec_mode 判定是否自动执行。should_auto=true 走 low_risk(自动),false 走原审批块。
|
||||||
RiskLevel::Low => low_risk.push((draft, args)),
|
// low(默认):Low→auto, Med/High→审批(等价现状)
|
||||||
RiskLevel::Medium | RiskLevel::High => {
|
// medium:Low/Med→auto, High→审批
|
||||||
// AE-2025-04 会话级信任(Session Trust):首批 write_file / run_command,
|
// all:全 auto(完全接管,无审批)
|
||||||
|
let should_auto = match risk_level {
|
||||||
|
RiskLevel::Low => true,
|
||||||
|
RiskLevel::Medium => auto_exec_mode == "medium" || auto_exec_mode == "all",
|
||||||
|
RiskLevel::High => auto_exec_mode == "all",
|
||||||
|
};
|
||||||
|
if should_auto {
|
||||||
|
low_risk.push((draft, args, risk_level));
|
||||||
|
} else {
|
||||||
|
// 原 Medium|High 审批分支(trust_key 命中/dedup 缓存/retry guard/pending insert + emit + audit_tool_call)
|
||||||
|
// 整体保留,逻辑不变。trust_hits 元组携带 risk_level 语义不变:Med 进此处=Medium,
|
||||||
|
// High 进此处(仅 low/medium 模式)时=High。
|
||||||
|
// AE-2025-04 会话级信任(Session Trust):首批 write_file / run_command,
|
||||||
// 同会话已批准过同工具+同目录 → TrustKey 命中 → 自动放行(跳过 pending + 二次确认)。
|
// 同会话已批准过同工具+同目录 → TrustKey 命中 → 自动放行(跳过 pending + 二次确认)。
|
||||||
// 命中后走与 F-05 去重命中相似的「直接执行 + Completed + 审计 decided_by=auto_trust」路径,
|
// 命中后走与 F-05 去重命中相似的「直接执行 + Completed + 审计 decided_by=auto_trust」路径,
|
||||||
// 但与 F-05 不同:F-05 复用缓存 tool_result 跳过执行;trust 放行**真实执行工具**
|
// 但与 F-05 不同:F-05 复用缓存 tool_result 跳过执行;trust 放行**真实执行工具**
|
||||||
@@ -559,7 +585,6 @@ pub(crate) async fn process_tool_calls(
|
|||||||
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
let _ = app_handle.emit("ai-chat-event", ev.clone());
|
||||||
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
let _ = app_handle.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||||
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, "pending", risk_level, None, None, current_message_id).await;
|
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, "pending", risk_level, None, None, current_message_id).await;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,8 +652,8 @@ pub(crate) async fn process_tool_calls(
|
|||||||
// join_all 保序——结果顺序 = low_risk 输入顺序 = tc_list 原始 index 顺序,不额外 sort
|
// join_all 保序——结果顺序 = low_risk 输入顺序 = tc_list 原始 index 顺序,不额外 sort
|
||||||
if !low_risk.is_empty() {
|
if !low_risk.is_empty() {
|
||||||
// 携带 args + 原始 JSON result(create_idea source 补全需解析 result.id + args.source)。
|
// 携带 args + 原始 JSON result(create_idea source 补全需解析 result.id + args.source)。
|
||||||
let results: Vec<(ToolCallDraft, serde_json::Value, Result<String, String>)> =
|
let results: Vec<(ToolCallDraft, serde_json::Value, RiskLevel, Result<String, String>)> =
|
||||||
futures::future::join_all(low_risk.into_iter().map(|(draft, args)| {
|
futures::future::join_all(low_risk.into_iter().map(|(draft, args, risk_level)| {
|
||||||
let tools = tools_arc.clone();
|
let tools = tools_arc.clone();
|
||||||
let app_clone = app_handle.clone();
|
let app_clone = app_handle.clone();
|
||||||
let conv_clone = conv_id.to_string();
|
let conv_clone = conv_id.to_string();
|
||||||
@@ -647,7 +672,7 @@ pub(crate) async fn process_tool_calls(
|
|||||||
// AR-11(方案A):数据变更工具执行成功后 emit df-data-changed,
|
// AR-11(方案A):数据变更工具执行成功后 emit df-data-changed,
|
||||||
// 前端 store listen 刷新列表(仅命中映射的工具 emit,见 emit_data_changed)。
|
// 前端 store listen 刷新列表(仅命中映射的工具 emit,见 emit_data_changed)。
|
||||||
emit_data_changed(&app_clone, &draft.name);
|
emit_data_changed(&app_clone, &draft.name);
|
||||||
(draft, val.clone(), Ok(val.to_string()))
|
(draft, val.clone(), risk_level, Ok(val.to_string()))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// AR-6(定向B):Low 工具失败不 emit AiError。
|
// AR-6(定向B):Low 工具失败不 emit AiError。
|
||||||
@@ -664,14 +689,14 @@ pub(crate) async fn process_tool_calls(
|
|||||||
};
|
};
|
||||||
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
let _ = app_clone.emit("ai-chat-event", ev.clone());
|
||||||
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
let _ = app_clone.state::<crate::state::AppState>().ai_event_bus.publish_event(ev);
|
||||||
(draft, serde_json::Value::Null, Err(err_msg))
|
(draft, serde_json::Value::Null, risk_level, Err(err_msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})).await;
|
})).await;
|
||||||
|
|
||||||
// 串行回填 tool_result + 审计(持 session 锁)
|
// 串行回填 tool_result + 审计(持 session 锁)
|
||||||
for (draft, raw_result, outcome) in results {
|
for (draft, raw_result, risk_level, outcome) in results {
|
||||||
let (status, content) = match outcome {
|
let (status, content) = match outcome {
|
||||||
Ok(c) => ("completed", c),
|
Ok(c) => ("completed", c),
|
||||||
Err(c) => ("failed", c),
|
Err(c) => ("failed", c),
|
||||||
@@ -685,7 +710,15 @@ pub(crate) async fn process_tool_calls(
|
|||||||
}
|
}
|
||||||
// F-260616-09 B 批2:写 per_conv.messages。
|
// F-260616-09 B 批2:写 per_conv.messages。
|
||||||
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, content.clone()));
|
session.conv(conv_id).messages.push(ChatMessage::tool_result(&draft.id, content.clone()));
|
||||||
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, status, RiskLevel::Low, Some(content), Some("auto"), current_message_id).await;
|
// F-#97 审计留痕:low_risk 向量中 risk_level != Low 即 mode 放行(Med 仅 medium/all、High 仅 all 才进)。
|
||||||
|
// decided_by 区分接管来源,审计表可追溯 all 模式执行了多少高危命令(治 securityReview blocker:
|
||||||
|
// 原硬编码 RiskLevel::Low 致 Med/High 接管操作全标 Low/auto 失真,等于没记)。
|
||||||
|
let decided_by: &str = match risk_level {
|
||||||
|
RiskLevel::Low => "auto",
|
||||||
|
RiskLevel::Medium => "auto_takeover_medium",
|
||||||
|
RiskLevel::High => "auto_takeover_all",
|
||||||
|
};
|
||||||
|
audit_tool_call(&audit_repo, conv_id, &draft.id, &draft.name, &draft.args, status, risk_level, Some(content), Some(decided_by), current_message_id).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- ═══ 高级设置(AI 自动执行 / 日志级别)═══ -->
|
<!-- ═══ 高级设置(AI 自动执行范围 / 日志级别)═══ -->
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<h2>{{ $t('settings.sectionAdvanced') }}</h2>
|
<h2>{{ $t('settings.sectionAdvanced') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="general-settings">
|
<div class="general-settings">
|
||||||
|
<!-- F-#97:三档 low/medium/all。low=仅低风险自动(等价旧 autoExecute=false);
|
||||||
|
medium=含中风险自动;all=全自动无审批(完全 AI 接管,选时二次确认) -->
|
||||||
<SettingRow ref="rowAutoExecute" :label="$t('settings.labelAutoExecute')" :desc="$t('settings.descAutoExecute')">
|
<SettingRow ref="rowAutoExecute" :label="$t('settings.labelAutoExecute')" :desc="$t('settings.descAutoExecute')">
|
||||||
<label class="toggle">
|
<select v-model="settings.autoExecuteMode" class="setting-select" @change="onAutoModeChange">
|
||||||
<input type="checkbox" v-model="settings.autoExecute" @change="markSaved('autoExecute')" />
|
<option value="low">{{ $t('settings.autoModeLow') }}</option>
|
||||||
<span class="toggle-slider"></span>
|
<option value="medium">{{ $t('settings.autoModeMedium') }}</option>
|
||||||
</label>
|
<option value="all">{{ $t('settings.autoModeAll') }}</option>
|
||||||
|
</select>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingRow ref="rowLogLevel" :label="$t('settings.labelLogLevel')" :desc="$t('settings.descLogLevel')">
|
<SettingRow ref="rowLogLevel" :label="$t('settings.labelLogLevel')" :desc="$t('settings.descLogLevel')">
|
||||||
<select v-model="settings.logLevel" class="setting-select" @change="markSaved('logLevel')">
|
<select v-model="settings.logLevel" class="setting-select" @change="markSaved">
|
||||||
<option value="error">Error</option>
|
<option value="error">Error</option>
|
||||||
<option value="warn">Warning</option>
|
<option value="warn">Warning</option>
|
||||||
<option value="info">Info</option>
|
<option value="info">Info</option>
|
||||||
@@ -20,35 +23,130 @@
|
|||||||
</select>
|
</select>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- all 二次确认弹层(独立,不与 Settings 壳层共用:壳层按钮写死「删除」不适合「确认接管」) -->
|
||||||
|
<Transition name="confirm">
|
||||||
|
<div v-if="confirmState.visible" class="confirm-mask" @click.self="answerConfirm(false)">
|
||||||
|
<div class="confirm-box">
|
||||||
|
<div class="confirm-msg">{{ confirmState.msg }}</div>
|
||||||
|
<div class="confirm-actions">
|
||||||
|
<button class="btn btn-ghost btn-sm" @click="answerConfirm(false)">{{ $t('common.cancel') }}</button>
|
||||||
|
<button class="btn btn-danger btn-sm" @click="answerConfirm(true)">{{ confirmState.dangerLabel || $t('common.confirm') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, useTemplateRef } from 'vue'
|
import { reactive, watch, useTemplateRef } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useConfirm } from '@/composables/useConfirm'
|
||||||
|
import { useAppSettingsStore } from '@/stores/appSettings'
|
||||||
import SettingRow from './SettingRow.vue'
|
import SettingRow from './SettingRow.vue'
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 高级域 — AI 自动执行开关 / 日志级别
|
// 高级域 — AI 自动执行范围(三档) / 日志级别
|
||||||
// 拆自 GeneralPanel,忠实迁移原逻辑:原 GeneralPanel 这两项为 session 内 reactive
|
// F-#97(2026-06-22):原 autoExecute boolean checkbox → autoExecuteMode select 三档
|
||||||
// (无 appSettings 持久化/watch),此处保持一致(无 watch/IPC),避免阶段2 引入行为变更。
|
// (low/medium/all),KV 持久化 df-ai-auto-execute-mode,默认 low。
|
||||||
// 阶段5:SettingRow 封装行布局,控件变更后调 markSaved 显"已保存"
|
// low 必须等价旧行为(autoExecute=false 仅 Low 风险自动):后端 audit/mod.rs 按 mode
|
||||||
// 后续若需持久化,单独建改进项。
|
// 判定 Med/High 是否走自动分支,low 时 Med/High 仍进审批块,行为与旧完全一致。
|
||||||
|
// all(完全接管)选时二次确认:防误选致无审批全自动执行高危操作。
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
const { t } = useI18n()
|
||||||
|
const appSettings = useAppSettingsStore()
|
||||||
|
// useConfirm 真实 API:返回 { confirmState, confirmDialog(msg, dangerLabel), answerConfirm(ok) }
|
||||||
|
// confirmDialog 返回 Promise<boolean>(true=确认,false=取消),无 title/content/confirmText/cancelText
|
||||||
|
// 字段。此处用 msg 拼标题+内容,dangerLabel 作确认按钮文案,自渲染独立弹层(上方模板)。
|
||||||
|
const { confirmState, confirmDialog, answerConfirm } = useConfirm()
|
||||||
|
|
||||||
|
// 三档合法值 + 脏值白名单兜底:KV 被外部改库为非法值(空串/非三档)时回退 low,
|
||||||
|
// 防 <select v-model> 无匹配 option 致控件空白(后端 audit 脏值 fail-safe 降级审批,前端需对齐可视)。
|
||||||
|
const VALID_MODES = ['low', 'medium', 'all'] as const
|
||||||
|
type AutoMode = (typeof VALID_MODES)[number]
|
||||||
|
function clampMode(raw: string): AutoMode {
|
||||||
|
return (VALID_MODES as readonly string[]).includes(raw) ? (raw as AutoMode) : 'low'
|
||||||
|
}
|
||||||
|
|
||||||
const settings = reactive({
|
const settings = reactive({
|
||||||
autoExecute: false,
|
// 三档 low/medium/all,默认 low(=旧 autoExecute=false 现状),KV 持久化 df-ai-auto-execute-mode。
|
||||||
|
// clampMode 兜底脏值。
|
||||||
|
autoExecuteMode: clampMode(appSettings.get<string>('df-ai-auto-execute-mode', 'low')),
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
})
|
})
|
||||||
|
|
||||||
const rowAutoExecute = useTemplateRef<InstanceType<typeof SettingRow>>('rowAutoExecute')
|
const rowAutoExecute = useTemplateRef<InstanceType<typeof SettingRow>>('rowAutoExecute')
|
||||||
const rowLogLevel = useTemplateRef<InstanceType<typeof SettingRow>>('rowLogLevel')
|
const rowLogLevel = useTemplateRef<InstanceType<typeof SettingRow>>('rowLogLevel')
|
||||||
function markSaved(key: 'autoExecute' | 'logLevel') {
|
|
||||||
const map = { autoExecute: rowAutoExecute.value, logLevel: rowLogLevel.value }
|
// 上一次确认生效的档位:all 二次确认取消时恢复此值(非写死 low),避免 medium 用户误选 all
|
||||||
map[key]?.markSaved()
|
// 取消后被强制降级 low。初始化 = 当前有效值;每次确认成功(含非 all 切换)更新。
|
||||||
|
let lastConfirmedMode: AutoMode = settings.autoExecuteMode
|
||||||
|
|
||||||
|
// all(完全接管)二次确认:防误选致无审批全自动执行高危操作(删文件/执行任意命令)
|
||||||
|
// 选 all → 弹确认 → 确认落 KV + markSaved + 更新 lastConfirmedMode;取消恢复 lastConfirmedMode(不落 KV)。
|
||||||
|
// 非 all 切换:直接落 KV + 更新 lastConfirmedMode(watch 兜底同值无副作用)。
|
||||||
|
async function onAutoModeChange() {
|
||||||
|
if (settings.autoExecuteMode === 'all') {
|
||||||
|
const msg = t('settings.autoModeAllConfirmTitle') + '\n\n' + t('settings.autoModeAllConfirmContent')
|
||||||
|
const ok = await confirmDialog(msg, t('common.confirm'))
|
||||||
|
if (!ok) {
|
||||||
|
settings.autoExecuteMode = lastConfirmedMode // 取消恢复原档位(非写死 low)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appSettings.set('df-ai-auto-execute-mode', settings.autoExecuteMode)
|
||||||
|
lastConfirmedMode = settings.autoExecuteMode
|
||||||
|
rowAutoExecute.value?.markSaved()
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch 兜底:非 all 切换直接落 KV + 更新 lastConfirmedMode(all 走 onAutoModeChange 确认后落,
|
||||||
|
// 避免 watch 抢先持久化未确认的 all 值)。onAutoModeChange 已落 KV 时此 watch 重复 set 同值无副作用。
|
||||||
|
watch(
|
||||||
|
() => settings.autoExecuteMode,
|
||||||
|
(v) => {
|
||||||
|
if (v !== 'all') {
|
||||||
|
appSettings.set('df-ai-auto-execute-mode', v)
|
||||||
|
lastConfirmedMode = v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// logLevel 行即时反馈(无参:logLevel 仅 session 内 reactive 无持久化,markSaved 只显「已保存」反馈)
|
||||||
|
function markSaved() {
|
||||||
|
rowLogLevel.value?.markSaved()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 阶段5 UX 重构:复用 settings.css 全局 .panel/.panel-header/.general-settings/
|
/* 阶段5 UX 重构:复用 settings.css 全局 .panel/.panel-header/.general-settings/
|
||||||
.setting-select/.toggle/.toggle-slider + SettingRow(封装 .setting-row/.setting-info/
|
.setting-select + SettingRow(封装 .setting-row/.setting-info/.setting-label/
|
||||||
.setting-label/.setting-desc/.setting-control)。本 Section 无特有 scoped 样式。 */
|
.setting-desc/.setting-control)。本 Section 无特有 scoped 样式。
|
||||||
|
|
||||||
|
F-#97 all 二次确认弹层:复用 Settings.vue 壳层 confirm 弹层同款样式(mask/box/msg/
|
||||||
|
actions/transition),此处独立持有 useConfirm 状态(不与壳层共用,壳层按钮写死「删除」)。 */
|
||||||
|
.confirm-mask {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1100;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.confirm-box {
|
||||||
|
background: var(--df-bg-card);
|
||||||
|
border: 0.5px solid var(--df-border);
|
||||||
|
border-radius: var(--df-radius-lg);
|
||||||
|
padding: 20px;
|
||||||
|
min-width: 280px;
|
||||||
|
max-width: 420px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
.confirm-msg { font-size: 13px; color: var(--df-text); line-height: 1.5; margin-bottom: 16px; white-space: pre-line; }
|
||||||
|
.confirm-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
||||||
|
.btn-danger { background: var(--df-danger); color: #fff; }
|
||||||
|
.btn-danger:hover { filter: brightness(1.1); }
|
||||||
|
.confirm-enter-active, .confirm-leave-active { transition: opacity 0.15s; }
|
||||||
|
.confirm-enter-from, .confirm-leave-to { opacity: 0; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
// knowledge 知识库(auto_extract/trigger_mode/embedding_*)
|
// knowledge 知识库(auto_extract/trigger_mode/embedding_*)
|
||||||
// performance 性能(global/per-conv concurrency/max-iterations/max-retries)
|
// performance 性能(global/per-conv concurrency/max-iterations/max-retries)
|
||||||
// security 安全(allowed_dirs)
|
// security 安全(allowed_dirs)
|
||||||
// advanced 高级(autoExecute/logLevel + 连接)
|
// advanced 高级(autoExecuteMode/logLevel + 连接)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export interface SettingIndexEntry {
|
|||||||
* performance 性能(global-concurrency/per-conv-concurrency/
|
* performance 性能(global-concurrency/per-conv-concurrency/
|
||||||
* max-iterations/max-retries)
|
* max-iterations/max-retries)
|
||||||
* security 安全(allowed_dirs)
|
* security 安全(allowed_dirs)
|
||||||
* advanced 高级(autoExecute/logLevel + 连接)
|
* advanced 高级(autoExecuteMode/logLevel + 连接)
|
||||||
*/
|
*/
|
||||||
export const SETTINGS_INDEX: SettingIndexEntry[] = [
|
export const SETTINGS_INDEX: SettingIndexEntry[] = [
|
||||||
// ===== 外观 =====
|
// ===== 外观 =====
|
||||||
@@ -82,7 +82,8 @@ export const SETTINGS_INDEX: SettingIndexEntry[] = [
|
|||||||
{ category: 'security', key: 'allowedDirs', labelKey: 'settings.panelAllowedDirs', descKey: 'settings.descAllowedDirs' },
|
{ category: 'security', key: 'allowedDirs', labelKey: 'settings.panelAllowedDirs', descKey: 'settings.descAllowedDirs' },
|
||||||
|
|
||||||
// ===== 高级 =====
|
// ===== 高级 =====
|
||||||
{ category: 'advanced', key: 'autoExecute', labelKey: 'settings.labelAutoExecute', descKey: 'settings.descAutoExecute' },
|
// F-#97:autoExecute(boolean) → autoExecuteMode(low/medium/all 三档),key 同步更名
|
||||||
|
{ category: 'advanced', key: 'autoExecuteMode', labelKey: 'settings.labelAutoExecute', descKey: 'settings.descAutoExecute' },
|
||||||
{ category: 'advanced', key: 'logLevel', labelKey: 'settings.labelLogLevel', descKey: 'settings.descLogLevel' },
|
{ category: 'advanced', key: 'logLevel', labelKey: 'settings.labelLogLevel', descKey: 'settings.descLogLevel' },
|
||||||
{ category: 'advanced', key: 'connection', labelKey: 'settings.panelConnection', descKey: 'settings.emptyConnection' },
|
{ category: 'advanced', key: 'connection', labelKey: 'settings.panelConnection', descKey: 'settings.emptyConnection' },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -111,8 +111,13 @@ export default {
|
|||||||
aiLanguageZh: '🇨🇳 简体中文',
|
aiLanguageZh: '🇨🇳 简体中文',
|
||||||
aiLanguageEn: '🇺🇸 English',
|
aiLanguageEn: '🇺🇸 English',
|
||||||
|
|
||||||
labelAutoExecute: 'Auto-execute',
|
labelAutoExecute: 'AI auto-execute scope',
|
||||||
descAutoExecute: 'Let AI auto-execute low-risk actions',
|
descAutoExecute: 'Control risk level AI auto-executes (low/medium/all-takeover)',
|
||||||
|
autoModeLow: 'Low risk (default)',
|
||||||
|
autoModeMedium: 'Include medium',
|
||||||
|
autoModeAll: 'Full takeover (no approval)',
|
||||||
|
autoModeAllConfirmTitle: 'Confirm full AI takeover?',
|
||||||
|
autoModeAllConfirmContent: 'All actions (including high-risk) will be auto-executed without approval. AI may directly perform dangerous operations (delete files / run arbitrary commands, etc.). Confirm only if you trust the current conversation AI.',
|
||||||
|
|
||||||
labelLogLevel: 'Log level',
|
labelLogLevel: 'Log level',
|
||||||
descLogLevel: 'Verbosity of workflow logs',
|
descLogLevel: 'Verbosity of workflow logs',
|
||||||
|
|||||||
@@ -111,8 +111,13 @@ export default {
|
|||||||
aiLanguageZh: '🇨🇳 简体中文',
|
aiLanguageZh: '🇨🇳 简体中文',
|
||||||
aiLanguageEn: '🇺🇸 English',
|
aiLanguageEn: '🇺🇸 English',
|
||||||
|
|
||||||
labelAutoExecute: 'AI 自动执行',
|
labelAutoExecute: 'AI 自动执行范围',
|
||||||
descAutoExecute: 'AI 可自动执行低风险操作',
|
descAutoExecute: '控制 AI 自动执行操作的风险等级(低/中/全部接管)',
|
||||||
|
autoModeLow: '低风险(默认)',
|
||||||
|
autoModeMedium: '含中风险',
|
||||||
|
autoModeAll: '全部接管(无审批)',
|
||||||
|
autoModeAllConfirmTitle: '确认完全 AI 接管?',
|
||||||
|
autoModeAllConfirmContent: '所有操作(含高危)将自动执行,无审批。AI 可能直接执行危险操作(删文件/执行任意命令等),请确认你信任当前对话的 AI。',
|
||||||
|
|
||||||
labelLogLevel: '日志级别',
|
labelLogLevel: '日志级别',
|
||||||
descLogLevel: '工作流日志详细程度',
|
descLogLevel: '工作流日志详细程度',
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
@toast="showToast"
|
@toast="showToast"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 高级:autoExecute/logLevel + 连接(暂留待定) -->
|
<!-- 高级:autoExecuteMode/logLevel + 连接(暂留待定) -->
|
||||||
<template v-else-if="activeCategory === 'advanced'">
|
<template v-else-if="activeCategory === 'advanced'">
|
||||||
<AdvancedSection />
|
<AdvancedSection />
|
||||||
<ConnectionPanel
|
<ConnectionPanel
|
||||||
|
|||||||
Reference in New Issue
Block a user