From 49f7a74386b8bab4a5dc681671dfc56177c5f976 Mon Sep 17 00:00:00 2001 From: matevip Date: Thu, 23 Jul 2026 11:46:22 +0800 Subject: [PATCH] fix(approval): persist the real pendingId on guard audit rows --- .../graph/executor/ToolExecutionExecutor.java | 20 ++++++------------- .../tool/guard/ToolExecutionGuardHelper.java | 17 ++++++++++++---- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/agent/graph/executor/ToolExecutionExecutor.java b/mateclaw-server/src/main/java/vip/mate/agent/graph/executor/ToolExecutionExecutor.java index 67716eb6..9aae7949 100644 --- a/mateclaw-server/src/main/java/vip/mate/agent/graph/executor/ToolExecutionExecutor.java +++ b/mateclaw-server/src/main/java/vip/mate/agent/graph/executor/ToolExecutionExecutor.java @@ -1083,14 +1083,12 @@ public class ToolExecutionExecutor { } List remaining = allToolCalls.subList(currentIndex + 1, allToolCalls.size()); - String approvalResponse = ToolExecutionGuardHelper.handleToolApproval( + ToolExecutionGuardHelper.ApprovalRequest approval = ToolExecutionGuardHelper.handleToolApproval( toolCall, toolName, arguments, evaluation, conversationId, agentId, requesterId, approvalService, streamTracker, events, remaining); - // Extract pendingId from response (format: "[APPROVAL_PENDING] tool=xxx awaiting user decision") - String pendingId = extractPendingId(approvalResponse); - toolGuardService.recordApprovalAudit(guardCtx, evaluation, pendingId, autoOutcome); - return GuardDecision.needsApproval(approvalResponse, pendingId); + toolGuardService.recordApprovalAudit(guardCtx, evaluation, approval.pendingId(), autoOutcome); + return GuardDecision.needsApproval(approval.response(), approval.pendingId()); } } else if (toolGuard != null) { ToolGuardResult guardResult = toolGuard.check(toolName, arguments); @@ -1111,7 +1109,9 @@ public class ToolExecutionExecutor { toolCall, toolName, arguments, guardResult, conversationId, agentId, requesterId, approvalService, streamTracker, events, remaining); - return GuardDecision.needsApproval(approvalResponse, extractPendingId(approvalResponse)); + // Legacy path never persisted a pendingId to carry here; the value + // is unused downstream (only the boolean awaitingApproval is read). + return GuardDecision.needsApproval(approvalResponse, null); } } @@ -1207,14 +1207,6 @@ public class ToolExecutionExecutor { return "Tool execution failed: " + message; } - /** - * 从 approval response 中提取 pendingId(best-effort) - */ - private String extractPendingId(String approvalResponse) { - // handleToolApproval 内部已经创建了 pending,这里只做标记 - return approvalResponse; - } - /** * Issue #46 — when a tool callback miss happens, check whether the * unrecognized name actually matches an active skill. If it does, return diff --git a/mateclaw-server/src/main/java/vip/mate/tool/guard/ToolExecutionGuardHelper.java b/mateclaw-server/src/main/java/vip/mate/tool/guard/ToolExecutionGuardHelper.java index 1a83c15b..2f86f1d6 100644 --- a/mateclaw-server/src/main/java/vip/mate/tool/guard/ToolExecutionGuardHelper.java +++ b/mateclaw-server/src/main/java/vip/mate/tool/guard/ToolExecutionGuardHelper.java @@ -27,7 +27,15 @@ public final class ToolExecutionGuardHelper { * * @return 审批提示文本,作为 tool response 返回给 LLM */ - public static String handleToolApproval( + /** + * Outcome of {@link #handleToolApproval}: the tool-response text handed back + * to the LLM plus the persisted pending-approval id. {@code pendingId} is + * null when the approval service is unavailable and the call degraded to a + * block-style message. + */ + public record ApprovalRequest(String response, String pendingId) {} + + public static ApprovalRequest handleToolApproval( AssistantMessage.ToolCall toolCall, String toolName, String arguments, GuardEvaluation evaluation, String conversationId, String agentId, String requesterId, @@ -39,8 +47,8 @@ public final class ToolExecutionGuardHelper { log.warn("[GuardHelper] ApprovalService not available, falling back to BLOCK for tool={}", toolName); events.add(GraphEventPublisher.toolComplete(toolName, evaluation.summary() != null ? evaluation.summary() : "需要审批", false)); - return "[安全拦截] " + (evaluation.summary() != null ? evaluation.summary() : "需要审批") - + "。审批服务不可用,请联系管理员。"; + return new ApprovalRequest("[安全拦截] " + (evaluation.summary() != null ? evaluation.summary() : "需要审批") + + "。审批服务不可用,请联系管理员。", null); } String toolCallPayload = serializeToolCall(toolCall); @@ -78,7 +86,8 @@ public final class ToolExecutionGuardHelper { log.info("[GuardHelper] Approval pending created: pendingId={}, tool={}, findings={}", pendingId, toolName, evaluation.hasFindings() ? evaluation.findings().size() : 0); - return "[APPROVAL_PENDING] tool=" + toolName + " awaiting user decision"; + return new ApprovalRequest( + "[APPROVAL_PENDING] tool=" + toolName + " awaiting user decision", pendingId); } /**