feat: add approve/reject buttons for wait_for_approval

- CommentSection shows explicit approve/reject buttons when waiting
- Reject aborts the workflow, approve continues with optional feedback
- Backend parses approved:/rejected: prefixes from comment content
This commit is contained in:
Fam Zheng
2026-03-07 16:41:15 +00:00
parent 938ba83f37
commit 47546a9d15
2 changed files with 91 additions and 11 deletions

View File

@@ -1177,7 +1177,27 @@ async fn run_agent_loop(
}
};
tracing::info!("[workflow {}] Approval received: {}", workflow_id, approval_content);
tracing::info!("[workflow {}] Approval response: {}", workflow_id, approval_content);
// Check if user rejected
if approval_content.starts_with("rejected:") {
let reason = approval_content.strip_prefix("rejected:").unwrap_or("").trim();
tracing::info!("[workflow {}] User rejected: {}", workflow_id, reason);
if let Some(step) = state.steps.iter_mut().find(|s| s.order == cur) {
step.status = StepStatus::Failed;
step.user_feedbacks.push(format!("用户终止: {}", reason));
}
log_execution(pool, broadcast_tx, workflow_id, cur, "wait_for_approval", "rejected", reason, "failed").await;
// Return error to end the agent loop; caller sets workflow to "failed"
return Err(anyhow::anyhow!("用户终止了执行: {}", reason));
}
// Approved — extract feedback after "approved:" prefix if present
let feedback = if approval_content.starts_with("approved:") {
approval_content.strip_prefix("approved:").unwrap_or("").trim().to_string()
} else {
approval_content.clone()
};
// Resume: restore Running status
if let Some(step) = state.steps.iter_mut().find(|s| s.order == cur) {
@@ -1197,8 +1217,13 @@ async fn run_agent_loop(
steps: plan_infos_from_state(&state),
});
let tool_msg = if feedback.is_empty() {
"用户已确认,继续执行。".to_string()
} else {
format!("用户已确认。反馈: {}", feedback)
};
state.current_step_chat_history.push(
ChatMessage::tool_result(&tc.id, &format!("用户已确认。反馈: {}", approval_content))
ChatMessage::tool_result(&tc.id, &tool_msg)
);
}