@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
@@ -4,37 +4,37 @@ import { ToolMessage, AIMessage, HumanMessage, getBufferString, SystemMessage }
4
4
  import { getCurrentTaskInput, Command, Annotation, messagesStateReducer, StateGraph, END, START } from '@langchain/langgraph';
5
5
  import '../messages/core.mjs';
6
6
  import 'nanoid';
7
- import { EdgeType, Constants, DEFAULT_DELEGATE_MAX_RESULT_CHARS } from '../common/enum.mjs';
7
+ import { EdgeType, Constants, DEFAULT_HANDOFF_MAX_RESULT_CHARS } from '../common/enum.mjs';
8
8
  import '../tools/approval/constants.mjs';
9
9
  import '../utils/toonFormat.mjs';
10
10
  import { summarize, createEmergencySummary } from '../messages/summarize.mjs';
11
11
  import { StandardGraph } from './Graph.mjs';
12
12
 
13
13
  /** Pattern to extract instructions from transfer ToolMessage content */
14
- const HANDOFF_INSTRUCTIONS_PATTERN = /(?:Instructions?|Context):\s*(.+)/is;
14
+ const TRANSFER_INSTRUCTIONS_PATTERN = /(?:Instructions?|Context):\s*(.+)/is;
15
15
  /**
16
16
  * MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
17
17
  * with handoffs, fan-in/fan-out, and other composable patterns.
18
18
  *
19
19
  * Key behavior:
20
- * - Agents with ONLY handoff edges: Can dynamically route to any handoff destination
21
- * - Agents with ONLY direct edges: Always follow their direct edges
22
- * - Agents with BOTH: Use Command for exclusive routing (handoff OR direct, not both)
23
- * - If handoff occurs: Only the handoff destination executes
24
- * - If no handoff: Direct edges execute (potentially in parallel)
20
+ * - Agents with ONLY transfer edges: Can dynamically route to any transfer destination
21
+ * - Agents with ONLY sequence edges: Always follow their sequence edges
22
+ * - Agents with BOTH: Use Command for exclusive routing (transfer OR sequence, not both)
23
+ * - If transfer occurs: Only the transfer destination executes
24
+ * - If no transfer: Sequence edges execute (potentially in parallel)
25
25
  *
26
- * This enables the common pattern where an agent either delegates (handoff)
27
- * OR continues its workflow (direct edges), but not both simultaneously.
26
+ * This enables the common pattern where an agent either transfers (one-way)
27
+ * OR continues its workflow (sequence edges), but not both simultaneously.
28
28
  */
