From 9c912fe694b60317f6d22f0b452266c6652557a9 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 14 Apr 2026 23:57:20 +0200 Subject: [PATCH] feat: distinguish agent re-registration from first registration Detect when an agent instance already exists in the registry and record a RE_REGISTERED event with route count and capabilities instead of a generic REGISTERED event. UI shows a refresh icon for re-registrations. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../AgentRegistrationController.java | 22 ++++++++++++++----- ui/src/utils/agent-utils.ts | 4 +++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java index 5d5dda8e..e4017fdd 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java @@ -119,16 +119,28 @@ public class AgentRegistrationController { List routeIds = request.routeIds() != null ? request.routeIds() : List.of(); var capabilities = request.capabilities() != null ? request.capabilities() : Collections.emptyMap(); + boolean reRegistration = registryService.findById(request.instanceId()) != null; + AgentInfo agent = registryService.register( request.instanceId(), request.instanceId(), application, environmentId, request.version(), routeIds, capabilities); - log.info("Agent registered: {} (application={})", request.instanceId(), application); - agentEventService.recordEvent(request.instanceId(), application, "REGISTERED", - "Agent registered: " + request.instanceId()); + if (reRegistration) { + log.info("Agent re-registered: {} (application={}, routes={}, capabilities={})", + request.instanceId(), application, routeIds.size(), capabilities.keySet()); + agentEventService.recordEvent(request.instanceId(), application, "RE_REGISTERED", + "Agent re-registered with " + routeIds.size() + " routes"); + } else { + log.info("Agent registered: {} (application={}, routes={})", + request.instanceId(), application, routeIds.size()); + agentEventService.recordEvent(request.instanceId(), application, "REGISTERED", + "Agent registered: " + request.instanceId()); + } - auditService.log(request.instanceId(), "agent_register", AuditCategory.AGENT, request.instanceId(), - Map.of("application", application), + auditService.log(request.instanceId(), reRegistration ? "agent_reregister" : "agent_register", + AuditCategory.AGENT, request.instanceId(), + Map.of("application", application, "routeCount", routeIds.size(), + "reRegistration", reRegistration), AuditResult.SUCCESS, httpRequest); // Issue JWT tokens with AGENT role + environment diff --git a/ui/src/utils/agent-utils.ts b/ui/src/utils/agent-utils.ts index 68b6e9bc..617a8293 100644 --- a/ui/src/utils/agent-utils.ts +++ b/ui/src/utils/agent-utils.ts @@ -1,6 +1,6 @@ import type { FeedEvent } from '@cameleer/design-system'; import type { LogEntry } from '@cameleer/design-system'; -import { UserPlus, UserMinus, Play, Square, Clock, Skull, HeartPulse, Route, Send, Activity } from 'lucide-react'; +import { UserPlus, UserMinus, RefreshCw, Play, Square, Clock, Skull, HeartPulse, Route, Send, Activity } from 'lucide-react'; import { createElement } from 'react'; export function formatUptime(seconds?: number): string { @@ -31,6 +31,7 @@ export function eventSeverity(type: string): FeedEvent['severity'] { case 'WENT_STALE': return 'warning'; case 'RECOVERED': case 'REGISTERED': + case 'RE_REGISTERED': case 'AGENT_STARTED': return 'success'; default: return 'running'; } @@ -39,6 +40,7 @@ export function eventSeverity(type: string): FeedEvent['severity'] { export function eventIcon(type: string) { switch (type) { case 'REGISTERED': return createElement(UserPlus, { size: 14 }); + case 'RE_REGISTERED': return createElement(RefreshCw, { size: 14 }); case 'DEREGISTERED': return createElement(UserMinus, { size: 14 }); case 'AGENT_STARTED': return createElement(Play, { size: 14 }); case 'AGENT_STOPPED': return createElement(Square, { size: 14 });