@illuma-ai/agents 1.1.15 → 1.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/cjs/common/enum.cjs +13 -13
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/graphs/MultiAgentGraph.cjs +146 -150
  4. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  5. package/dist/cjs/main.cjs +2 -2
  6. package/dist/cjs/types/graph.cjs.map +1 -1
  7. package/dist/esm/common/enum.mjs +12 -12
  8. package/dist/esm/common/enum.mjs.map +1 -1
  9. package/dist/esm/graphs/MultiAgentGraph.mjs +147 -151
  10. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  11. package/dist/esm/main.mjs +1 -1
  12. package/dist/esm/types/graph.mjs.map +1 -1
  13. package/dist/types/common/enum.d.ts +11 -11
  14. package/dist/types/graphs/MultiAgentGraph.d.ts +38 -36
  15. package/dist/types/types/graph.d.ts +13 -7
  16. package/package.json +1 -1
  17. package/src/common/__tests__/enum.test.ts +14 -6
  18. package/src/common/enum.ts +11 -11
  19. package/src/graphs/MultiAgentGraph.ts +148 -152
  20. package/src/graphs/__tests__/multi-agent-delegate.test.ts +44 -44
  21. package/src/graphs/__tests__/multi-agent-edges.test.ts +83 -85
  22. package/src/scripts/multi-agent-chain.js +1 -1
  23. package/src/scripts/multi-agent-chain.ts +1 -1
  24. package/src/scripts/multi-agent-document-review-chain.js +1 -1
  25. package/src/scripts/multi-agent-document-review-chain.ts +1 -1
  26. package/src/scripts/multi-agent-hybrid-flow.js +3 -3
  27. package/src/scripts/multi-agent-hybrid-flow.ts +3 -3
  28. package/src/scripts/multi-agent-parallel.js +2 -2
  29. package/src/scripts/multi-agent-parallel.ts +2 -2
  30. package/src/scripts/multi-agent-sequence.js +2 -2
  31. package/src/scripts/multi-agent-sequence.ts +2 -2
  32. package/src/scripts/multi-agent-supervisor.js +5 -5
  33. package/src/scripts/multi-agent-supervisor.ts +5 -5
  34. package/src/scripts/poc-multi-agent-comprehensive.ts +7 -7
  35. package/src/scripts/sequential-full-metadata-test.js +1 -1
  36. package/src/scripts/sequential-full-metadata-test.ts +1 -1
  37. package/src/scripts/test-custom-prompt-key.js +3 -3
  38. package/src/scripts/test-custom-prompt-key.ts +3 -3
  39. package/src/scripts/test-handoff-input.js +1 -1
  40. package/src/scripts/test-handoff-input.ts +1 -1
  41. package/src/scripts/test-handoff-preamble.js +1 -1
  42. package/src/scripts/test-handoff-preamble.ts +1 -1
  43. package/src/scripts/test-handoff-steering.js +3 -3
  44. package/src/scripts/test-handoff-steering.ts +3 -3
  45. package/src/scripts/test-multi-agent-list-handoff.js +1 -1
  46. package/src/scripts/test-multi-agent-list-handoff.ts +1 -1
  47. package/src/scripts/test-parallel-agent-labeling.js +2 -2
  48. package/src/scripts/test-parallel-agent-labeling.ts +2 -2
  49. package/src/scripts/test-parallel-handoffs.js +2 -2
  50. package/src/scripts/test-parallel-handoffs.ts +2 -2
  51. package/src/scripts/test-thinking-handoff-bedrock.js +1 -1
  52. package/src/scripts/test-thinking-handoff-bedrock.ts +1 -1
  53. package/src/scripts/test-thinking-handoff.js +1 -1
  54. package/src/scripts/test-thinking-handoff.ts +1 -1
  55. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.js +1 -1
  56. package/src/scripts/test-thinking-to-thinking-handoff-bedrock.ts +1 -1
  57. package/src/scripts/test-tool-before-handoff-role-order.js +1 -1
  58. package/src/scripts/test-tool-before-handoff-role-order.ts +1 -1
  59. package/src/scripts/test-tools-before-handoff.js +1 -1
  60. package/src/scripts/test-tools-before-handoff.ts +1 -1
  61. package/src/specs/agent-handoffs-bedrock.integration.test.ts +6 -6
  62. package/src/specs/agent-handoffs.test.ts +35 -35
  63. package/src/specs/thinking-handoff.test.ts +9 -9
  64. package/src/types/graph.ts +13 -7
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Unit tests for the delegate pattern in MultiAgentGraph.
2
+ * Unit tests for the handoff pattern (supervisor-delegate) in MultiAgentGraph.
3
3
  *
4
4
  * Tests cover:
