@librechat/agents 3.0.64 → 3.0.65
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 +66 -10
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +29 -7
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +66 -10
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +29 -7
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +29 -1
- package/dist/types/graphs/MultiAgentGraph.d.ts +5 -2
- package/dist/types/types/graph.d.ts +2 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +79 -8
- package/src/graphs/MultiAgentGraph.ts +46 -8
- package/src/scripts/test-parallel-handoffs.ts +4 -6
- package/src/types/graph.ts +2 -0
|
@@ -27,6 +27,7 @@ export class AgentContext {
|
|
|
27
27
|
): AgentContext {
|
|
28
28
|
const {
|
|
29
29
|
agentId,
|
|
30
|
+
name,
|
|
30
31
|
provider,
|
|
31
32
|
clientOptions,
|
|
32
33
|
tools,
|
|
@@ -43,6 +44,7 @@ export class AgentContext {
|
|
|
43
44
|
|
|
44
45
|
const agentContext = new AgentContext({
|
|
45
46
|
agentId,
|
|
47
|
+
name: name ?? agentId,
|
|
46
48
|
provider,
|
|
47
49
|
clientOptions,
|
|
48
50
|
maxContextTokens,
|
|
@@ -85,6 +87,8 @@ export class AgentContext {
|
|
|
85
87
|
|
|
86
88
|
/** Agent identifier */
|
|
87
89
|
agentId: string;
|
|
90
|
+
/** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
|
|
91
|
+
name?: string;
|
|
88
92
|
/** Provider for this specific agent */
|
|
89
93
|
provider: Providers;
|
|
90
94
|
/** Client options for this agent */
|
|
@@ -145,9 +149,17 @@ export class AgentContext {
|
|
|
145
149
|
tokenCalculationPromise?: Promise<void>;
|
|
146
150
|
/** Format content blocks as strings (for legacy compatibility) */
|
|
147
151
|
useLegacyContent: boolean = false;
|
|
152
|
+
/**
|
|
153
|
+
* Handoff context when this agent receives control via handoff.
|
|
154
|
+
* Contains source agent name for system message identity context.
|
|
155
|
+
*/
|
|
156
|
+
handoffContext?: {
|
|
157
|
+
sourceAgentName: string;
|
|
158
|
+
};
|
|
148
159
|
|
|
149
160
|
constructor({
|
|
150
161
|
agentId,
|
|
162
|
+
name,
|
|
151
163
|
provider,
|
|
152
164
|
clientOptions,
|
|
153
165
|
maxContextTokens,
|
|
@@ -164,6 +176,7 @@ export class AgentContext {
|
|
|
164
176
|
useLegacyContent,
|
|
165
177
|
}: {
|
|
166
178
|
agentId: string;
|
|
179
|
+
name?: string;
|
|
167
180
|
provider: Providers;
|
|
168
181
|
clientOptions?: t.ClientOptions;
|
|
169
182
|
maxContextTokens?: number;
|
|
@@ -180,6 +193,7 @@ export class AgentContext {
|
|
|
180
193
|
useLegacyContent?: boolean;
|
|
181
194
|
}) {
|
|
182
195
|
this.agentId = agentId;
|
|
196
|
+
this.name = name;
|
|
183
197
|
this.provider = provider;
|
|
184
198
|
this.clientOptions = clientOptions;
|
|
185
199
|
this.maxContextTokens = maxContextTokens;
|
|
@@ -293,27 +307,61 @@ export class AgentContext {
|
|
|
293
307
|
|
|
294
308
|
/**
|
|
295
309
|
* Builds the raw instructions string (without creating SystemMessage).
|
|
310
|
+
* Includes agent identity preamble and handoff context when available.
|
|
296
311
|
*/
|
|
297
312
|
private buildInstructionsString(): string {
|
|
298
|
-
|
|
313
|
+
const parts: string[] = [];
|
|
314
|
+
|
|
315
|
+
/** Build agent identity and handoff context preamble */
|
|
316
|
+
const identityPreamble = this.buildIdentityPreamble();
|
|
317
|
+
if (identityPreamble) {
|
|
318
|
+
parts.push(identityPreamble);
|
|
319
|
+
}
|
|
299
320
|
|
|
321
|
+
/** Add main instructions */
|
|
322
|
+
if (this.instructions != null && this.instructions !== '') {
|
|
323
|
+
parts.push(this.instructions);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Add additional instructions */
|
|
300
327
|
if (
|
|
301
328
|
this.additionalInstructions != null &&
|
|
302
329
|
this.additionalInstructions !== ''
|
|
303
330
|
) {
|
|
304
|
-
|
|
305
|
-
? `${result}\n\n${this.additionalInstructions}`
|
|
306
|
-
: this.additionalInstructions;
|
|
331
|
+
parts.push(this.additionalInstructions);
|
|
307
332
|
}
|
|
308
333
|
|
|
334
|
+
/** Add programmatic tools documentation */
|
|
309
335
|
const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
|
|
310
336
|
if (programmaticToolsDoc) {
|
|
311
|
-
|
|
312
|
-
? `${result}${programmaticToolsDoc}`
|
|
313
|
-
: programmaticToolsDoc;
|
|
337
|
+
parts.push(programmaticToolsDoc);
|
|
314
338
|
}
|
|
315
339
|
|
|
316
|
-
return
|
|
340
|
+
return parts.join('\n\n');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Builds the agent identity preamble including handoff context if present.
|
|
345
|
+
* This helps the agent understand its role in the multi-agent workflow.
|
|
346
|
+
*/
|
|
347
|
+
private buildIdentityPreamble(): string {
|
|
348
|
+
/** Only include preamble if we have handoff context (indicates multi-agent workflow) */
|
|
349
|
+
if (!this.handoffContext) return '';
|
|
350
|
+
|
|
351
|
+
/** Use name (falls back to agentId if not provided) */
|
|
352
|
+
const displayName = this.name ?? this.agentId;
|
|
353
|
+
|
|
354
|
+
const lines: string[] = [];
|
|
355
|
+
lines.push('## Agent Context');
|
|
356
|
+
lines.push(`You are the "${displayName}" agent.`);
|
|
357
|
+
|
|
358
|
+
if (this.handoffContext.sourceAgentName) {
|
|
359
|
+
lines.push(
|
|
360
|
+
`Control was transferred to you from the "${this.handoffContext.sourceAgentName}" agent.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return lines.join('\n');
|
|
317
365
|
}
|
|
318
366
|
|
|
319
367
|
/**
|
|
@@ -393,6 +441,7 @@ export class AgentContext {
|
|
|
393
441
|
this.tokenTypeSwitch = undefined;
|
|
394
442
|
this.currentTokenType = ContentTypes.TEXT;
|
|
395
443
|
this.discoveredToolNames.clear();
|
|
444
|
+
this.handoffContext = undefined;
|
|
396
445
|
}
|
|
397
446
|
|
|
398
447
|
/**
|
|
@@ -472,6 +521,28 @@ export class AgentContext {
|
|
|
472
521
|
return registry;
|
|
473
522
|
}
|
|
474
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Sets the handoff context for this agent.
|
|
526
|
+
* Call this when the agent receives control via handoff from another agent.
|
|
527
|
+
* Marks system runnable as stale to include handoff context in system message.
|
|
528
|
+
* @param sourceAgentName - The name of the agent that handed off to this agent
|
|
529
|
+
*/
|
|
530
|
+
setHandoffContext(sourceAgentName: string): void {
|
|
531
|
+
this.handoffContext = { sourceAgentName };
|
|
532
|
+
this.systemRunnableStale = true;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Clears any handoff context.
|
|
537
|
+
* Call this when resetting the agent or when handoff context is no longer relevant.
|
|
538
|
+
*/
|
|
539
|
+
clearHandoffContext(): void {
|
|
540
|
+
if (this.handoffContext) {
|
|
541
|
+
this.handoffContext = undefined;
|
|
542
|
+
this.systemRunnableStale = true;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
475
546
|
/**
|
|
476
547
|
* Marks tools as discovered via tool search.
|
|
477
548
|
* Discovered tools will be included in the next model binding.
|
|
@@ -249,8 +249,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
249
249
|
|
|
250
250
|
// Create handoff tools for this agent's outgoing edges
|
|
251
251
|
const handoffTools: t.GenericTool[] = [];
|
|
252
|
+
const sourceAgentName = agentContext.name ?? agentId;
|
|
252
253
|
for (const edge of edges) {
|
|
253
|
-
handoffTools.push(
|
|
254
|
+
handoffTools.push(
|
|
255
|
+
...this.createHandoffToolsForEdge(edge, agentId, sourceAgentName)
|
|
256
|
+
);
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
// Add handoff tools to the agent's existing tools
|
|
@@ -263,8 +266,15 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
263
266
|
|
|
264
267
|
/**
|
|
265
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
|
|
266
272
|
*/
|
|
267
|
-
private createHandoffToolsForEdge(
|
|
273
|
+
private createHandoffToolsForEdge(
|
|
274
|
+
edge: t.GraphEdge,
|
|
275
|
+
sourceAgentId: string,
|
|
276
|
+
sourceAgentName: string
|
|
277
|
+
): t.GenericTool[] {
|
|
268
278
|
const tools: t.GenericTool[] = [];
|
|
269
279
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
270
280
|
|
|
@@ -319,6 +329,8 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
319
329
|
additional_kwargs: {
|
|
320
330
|
/** Store destination for programmatic access in handoff detection */
|
|
321
331
|
handoff_destination: destination,
|
|
332
|
+
/** Store source agent name for receiving agent to know who handed off */
|
|
333
|
+
handoff_source_name: sourceAgentName,
|
|
322
334
|
},
|
|
323
335
|
});
|
|
324
336
|
|
|
@@ -377,6 +389,10 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
377
389
|
content,
|
|
378
390
|
name: toolName,
|
|
379
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
|
+
},
|
|
380
396
|
});
|
|
381
397
|
|
|
382
398
|
const state = getCurrentTaskInput() as t.BaseGraphState;
|
|
@@ -482,19 +498,23 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
482
498
|
/**
|
|
483
499
|
* Detects if the current agent is receiving a handoff and processes the messages accordingly.
|
|
484
500
|
* Returns filtered messages with the transfer tool call/message removed, plus any instructions
|
|
485
|
-
* extracted from the transfer
|
|
501
|
+
* and source agent information extracted from the transfer.
|
|
486
502
|
*
|
|
487
503
|
* Supports both single handoffs (last message is the transfer) and parallel handoffs
|
|
488
504
|
* (multiple transfer ToolMessages, need to find the one targeting this agent).
|
|
489
505
|
*
|
|
490
506
|
* @param messages - Current state messages
|
|
491
507
|
* @param agentId - The agent ID to check for handoff reception
|
|
492
|
-
* @returns Object with filtered messages
|
|
508
|
+
* @returns Object with filtered messages, extracted instructions, and source agent, or null if not a handoff
|
|
493
509
|
*/
|
|
494
510
|
private processHandoffReception(
|
|
495
511
|
messages: BaseMessage[],
|
|
496
512
|
agentId: string
|
|
497
|
-
): {
|
|
513
|
+
): {
|
|
514
|
+
filteredMessages: BaseMessage[];
|
|
515
|
+
instructions: string | null;
|
|
516
|
+
sourceAgentName: string | null;
|
|
517
|
+
} | null {
|
|
498
518
|
if (messages.length === 0) return null;
|
|
499
519
|
|
|
500
520
|
/**
|
|
@@ -550,6 +570,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
550
570
|
const instructionsMatch = contentStr.match(HANDOFF_INSTRUCTIONS_PATTERN);
|
|
551
571
|
const instructions = instructionsMatch?.[1]?.trim() ?? null;
|
|
552
572
|
|
|
573
|
+
/** Extract source agent name from additional_kwargs */
|
|
574
|
+
const handoffSourceName = toolMessage.additional_kwargs.handoff_source_name;
|
|
575
|
+
const sourceAgentName =
|
|
576
|
+
typeof handoffSourceName === 'string' ? handoffSourceName : null;
|
|
577
|
+
|
|
553
578
|
/** Get the tool_call_id to find and filter the AI message's tool call */
|
|
554
579
|
const toolCallId = toolMessage.tool_call_id;
|
|
555
580
|
|
|
@@ -623,7 +648,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
623
648
|
filteredMessages.push(msg);
|
|
624
649
|
}
|
|
625
650
|
|
|
626
|
-
return { filteredMessages, instructions };
|
|
651
|
+
return { filteredMessages, instructions, sourceAgentName };
|
|
627
652
|
}
|
|
628
653
|
|
|
629
654
|
/**
|
|
@@ -711,7 +736,21 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
711
736
|
);
|
|
712
737
|
|
|
713
738
|
if (handoffContext !== null) {
|
|
714
|
-
const { filteredMessages, instructions } =
|
|
739
|
+
const { filteredMessages, instructions, sourceAgentName } =
|
|
740
|
+
handoffContext;
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Set handoff context on the receiving agent.
|
|
744
|
+
* This updates the system message to include agent identity info.
|
|
745
|
+
*/
|
|
746
|
+
const agentContext = this.agentContexts.get(agentId);
|
|
747
|
+
if (
|
|
748
|
+
agentContext &&
|
|
749
|
+
sourceAgentName != null &&
|
|
750
|
+
sourceAgentName !== ''
|
|
751
|
+
) {
|
|
752
|
+
agentContext.setHandoffContext(sourceAgentName);
|
|
753
|
+
}
|
|
715
754
|
|
|
716
755
|
/** Build messages for the receiving agent */
|
|
717
756
|
let messagesForAgent = filteredMessages;
|
|
@@ -726,7 +765,6 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
726
765
|
}
|
|
727
766
|
|
|
728
767
|
/** Update token map if we have a token counter */
|
|
729
|
-
const agentContext = this.agentContexts.get(agentId);
|
|
730
768
|
if (agentContext?.tokenCounter && hasInstructions) {
|
|
731
769
|
const freshTokenMap: Record<string, number> = {};
|
|
732
770
|
for (
|
|
@@ -58,9 +58,8 @@ When delegating, provide clear instructions to each agent about what they should
|
|
|
58
58
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
59
59
|
},
|
|
60
60
|
instructions: `You are a RESEARCHER. When you receive a task:
|
|
61
|
-
1.
|
|
62
|
-
2.
|
|
63
|
-
3. Start your response with "📚 RESEARCH FINDINGS:"`,
|
|
61
|
+
1. Provide concise research findings (100-150 words)
|
|
62
|
+
2. Start your response with "📚 RESEARCH FINDINGS:"`,
|
|
64
63
|
},
|
|
65
64
|
{
|
|
66
65
|
agentId: 'writer',
|
|
@@ -70,9 +69,8 @@ When delegating, provide clear instructions to each agent about what they should
|
|
|
70
69
|
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
71
70
|
},
|
|
72
71
|
instructions: `You are a WRITER. When you receive a task:
|
|
73
|
-
1.
|
|
74
|
-
2.
|
|
75
|
-
3. Start your response with "✍️ WRITTEN CONTENT:"`,
|
|
72
|
+
1. Provide creative content (100-150 words)
|
|
73
|
+
2. Start your response with "✍️ WRITTEN CONTENT:"`,
|
|
76
74
|
},
|
|
77
75
|
];
|
|
78
76
|
|
package/src/types/graph.ts
CHANGED
|
@@ -357,6 +357,8 @@ export type MultiAgentGraphInput = StandardGraphInput & {
|
|
|
357
357
|
|
|
358
358
|
export interface AgentInputs {
|
|
359
359
|
agentId: string;
|
|
360
|
+
/** Human-readable name for the agent (used in handoff context). Defaults to agentId if not provided. */
|
|
361
|
+
name?: string;
|
|
360
362
|
toolEnd?: boolean;
|
|
361
363
|
toolMap?: ToolMap;
|
|
362
364
|
tools?: GraphTools;
|