mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-08 20:58:45 +02:00
fix: wire PatternEditor to mode-aware graph mutations
Replace the old disconnected-node addAgentNodeToGraph with the mode-aware addAgentToGraph so new agents are automatically wired into the graph topology. Replace the graph-nuking removeAgent path with removeAgentFromGraph for surgical node removal. Split emitChange into graph-preserving and mode-change paths so editing agent properties no longer discards custom positions and edges. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -18,6 +18,8 @@ import {
|
|||||||
import type { ApprovalCheckpointKind, ApprovalPolicy } from '@shared/domain/approval';
|
import type { ApprovalCheckpointKind, ApprovalPolicy } from '@shared/domain/approval';
|
||||||
import type { ModelDefinition } from '@shared/domain/models';
|
import type { ModelDefinition } from '@shared/domain/models';
|
||||||
import {
|
import {
|
||||||
|
addAgentToGraph,
|
||||||
|
removeAgentFromGraph,
|
||||||
resolvePatternGraph,
|
resolvePatternGraph,
|
||||||
syncPatternGraph,
|
syncPatternGraph,
|
||||||
validatePatternDefinition,
|
validatePatternDefinition,
|
||||||
@@ -35,7 +37,6 @@ import {
|
|||||||
} from '@shared/domain/tooling';
|
} from '@shared/domain/tooling';
|
||||||
|
|
||||||
import { ToggleSwitch } from '@renderer/components/ui';
|
import { ToggleSwitch } from '@renderer/components/ui';
|
||||||
import { addAgentNodeToGraph } from '@renderer/lib/patternGraph';
|
|
||||||
import { PatternGraphCanvas } from './pattern-graph/PatternGraphCanvas';
|
import { PatternGraphCanvas } from './pattern-graph/PatternGraphCanvas';
|
||||||
import { PatternGraphInspector } from './pattern-graph/PatternGraphInspector';
|
import { PatternGraphInspector } from './pattern-graph/PatternGraphInspector';
|
||||||
|
|
||||||
@@ -143,6 +144,10 @@ export function PatternEditor({
|
|||||||
const graph = resolvePatternGraph(pattern);
|
const graph = resolvePatternGraph(pattern);
|
||||||
|
|
||||||
function emitChange(nextPattern: PatternDefinition) {
|
function emitChange(nextPattern: PatternDefinition) {
|
||||||
|
onChange({ ...nextPattern, graph: resolvePatternGraph(nextPattern) });
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitModeChange(nextPattern: PatternDefinition) {
|
||||||
onChange(syncPatternGraph(nextPattern));
|
onChange(syncPatternGraph(nextPattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +164,7 @@ export function PatternEditor({
|
|||||||
model: 'gpt-5.4',
|
model: 'gpt-5.4',
|
||||||
reasoningEffort: 'high',
|
reasoningEffort: 'high',
|
||||||
};
|
};
|
||||||
const updatedGraph = addAgentNodeToGraph(graph, newAgent);
|
const updatedGraph = addAgentToGraph(graph, pattern.mode, newAgent);
|
||||||
onChange({ ...pattern, agents: [...pattern.agents, newAgent], graph: updatedGraph });
|
onChange({ ...pattern, agents: [...pattern.agents, newAgent], graph: updatedGraph });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,9 +180,11 @@ export function PatternEditor({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emitChange({
|
const updatedGraph = removeAgentFromGraph(graph, pattern.mode, agentId);
|
||||||
|
onChange({
|
||||||
...pattern,
|
...pattern,
|
||||||
agents: pattern.agents.filter((a) => a.id !== agentId),
|
agents: pattern.agents.filter((a) => a.id !== agentId),
|
||||||
|
graph: updatedGraph,
|
||||||
});
|
});
|
||||||
setSelectedNodeId(null);
|
setSelectedNodeId(null);
|
||||||
}
|
}
|
||||||
@@ -374,7 +381,7 @@ export function PatternEditor({
|
|||||||
}`}
|
}`}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
key={mode}
|
key={mode}
|
||||||
onClick={() => emitChange({ ...pattern, mode })}
|
onClick={() => emitModeChange({ ...pattern, mode })}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
|
|||||||
@@ -246,32 +246,6 @@ export function removeEdge(graph: PatternGraph, removeEdgeId: string): PatternGr
|
|||||||
return { ...graph, edges: graph.edges.filter((e) => e.id !== removeEdgeId) };
|
return { ...graph, edges: graph.edges.filter((e) => e.id !== removeEdgeId) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Add disconnected agent node ───────────────────────────── */
|
|
||||||
|
|
||||||
export function addAgentNodeToGraph(
|
|
||||||
graph: PatternGraph,
|
|
||||||
agent: PatternAgentDefinition,
|
|
||||||
): PatternGraph {
|
|
||||||
const existingAgentNodes = graph.nodes.filter((n) => n.kind === 'agent');
|
|
||||||
const nextOrder = existingAgentNodes.length;
|
|
||||||
|
|
||||||
// Place new node below existing agent nodes
|
|
||||||
const maxY = existingAgentNodes.reduce((max, n) => Math.max(max, n.position.y), 0);
|
|
||||||
const avgX = existingAgentNodes.length > 0
|
|
||||||
? Math.round(existingAgentNodes.reduce((sum, n) => sum + n.position.x, 0) / existingAgentNodes.length)
|
|
||||||
: 400;
|
|
||||||
|
|
||||||
const newNode: PatternGraphNode = {
|
|
||||||
id: `agent-node-${agent.id}`,
|
|
||||||
kind: 'agent',
|
|
||||||
agentId: agent.id,
|
|
||||||
order: nextOrder,
|
|
||||||
position: { x: avgX, y: maxY + 120 },
|
|
||||||
};
|
|
||||||
|
|
||||||
return { ...graph, nodes: [...graph.nodes, newNode] };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Sequential reorder ────────────────────────────────────── */
|
/* ── Sequential reorder ────────────────────────────────────── */
|
||||||
|
|
||||||
export function swapSequentialOrder(
|
export function swapSequentialOrder(
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { describe, expect, test } from 'bun:test';
|
import { describe, expect, test } from 'bun:test';
|
||||||
|
|
||||||
import { createBuiltinPatterns, resolvePatternGraph, type PatternDefinition } from '@shared/domain/pattern';
|
import { addAgentToGraph, createBuiltinPatterns, resolvePatternGraph, type PatternDefinition } from '@shared/domain/pattern';
|
||||||
import {
|
import {
|
||||||
addAgentNodeToGraph,
|
|
||||||
addEdge,
|
addEdge,
|
||||||
autoLayoutGraph,
|
autoLayoutGraph,
|
||||||
canMoveSequential,
|
canMoveSequential,
|
||||||
@@ -208,20 +207,20 @@ describe('pattern graph mutation helpers', () => {
|
|||||||
expect(updated.edges.find((e) => e.id === firstEdge.id)).toBeUndefined();
|
expect(updated.edges.find((e) => e.id === firstEdge.id)).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('addAgentNodeToGraph places a disconnected agent node on the canvas', () => {
|
test('addAgentToGraph inserts a wired agent node for sequential mode', () => {
|
||||||
const pattern = findPattern('sequential');
|
const pattern = findPattern('sequential');
|
||||||
const graph = resolvePatternGraph(pattern);
|
const graph = resolvePatternGraph(pattern);
|
||||||
const newAgent = { id: 'new-1', name: 'New Agent', description: '', instructions: '', model: 'gpt-5.4' };
|
const newAgent = { id: 'new-1', name: 'New Agent', description: '', instructions: '', model: 'gpt-5.4' };
|
||||||
|
|
||||||
const updated = addAgentNodeToGraph(graph, newAgent);
|
const updated = addAgentToGraph(graph, pattern.mode, newAgent);
|
||||||
expect(updated.nodes.length).toBe(graph.nodes.length + 1);
|
expect(updated.nodes.length).toBe(graph.nodes.length + 1);
|
||||||
|
|
||||||
const newNode = updated.nodes.find((n) => n.agentId === 'new-1');
|
const newNode = updated.nodes.find((n) => n.agentId === 'new-1');
|
||||||
expect(newNode).toBeDefined();
|
expect(newNode).toBeDefined();
|
||||||
expect(newNode!.kind).toBe('agent');
|
expect(newNode!.kind).toBe('agent');
|
||||||
|
|
||||||
// No new edges added — node is disconnected
|
// New agent is wired into the chain, so edge count stays at agents+1
|
||||||
expect(updated.edges.length).toBe(graph.edges.length);
|
expect(updated.edges.length).toBe(graph.edges.length + 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user