5
- * - Result extraction from child agent messages (extractDelegateResult)
6
- * - Result truncation for parent context protection (truncateDelegateResult)
5
+ * - Result extraction from child agent messages (extractHandoffResult)
6
+ * - Result truncation for parent context protection (truncateHandoffResult)
7
7
  * - Constants and naming conventions
8
8
  */
9
9
  import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
@@ -11,8 +11,8 @@ import type { BaseMessage } from '@langchain/core/messages';
11
11
  import {
12
12
  Constants,
13
13
  EdgeType,
14
- DEFAULT_DELEGATE_MAX_RESULT_CHARS,
15
- DELEGATE_TIMEOUT_MS,
14
+ DEFAULT_HANDOFF_MAX_RESULT_CHARS,
15
+ HANDOFF_TIMEOUT_MS,
16
16
  } from '@/common';
17
17
 
18
18
  /**
@@ -24,38 +24,38 @@ import { MultiAgentGraph } from '../MultiAgentGraph';
24
24
  // ---------------------------------------------------------------------------
25
25
  // Constants
26
26
  // ---------------------------------------------------------------------------
27
- describe('delegate constants', () => {
28
- it('LC_DELEGATE_TO_ prefix matches expected pattern', () => {
29
- expect(Constants.LC_DELEGATE_TO_).toBe('lc_delegate_to_');
27
+ describe('handoff constants', () => {
28
+ it('LC_HANDOFF_TO_ prefix matches expected pattern', () => {
29
+ expect(Constants.LC_HANDOFF_TO_).toBe('lc_handoff_to_');
30
30
  });
31
31
 
32
- it('LC_DELEGATE_TO_ is distinct from LC_TRANSFER_TO_', () => {
33
- expect(Constants.LC_DELEGATE_TO_).not.toBe(Constants.LC_TRANSFER_TO_);
32
+ it('LC_HANDOFF_TO_ is distinct from LC_TRANSFER_TO_', () => {
33
+ expect(Constants.LC_HANDOFF_TO_).not.toBe(Constants.LC_TRANSFER_TO_);
34
34
  });
35
35
 
36
- it('DEFAULT_DELEGATE_MAX_RESULT_CHARS is 32768', () => {
37
- expect(DEFAULT_DELEGATE_MAX_RESULT_CHARS).toBe(32768);
36
+ it('DEFAULT_HANDOFF_MAX_RESULT_CHARS is 32768', () => {
37
+ expect(DEFAULT_HANDOFF_MAX_RESULT_CHARS).toBe(32768);
38
38
  });
39
39
 
40
- it('DELEGATE_TIMEOUT_MS is 5 minutes', () => {
41
- expect(DELEGATE_TIMEOUT_MS).toBe(300_000);
40
+ it('HANDOFF_TIMEOUT_MS is 5 minutes', () => {
41
+ expect(HANDOFF_TIMEOUT_MS).toBe(300_000);
42
42
  });
43
43
 
44
- it('EdgeType.DELEGATE has correct value', () => {
45
- expect(EdgeType.DELEGATE).toBe('delegate');
44
+ it('EdgeType.HANDOFF has correct value', () => {
45
+ expect(EdgeType.HANDOFF).toBe('handoff');
46
46
  });
47
47
  });
48
48
 
49
49
  // ---------------------------------------------------------------------------
50
- // extractDelegateResult
50
+ // extractHandoffResult
51
51
  // ---------------------------------------------------------------------------
52
- describe('extractDelegateResult', () => {
52
+ describe('extractHandoffResult', () => {
53
53
  it('extracts text from last AIMessage with string content', () => {
54
54
  const messages: BaseMessage[] = [
55
55
  new HumanMessage('find sales data'),
56
56
  new AIMessage('Here are the sales figures for Q1...'),
57
57
  ];
58
- const result = MultiAgentGraph.extractDelegateResult(messages, 'researcher');
58
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'researcher');
59
59
  expect(result).toBe('Here are the sales figures for Q1...');
60
60
  });
61
61
 
@@ -69,7 +69,7 @@ describe('extractDelegateResult', () => {
69
69
  ],
70
70
  }),
71
71
  ];
72
- const result = MultiAgentGraph.extractDelegateResult(messages, 'analyst');
72
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'analyst');
73
73
  expect(result).toBe('Analysis shows growth.\nRevenue up 15%.');
74
74
  });
75
75
 
@@ -80,7 +80,7 @@ describe('extractDelegateResult', () => {
80
80
  new HumanMessage('continue'),
81
81
  new AIMessage('final result here'),
82
82
  ];
83
- const result = MultiAgentGraph.extractDelegateResult(messages, 'agent');
83
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'agent');
84
84
  expect(result).toBe('final result here');
85
85
  });
86
86
 
@@ -90,7 +90,7 @@ describe('extractDelegateResult', () => {
90
90
  new AIMessage('good result'),
91
91
  new AIMessage(''),
92
92
  ];
93
- const result = MultiAgentGraph.extractDelegateResult(messages, 'agent');
93
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'agent');
94
94
  expect(result).toBe('good result');
95
95
  });
96
96
 
@@ -104,7 +104,7 @@ describe('extractDelegateResult', () => {
104
104
  }),
105
105
  new ToolMessage({ content: 'search result', tool_call_id: 'tc1' }),
106
106
  ];
107
- const result = MultiAgentGraph.extractDelegateResult(messages, 'agent');
107
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'agent');
108
108
  expect(result).toBe('found the data');
109
109
  });
110
110
 
@@ -112,12 +112,12 @@ describe('extractDelegateResult', () => {
112
112
  const messages: BaseMessage[] = [
113
113
  new HumanMessage('task'),
114
114
  ];
115
- const result = MultiAgentGraph.extractDelegateResult(messages, 'researcher');
115
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'researcher');
116
116
  expect(result).toBe('[Agent "researcher" completed but produced no text output]');
117
117
  });
118
118
 
119
119
  it('returns fallback for empty messages array', () => {
120
- const result = MultiAgentGraph.extractDelegateResult([], 'agent');
120
+ const result = MultiAgentGraph.extractHandoffResult([], 'agent');
121
121
  expect(result).toBe('[Agent "agent" completed but produced no text output]');
122
122
  });
123
123
 
@@ -125,30 +125,30 @@ describe('extractDelegateResult', () => {
125
125
  const messages: BaseMessage[] = [
126
126
  new AIMessage(' result with spaces \n\n'),
127
127
  ];
128
- const result = MultiAgentGraph.extractDelegateResult(messages, 'agent');
128
+ const result = MultiAgentGraph.extractHandoffResult(messages, 'agent');
129
129
  expect(result).toBe('result with spaces');
130
130
  });
131
131
  });
132
132
 
133
133
  // ---------------------------------------------------------------------------
134
- // truncateDelegateResult
134
+ // truncateHandoffResult
135
135
  // ---------------------------------------------------------------------------
136
- describe('truncateDelegateResult', () => {
136
+ describe('truncateHandoffResult', () => {
137
137
  it('returns result unchanged when within budget', () => {
138
138
  const result = 'short result';
139
- expect(MultiAgentGraph.truncateDelegateResult(result, 1000)).toBe(result);
139
+ expect(MultiAgentGraph.truncateHandoffResult(result, 1000)).toBe(result);
140
140
  });
141
141
 
142
142
  it('returns empty string unchanged', () => {
143
- expect(MultiAgentGraph.truncateDelegateResult('', 1000)).toBe('');
143
+ expect(MultiAgentGraph.truncateHandoffResult('', 1000)).toBe('');
144
144
  });
145
145
 
146
146
  it('truncates with head/tail split when over budget', () => {
147
147
  const longText = 'A'.repeat(1000);
148
- const truncated = MultiAgentGraph.truncateDelegateResult(longText, 500);
148
+ const truncated = MultiAgentGraph.truncateHandoffResult(longText, 500);
149
149
 
150
150
  expect(truncated.length).toBeLessThanOrEqual(500);
151
- expect(truncated).toContain('delegate output truncated');
151
+ expect(truncated).toContain('handoff output truncated');
152
152
  // Head should be present
153
153
  expect(truncated.startsWith('AAAA')).toBe(true);
154
154
  // Tail should be present
@@ -158,9 +158,9 @@ describe('truncateDelegateResult', () => {
158
158
  it('preserves 60/40 head/tail ratio', () => {
159
159
  const longText = 'H'.repeat(500) + 'T'.repeat(500);
160
160
  const maxChars = 200;
161
- const truncated = MultiAgentGraph.truncateDelegateResult(longText, maxChars);
161
+ const truncated = MultiAgentGraph.truncateHandoffResult(longText, maxChars);
162
162
 
163
- const notice = '\n\n[... delegate output truncated — middle section omitted to fit parent context ...]\n\n';
163
+ const notice = '\n\n[... handoff output truncated — middle section omitted to fit parent context ...]\n\n';
164
164
  const available = maxChars - notice.length;
165
165
  const expectedHead = Math.floor(available * 0.6);
166
166
  const expectedTail = available - expectedHead;
@@ -176,13 +176,13 @@ describe('truncateDelegateResult', () => {
176
176
 
177
177
  it('handles result exactly at maxChars boundary', () => {
178
178
  const result = 'X'.repeat(100);
179
- expect(MultiAgentGraph.truncateDelegateResult(result, 100)).toBe(result);
179
+ expect(MultiAgentGraph.truncateHandoffResult(result, 100)).toBe(result);
180
180
  });
181
181
 
182
182
  it('handles very small maxChars gracefully', () => {
183
183
  const result = 'A'.repeat(200);
184
184
  // When maxChars is smaller than the truncation notice itself
185
- const truncated = MultiAgentGraph.truncateDelegateResult(result, 10);
185
+ const truncated = MultiAgentGraph.truncateHandoffResult(result, 10);
186
186
  expect(truncated.length).toBeLessThanOrEqual(200);
187
187
  });
188
188
  });
@@ -190,19 +190,19 @@ describe('truncateDelegateResult', () => {
190
190
  // ---------------------------------------------------------------------------
191
191
  // Tool naming convention
192
192
  // ---------------------------------------------------------------------------
193
- describe('delegate tool naming', () => {
194
- it('delegate tool name follows lc_delegate_to_ pattern', () => {
193
+ describe('handoff tool naming', () => {
194
+ it('handoff tool name follows lc_handoff_to_ pattern', () => {
195
195
  const agentId = 'researcher_agent_123';
196
- const expectedToolName = `${Constants.LC_DELEGATE_TO_}${agentId}`;
197
- expect(expectedToolName).toBe('lc_delegate_to_researcher_agent_123');
196
+ const expectedToolName = `${Constants.LC_HANDOFF_TO_}${agentId}`;
197
+ expect(expectedToolName).toBe('lc_handoff_to_researcher_agent_123');
198
198
  });
199
199
 
200
- it('delegate tool prefix is distinct from transfer tool prefix', () => {
200
+ it('handoff tool prefix is distinct from transfer tool prefix', () => {
201
201
  const agentId = 'test';
202
- const delegateName = `${Constants.LC_DELEGATE_TO_}${agentId}`;
202
+ const handoffName = `${Constants.LC_HANDOFF_TO_}${agentId}`;
203
203
  const transferName = `${Constants.LC_TRANSFER_TO_}${agentId}`;
204
- expect(delegateName).not.toBe(transferName);
205
- expect(delegateName).toBe('lc_delegate_to_test');
204
+ expect(handoffName).not.toBe(transferName);
205
+ expect(handoffName).toBe('lc_handoff_to_test');
206
206
  expect(transferName).toBe('lc_transfer_to_test');
207
207
  });
208
208
  });
@@ -3,11 +3,11 @@
3
3
  * and parallel group computation in MultiAgentGraph.
4
4
  *
5
5
  * These test the edge classification rules without instantiating the full graph:
6
- * - Explicit EdgeType.HANDOFFhandoff
7
- * - Explicit EdgeType.DIRECTdirect
8
- * - Condition-based edges → handoff (always, regardless of edgeType)
9
- * - Default single→single → handoff
10
- * - Default single→many → direct (fan-out pattern)
6
+ * - Explicit EdgeType.TRANSFERtransfer
7
+ * - Explicit EdgeType.SEQUENCEsequence
8
+ * - Condition-based edges → transfer (always, regardless of edgeType)
9
+ * - Default single→single → transfer
10
+ * - Default single→many → sequence (fan-out pattern)
11
11
  */