29
29
  class MultiAgentGraph extends StandardGraph {
30
30
  edges;
31
31
  startingNodes = new Set();
32
- directEdges = [];
32
+ sequenceEdges = [];
33
+ transferEdges = [];
33
34
  handoffEdges = [];
34
- delegateEdges = [];
35
35
  /**
36
36
  * Lazily populated registry of compiled subgraphs, keyed by agentId.
37
- * Delegate tools are created in the constructor but reference subgraphs
37
+ * Handoff tools are created in the constructor but reference subgraphs
38
38
  * that are only created in createWorkflow(). This Map bridges that gap —
39
39
  * tools capture the Map reference in their closure, and createWorkflow()
40
40
  * populates it before any tool invocation occurs.
@@ -61,41 +61,39 @@ class MultiAgentGraph extends StandardGraph {
61
61
  this.edges = input.edges;
62
62
  this.categorizeEdges();
63
63
  this.analyzeGraph();
64
+ this.createTransferTools();
64
65
  this.createHandoffTools();
65
- this.createDelegateTools();
66
66
  console.debug(`[MultiAgentGraph] Constructor complete: ${this.agentContexts.size} agents, ${this.edges.length} edges`);
67
67
  }
68
68
  /**
69
- * Categorize edges into handoff and direct types
69
+ * Categorize edges into handoff, transfer, and sequence types
70
70
  */
71
71
  categorizeEdges() {
72
72
  for (const edge of this.edges) {
73
- // Default behavior: edges with conditions or explicit 'handoff' type are handoff edges
74
- // Edges with explicit 'direct' type or multi-destination without conditions are direct edges
75
- if (edge.edgeType === EdgeType.DELEGATE) {
76
- this.delegateEdges.push(edge);
73
+ if (edge.edgeType === EdgeType.HANDOFF) {
74
+ this.handoffEdges.push(edge);
77
75
  }
78
- else if (edge.edgeType === EdgeType.DIRECT) {
79
- this.directEdges.push(edge);
76
+ else if (edge.edgeType === EdgeType.SEQUENCE) {
77
+ this.sequenceEdges.push(edge);
80
78
  }
81
- else if (edge.edgeType === EdgeType.HANDOFF || edge.condition != null) {
82
- this.handoffEdges.push(edge);
79
+ else if (edge.edgeType === EdgeType.TRANSFER || edge.condition != null) {
80
+ this.transferEdges.push(edge);
83
81
  }
84
82
  else {
85
- // Default: single-to-single edges are handoff, single-to-multiple are direct
83
+ // Default: single-to-single edges are transfer, single-to-multiple are sequence
86
84
  const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
87
85
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
88
86
  if (sources.length === 1 && destinations.length > 1) {
89
- // Fan-out pattern defaults to direct
90
- this.directEdges.push(edge);
87
+ // Fan-out pattern defaults to sequence
88
+ this.sequenceEdges.push(edge);
91
89
  }
92
90
  else {
93
- // Everything else defaults to handoff
94
- this.handoffEdges.push(edge);
91
+ // Everything else defaults to transfer
92
+ this.transferEdges.push(edge);
95
93
  }
96
94
  }
97
95
  }
98
- console.debug(`[MultiAgentGraph] Edge categorization: ${this.handoffEdges.length} handoff, ${this.directEdges.length} direct, ${this.delegateEdges.length} delegate (of ${this.edges.length} total)`);
96
+ console.debug(`[MultiAgentGraph] Edge categorization: ${this.handoffEdges.length} handoff, ${this.transferEdges.length} transfer, ${this.sequenceEdges.length} sequence (of ${this.edges.length} total)`);
99
97
  }
100
98
  /**
101
99
  * Analyze graph structure to determine starting nodes and connections
@@ -153,8 +151,8 @@ class MultiAgentGraph extends StandardGraph {
153
151
  if (visited.has(current))
154
152
  continue;
155
153
  visited.add(current);
156
- // Find direct edges from this node
157
- for (const edge of this.directEdges) {
154
+ // Find sequence edges from this node
155
+ for (const edge of this.sequenceEdges) {
158
156
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
159
157
  if (!sources.includes(current))
160
158
  continue;
@@ -181,8 +179,8 @@ class MultiAgentGraph extends StandardGraph {
181
179
  }
182
180
  }
183
181
  }
184
- // Also follow handoff and delegate edges for traversal (they don't create parallel groups)
185
- for (const edge of [...this.handoffEdges, ...this.delegateEdges]) {
182
+ // Also follow transfer and handoff edges for traversal (they don't create parallel groups)
183
+ for (const edge of [...this.transferEdges, ...this.handoffEdges]) {
186
184
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
187
185
  if (!sources.includes(current))
188
186
  continue;
@@ -225,46 +223,44 @@ class MultiAgentGraph extends StandardGraph {
225
223
  return this.agentParallelGroups.get(agentId);
226
224
  }
227
225
  /**
228
- * Create handoff tools for agents based on handoff edges only
226
+ * Create transfer tools for agents based on transfer edges only.
227
+ * Transfer tools return Command for one-way routing — parent exits, child takes over.
229
228
  */
230
- createHandoffTools() {
231
- // Group handoff edges by source agent(s)
232
- const handoffsByAgent = new Map();
233
- // Only process handoff edges for tool creation
234
- for (const edge of this.handoffEdges) {
229
+ createTransferTools() {
230
+ const transfersByAgent = new Map();
231
+ for (const edge of this.transferEdges) {
235
232
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
236
233
  sources.forEach((source) => {
237
- if (!handoffsByAgent.has(source)) {
238
- handoffsByAgent.set(source, []);
234
+ if (!transfersByAgent.has(source)) {
235
+ transfersByAgent.set(source, []);
239
236
  }
240
- handoffsByAgent.get(source).push(edge);
237
+ transfersByAgent.get(source).push(edge);
241
238
  });
242
239
  }
243
- // Create handoff tools for each agent
244
- for (const [agentId, edges] of handoffsByAgent) {
240
+ for (const [agentId, edges] of transfersByAgent) {
245
241
  const agentContext = this.agentContexts.get(agentId);
246
242
  if (!agentContext)
247
243
  continue;
248
- // Create handoff tools for this agent's outgoing edges
249
- const handoffTools = [];
244
+ const transferTools = [];
250
245
  const sourceAgentName = agentContext.name ?? agentId;
251
246
  for (const edge of edges) {
252
- handoffTools.push(...this.createHandoffToolsForEdge(edge, agentId, sourceAgentName));
247
+ transferTools.push(...this.createTransferToolsForEdge(edge, agentId, sourceAgentName));
253
248
  }
254
249
  if (!agentContext.graphTools) {
255
250
  agentContext.graphTools = [];
256
251
  }
257
- agentContext.graphTools.push(...handoffTools);
258
- console.debug(`[MultiAgentGraph] Handoff tools for "${agentId}": [${handoffTools.map((t) => t.name).join(', ')}]`);
252
+ agentContext.graphTools.push(...transferTools);
253
+ console.debug(`[MultiAgentGraph] Transfer tools for "${agentId}": [${transferTools.map((t) => t.name).join(', ')}]`);
259
254
  }
260
255
  }
261
256
  /**
262
- * Create handoff tools for an edge (handles multiple destinations)
263
- * @param edge - The graph edge defining the handoff
264
- * @param sourceAgentId - The ID of the agent that will perform the handoff
257
+ * Create transfer tools for an edge (handles multiple destinations).
258
+ * Transfer tools return Command for one-way routing parent exits, child takes over.
259
+ * @param edge - The graph edge defining the transfer
260
+ * @param sourceAgentId - The ID of the agent that will perform the transfer
265
261
  * @param sourceAgentName - The human-readable name of the source agent
266
262
  */
267
- createHandoffToolsForEdge(edge, sourceAgentId, sourceAgentName) {
263
+ createTransferToolsForEdge(edge, sourceAgentId, sourceAgentName) {
268
264
  const tools = [];
269
265
  const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
270
266
  /** If there's a condition, create a single conditional handoff tool */
@@ -272,8 +268,8 @@ class MultiAgentGraph extends StandardGraph {
272
268
  const toolName = 'conditional_transfer';
273
269
  const toolDescription = edge.description ?? 'Conditionally transfer control based on state';
274
270
  /** Check if we have a prompt for handoff input */
275
- const hasHandoffInput = edge.prompt != null && typeof edge.prompt === 'string';
276
- const handoffInputDescription = hasHandoffInput ? edge.prompt : undefined;
271
+ const hasTransferInput = edge.prompt != null && typeof edge.prompt === 'string';
272
+ const transferInputDescription = hasTransferInput ? edge.prompt : undefined;
277
273
  const promptKey = edge.promptKey ?? 'instructions';
278
274
  tools.push(tool(async (rawInput, config) => {
279
275
  const input = rawInput;
@@ -297,7 +293,7 @@ class MultiAgentGraph extends StandardGraph {
297
293
  destination = Array.isArray(result) ? result[0] : destinations[0];
298
294
  }
299
295
  let content = `Conditionally transferred to ${destination}`;
300
- if (hasHandoffInput &&
296
+ if (hasTransferInput &&
301
297
  promptKey in input &&
302
298
  input[promptKey] != null) {
303
299
  content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
@@ -320,13 +316,13 @@ class MultiAgentGraph extends StandardGraph {
320
316
  });
321
317
  }, {
322
318
  name: toolName,
323
- schema: hasHandoffInput
319
+ schema: hasTransferInput
324
320
  ? {
325
321
  type: 'object',
326
322
  properties: {
327
323
  [promptKey]: {
328
324
  type: 'string',
329
- description: handoffInputDescription,
325
+ description: transferInputDescription,
330
326
  },
331
327
  },
332
328
  required: [],
@@ -341,10 +337,10 @@ class MultiAgentGraph extends StandardGraph {
341
337
  const toolName = `${Constants.LC_TRANSFER_TO_}${destination}`;
342
338
  const destContext = this.agentContexts.get(destination);
343
339
  const toolDescription = edge.description ??
344
- this.buildDefaultHandoffDescription(destContext, destination);
340
+ this.buildDefaultTransferDescription(destContext, destination);
345
341
  /** Check if we have a prompt for handoff input */
346
- const hasHandoffInput = edge.prompt != null && typeof edge.prompt === 'string';
347
- const handoffInputDescription = hasHandoffInput
342
+ const hasTransferInput = edge.prompt != null && typeof edge.prompt === 'string';
343
+ const transferInputDescription = hasTransferInput
348
344
  ? edge.prompt
349
345
  : undefined;
350
346
  const promptKey = edge.promptKey ?? 'instructions';
@@ -353,7 +349,7 @@ class MultiAgentGraph extends StandardGraph {
353
349
  const toolCallId = config?.toolCall?.id ??
354
350
  'unknown';
355
351
  let content = `Successfully transferred to ${destination}`;
356
- if (hasHandoffInput &&
352
+ if (hasTransferInput &&
357
353
  promptKey in input &&
358
354
  input[promptKey] != null) {
359
355
  content += `\n\n${promptKey.charAt(0).toUpperCase() + promptKey.slice(1)}: ${input[promptKey]}`;
@@ -430,13 +426,13 @@ class MultiAgentGraph extends StandardGraph {
430
426
  });
431
427
  }, {
432
428
  name: toolName,
433
- schema: hasHandoffInput
429
+ schema: hasTransferInput
434
430
  ? {
435
431
  type: 'object',
436
432
  properties: {
437
433
  [promptKey]: {
438
434
  type: 'string',
439
- description: handoffInputDescription,
435
+ description: transferInputDescription,
440
436
  },
441
437
  },
442
438
  required: [],
@@ -449,13 +445,13 @@ class MultiAgentGraph extends StandardGraph {
449
445
  return tools;
450
446
  }
451
447
  /**
452
- * Builds a meaningful default description for a handoff tool when no explicit
448
+ * Builds a meaningful default description for a transfer tool when no explicit
453
449
  * edge.description is provided. Uses the destination agent's name and description
454
450
  * so the LLM can make informed routing decisions.
455
451
  * @param destContext - AgentContext of the destination agent (may be undefined)
456
452
  * @param destinationId - Raw agent ID (fallback when context unavailable)
457
453
  */
458
- buildDefaultHandoffDescription(destContext, destinationId) {
454
+ buildDefaultTransferDescription(destContext, destinationId) {
459
455
  const displayName = destContext?.name ?? destinationId;
460
456
  const agentDescription = destContext?.description;
461
457
  if (agentDescription != null && agentDescription !== '') {
@@ -464,58 +460,58 @@ class MultiAgentGraph extends StandardGraph {
464
460
  return `Transfer control to "${displayName}"`;
465
461
  }
466
462
  /**
467
- * Create delegate tools for agents based on delegate edges.
468
- * Delegate tools invoke child agent subgraphs inline and return the result
469
- * as a string to the parent agent's context. Unlike handoff tools (which
470
- * return Command for fire-and-forget routing), delegate tools execute the
471
- * child, extract the final text, and return it within the parent's agent loop.
463
+ * Create handoff tools for agents based on handoff edges.
464
+ * Handoff tools invoke child agent subgraphs inline and return the result
465
+ * as a string to the parent agent's context. Unlike transfer tools (which
466
+ * return Command for one-way routing), handoff tools execute the child,
467
+ * extract the final text, and return it within the parent's agent loop.
472
468
  *
473
469
  * This enables the supervisor pattern: parent calls child → gets result → thinks → calls another.
474
470
  */
475
- createDelegateTools() {
476
- const delegatesByAgent = new Map();
477
- for (const edge of this.delegateEdges) {
471
+ createHandoffTools() {
472
+ const handoffsByAgent = new Map();
473
+ for (const edge of this.handoffEdges) {
478
474
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
479
475
  sources.forEach((source) => {
480
- if (!delegatesByAgent.has(source)) {
481
- delegatesByAgent.set(source, []);
476
+ if (!handoffsByAgent.has(source)) {
477
+ handoffsByAgent.set(source, []);
482
478
  }
483
- delegatesByAgent.get(source).push(edge);
479
+ handoffsByAgent.get(source).push(edge);
484
480
  });
485
481
  }
486
- for (const [agentId, edges] of delegatesByAgent) {
482
+ for (const [agentId, edges] of handoffsByAgent) {
487
483
  const agentContext = this.agentContexts.get(agentId);
488
484
  if (!agentContext)
489
485
  continue;
490
- const delegateTools = [];
486
+ const handoffTools = [];
491
487
  for (const edge of edges) {
492
- delegateTools.push(...this.createDelegateToolsForEdge(edge, agentId));
488
+ handoffTools.push(...this.createHandoffToolsForEdge(edge, agentId));
493
489
  }
494
490
  if (!agentContext.graphTools) {
495
491
  agentContext.graphTools = [];
496
492
  }
497
- agentContext.graphTools.push(...delegateTools);
498
- console.debug(`[MultiAgentGraph] Delegate tools for "${agentId}": [${delegateTools.map((t) => t.name).join(', ')}]`);
493
+ agentContext.graphTools.push(...handoffTools);
494
+ console.debug(`[MultiAgentGraph] Handoff tools for "${agentId}": [${handoffTools.map((t) => t.name).join(', ')}]`);
499
495
  }
500
496
  }
501
497
  /**
502
- * Create delegate tools for an edge (handles multiple destinations).
503
- * Each delegate tool invokes the child agent's compiled subgraph inline,
498
+ * Create handoff tools for an edge (handles multiple destinations).
499
+ * Each handoff tool invokes the child agent's compiled subgraph inline,
504
500
  * extracts the final AI message text, truncates it, and returns it as
505
501
  * a string (which becomes a ToolMessage in the parent's context).
506
502
  *
507
- * @param edge - The graph edge defining the delegation
503
+ * @param edge - The graph edge defining the handoff
508
504
  * @param sourceAgentId - The ID of the parent/supervisor agent
509
505
  */
510
- createDelegateToolsForEdge(edge, sourceAgentId) {
506
+ createHandoffToolsForEdge(edge, sourceAgentId) {
511
507
  const tools = [];
512
508
  const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
513
- const maxResultChars = edge.maxResultChars ?? DEFAULT_DELEGATE_MAX_RESULT_CHARS;
509
+ const maxResultChars = edge.maxResultChars ?? DEFAULT_HANDOFF_MAX_RESULT_CHARS;
514
510
  for (const destination of destinations) {
515
- const toolName = `${Constants.LC_DELEGATE_TO_}${destination}`;
511
+ const toolName = `${Constants.LC_HANDOFF_TO_}${destination}`;
516
512
  const destContext = this.agentContexts.get(destination);
517
513
  const toolDescription = edge.description ??
518
- this.buildDefaultDelegateDescription(destContext, destination);
514
+ this.buildDefaultHandoffDescription(destContext, destination);
519
515
  const hasPromptInput = edge.prompt != null && typeof edge.prompt === 'string';
520
516
  const promptInputDescription = hasPromptInput ? edge.prompt : undefined;
521
517
  const promptKey = edge.promptKey ?? 'instructions';
@@ -525,7 +521,7 @@ class MultiAgentGraph extends StandardGraph {
525
521
  const input = rawInput;
526
522
  const subgraph = registry.get(destination);
527
523
  if (!subgraph) {
528
- throw new Error(`Delegate target "${destination}" subgraph not found in registry. ` +
524
+ throw new Error(`Handoff target "${destination}" subgraph not found in registry. ` +
529
525
  'This is a bug: createWorkflow() should have populated the subgraph registry.');
530
526
  }
531
527
  const state = getCurrentTaskInput();
@@ -542,7 +538,7 @@ class MultiAgentGraph extends StandardGraph {
542
538
  const childState = {
543
539
  messages: childMessages,
544
540
  };
545
- console.debug(`[MultiAgentGraph] Delegate "${sourceAgentId}" -> "${destination}" START ` +
541
+ console.debug(`[MultiAgentGraph] Handoff "${sourceAgentId}" -> "${destination}" START ` +
546
542
  `(messages: ${childMessages.length})`);
547
543
  try {
548
544
  /**
@@ -551,17 +547,17 @@ class MultiAgentGraph extends StandardGraph {
551
547
  * and configurable data (thread_id, user_id) to the child.
552
548
  */
553
549
  const result = await subgraph.invoke(childState, config);
554
- const resultText = MultiAgentGraph.extractDelegateResult(result.messages, destination);
555
- const truncatedResult = MultiAgentGraph.truncateDelegateResult(resultText, maxResultChars);
556
- console.debug(`[MultiAgentGraph] Delegate "${sourceAgentId}" -> "${destination}" DONE ` +
550
+ const resultText = MultiAgentGraph.extractHandoffResult(result.messages, destination);
551
+ const truncatedResult = MultiAgentGraph.truncateHandoffResult(resultText, maxResultChars);
552
+ console.debug(`[MultiAgentGraph] Handoff "${sourceAgentId}" -> "${destination}" DONE ` +
557
553
  `(result: ${resultText.length} chars` +
558
554
  `${truncatedResult.length < resultText.length ? `, truncated to ${truncatedResult.length}` : ''})`);
559
555
  return truncatedResult;
560
556
  }
561
557
  catch (err) {
562
558
  const errorMessage = err instanceof Error ? err.message : String(err);
563
- console.error(`[MultiAgentGraph] Delegate "${sourceAgentId}" -> "${destination}" ERROR:`, errorMessage);
564
- return `[Delegate to "${destination}" failed: ${errorMessage}]`;
559
+ console.error(`[MultiAgentGraph] Handoff "${sourceAgentId}" -> "${destination}" ERROR:`, errorMessage);
560
+ return `[Handoff to "${destination}" failed: ${errorMessage}]`;
565
561
  }
566
562
  }, {
567
563
  name: toolName,
@@ -589,7 +585,7 @@ class MultiAgentGraph extends StandardGraph {
589
585
  * @param messages - The child agent's output messages
590
586
  * @param agentId - The child agent ID (for fallback message)
591
587
  */
592
- static extractDelegateResult(messages, agentId) {
588
+ static extractHandoffResult(messages, agentId) {
593
589
  for (let i = messages.length - 1; i >= 0; i--) {
594
590
  const msg = messages[i];
595
591
  if (msg.getType() !== 'ai')
@@ -616,17 +612,17 @@ class MultiAgentGraph extends StandardGraph {
616
612
  return `[Agent "${agentId}" completed but produced no text output]`;
617
613
  }
618
614
  /**
619
- * Truncate delegate result using head/tail strategy (60/40 split).
615
+ * Truncate handoff result using head/tail strategy (60/40 split).
620
616
  * Preserves the beginning (key findings) and end (conclusions).
621
617
  * Matches the TaskTool.truncateResult pattern from Ranger.
622
618
  * @param result - The full result text
623
619
  * @param maxChars - Maximum allowed characters
624
620
  */
625
- static truncateDelegateResult(result, maxChars) {
621
+ static truncateHandoffResult(result, maxChars) {
626
622
  if (!result || result.length <= maxChars) {
627
623
  return result;
628
624
  }
629
- const truncationNotice = '\n\n[... delegate output truncated — middle section omitted to fit parent context ...]\n\n';
625
+ const truncationNotice = '\n\n[... handoff output truncated — middle section omitted to fit parent context ...]\n\n';
630
626
  const available = maxChars - truncationNotice.length;
631
627
  if (available <= 0) {
632
628
  return result.substring(0, maxChars);
@@ -638,17 +634,17 @@ class MultiAgentGraph extends StandardGraph {
638
634
  result.substring(result.length - tailSize));
639
635
  }
640
636
  /**
641
- * Build a meaningful default description for a delegate tool.
637
+ * Build a meaningful default description for a handoff tool.
642
638
  * @param destContext - AgentContext of the destination agent
643
639
  * @param destinationId - Raw agent ID (fallback)
644
640
  */
645
- buildDefaultDelegateDescription(destContext, destinationId) {
641
+ buildDefaultHandoffDescription(destContext, destinationId) {
646
642
  const displayName = destContext?.name ?? destinationId;
647
643
  const agentDescription = destContext?.description;
648
644
  if (agentDescription != null && agentDescription !== '') {
649
- return `Delegate task to "${displayName}": ${agentDescription}. The agent will execute and return its result.`;
645
+ return `Hand off task to "${displayName}": ${agentDescription}. The agent will execute and return its result.`;
650
646
  }
651
- return `Delegate task to "${displayName}" and receive its result.`;
647
+ return `Hand off task to "${displayName}" and receive its result.`;
652
648
  }
653
649
  /**
654
650
  * Create a complete agent subgraph (similar to createReactAgent)
@@ -669,7 +665,7 @@ class MultiAgentGraph extends StandardGraph {
669
665
  * @param agentId - The agent ID to check for handoff reception
670
666
  * @returns Object with filtered messages, extracted instructions, source agent, and parallel siblings
671
667
  */
672
- processHandoffReception(messages, agentId) {
668
+ processTransferReception(messages, agentId) {
673
669
  if (messages.length === 0)
674
670
  return null;
675
671
  /**
@@ -698,8 +694,8 @@ class MultiAgentGraph extends StandardGraph {
698
694
  destinationAgent = toolName.replace(Constants.LC_TRANSFER_TO_, '');
699
695
  }
700
696
  else if (isConditionalTransfer) {
701
- const handoffDest = candidateMsg.additional_kwargs.handoff_destination;
702
- destinationAgent = typeof handoffDest === 'string' ? handoffDest : null;
697
+ const transferDest = candidateMsg.additional_kwargs.handoff_destination;
698
+ destinationAgent = typeof transferDest === 'string' ? transferDest : null;
703
699
  }
704
700
  /** Check if this transfer targets our agent */
705
701
  if (destinationAgent === agentId) {
@@ -715,7 +711,7 @@ class MultiAgentGraph extends StandardGraph {
715
711
  const contentStr = typeof toolMessage.content === 'string'
716
712
  ? toolMessage.content
717
713
  : JSON.stringify(toolMessage.content);
718
- const instructionsMatch = contentStr.match(HANDOFF_INSTRUCTIONS_PATTERN);
714
+ const instructionsMatch = contentStr.match(TRANSFER_INSTRUCTIONS_PATTERN);
719
715
  const instructions = instructionsMatch?.[1]?.trim() ?? null;
720
716
  /** Extract source agent name from additional_kwargs */
721
717
  const handoffSourceName = toolMessage.additional_kwargs.handoff_source_name;
@@ -893,7 +889,7 @@ class MultiAgentGraph extends StandardGraph {
893
889
  };
894
890
  }
895
891
  /**
896
- * Create the multi-agent workflow with dynamic handoffs
892
+ * Create the multi-agent workflow with handoffs, transfers, and sequences
897
893
  */
898
894
  createWorkflow() {
899
895
  const StateAnnotation = Annotation.Root({
@@ -919,54 +915,54 @@ class MultiAgentGraph extends StandardGraph {
919
915
  // Add all agents as complete subgraphs
920
916
  for (const [agentId] of this.agentContexts) {
921
917
  // Get all possible destinations for this agent
922
- const handoffDestinations = new Set();
923
- const directDestinations = new Set();
924
- // Check handoff edges for destinations
925
- for (const edge of this.handoffEdges) {
918
+ const transferDestinations = new Set();
919
+ const sequenceDestinations = new Set();
920
+ // Check transfer edges for destinations
921
+ for (const edge of this.transferEdges) {
926
922
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
927
923
  if (sources.includes(agentId) === true) {
928
924
  const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
929
- dests.forEach((dest) => handoffDestinations.add(dest));
925
+ dests.forEach((dest) => transferDestinations.add(dest));
930
926
  }
931
927
  }
932
- // Check direct edges for destinations
933
- for (const edge of this.directEdges) {
928
+ // Check sequence edges for destinations
929
+ for (const edge of this.sequenceEdges) {
934
930
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
935
931
  if (sources.includes(agentId) === true) {
936
932
  const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
937
- dests.forEach((dest) => directDestinations.add(dest));
933
+ dests.forEach((dest) => sequenceDestinations.add(dest));
938
934
  }
939
935
  }
940
- /** Check if this agent has BOTH handoff and direct edges */
941
- const hasHandoffEdges = handoffDestinations.size > 0;
942
- const hasDirectEdges = directDestinations.size > 0;
943
- const needsCommandRouting = hasHandoffEdges && hasDirectEdges;
936
+ /** Check if this agent has BOTH transfer and sequence edges */
937
+ const hasTransferEdges = transferDestinations.size > 0;
938
+ const hasSequenceEdges = sequenceDestinations.size > 0;
939
+ const needsCommandRouting = hasTransferEdges && hasSequenceEdges;
944
940
  /** Collect all possible destinations for this agent */
945
941
  const allDestinations = new Set([
946
- ...handoffDestinations,
947
- ...directDestinations,
942
+ ...transferDestinations,
943
+ ...sequenceDestinations,
948
944
  ]);
949
- if (handoffDestinations.size > 0 || directDestinations.size === 0) {
945
+ if (transferDestinations.size > 0 || sequenceDestinations.size === 0) {
950
946
  allDestinations.add(END);
951
947
  }
952
948
  /** Agent subgraph (includes agent + tools) */
953
949
  const agentSubgraph = this.createAgentSubgraph(agentId);
954
- /** Register subgraph for delegate tools (lazy reference resolution) */
950
+ /** Register subgraph for handoff tools (lazy reference resolution) */
955
951
  this.subgraphRegistry.set(agentId, agentSubgraph);
956
952
  /** Wrapper function that handles agentMessages channel, handoff reception, and conditional routing */
957
953
  const agentWrapper = async (state, config) => {
958
954
  console.debug(`[MultiAgentGraph] Agent "${agentId}" wrapper ENTRY (messages: ${state.messages.length}, needsCommandRouting: ${needsCommandRouting})`);
959
955
  let result;
960
956
  /**
961
- * Check if this agent is receiving a handoff.
957
+ * Check if this agent is receiving a transfer.
962
958
  * If so, filter out the transfer messages and inject instructions as preamble.
963
959
  * This prevents the receiving agent from seeing the transfer as "completed work"
964
960
  * and prematurely producing an end token.
965
961
  */
966
- const handoffContext = this.processHandoffReception(state.messages, agentId);
967
- if (handoffContext !== null) {
968
- const { filteredMessages, instructions, sourceAgentName, parallelSiblings, } = handoffContext;
969
- console.debug(`[MultiAgentGraph] Agent "${agentId}" receiving handoff from "${sourceAgentName}" (instructions: ${instructions != null}, parallelSiblings: ${parallelSiblings.length})`);
962
+ const transferContext = this.processTransferReception(state.messages, agentId);
963
+ if (transferContext !== null) {
964
+ const { filteredMessages, instructions, sourceAgentName, parallelSiblings, } = transferContext;
965
+ console.debug(`[MultiAgentGraph] Agent "${agentId}" receiving transfer from "${sourceAgentName}" (instructions: ${instructions != null}, parallelSiblings: ${parallelSiblings.length})`);
970
966
  /**
971
967
  * Set handoff context on the receiving agent.
972
968
  * Uses pre-computed graph position for depth and parallel info.
@@ -1083,24 +1079,24 @@ class MultiAgentGraph extends StandardGraph {
1083
1079
  /** Track the last agent that produced output for continuation support */
1084
1080
  this.lastActiveAgentId = agentId;
1085
1081
  console.debug(`[MultiAgentGraph] Agent "${agentId}" wrapper EXIT (result messages: ${result.messages.length})`);
1086
- /** If agent has both handoff and direct edges, use Command for exclusive routing */
1082
+ /** If agent has both transfer and sequence edges, use Command for exclusive routing */
1087
1083
  if (needsCommandRouting) {
1088
- /** Check if a handoff occurred */
1084
+ /** Check if a transfer occurred */
1089
1085
  const lastMessage = result.messages[result.messages.length - 1];
1090
1086
  if (lastMessage != null &&
1091
1087
  lastMessage.getType() === 'tool' &&
1092
1088
  typeof lastMessage.name === 'string' &&
1093
1089
  lastMessage.name.startsWith(Constants.LC_TRANSFER_TO_)) {
1094
- /** Handoff occurred - extract destination and navigate there exclusively */
1095
- const handoffDest = lastMessage.name.replace(Constants.LC_TRANSFER_TO_, '');
1096
- console.debug(`[MultiAgentGraph] Command routing: "${agentId}" -> handoff to "${handoffDest}" (direct edges skipped: [${Array.from(directDestinations).join(', ')}])`);
1090
+ /** Transfer occurred - extract destination and navigate there exclusively */
1091
+ const transferDest = lastMessage.name.replace(Constants.LC_TRANSFER_TO_, '');
1092
+ console.debug(`[MultiAgentGraph] Command routing: "${agentId}" -> transfer to "${transferDest}" (sequence edges skipped: [${Array.from(sequenceDestinations).join(', ')}])`);
1097
1093
  /** Validate destination agent exists */
1098
- if (!this.agentContexts.has(handoffDest)) {
1094
+ if (!this.agentContexts.has(transferDest)) {
1099
1095
  const availableAgents = Array.from(this.agentContexts.keys()).join(', ');
1100
- console.error(`[MultiAgentGraph] Handoff to non-existent agent "${handoffDest}". Available: ${availableAgents}`);
1096
+ console.error(`[MultiAgentGraph] Transfer to non-existent agent "${transferDest}". Available: ${availableAgents}`);
1101
1097
  /** Return error to model so it can self-correct */
1102
1098
  const errorMsg = new ToolMessage({
1103
- content: `Transfer failed: agent "${handoffDest}" does not exist. Available agents: ${availableAgents}. Please choose a valid agent to transfer to.`,
1099
+ content: `Transfer failed: agent "${transferDest}" does not exist. Available agents: ${availableAgents}. Please choose a valid agent to transfer to.`,
1104
1100
  tool_call_id: lastMessage.tool_call_id,
1105
1101
  name: lastMessage.name,
1106
1102
  });
@@ -1110,7 +1106,7 @@ class MultiAgentGraph extends StandardGraph {
1110
1106
  };
1111
1107
  }
1112
1108
  /** Pre-handoff context compaction: if receiving agent has smaller budget */
1113
- const receiverContext = this.agentContexts.get(handoffDest);
1109
+ const receiverContext = this.agentContexts.get(transferDest);
1114
1110
  const senderContext = this.agentContexts.get(agentId);
1115
1111
  if (receiverContext?.maxContextTokens != null &&
1116
1112
  senderContext?.tokenCounter != null &&
@@ -1122,7 +1118,7 @@ class MultiAgentGraph extends StandardGraph {
1122
1118
  const receiverBudget = receiverContext.maxContextTokens;
1123
1119
  if (currentSize > receiverBudget * 0.7) {
1124
1120
  console.warn(`[MultiAgentGraph] Pre-handoff compaction: context (${currentSize} tokens) exceeds ` +
1125
- `70% of receiver "${handoffDest}" budget (${receiverBudget} tokens)`);
1121
+ `70% of receiver "${transferDest}" budget (${receiverBudget} tokens)`);
1126
1122
  /** Generate handoff briefing */
1127
1123
  const senderName = senderContext.name ?? agentId;
1128
1124
  if (senderContext.summarizeCallback) {
@@ -1134,8 +1130,8 @@ class MultiAgentGraph extends StandardGraph {
1134
1130
  summaryBudget: Math.floor(receiverBudget * 0.2),
1135
1131
  isMultiAgent: true,
1136
1132
  agentWorkflowState: {
1137
- currentAgentId: handoffDest,
1138
- agentChain: [agentId, handoffDest],
1133
+ currentAgentId: transferDest,
1134
+ agentChain: [agentId, transferDest],
1139
1135
  pendingAgents: [],
1140
1136
  },
1141
1137
  });
@@ -1173,13 +1169,13 @@ class MultiAgentGraph extends StandardGraph {
1173
1169
  }
1174
1170
  return new Command({
1175
1171
  update: result,
1176
- goto: handoffDest,
1172
+ goto: transferDest,
1177
1173
  });
1178
1174
  }
1179
1175
  else {
1180
- /** No handoff - proceed with direct edges */
1181
- console.debug(`[MultiAgentGraph] Command routing: "${agentId}" -> no handoff, following direct edges: [${Array.from(directDestinations).join(', ')}]`);
1182
- const directDests = Array.from(directDestinations);
1176
+ /** No transfer - proceed with sequence edges */
1177
+ console.debug(`[MultiAgentGraph] Command routing: "${agentId}" -> no transfer, following sequence edges: [${Array.from(sequenceDestinations).join(', ')}]`);
1178
+ const directDests = Array.from(sequenceDestinations);
1183
1179
  if (directDests.length === 1) {
1184
1180
  return new Command({
1185
1181
  update: result,
@@ -1210,11 +1206,11 @@ class MultiAgentGraph extends StandardGraph {
1210
1206
  builder.addEdge(START, startNode);
1211
1207
  }
1212
1208
  /**
1213
- * Add direct edges for automatic transitions
1209
+ * Add sequence edges for automatic transitions
1214
1210
  * Group edges by destination to handle fan-in scenarios
1215
1211
  */
1216
1212
  const edgesByDestination = new Map();
1217
- for (const edge of this.directEdges) {
1213
+ for (const edge of this.sequenceEdges) {
1218
1214
  const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
1219
1215
  for (const destination of destinations) {
1220
1216
  if (!edgesByDestination.has(destination)) {
@@ -1303,17 +1299,17 @@ class MultiAgentGraph extends StandardGraph {
1303
1299
  for (const edge of edges) {
1304
1300
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
1305
1301
  for (const source of sources) {
1306
- /** Check if this source node has both handoff and direct edges */
1307
- const sourceHandoffEdges = this.handoffEdges.filter((e) => {
1302
+ /** Check if this source node has both transfer and sequence edges */
1303
+ const sourceTransferEdges = this.transferEdges.filter((e) => {
1308
1304
  const eSources = Array.isArray(e.from) ? e.from : [e.from];
1309
1305
  return eSources.includes(source);
1310
1306
  });
1311
- const sourceDirectEdges = this.directEdges.filter((e) => {
1307
+ const sourceSequenceEdges = this.sequenceEdges.filter((e) => {
1312
1308
  const eSources = Array.isArray(e.from) ? e.from : [e.from];
1313
1309
  return eSources.includes(source);
1314
1310
  });
1315
1311
  /** Skip adding edge if source uses Command routing (has both types) */
1316
- if (sourceHandoffEdges.length > 0 && sourceDirectEdges.length > 0) {
1312
+ if (sourceTransferEdges.length > 0 && sourceSequenceEdges.length > 0) {
1317
1313
  continue;
1318
1314
  }
1319
1315
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment