From 79762c3f0d99c17f798a4ee4cace8fed4e90cc52 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:14:36 +0200 Subject: [PATCH] fix: audit replay with actual outcome, not premature SUCCESS Replay audit log now records the agent's reply status (SUCCESS/FAILURE), message, and error details. Timeout and internal errors are also logged as FAILURE with the cause. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../controller/AgentCommandController.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java index 388a7722..30ecf162 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java @@ -233,19 +233,30 @@ public class AgentCommandController { CompletableFuture future = registryService.addCommandWithReply( id, CommandType.REPLAY, payloadJson); - auditService.log("replay_exchange", AuditCategory.AGENT, id, - Map.of("routeId", request.routeId(), - "originalExchangeId", request.originalExchangeId() != null ? request.originalExchangeId() : ""), - AuditResult.SUCCESS, httpRequest); + Map auditDetails = new LinkedHashMap<>(); + auditDetails.put("routeId", request.routeId()); + if (request.originalExchangeId() != null) { + auditDetails.put("originalExchangeId", request.originalExchangeId()); + } try { CommandReply reply = future.orTimeout(30, TimeUnit.SECONDS).join(); + auditDetails.put("replyStatus", reply.status()); + auditDetails.put("replyMessage", reply.message() != null ? reply.message() : ""); + auditService.log("replay_exchange", AuditCategory.AGENT, id, auditDetails, + "SUCCESS".equals(reply.status()) ? AuditResult.SUCCESS : AuditResult.FAILURE, httpRequest); return ResponseEntity.ok(new ReplayResponse(reply.status(), reply.message(), reply.data())); } catch (CompletionException e) { if (e.getCause() instanceof TimeoutException) { + auditDetails.put("error", "timeout"); + auditService.log("replay_exchange", AuditCategory.AGENT, id, auditDetails, + AuditResult.FAILURE, httpRequest); return ResponseEntity.status(HttpStatus.GATEWAY_TIMEOUT) .body(new ReplayResponse("FAILURE", "Agent did not respond within 30 seconds", null)); } + auditDetails.put("error", e.getCause().getMessage()); + auditService.log("replay_exchange", AuditCategory.AGENT, id, auditDetails, + AuditResult.FAILURE, httpRequest); log.error("Error awaiting replay reply from agent {}", id, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ReplayResponse("FAILURE", "Internal error: " + e.getCause().getMessage(), null));