12
12
  import { EdgeType } from '@/common';
13
13
  import type { GraphEdge, BaseGraphState } from '@/types';
@@ -17,34 +17,34 @@ import type { GraphEdge, BaseGraphState } from '@/types';
17
17
  * Kept in sync with the private method for testability.
18
18
  */
19
19
  function categorizeEdges(edges: GraphEdge[]): {
20
- directEdges: GraphEdge[];
20
+ sequenceEdges: GraphEdge[];
21
+ transferEdges: GraphEdge[];
21
22
  handoffEdges: GraphEdge[];
22
- delegateEdges: GraphEdge[];
23
23
  } {
24
- const directEdges: GraphEdge[] = [];
24
+ const sequenceEdges: GraphEdge[] = [];
25
+ const transferEdges: GraphEdge[] = [];
25
26
  const handoffEdges: GraphEdge[] = [];
26
- const delegateEdges: GraphEdge[] = [];
27
27
 
28
28
  for (const edge of edges) {
29
- if (edge.edgeType === EdgeType.DELEGATE) {
30
- delegateEdges.push(edge);
31
- } else if (edge.edgeType === EdgeType.DIRECT) {
32
- directEdges.push(edge);
33
- } else if (edge.edgeType === EdgeType.HANDOFF || edge.condition != null) {
29
+ if (edge.edgeType === EdgeType.HANDOFF) {
34
30
  handoffEdges.push(edge);
31
+ } else if (edge.edgeType === EdgeType.SEQUENCE) {
32
+ sequenceEdges.push(edge);
33
+ } else if (edge.edgeType === EdgeType.TRANSFER || edge.condition != null) {
34
+ transferEdges.push(edge);
35
35
  } else {
36
36
  const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
37
37
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
38
38
 
39
39
  if (sources.length === 1 && destinations.length > 1) {
40
- directEdges.push(edge);
40
+ sequenceEdges.push(edge);
41
41
  } else {
42
- handoffEdges.push(edge);
42
+ transferEdges.push(edge);
43
43
  }
44
44
  }
45
45
  }
46
46
 
47
- return { directEdges, handoffEdges, delegateEdges };
47
+ return { sequenceEdges, transferEdges, handoffEdges };
48
48
  }
49
49
 
50
50
  /**
@@ -79,114 +79,114 @@ function findStartingNodes(
79
79
  // Edge categorization
80
80
  // ---------------------------------------------------------------------------
81
81
  describe('edge categorization', () => {
82
- it('classifies explicit EdgeType.HANDOFF as handoff', () => {
82
+ it('classifies explicit EdgeType.TRANSFER as transfer', () => {
83
83
  const edges: GraphEdge[] = [
84
- { from: 'a', to: 'b', edgeType: EdgeType.HANDOFF },
84
+ { from: 'a', to: 'b', edgeType: EdgeType.TRANSFER },
85
85
  ];
86
- const { directEdges, handoffEdges } = categorizeEdges(edges);
87
- expect(handoffEdges).toHaveLength(1);
88
- expect(directEdges).toHaveLength(0);
86
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
87
+ expect(transferEdges).toHaveLength(1);
88
+ expect(sequenceEdges).toHaveLength(0);
89
89
  });
90
90
 
91
- it('classifies explicit EdgeType.DIRECT as direct', () => {
91
+ it('classifies explicit EdgeType.SEQUENCE as sequence', () => {
92
92
  const edges: GraphEdge[] = [
93
- { from: 'a', to: 'b', edgeType: EdgeType.DIRECT },
93
+ { from: 'a', to: 'b', edgeType: EdgeType.SEQUENCE },
94
94
  ];
95
- const { directEdges, handoffEdges } = categorizeEdges(edges);
96
- expect(directEdges).toHaveLength(1);
97
- expect(handoffEdges).toHaveLength(0);
95
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
96
+ expect(sequenceEdges).toHaveLength(1);
97
+ expect(transferEdges).toHaveLength(0);
98
98
  });
99
99
 
100
- it('classifies condition-based edges as handoff regardless of edgeType', () => {
100
+ it('classifies condition-based edges as transfer regardless of edgeType', () => {
101
101
  const condition = (_state: BaseGraphState) => true;
102
102
  const edges: GraphEdge[] = [
103
103
  { from: 'a', to: 'b', condition },
104
- { from: 'c', to: 'd', condition, edgeType: EdgeType.HANDOFF },
104
+ { from: 'c', to: 'd', condition, edgeType: EdgeType.TRANSFER },
105
105
  ];
106
- const { directEdges, handoffEdges } = categorizeEdges(edges);
107
- expect(handoffEdges).toHaveLength(2);
108
- expect(directEdges).toHaveLength(0);
106
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
107
+ expect(transferEdges).toHaveLength(2);
108
+ expect(sequenceEdges).toHaveLength(0);
109
109
  });
110
110
 
111
- it('defaults single→single edges to handoff', () => {
111
+ it('defaults single→single edges to transfer', () => {
112
112
  const edges: GraphEdge[] = [{ from: 'supervisor', to: 'worker' }];
113
- const { directEdges, handoffEdges } = categorizeEdges(edges);
114
- expect(handoffEdges).toHaveLength(1);
115
- expect(directEdges).toHaveLength(0);
113
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
114
+ expect(transferEdges).toHaveLength(1);
115
+ expect(sequenceEdges).toHaveLength(0);
116
116
  });
117
117
 
118
- it('defaults single→many edges to direct (fan-out pattern)', () => {
118
+ it('defaults single→many edges to sequence (fan-out pattern)', () => {
119
119
  const edges: GraphEdge[] = [
120
120
  { from: 'coordinator', to: ['analyst1', 'analyst2', 'analyst3'] },
121
121
  ];
122
- const { directEdges, handoffEdges } = categorizeEdges(edges);
123
- expect(directEdges).toHaveLength(1);
124
- expect(handoffEdges).toHaveLength(0);
122
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
123
+ expect(sequenceEdges).toHaveLength(1);
124
+ expect(transferEdges).toHaveLength(0);
125
125
  });
126
126
 
127
- it('defaults many→single edges to handoff', () => {
127
+ it('defaults many→single edges to transfer', () => {
128
128
  const edges: GraphEdge[] = [
129
129
  { from: ['analyst1', 'analyst2'], to: 'summarizer' },
130
130
  ];
131
- const { directEdges, handoffEdges } = categorizeEdges(edges);
132
- expect(handoffEdges).toHaveLength(1);
133
- expect(directEdges).toHaveLength(0);
131
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
132
+ expect(transferEdges).toHaveLength(1);
133
+ expect(sequenceEdges).toHaveLength(0);
134
134
  });
135
135
 
136
136
  it('correctly categorizes a mixed set of edges', () => {
137
137
  const edges: GraphEdge[] = [
138
- // Explicit direct: sequential chain
139
- { from: 'researcher', to: 'analyst', edgeType: EdgeType.DIRECT },
140
- // Fan-out: defaults to direct
138
+ // Explicit sequence: sequential chain
139
+ { from: 'researcher', to: 'analyst', edgeType: EdgeType.SEQUENCE },
140
+ // Fan-out: defaults to sequence
141
141
  { from: 'analyst', to: ['reviewer1', 'reviewer2'] },
142
- // Explicit handoff: dynamic routing
143
- { from: 'supervisor', to: 'specialist', edgeType: EdgeType.HANDOFF },
144
- // Default single→single: handoff
142
+ // Explicit transfer: dynamic routing
143
+ { from: 'supervisor', to: 'specialist', edgeType: EdgeType.TRANSFER },
144
+ // Default single→single: transfer
145
145
  { from: 'triage', to: 'handler' },
146
- // Condition-based: always handoff
146
+ // Condition-based: always transfer
147
147
  { from: 'router', to: 'a', condition: () => true },
148
148
  ];
149
149
 
150
- const { directEdges, handoffEdges } = categorizeEdges(edges);
151
- expect(directEdges).toHaveLength(2); // explicit direct + fan-out
152
- expect(handoffEdges).toHaveLength(3); // explicit handoff + default single→single + condition
150
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
151
+ expect(sequenceEdges).toHaveLength(2); // explicit sequence + fan-out
152
+ expect(transferEdges).toHaveLength(3); // explicit transfer + default single→single + condition
153
153
  });
154
154
 
155
155
  it('handles empty edges array', () => {
156
- const { directEdges, handoffEdges, delegateEdges } = categorizeEdges([]);
157
- expect(directEdges).toHaveLength(0);
156
+ const { sequenceEdges, transferEdges, handoffEdges } = categorizeEdges([]);
157
+ expect(sequenceEdges).toHaveLength(0);
158
+ expect(transferEdges).toHaveLength(0);
158
159
  expect(handoffEdges).toHaveLength(0);
159
- expect(delegateEdges).toHaveLength(0);
160
160
  });
161
161
 
162
- it('classifies explicit EdgeType.DELEGATE as delegate', () => {
162
+ it('classifies explicit EdgeType.HANDOFF as handoff', () => {
163
163
  const edges: GraphEdge[] = [
164
- { from: 'supervisor', to: 'researcher', edgeType: EdgeType.DELEGATE },
164
+ { from: 'supervisor', to: 'researcher', edgeType: EdgeType.HANDOFF },
165
165
  ];
166
- const { directEdges, handoffEdges, delegateEdges } = categorizeEdges(edges);
167
- expect(delegateEdges).toHaveLength(1);
168
- expect(handoffEdges).toHaveLength(0);
169
- expect(directEdges).toHaveLength(0);
166
+ const { sequenceEdges, transferEdges, handoffEdges } = categorizeEdges(edges);
167
+ expect(handoffEdges).toHaveLength(1);
168
+ expect(transferEdges).toHaveLength(0);
169
+ expect(sequenceEdges).toHaveLength(0);
170
170
  });
171
171
 
172
- it('supports mixed handoff, direct, and delegate edges from same source', () => {
172
+ it('supports mixed transfer, sequence, and handoff edges from same source', () => {
173
173
  const edges: GraphEdge[] = [
174
- { from: 'supervisor', to: 'researcher', edgeType: EdgeType.DELEGATE },
175
- { from: 'supervisor', to: 'writer', edgeType: EdgeType.HANDOFF },
176
- { from: 'supervisor', to: 'formatter', edgeType: EdgeType.DIRECT },
174
+ { from: 'supervisor', to: 'researcher', edgeType: EdgeType.HANDOFF },
175
+ { from: 'supervisor', to: 'writer', edgeType: EdgeType.TRANSFER },
176
+ { from: 'supervisor', to: 'formatter', edgeType: EdgeType.SEQUENCE },
177
177
  ];
178
- const { directEdges, handoffEdges, delegateEdges } = categorizeEdges(edges);
179
- expect(delegateEdges).toHaveLength(1);
178
+ const { sequenceEdges, transferEdges, handoffEdges } = categorizeEdges(edges);
180
179
  expect(handoffEdges).toHaveLength(1);
181
- expect(directEdges).toHaveLength(1);
180
+ expect(transferEdges).toHaveLength(1);
181
+ expect(sequenceEdges).toHaveLength(1);
182
182
  });
183
183
 
184
- it('categorization works with delegate string value cast as EdgeType', () => {
184
+ it('categorization works with handoff string value cast as EdgeType', () => {
185
185
  const edges: GraphEdge[] = [
186
- { from: 'a', to: 'b', edgeType: 'delegate' as EdgeType },
186
+ { from: 'a', to: 'b', edgeType: 'handoff' as EdgeType },
187
187
  ];
188
- const { delegateEdges } = categorizeEdges(edges);
189
- expect(delegateEdges).toHaveLength(1);
188
+ const { handoffEdges } = categorizeEdges(edges);
189
+ expect(handoffEdges).toHaveLength(1);
190
190
  });
191
191
  });
192
192
 
@@ -250,27 +250,25 @@ describe('starting node identification', () => {
250
250
  // ---------------------------------------------------------------------------
251
251
  describe('EdgeType values match GraphEdge string literals', () => {
252
252
  it('HANDOFF matches the string used in MongoDB documents', () => {
253
- // ranger stores edges in MongoDB with string values
254
- // This ensures the enum stays compatible
255
253
  expect(EdgeType.HANDOFF).toBe('handoff');
256
254
  });
257
255
 
258
- it('DIRECT matches the string used in MongoDB documents', () => {
259
- expect(EdgeType.DIRECT).toBe('direct');
256
+ it('TRANSFER matches the string used in MongoDB documents', () => {
257
+ expect(EdgeType.TRANSFER).toBe('transfer');
260
258
  });
261
259
 
262
- it('DELEGATE matches the string used in MongoDB documents', () => {
263
- expect(EdgeType.DELEGATE).toBe('delegate');
260
+ it('SEQUENCE matches the string used in MongoDB documents', () => {
261
+ expect(EdgeType.SEQUENCE).toBe('sequence');
264
262
  });
265
263
 
266
264
  it('categorization works with string values cast as EdgeType', () => {
267
265
  // Simulates data coming from MongoDB where edgeType is stored as string
268
266
  const edges: GraphEdge[] = [
269
- { from: 'a', to: 'b', edgeType: 'handoff' as EdgeType },
270
- { from: 'c', to: 'd', edgeType: 'direct' as EdgeType },
267
+ { from: 'a', to: 'b', edgeType: 'transfer' as EdgeType },
268
+ { from: 'c', to: 'd', edgeType: 'sequence' as EdgeType },
271
269
  ];
272
- const { directEdges, handoffEdges } = categorizeEdges(edges);
273
- expect(handoffEdges).toHaveLength(1);
274
- expect(directEdges).toHaveLength(1);
270
+ const { sequenceEdges, transferEdges } = categorizeEdges(edges);
271
+ expect(transferEdges).toHaveLength(1);
272
+ expect(sequenceEdges).toHaveLength(1);
275
273
  });
276
274
  });
@@ -19,7 +19,7 @@ function createSequentialChainEdges(agentIds) {
19
19
  edges.push({
20
20
  from: fromAgent,
21
21
  to: toAgent,
22
- edgeType: 'direct',
22
+ edgeType: 'sequence',
23
23
  // Use a prompt function to create the buffer string from all previous results
24
24
  prompt: (messages, startIndex) => {
25
25
  // Get only the messages from this run (after startIndex)
@@ -28,7 +28,7 @@ function createSequentialChainEdges(agentIds: string[]): t.GraphEdge[] {
28
28
  edges.push({
29
29
  from: fromAgent,
30
30
  to: toAgent,
31
- edgeType: 'direct',
31
+ edgeType: 'sequence',
32
32
  // Use a prompt function to create the buffer string from all previous results
33
33
  prompt: (messages: BaseMessage[], startIndex: number) => {
34
34
  // Get only the messages from this run (after startIndex)
@@ -13,7 +13,7 @@ function createDocumentReviewChain(agentIds) {
13
13
  edges.push({
14
14
  from: agentIds[i],
15
15
  to: agentIds[i + 1],
16
- edgeType: 'direct',
16
+ edgeType: 'sequence',
17
17
  prompt: (messages, startIndex) => {
18
18
  const runMessages = messages.slice(startIndex);
19
19
  const bufferString = getBufferString(runMessages);
@@ -22,7 +22,7 @@ function createDocumentReviewChain(agentIds: string[]): t.GraphEdge[] {
22
22
  edges.push({
23
23
  from: agentIds[i],
24
24
  to: agentIds[i + 1],
25
- edgeType: 'direct',
25
+ edgeType: 'sequence',
26
26
  prompt: (messages: BaseMessage[], startIndex: number) => {
27
27
  const runMessages = messages.slice(startIndex);
28
28
  const bufferString = getBufferString(runMessages);
@@ -102,7 +102,7 @@ async function testHybridMultiAgent() {
102
102
  {
103
103
  from: 'primary_agent',
104
104
  to: 'standalone_agent',
105
- edgeType: 'handoff',
105
+ edgeType: 'transfer',
106
106
  description: 'Transfer to standalone specialist for complex requests',
107
107
  prompt: 'Specific instructions for the specialist',
108
108
  },
@@ -110,14 +110,14 @@ async function testHybridMultiAgent() {
110
110
  {
111
111
  from: 'primary_agent',
112
112
  to: 'agent_b',
113
- edgeType: 'direct',
113
+ edgeType: 'sequence',
114
114
  description: 'Continue to Agent B only if no handoff occurs',
115
115
  },
116
116
  // Direct edge: agent_b automatically continues to agent_c
117
117
  {
118
118
  from: 'agent_b',
119
119
  to: 'agent_c',
120
- edgeType: 'direct',
120
+ edgeType: 'sequence',
121
121
  description: 'Automatic progression from B to C',
122
122
  },
123
123
  ];
@@ -108,7 +108,7 @@ async function testHybridMultiAgent() {
108
108
  {
109
109
  from: 'primary_agent',
110
110
  to: 'standalone_agent',
111
- edgeType: 'handoff',
111
+ edgeType: 'transfer',
112
112
  description: 'Transfer to standalone specialist for complex requests',
113
113
  prompt: 'Specific instructions for the specialist',
114
114
  },
@@ -116,14 +116,14 @@ async function testHybridMultiAgent() {
116
116
  {
117
117
  from: 'primary_agent',
118
118
  to: 'agent_b',
119
- edgeType: 'direct',
119
+ edgeType: 'sequence',
120
120
  description: 'Continue to Agent B only if no handoff occurs',
121
121
  },
122
122
  // Direct edge: agent_b automatically continues to agent_c
123
123
  {
124
124
  from: 'agent_b',
125
125
  to: 'agent_c',
126
- edgeType: 'direct',
126
+ edgeType: 'sequence',
127
127
  description: 'Automatic progression from B to C',
128
128
  },
129
129
  ];
@@ -152,13 +152,13 @@ async function testParallelMultiAgent() {
152
152
  from: 'researcher',
153
153
  to: ['analyst1', 'analyst2', 'analyst3'], // Fan-out to multiple analysts
154
154
  description: 'Distribute research to specialist analysts',
155
- edgeType: 'direct', // Explicitly set as direct for automatic transition (enables parallel execution)
155
+ edgeType: 'sequence', // Explicitly set as direct for automatic transition (enables parallel execution)
156
156
  },
157
157
  {
158
158
  from: ['analyst1', 'analyst2', 'analyst3'], // Fan-in from multiple sources
159
159
  to: 'summarizer',
160
160
  description: 'Aggregate analysis results',
161
- edgeType: 'direct', // Fan-in is also direct
161
+ edgeType: 'sequence', // Fan-in is also direct
162
162
  // Add prompt when all analysts have provided input
163
163
  // prompt: (messages, runStartIndex) => {
164
164
  // // Check if we have analysis content from all three analysts
@@ -160,13 +160,13 @@ async function testParallelMultiAgent() {
160
160
  from: 'researcher',
161
161
  to: ['analyst1', 'analyst2', 'analyst3'], // Fan-out to multiple analysts
162
162
  description: 'Distribute research to specialist analysts',
163
- edgeType: 'direct', // Explicitly set as direct for automatic transition (enables parallel execution)
163
+ edgeType: 'sequence', // Explicitly set as direct for automatic transition (enables parallel execution)
164
164
  },
165
165
  {
166
166
  from: ['analyst1', 'analyst2', 'analyst3'], // Fan-in from multiple sources
167
167
  to: 'summarizer',
168
168
  description: 'Aggregate analysis results',
169
- edgeType: 'direct', // Fan-in is also direct
169
+ edgeType: 'sequence', // Fan-in is also direct
170
170
  // Add prompt when all analysts have provided input
171
171
  // prompt: (messages, runStartIndex) => {
172
172
  // // Check if we have analysis content from all three analysts
@@ -75,13 +75,13 @@ async function testSequentialMultiAgent() {
75
75
  {
76
76
  from: 'agent_a',
77
77
  to: 'agent_b',
78
- edgeType: 'direct', // This creates direct edges without tools
78
+ edgeType: 'sequence', // This creates direct edges without tools
79
79
  description: 'Automatic transition from A to B',
80
80
  },
81
81
  {
82
82
  from: 'agent_b',
83
83
  to: 'agent_c',
84
- edgeType: 'direct', // This creates direct edges without tools
84
+ edgeType: 'sequence', // This creates direct edges without tools
85
85
  description: 'Automatic transition from B to C',
86
86
  },
87
87
  ];