illuma-agents 1.0.36 → 1.0.38
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.
- package/dist/cjs/agents/AgentContext.cjs +69 -14
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +3 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +50 -8
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +277 -11
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +128 -61
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +16 -7
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +1 -0
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +1 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +2 -2
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -2
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/BrowserTools.cjs +27 -3
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +22 -21
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +14 -11
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +101 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +862 -0
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +69 -14
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +3 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +51 -9
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +278 -12
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +127 -60
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/cache.mjs +1 -0
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +1 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +2 -2
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/BrowserTools.mjs +27 -3
- package/dist/esm/tools/BrowserTools.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +22 -21
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +14 -11
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +102 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +827 -0
- package/dist/esm/tools/ToolSearch.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +33 -1
- package/dist/types/common/enum.d.ts +4 -2
- package/dist/types/graphs/Graph.d.ts +6 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +16 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/llm/bedrock/index.d.ts +89 -11
- package/dist/types/llm/bedrock/types.d.ts +27 -0
- package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
- package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
- package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
- package/dist/types/tools/BrowserTools.d.ts +2 -0
- package/dist/types/tools/CodeExecutor.d.ts +0 -3
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +0 -3
- package/dist/types/tools/ToolNode.d.ts +3 -1
- package/dist/types/tools/ToolSearch.d.ts +148 -0
- package/dist/types/types/graph.d.ts +2 -0
- package/dist/types/types/llm.d.ts +3 -1
- package/dist/types/types/tools.d.ts +42 -2
- package/package.json +12 -5
- package/src/agents/AgentContext.ts +88 -16
- package/src/common/enum.ts +3 -1
- package/src/graphs/Graph.ts +64 -13
- package/src/graphs/MultiAgentGraph.ts +350 -13
- package/src/index.ts +1 -1
- package/src/llm/bedrock/index.ts +221 -99
- package/src/llm/bedrock/llm.spec.ts +616 -0
- package/src/llm/bedrock/types.ts +51 -0
- package/src/llm/bedrock/utils/index.ts +18 -0
- package/src/llm/bedrock/utils/message_inputs.ts +563 -0
- package/src/llm/bedrock/utils/message_outputs.ts +310 -0
- package/src/messages/__tests__/tools.test.ts +21 -21
- package/src/messages/cache.test.ts +259 -0
- package/src/messages/cache.ts +104 -1
- package/src/messages/core.ts +1 -1
- package/src/messages/tools.ts +2 -2
- package/src/scripts/caching.ts +27 -19
- package/src/scripts/code_exec_files.ts +58 -15
- package/src/scripts/code_exec_multi_session.ts +241 -0
- package/src/scripts/code_exec_session.ts +282 -0
- package/src/scripts/multi-agent-conditional.ts +1 -0
- package/src/scripts/multi-agent-supervisor.ts +1 -0
- package/src/scripts/programmatic_exec_agent.ts +4 -4
- package/src/scripts/test-handoff-preamble.ts +277 -0
- package/src/scripts/test-parallel-handoffs.ts +291 -0
- package/src/scripts/test-tools-before-handoff.ts +8 -4
- package/src/scripts/test_code_api.ts +361 -0
- package/src/scripts/thinking-bedrock.ts +159 -0
- package/src/scripts/thinking.ts +39 -18
- package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
- package/src/scripts/tools.ts +7 -3
- package/src/stream.ts +4 -2
- package/src/tools/BrowserTools.ts +68 -14
- package/src/tools/CodeExecutor.ts +26 -23
- package/src/tools/ProgrammaticToolCalling.ts +18 -14
- package/src/tools/ToolNode.ts +114 -1
- package/src/tools/ToolSearch.ts +1041 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -2
- package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
- package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
- package/src/types/graph.ts +2 -0
- package/src/types/llm.ts +3 -1
- package/src/types/tools.ts +51 -2
- package/dist/cjs/tools/ToolSearchRegex.cjs +0 -455
- package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
- package/dist/esm/tools/ToolSearchRegex.mjs +0 -448
- package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
- package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
- package/src/tools/ToolSearchRegex.ts +0 -535
- package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { tool } from '@langchain/core/tools';
|
|
3
3
|
import { PromptTemplate } from '@langchain/core/prompts';
|
|
4
4
|
import {
|
|
5
|
+
AIMessage,
|
|
5
6
|
ToolMessage,
|
|
6
7
|
HumanMessage,
|
|
7
8
|
getBufferString,
|
|
@@ -15,12 +16,15 @@ import {
|
|
|
15
16
|
getCurrentTaskInput,
|
|
16
17
|
messagesStateReducer,
|
|
17
18
|
} from '@langchain/langgraph';
|
|
19
|
+
import type { BaseMessage, AIMessageChunk } from '@langchain/core/messages';
|
|
18
20
|
import type { ToolRunnableConfig } from '@langchain/core/tools';
|
|
19
|
-
import type { BaseMessage } from '@langchain/core/messages';
|
|
20
21
|
import type * as t from '@/types';
|
|
21
22
|
import { StandardGraph } from './Graph';
|
|
22
23
|
import { Constants } from '@/common';
|
|
23
24
|
|
|
25
|
+
/** Pattern to extract instructions from transfer ToolMessage content */
|
|
26
|
+
const HANDOFF_INSTRUCTIONS_PATTERN = /(?:Instructions?|Context):\s*(.+)/is;
|
|
27
|
+
|
|
24
28
|
/**
|
|
25
29
|
* MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
|
|
26
30
|
* with handoffs, fan-in/fan-out, and other composable patterns.
|
|
@@ -245,8 +249,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
245
249
|
|
|
246
250
|
// Create handoff tools for this agent's outgoing edges
|
|
247
251
|
const handoffTools: t.GenericTool[] = [];
|
|
252
|
+
const sourceAgentName = agentContext.name ?? agentId;
|
|
248
253
|
for (const edge of edges) {
|
|
249
|
-
handoffTools.push(
|
|
254
|
+
handoffTools.push(
|
|
255
|
+
...this.createHandoffToolsForEdge(edge, agentId, sourceAgentName)
|
|
256
|
+
);
|
|
250
257
|
}
|
|
251
258
|
|
|
252
259
|
// Add handoff tools to the agent's existing tools
|
|
@@ -259,8 +266,15 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
259
266
|
|
|
260
267
|
/**
|
|
261
268
|
* Create handoff tools for an edge (handles multiple destinations)
|
|
269
|
+
* @param edge - The graph edge defining the handoff
|
|
270
|
+
* @param sourceAgentId - The ID of the agent that will perform the handoff
|
|
271
|
+
* @param sourceAgentName - The human-readable name of the source agent
|
|
262
272
|
*/
|
|
263
|
-
private createHandoffToolsForEdge(
|
|
273
|
+
private createHandoffToolsForEdge(
|
|
274
|
+
edge: t.GraphEdge,
|
|
275
|
+
sourceAgentId: string,
|
|
276
|
+
sourceAgentName: string
|
|
277
|
+
): t.GenericTool[] {
|
|
264
278
|
const tools: t.GenericTool[] = [];
|
|
265
279
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
266
280
|
|
|
@@ -312,6 +326,12 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
312
326
|
content,
|
|
313
327
|
name: toolName,
|
|
314
328
|
tool_call_id: toolCallId,
|
|
329
|
+
additional_kwargs: {
|
|
330
|
+
/** Store destination for programmatic access in handoff detection */
|
|
331
|
+
handoff_destination: destination,
|
|
332
|
+
/** Store source agent name for receiving agent to know who handed off */
|
|
333
|
+
handoff_source_name: sourceAgentName,
|
|
334
|
+
},
|
|
315
335
|
});
|
|
316
336
|
|
|
317
337
|
return new Command({
|
|
@@ -369,13 +389,81 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
369
389
|
content,
|
|
370
390
|
name: toolName,
|
|
371
391
|
tool_call_id: toolCallId,
|
|
392
|
+
additional_kwargs: {
|
|
393
|
+
/** Store source agent name for receiving agent to know who handed off */
|
|
394
|
+
handoff_source_name: sourceAgentName,
|
|
395
|
+
},
|
|
372
396
|
});
|
|
373
397
|
|
|
374
398
|
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
375
399
|
|
|
400
|
+
/**
|
|
401
|
+
* For parallel handoff support:
|
|
402
|
+
* Build messages that include ONLY this tool call's context.
|
|
403
|
+
* This prevents errors when LLM calls multiple transfers simultaneously -
|
|
404
|
+
* each destination gets a valid AIMessage with matching tool_call and tool_result.
|
|
405
|
+
*
|
|
406
|
+
* Strategy:
|
|
407
|
+
* 1. Find the AIMessage containing this tool call
|
|
408
|
+
* 2. Create a filtered AIMessage with ONLY this tool_call
|
|
409
|
+
* 3. Include all messages before the AIMessage plus the filtered pair
|
|
410
|
+
*/
|
|
411
|
+
const messages = state.messages;
|
|
412
|
+
let filteredMessages = messages;
|
|
413
|
+
let aiMessageIndex = -1;
|
|
414
|
+
|
|
415
|
+
/** Find the AIMessage containing this tool call */
|
|
416
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
417
|
+
const msg = messages[i];
|
|
418
|
+
if (msg.getType() === 'ai') {
|
|
419
|
+
const aiMsg = msg as AIMessage;
|
|
420
|
+
const hasThisCall = aiMsg.tool_calls?.some(
|
|
421
|
+
(tc) => tc.id === toolCallId
|
|
422
|
+
);
|
|
423
|
+
if (hasThisCall === true) {
|
|
424
|
+
aiMessageIndex = i;
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (aiMessageIndex >= 0) {
|
|
431
|
+
const originalAiMsg = messages[aiMessageIndex] as AIMessage;
|
|
432
|
+
const thisToolCall = originalAiMsg.tool_calls?.find(
|
|
433
|
+
(tc) => tc.id === toolCallId
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
if (
|
|
437
|
+
thisToolCall != null &&
|
|
438
|
+
(originalAiMsg.tool_calls?.length ?? 0) > 1
|
|
439
|
+
) {
|
|
440
|
+
/**
|
|
441
|
+
* Multiple tool calls - create filtered AIMessage with ONLY this call.
|
|
442
|
+
* This ensures valid message structure for parallel handoffs.
|
|
443
|
+
*/
|
|
444
|
+
const filteredAiMsg = new AIMessage({
|
|
445
|
+
content: originalAiMsg.content,
|
|
446
|
+
tool_calls: [thisToolCall],
|
|
447
|
+
id: originalAiMsg.id,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
filteredMessages = [
|
|
451
|
+
...messages.slice(0, aiMessageIndex),
|
|
452
|
+
filteredAiMsg,
|
|
453
|
+
toolMessage,
|
|
454
|
+
];
|
|
455
|
+
} else {
|
|
456
|
+
/** Single tool call - use messages as-is */
|
|
457
|
+
filteredMessages = messages.concat(toolMessage);
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
/** Fallback - append tool message */
|
|
461
|
+
filteredMessages = messages.concat(toolMessage);
|
|
462
|
+
}
|
|
463
|
+
|
|
376
464
|
return new Command({
|
|
377
465
|
goto: destination,
|
|
378
|
-
update: { messages:
|
|
466
|
+
update: { messages: filteredMessages },
|
|
379
467
|
graph: Command.PARENT,
|
|
380
468
|
});
|
|
381
469
|
},
|
|
@@ -407,6 +495,179 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
407
495
|
return this.createAgentNode(agentId);
|
|
408
496
|
}
|
|
409
497
|
|
|
498
|
+
/**
|
|
499
|
+
* Detects if the current agent is receiving a handoff and processes the messages accordingly.
|
|
500
|
+
* Returns filtered messages with the transfer tool call/message removed, plus any instructions,
|
|
501
|
+
* source agent, and parallel sibling information extracted from the transfer.
|
|
502
|
+
*
|
|
503
|
+
* Supports both single handoffs (last message is the transfer) and parallel handoffs
|
|
504
|
+
* (multiple transfer ToolMessages, need to find the one targeting this agent).
|
|
505
|
+
*
|
|
506
|
+
* @param messages - Current state messages
|
|
507
|
+
* @param agentId - The agent ID to check for handoff reception
|
|
508
|
+
* @returns Object with filtered messages, extracted instructions, source agent, and parallel siblings
|
|
509
|
+
*/
|
|
510
|
+
private processHandoffReception(
|
|
511
|
+
messages: BaseMessage[],
|
|
512
|
+
agentId: string
|
|
513
|
+
): {
|
|
514
|
+
filteredMessages: BaseMessage[];
|
|
515
|
+
instructions: string | null;
|
|
516
|
+
sourceAgentName: string | null;
|
|
517
|
+
parallelSiblings: string[];
|
|
518
|
+
} | null {
|
|
519
|
+
if (messages.length === 0) return null;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Search for a transfer ToolMessage targeting this agent.
|
|
523
|
+
* For parallel handoffs, multiple transfer messages may exist - find ours.
|
|
524
|
+
* Search backwards from the end to find the most recent transfer to this agent.
|
|
525
|
+
*/
|
|
526
|
+
let toolMessage: ToolMessage | null = null;
|
|
527
|
+
let toolMessageIndex = -1;
|
|
528
|
+
|
|
529
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
530
|
+
const msg = messages[i];
|
|
531
|
+
if (msg.getType() !== 'tool') continue;
|
|
532
|
+
|
|
533
|
+
const candidateMsg = msg as ToolMessage;
|
|
534
|
+
const toolName = candidateMsg.name;
|
|
535
|
+
|
|
536
|
+
if (typeof toolName !== 'string') continue;
|
|
537
|
+
|
|
538
|
+
/** Check for standard transfer pattern */
|
|
539
|
+
const isTransferMessage = toolName.startsWith(Constants.LC_TRANSFER_TO_);
|
|
540
|
+
const isConditionalTransfer = toolName === 'conditional_transfer';
|
|
541
|
+
|
|
542
|
+
if (!isTransferMessage && !isConditionalTransfer) continue;
|
|
543
|
+
|
|
544
|
+
/** Extract destination from tool name or additional_kwargs */
|
|
545
|
+
let destinationAgent: string | null = null;
|
|
546
|
+
|
|
547
|
+
if (isTransferMessage) {
|
|
548
|
+
destinationAgent = toolName.replace(Constants.LC_TRANSFER_TO_, '');
|
|
549
|
+
} else if (isConditionalTransfer) {
|
|
550
|
+
const handoffDest = candidateMsg.additional_kwargs.handoff_destination;
|
|
551
|
+
destinationAgent = typeof handoffDest === 'string' ? handoffDest : null;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/** Check if this transfer targets our agent */
|
|
555
|
+
if (destinationAgent === agentId) {
|
|
556
|
+
toolMessage = candidateMsg;
|
|
557
|
+
toolMessageIndex = i;
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/** No transfer targeting this agent found */
|
|
563
|
+
if (toolMessage === null || toolMessageIndex < 0) return null;
|
|
564
|
+
|
|
565
|
+
/** Extract instructions from the ToolMessage content */
|
|
566
|
+
const contentStr =
|
|
567
|
+
typeof toolMessage.content === 'string'
|
|
568
|
+
? toolMessage.content
|
|
569
|
+
: JSON.stringify(toolMessage.content);
|
|
570
|
+
|
|
571
|
+
const instructionsMatch = contentStr.match(HANDOFF_INSTRUCTIONS_PATTERN);
|
|
572
|
+
const instructions = instructionsMatch?.[1]?.trim() ?? null;
|
|
573
|
+
|
|
574
|
+
/** Extract source agent name from additional_kwargs */
|
|
575
|
+
const handoffSourceName = toolMessage.additional_kwargs.handoff_source_name;
|
|
576
|
+
const sourceAgentName =
|
|
577
|
+
typeof handoffSourceName === 'string' ? handoffSourceName : null;
|
|
578
|
+
|
|
579
|
+
/** Extract parallel siblings (set by ToolNode for parallel handoffs) */
|
|
580
|
+
const rawSiblings = toolMessage.additional_kwargs.handoff_parallel_siblings;
|
|
581
|
+
const siblingIds: string[] = Array.isArray(rawSiblings)
|
|
582
|
+
? rawSiblings.filter((s): s is string => typeof s === 'string')
|
|
583
|
+
: [];
|
|
584
|
+
/** Convert IDs to display names */
|
|
585
|
+
const parallelSiblings = siblingIds.map((id) => {
|
|
586
|
+
const ctx = this.agentContexts.get(id);
|
|
587
|
+
return ctx?.name ?? id;
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
/** Get the tool_call_id to find and filter the AI message's tool call */
|
|
591
|
+
const toolCallId = toolMessage.tool_call_id;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Collect all transfer tool_call_ids to filter out.
|
|
595
|
+
* For parallel handoffs, we filter ALL transfer messages (not just ours)
|
|
596
|
+
* to give the receiving agent a clean context without handoff noise.
|
|
597
|
+
*/
|
|
598
|
+
const transferToolCallIds = new Set<string>([toolCallId]);
|
|
599
|
+
for (const msg of messages) {
|
|
600
|
+
if (msg.getType() !== 'tool') continue;
|
|
601
|
+
const tm = msg as ToolMessage;
|
|
602
|
+
const tName = tm.name;
|
|
603
|
+
if (typeof tName !== 'string') continue;
|
|
604
|
+
if (
|
|
605
|
+
tName.startsWith(Constants.LC_TRANSFER_TO_) ||
|
|
606
|
+
tName === 'conditional_transfer'
|
|
607
|
+
) {
|
|
608
|
+
transferToolCallIds.add(tm.tool_call_id);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** Filter out all transfer messages */
|
|
613
|
+
const filteredMessages: BaseMessage[] = [];
|
|
614
|
+
|
|
615
|
+
for (let i = 0; i < messages.length; i++) {
|
|
616
|
+
const msg = messages[i];
|
|
617
|
+
const msgType = msg.getType();
|
|
618
|
+
|
|
619
|
+
/** Skip transfer ToolMessages */
|
|
620
|
+
if (msgType === 'tool') {
|
|
621
|
+
const tm = msg as ToolMessage;
|
|
622
|
+
if (transferToolCallIds.has(tm.tool_call_id)) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (msgType === 'ai') {
|
|
628
|
+
/** Check if this AI message contains any transfer tool calls */
|
|
629
|
+
const aiMsg = msg as AIMessage | AIMessageChunk;
|
|
630
|
+
const toolCalls = aiMsg.tool_calls;
|
|
631
|
+
|
|
632
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
633
|
+
/** Filter out all transfer tool calls */
|
|
634
|
+
const remainingToolCalls = toolCalls.filter(
|
|
635
|
+
(tc) => tc.id == null || !transferToolCallIds.has(tc.id)
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
const hasTransferCalls = remainingToolCalls.length < toolCalls.length;
|
|
639
|
+
|
|
640
|
+
if (hasTransferCalls) {
|
|
641
|
+
if (
|
|
642
|
+
remainingToolCalls.length > 0 ||
|
|
643
|
+
(typeof aiMsg.content === 'string' && aiMsg.content.trim())
|
|
644
|
+
) {
|
|
645
|
+
/** Keep the message but without transfer tool calls */
|
|
646
|
+
const filteredAiMsg = new AIMessage({
|
|
647
|
+
content: aiMsg.content,
|
|
648
|
+
tool_calls: remainingToolCalls,
|
|
649
|
+
id: aiMsg.id,
|
|
650
|
+
});
|
|
651
|
+
filteredMessages.push(filteredAiMsg);
|
|
652
|
+
}
|
|
653
|
+
/** If no remaining content or tool calls, skip this message entirely */
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/** Keep all other messages */
|
|
660
|
+
filteredMessages.push(msg);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return {
|
|
664
|
+
filteredMessages,
|
|
665
|
+
instructions,
|
|
666
|
+
sourceAgentName,
|
|
667
|
+
parallelSiblings,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
410
671
|
/**
|
|
411
672
|
* Create the multi-agent workflow with dynamic handoffs
|
|
412
673
|
*/
|
|
@@ -474,26 +735,102 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
474
735
|
/** Agent subgraph (includes agent + tools) */
|
|
475
736
|
const agentSubgraph = this.createAgentSubgraph(agentId);
|
|
476
737
|
|
|
477
|
-
/** Wrapper function that handles agentMessages channel and conditional routing */
|
|
738
|
+
/** Wrapper function that handles agentMessages channel, handoff reception, and conditional routing */
|
|
478
739
|
const agentWrapper = async (
|
|
479
740
|
state: t.MultiAgentGraphState
|
|
480
741
|
): Promise<t.MultiAgentGraphState | Command> => {
|
|
481
742
|
let result: t.MultiAgentGraphState;
|
|
482
743
|
|
|
483
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Check if this agent is receiving a handoff.
|
|
746
|
+
* If so, filter out the transfer messages and inject instructions as preamble.
|
|
747
|
+
* This prevents the receiving agent from seeing the transfer as "completed work"
|
|
748
|
+
* and prematurely producing an end token.
|
|
749
|
+
*/
|
|
750
|
+
const handoffContext = this.processHandoffReception(
|
|
751
|
+
state.messages,
|
|
752
|
+
agentId
|
|
753
|
+
);
|
|
754
|
+
|
|
755
|
+
if (handoffContext !== null) {
|
|
756
|
+
const {
|
|
757
|
+
filteredMessages,
|
|
758
|
+
instructions,
|
|
759
|
+
sourceAgentName,
|
|
760
|
+
parallelSiblings,
|
|
761
|
+
} = handoffContext;
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Set handoff context on the receiving agent.
|
|
765
|
+
* Uses pre-computed graph position for depth and parallel info.
|
|
766
|
+
*/
|
|
767
|
+
const agentContext = this.agentContexts.get(agentId);
|
|
768
|
+
if (
|
|
769
|
+
agentContext &&
|
|
770
|
+
sourceAgentName != null &&
|
|
771
|
+
sourceAgentName !== ''
|
|
772
|
+
) {
|
|
773
|
+
agentContext.setHandoffContext(sourceAgentName, parallelSiblings);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/** Build messages for the receiving agent */
|
|
777
|
+
let messagesForAgent = filteredMessages;
|
|
778
|
+
|
|
779
|
+
/** If there are instructions, inject them as a HumanMessage to ground the agent */
|
|
780
|
+
const hasInstructions = instructions !== null && instructions !== '';
|
|
781
|
+
if (hasInstructions) {
|
|
782
|
+
messagesForAgent = [
|
|
783
|
+
...filteredMessages,
|
|
784
|
+
new HumanMessage(instructions),
|
|
785
|
+
];
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/** Update token map if we have a token counter */
|
|
789
|
+
if (agentContext?.tokenCounter && hasInstructions) {
|
|
790
|
+
const freshTokenMap: Record<string, number> = {};
|
|
791
|
+
for (
|
|
792
|
+
let i = 0;
|
|
793
|
+
i < Math.min(filteredMessages.length, this.startIndex);
|
|
794
|
+
i++
|
|
795
|
+
) {
|
|
796
|
+
const tokenCount = agentContext.indexTokenCountMap[i];
|
|
797
|
+
if (tokenCount !== undefined) {
|
|
798
|
+
freshTokenMap[i] = tokenCount;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/** Add tokens for the instructions message */
|
|
802
|
+
const instructionsMsg = new HumanMessage(instructions);
|
|
803
|
+
freshTokenMap[messagesForAgent.length - 1] =
|
|
804
|
+
agentContext.tokenCounter(instructionsMsg);
|
|
805
|
+
agentContext.updateTokenMapWithInstructions(freshTokenMap);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const transformedState: t.MultiAgentGraphState = {
|
|
809
|
+
...state,
|
|
810
|
+
messages: messagesForAgent,
|
|
811
|
+
};
|
|
812
|
+
result = await agentSubgraph.invoke(transformedState);
|
|
813
|
+
result = {
|
|
814
|
+
...result,
|
|
815
|
+
agentMessages: [],
|
|
816
|
+
};
|
|
817
|
+
} else if (
|
|
818
|
+
state.agentMessages != null &&
|
|
819
|
+
state.agentMessages.length > 0
|
|
820
|
+
) {
|
|
484
821
|
/**
|
|
485
822
|
* When using agentMessages (excludeResults=true), we need to update
|
|
486
823
|
* the token map to account for the new prompt message
|
|
487
824
|
*/
|
|
488
825
|
const agentContext = this.agentContexts.get(agentId);
|
|
489
826
|
if (agentContext && agentContext.tokenCounter) {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
827
|
+
/** The agentMessages contains:
|
|
828
|
+
* 1. Filtered messages (0 to startIndex) - already have token counts
|
|
829
|
+
* 2. New prompt message - needs token counting
|
|
830
|
+
*/
|
|
494
831
|
const freshTokenMap: Record<string, number> = {};
|
|
495
832
|
|
|
496
|
-
|
|
833
|
+
/** Copy existing token counts for filtered messages (0 to startIndex) */
|
|
497
834
|
for (let i = 0; i < this.startIndex; i++) {
|
|
498
835
|
const tokenCount = agentContext.indexTokenCountMap[i];
|
|
499
836
|
if (tokenCount !== undefined) {
|
|
@@ -501,7 +838,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
501
838
|
}
|
|
502
839
|
}
|
|
503
840
|
|
|
504
|
-
|
|
841
|
+
/** Calculate tokens only for the new prompt message (last message) */
|
|
505
842
|
const promptMessageIndex = state.agentMessages.length - 1;
|
|
506
843
|
if (promptMessageIndex >= this.startIndex) {
|
|
507
844
|
const promptMessage = state.agentMessages[promptMessageIndex];
|
|
@@ -509,7 +846,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
509
846
|
agentContext.tokenCounter(promptMessage);
|
|
510
847
|
}
|
|
511
848
|
|
|
512
|
-
|
|
849
|
+
/** Update the agent's token map with instructions added */
|
|
513
850
|
agentContext.updateTokenMapWithInstructions(freshTokenMap);
|
|
514
851
|
}
|
|
515
852
|
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export * from './tools/Calculator';
|
|
|
13
13
|
export * from './tools/CodeExecutor';
|
|
14
14
|
export * from './tools/BrowserTools';
|
|
15
15
|
export * from './tools/ProgrammaticToolCalling';
|
|
16
|
-
export * from './tools/
|
|
16
|
+
export * from './tools/ToolSearch';
|
|
17
17
|
export * from './tools/handlers';
|
|
18
18
|
export * from './tools/search';
|
|
19
19
|
|