From 698a874709bf026834e976f6275701d814b33f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Sun, 14 Jun 2026 17:03:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20FR-S3=20approve=20decisio?= =?UTF-8?q?n=20=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C=20+=20FR-R1=20switchCo?= =?UTF-8?q?nversation=20=E5=88=87=E6=8D=A2=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FR-S3: approve_human_approval 加 decision 非空校验(防 "" 透传;HumanNode options 非空时还校验 ∈ options) - FR-R1: switchConversation 加 _latestSwitchId token,过期响应丢弃(防快速连点 A→B 后返回的 A 覆盖 B messages) 来源 fullstack-review §2/§4(核实确认);cargo/vue-tsc 0 err --- src-tauri/src/commands/workflow.rs | 4 ++++ src/composables/ai/useAiConversations.ts | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src-tauri/src/commands/workflow.rs b/src-tauri/src/commands/workflow.rs index 9af3cec..e648492 100644 --- a/src-tauri/src/commands/workflow.rs +++ b/src-tauri/src/commands/workflow.rs @@ -185,6 +185,10 @@ pub async fn approve_human_approval( decision: String, comment: Option, ) -> Result<(), String> { + // decision 非空校验(FR-S3: 防 "" 透传;HumanNode options 非空时还校验 ∈ options) + if decision.trim().is_empty() { + return Err("审批决策不能为空".to_string()); + } // 发送审批响应到事件总线 let response = HumanApprovalResponse { execution_id: execution_id.clone(), diff --git a/src/composables/ai/useAiConversations.ts b/src/composables/ai/useAiConversations.ts index 65013c1..4f03cbd 100644 --- a/src/composables/ai/useAiConversations.ts +++ b/src/composables/ai/useAiConversations.ts @@ -40,10 +40,16 @@ async function newConversation() { notifyConversationChanged() } +// 切换 token:快速连点 A→B 时,后返回的 A 响应按 token 丢弃,防 messages 错配(FR-R1) +let _latestSwitchId = 0 + /** 切换到指定会话:加载历史消息(含 tool_calls 回填 + tool_result 映射 + pending 审批恢复) */ export async function switchConversation(id: string) { + const mySwitchId = ++_latestSwitchId // 允许生成中切换:后台继续生成,事件按 conversation_id 路由不污染当前视图 const detail = await aiApi.switchConversation(id) + // 过期响应丢弃(用户已切到别的对话,防 A 后返回覆盖 B) + if (mySwitchId !== _latestSwitchId) return state.activeConversationId = id void appSettings.set('df-ai-active-conv', id)