fix: edge selection highlighting and group-chat connection rules

- Add CSS styling for selected edges (indigo highlight with glow)
- Enable orchestrator↔agent connections in group-chat mode
- Downgrade disconnected-agent validation from error to warning
- Rename addHandoffEdge to addEdge (generic for all modes)
- Add tests for group-chat connection rules

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-24 23:41:00 +01:00
co-authored by Copilot
parent f0d7d44589
commit f67fab0816
5 changed files with 62 additions and 15 deletions
@@ -22,7 +22,7 @@ import type { OrchestrationMode, PatternDefinition, PatternGraph } from '@shared
import { resolvePatternGraph } from '@shared/domain/pattern';
import type { ModelDefinition } from '@shared/domain/models';
import {
addHandoffEdge,
addEdge,
autoLayoutGraph,
fromCanvasPositions,
isConnectionAllowed,
@@ -143,7 +143,7 @@ function PatternGraphCanvasInner({
}
if (connection.source && connection.target) {
const updatedGraph = addHandoffEdge(graph, connection.source, connection.target);
const updatedGraph = addEdge(graph, connection.source, connection.target);
onGraphChange(updatedGraph);
}
},
+5 -3
View File
@@ -208,8 +208,10 @@ export function isConnectionAllowed(
return false;
case 'handoff':
return sourceNode.kind === 'agent' && targetNode.kind === 'agent';
case 'group-chat':
return false;
case 'group-chat': {
const kinds = new Set([sourceNode.kind, targetNode.kind]);
return kinds.has('orchestrator') && kinds.has('agent');
}
default:
return false;
}
@@ -226,7 +228,7 @@ function edgeId(source: string, target: string): string {
return `edge-${source}-to-${target}`;
}
export function addHandoffEdge(graph: PatternGraph, source: string, target: string): PatternGraph {
export function addEdge(graph: PatternGraph, source: string, target: string): PatternGraph {
const newEdge: PatternGraphEdge = {
id: edgeId(source, target),
source,
+6
View File
@@ -150,6 +150,12 @@ textarea {
stroke-width: 1.5;
}
.react-flow__edge.selected .react-flow__edge-path {
stroke: #818cf8;
stroke-width: 2.5;
filter: drop-shadow(0 0 4px rgba(129, 140, 248, 0.4));
}
.react-flow__edge.animated .react-flow__edge-path {
stroke-dasharray: 5;
animation: reactflow-dash 0.5s linear infinite;
+11 -6
View File
@@ -531,8 +531,13 @@ function getAgentNodes(graph: PatternGraph): PatternGraphNode[] {
return graph.nodes.filter((node) => node.kind === 'agent');
}
function pushGraphIssue(issues: PatternValidationIssue[], message: string, field = 'graph'): void {
issues.push({ level: 'error', field, message });
function pushGraphIssue(
issues: PatternValidationIssue[],
message: string,
field = 'graph',
level: 'error' | 'warning' = 'error',
): void {
issues.push({ level, field, message });
}
function buildAdjacency(graph: PatternGraph): {
@@ -779,7 +784,7 @@ function validateHandoffGraph(
for (const agentNode of agentNodes) {
if (!reachable.has(agentNode.id)) {
pushGraphIssue(issues, `Handoff entry agent must be able to reach "${agentNode.agentId}".`);
pushGraphIssue(issues, `Handoff entry agent must be able to reach "${agentNode.agentId}".`, 'graph', 'warning');
}
}
@@ -807,7 +812,7 @@ function validateGroupChatGraph(
const orchestratorSources = new Set((incoming.get(orchestratorNode.id) ?? []).map((edge) => edge.source));
if (graph.edges.length !== pattern.agents.length * 2 + 2) {
pushGraphIssue(issues, 'Group chat graphs must connect the orchestrator to every participant and then back to user output.');
pushGraphIssue(issues, 'Group chat graphs must connect the orchestrator to every participant and then back to user output.', 'graph', 'warning');
}
if ((outgoing.get(inputNode.id) ?? []).some((edge) => edge.target !== orchestratorNode.id)) {
@@ -820,11 +825,11 @@ function validateGroupChatGraph(
for (const agentNode of agentNodes) {
if (!orchestratorTargets.has(agentNode.id)) {
pushGraphIssue(issues, `Orchestrator must connect to agent "${agentNode.agentId}".`);
pushGraphIssue(issues, `Orchestrator must connect to agent "${agentNode.agentId}".`, 'graph', 'warning');
}
if (!orchestratorSources.has(agentNode.id)) {
pushGraphIssue(issues, `Agent "${agentNode.agentId}" must connect back to the orchestrator.`);
pushGraphIssue(issues, `Agent "${agentNode.agentId}" must connect back to the orchestrator.`, 'graph', 'warning');
}
}
}