feat: enable edge deletion in group-chat mode

Extend edge deletion to group-chat orchestrator↔agent edges in addition
to handoff agent↔agent edges. Structural edges (user-input/output,
distributor/collector) remain non-deletable across all modes.

Sequential, concurrent, and single modes keep fully structural topologies
with no user-deletable edges.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-24 23:34:38 +01:00
co-authored by Copilot
parent 24bbf1ded3
commit f0d7d44589
3 changed files with 43 additions and 22 deletions
@@ -114,18 +114,11 @@ function PatternGraphCanvasInner({
const handleEdgesChange: OnEdgesChange = useCallback(
(changes) => {
// Block all edge deletions in modes that don't support it
if (!isEdgeDeletionAllowed(pattern.mode)) {
const filtered = changes.filter((c) => c.type !== 'remove');
if (filtered.length > 0) {
onEdgesChange(filtered);
}
return;
}
// For handoff mode, apply removals to the authoritative graph
// Route edge removals through the authoritative graph.
// Non-deletable edges are already protected by the per-edge `deletable`
// flag, so React Flow will not emit removal changes for them.
const removals = changes.filter((c) => c.type === 'remove');
if (removals.length > 0) {
if (removals.length > 0 && isEdgeDeletionAllowed(pattern.mode)) {
let updatedGraph = graph;
for (const removal of removals) {
if (removal.type === 'remove') {
+23 -9
View File
@@ -101,18 +101,32 @@ export function toCanvasNodes(
});
}
/** Determines whether user-created edges can be deleted in this mode. */
/** Determines whether an edge can be deleted by the user.
*
* Deletable edges:
* - handoff: agent ↔ agent edges (handoff routes)
* - group-chat: orchestrator ↔ agent edges (participation links)
*
* Structural edges (user-input/output, distributor/collector) are never deletable.
* Sequential, concurrent, and single modes have fully structural topologies.
*/
function isEdgeDeletable(edge: PatternGraphEdge, mode: OrchestrationMode, graph: PatternGraph): boolean {
if (mode !== 'handoff') {
const sourceNode = graph.nodes.find((n) => n.id === edge.source);
const targetNode = graph.nodes.find((n) => n.id === edge.target);
if (!sourceNode || !targetNode) {
return false;
}
const sourceNode = graph.nodes.find((n) => n.id === edge.source);
const targetNode = graph.nodes.find((n) => n.id === edge.target);
if (mode === 'handoff') {
return sourceNode.kind === 'agent' && targetNode.kind === 'agent';
}
// Only agent↔agent edges in handoff mode are user-deletable;
// system edges (user-input → triage, agent → user-output) are structural.
return sourceNode?.kind === 'agent' && targetNode?.kind === 'agent';
if (mode === 'group-chat') {
const kinds = new Set([sourceNode.kind, targetNode.kind]);
return kinds.has('orchestrator') && kinds.has('agent');
}
return false;
}
const EDGE_COLORS = {
@@ -201,9 +215,9 @@ export function isConnectionAllowed(
}
}
/** Whether edges can be deleted by the user in this mode. */
/** Whether any edges can be deleted by the user in this mode. */
export function isEdgeDeletionAllowed(mode: OrchestrationMode): boolean {
return mode === 'handoff';
return mode === 'handoff' || mode === 'group-chat';
}
/* ── Graph mutation helpers ─────────────────────────────────── */